xref: /illumos-gate/usr/src/uts/common/fs/zfs/zfs_ioctl.c (revision 0a586cea3ceec7e5e50e7e54c745082a7a333ac2)
1fa9e4066Sahrens /*
2fa9e4066Sahrens  * CDDL HEADER START
3fa9e4066Sahrens  *
4fa9e4066Sahrens  * The contents of this file are subject to the terms of the
5441d80aaSlling  * Common Development and Distribution License (the "License").
6441d80aaSlling  * You may not use this file except in compliance with the License.
7fa9e4066Sahrens  *
8fa9e4066Sahrens  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9fa9e4066Sahrens  * or http://www.opensolaris.org/os/licensing.
10fa9e4066Sahrens  * See the License for the specific language governing permissions
11fa9e4066Sahrens  * and limitations under the License.
12fa9e4066Sahrens  *
13fa9e4066Sahrens  * When distributing Covered Code, include this CDDL HEADER in each
14fa9e4066Sahrens  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15fa9e4066Sahrens  * If applicable, add the following below this CDDL HEADER, with the
16fa9e4066Sahrens  * fields enclosed by brackets "[]" replaced with your own identifying
17fa9e4066Sahrens  * information: Portions Copyright [yyyy] [name of copyright owner]
18fa9e4066Sahrens  *
19fa9e4066Sahrens  * CDDL HEADER END
20fa9e4066Sahrens  */
21fa9e4066Sahrens /*
221195e687SMark J Musante  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
23fa9e4066Sahrens  * Use is subject to license terms.
24fa9e4066Sahrens  */
25fa9e4066Sahrens 
26fa9e4066Sahrens #include <sys/types.h>
27fa9e4066Sahrens #include <sys/param.h>
28fa9e4066Sahrens #include <sys/errno.h>
29fa9e4066Sahrens #include <sys/uio.h>
30fa9e4066Sahrens #include <sys/buf.h>
31fa9e4066Sahrens #include <sys/modctl.h>
32fa9e4066Sahrens #include <sys/open.h>
33fa9e4066Sahrens #include <sys/file.h>
34fa9e4066Sahrens #include <sys/kmem.h>
35fa9e4066Sahrens #include <sys/conf.h>
36fa9e4066Sahrens #include <sys/cmn_err.h>
37fa9e4066Sahrens #include <sys/stat.h>
38fa9e4066Sahrens #include <sys/zfs_ioctl.h>
394201a95eSRic Aleshire #include <sys/zfs_vfsops.h>
40da6c28aaSamw #include <sys/zfs_znode.h>
41fa9e4066Sahrens #include <sys/zap.h>
42fa9e4066Sahrens #include <sys/spa.h>
43b1b8ab34Slling #include <sys/spa_impl.h>
44fa9e4066Sahrens #include <sys/vdev.h>
454201a95eSRic Aleshire #include <sys/priv_impl.h>
46fa9e4066Sahrens #include <sys/dmu.h>
47fa9e4066Sahrens #include <sys/dsl_dir.h>
48fa9e4066Sahrens #include <sys/dsl_dataset.h>
49fa9e4066Sahrens #include <sys/dsl_prop.h>
50ecd6cf80Smarks #include <sys/dsl_deleg.h>
51ecd6cf80Smarks #include <sys/dmu_objset.h>
52fa9e4066Sahrens #include <sys/ddi.h>
53fa9e4066Sahrens #include <sys/sunddi.h>
54fa9e4066Sahrens #include <sys/sunldi.h>
55fa9e4066Sahrens #include <sys/policy.h>
56fa9e4066Sahrens #include <sys/zone.h>
57fa9e4066Sahrens #include <sys/nvpair.h>
58fa9e4066Sahrens #include <sys/pathname.h>
59fa9e4066Sahrens #include <sys/mount.h>
60fa9e4066Sahrens #include <sys/sdt.h>
61fa9e4066Sahrens #include <sys/fs/zfs.h>
62fa9e4066Sahrens #include <sys/zfs_ctldir.h>
63da6c28aaSamw #include <sys/zfs_dir.h>
64a2eea2e1Sahrens #include <sys/zvol.h>
65ecd6cf80Smarks #include <sharefs/share.h>
66f18faf3fSek #include <sys/dmu_objset.h>
67fa9e4066Sahrens 
68fa9e4066Sahrens #include "zfs_namecheck.h"
69e9dbad6fSeschrock #include "zfs_prop.h"
70ecd6cf80Smarks #include "zfs_deleg.h"
71*0a586ceaSMark Shellenbaum #include "zfs_comutil.h"
72fa9e4066Sahrens 
73fa9e4066Sahrens extern struct modlfs zfs_modlfs;
74fa9e4066Sahrens 
75fa9e4066Sahrens extern void zfs_init(void);
76fa9e4066Sahrens extern void zfs_fini(void);
77fa9e4066Sahrens 
78fa9e4066Sahrens ldi_ident_t zfs_li = NULL;
79fa9e4066Sahrens dev_info_t *zfs_dip;
80fa9e4066Sahrens 
81fa9e4066Sahrens typedef int zfs_ioc_func_t(zfs_cmd_t *);
82ecd6cf80Smarks typedef int zfs_secpolicy_func_t(zfs_cmd_t *, cred_t *);
83fa9e4066Sahrens 
8454d692b7SGeorge Wilson typedef enum {
8554d692b7SGeorge Wilson 	NO_NAME,
8654d692b7SGeorge Wilson 	POOL_NAME,
8754d692b7SGeorge Wilson 	DATASET_NAME
8854d692b7SGeorge Wilson } zfs_ioc_namecheck_t;
8954d692b7SGeorge Wilson 
90fa9e4066Sahrens typedef struct zfs_ioc_vec {
91fa9e4066Sahrens 	zfs_ioc_func_t		*zvec_func;
92fa9e4066Sahrens 	zfs_secpolicy_func_t	*zvec_secpolicy;
9354d692b7SGeorge Wilson 	zfs_ioc_namecheck_t	zvec_namecheck;
94ecd6cf80Smarks 	boolean_t		zvec_his_log;
9554d692b7SGeorge Wilson 	boolean_t		zvec_pool_check;
96fa9e4066Sahrens } zfs_ioc_vec_t;
97fa9e4066Sahrens 
9814843421SMatthew Ahrens /* This array is indexed by zfs_userquota_prop_t */
9914843421SMatthew Ahrens static const char *userquota_perms[] = {
10014843421SMatthew Ahrens 	ZFS_DELEG_PERM_USERUSED,
10114843421SMatthew Ahrens 	ZFS_DELEG_PERM_USERQUOTA,
10214843421SMatthew Ahrens 	ZFS_DELEG_PERM_GROUPUSED,
10314843421SMatthew Ahrens 	ZFS_DELEG_PERM_GROUPQUOTA,
10414843421SMatthew Ahrens };
10514843421SMatthew Ahrens 
10614843421SMatthew Ahrens static int zfs_ioc_userspace_upgrade(zfs_cmd_t *zc);
10792241e0bSTom Erickson static int zfs_check_settable(const char *name, nvpair_t *property,
10892241e0bSTom Erickson     cred_t *cr);
10992241e0bSTom Erickson static int zfs_check_clearable(char *dataset, nvlist_t *props,
11092241e0bSTom Erickson     nvlist_t **errors);
1110a48a24eStimh static int zfs_fill_zplprops_root(uint64_t, nvlist_t *, nvlist_t *,
1120a48a24eStimh     boolean_t *);
11392241e0bSTom Erickson int zfs_set_prop_nvlist(const char *, zprop_source_t, nvlist_t *, nvlist_t **);
1140a48a24eStimh 
115fa9e4066Sahrens /* _NOTE(PRINTFLIKE(4)) - this is printf-like, but lint is too whiney */
116fa9e4066Sahrens void
117fa9e4066Sahrens __dprintf(const char *file, const char *func, int line, const char *fmt, ...)
118fa9e4066Sahrens {
119fa9e4066Sahrens 	const char *newfile;
120fa9e4066Sahrens 	char buf[256];
121fa9e4066Sahrens 	va_list adx;
122fa9e4066Sahrens 
123fa9e4066Sahrens 	/*
124fa9e4066Sahrens 	 * Get rid of annoying "../common/" prefix to filename.
125fa9e4066Sahrens 	 */
126fa9e4066Sahrens 	newfile = strrchr(file, '/');
127fa9e4066Sahrens 	if (newfile != NULL) {
128fa9e4066Sahrens 		newfile = newfile + 1; /* Get rid of leading / */
129fa9e4066Sahrens 	} else {
130fa9e4066Sahrens 		newfile = file;
131fa9e4066Sahrens 	}
132fa9e4066Sahrens 
133fa9e4066Sahrens 	va_start(adx, fmt);
134fa9e4066Sahrens 	(void) vsnprintf(buf, sizeof (buf), fmt, adx);
135fa9e4066Sahrens 	va_end(adx);
136fa9e4066Sahrens 
137fa9e4066Sahrens 	/*
138fa9e4066Sahrens 	 * To get this data, use the zfs-dprintf probe as so:
139fa9e4066Sahrens 	 * dtrace -q -n 'zfs-dprintf \
140fa9e4066Sahrens 	 *	/stringof(arg0) == "dbuf.c"/ \
141fa9e4066Sahrens 	 *	{printf("%s: %s", stringof(arg1), stringof(arg3))}'
142fa9e4066Sahrens 	 * arg0 = file name
143fa9e4066Sahrens 	 * arg1 = function name
144fa9e4066Sahrens 	 * arg2 = line number
145fa9e4066Sahrens 	 * arg3 = message
146fa9e4066Sahrens 	 */
147fa9e4066Sahrens 	DTRACE_PROBE4(zfs__dprintf,
148fa9e4066Sahrens 	    char *, newfile, char *, func, int, line, char *, buf);
149fa9e4066Sahrens }
150fa9e4066Sahrens 
151ecd6cf80Smarks static void
152228975ccSek history_str_free(char *buf)
153228975ccSek {
154228975ccSek 	kmem_free(buf, HIS_MAX_RECORD_LEN);
155228975ccSek }
156228975ccSek 
157228975ccSek static char *
158228975ccSek history_str_get(zfs_cmd_t *zc)
159ecd6cf80Smarks {
16040feaa91Sahrens 	char *buf;
161ecd6cf80Smarks 
162ecd6cf80Smarks 	if (zc->zc_history == NULL)
163228975ccSek 		return (NULL);
164e7437265Sahrens 
165ecd6cf80Smarks 	buf = kmem_alloc(HIS_MAX_RECORD_LEN, KM_SLEEP);
166ecd6cf80Smarks 	if (copyinstr((void *)(uintptr_t)zc->zc_history,
167ecd6cf80Smarks 	    buf, HIS_MAX_RECORD_LEN, NULL) != 0) {
168228975ccSek 		history_str_free(buf);
169228975ccSek 		return (NULL);
170ecd6cf80Smarks 	}
171ecd6cf80Smarks 
172ecd6cf80Smarks 	buf[HIS_MAX_RECORD_LEN -1] = '\0';
173ecd6cf80Smarks 
174228975ccSek 	return (buf);
175228975ccSek }
176ecd6cf80Smarks 
17715e6edf1Sgw /*
17815e6edf1Sgw  * Check to see if the named dataset is currently defined as bootable
17915e6edf1Sgw  */
18015e6edf1Sgw static boolean_t
18115e6edf1Sgw zfs_is_bootfs(const char *name)
18215e6edf1Sgw {
183503ad85cSMatthew Ahrens 	objset_t *os;
18415e6edf1Sgw 
185503ad85cSMatthew Ahrens 	if (dmu_objset_hold(name, FTAG, &os) == 0) {
186503ad85cSMatthew Ahrens 		boolean_t ret;
187b24ab676SJeff Bonwick 		ret = (dmu_objset_id(os) == spa_bootfs(dmu_objset_spa(os)));
188503ad85cSMatthew Ahrens 		dmu_objset_rele(os, FTAG);
189503ad85cSMatthew Ahrens 		return (ret);
19015e6edf1Sgw 	}
191503ad85cSMatthew Ahrens 	return (B_FALSE);
19215e6edf1Sgw }
19315e6edf1Sgw 
194c2a93d44Stimh /*
1950a48a24eStimh  * zfs_earlier_version
196c2a93d44Stimh  *
197c2a93d44Stimh  *	Return non-zero if the spa version is less than requested version.
198c2a93d44Stimh  */
199da6c28aaSamw static int
2000a48a24eStimh zfs_earlier_version(const char *name, int version)
201da6c28aaSamw {
202da6c28aaSamw 	spa_t *spa;
203da6c28aaSamw 
204da6c28aaSamw 	if (spa_open(name, &spa, FTAG) == 0) {
205da6c28aaSamw 		if (spa_version(spa) < version) {
206da6c28aaSamw 			spa_close(spa, FTAG);
207da6c28aaSamw 			return (1);
208da6c28aaSamw 		}
209da6c28aaSamw 		spa_close(spa, FTAG);
210da6c28aaSamw 	}
211da6c28aaSamw 	return (0);
212da6c28aaSamw }
213da6c28aaSamw 
2149e6eda55Smarks /*
215745cd3c5Smaybee  * zpl_earlier_version
2169e6eda55Smarks  *
217745cd3c5Smaybee  * Return TRUE if the ZPL version is less than requested version.
2189e6eda55Smarks  */
219745cd3c5Smaybee static boolean_t
220745cd3c5Smaybee zpl_earlier_version(const char *name, int version)
2219e6eda55Smarks {
2229e6eda55Smarks 	objset_t *os;
223745cd3c5Smaybee 	boolean_t rc = B_TRUE;
2249e6eda55Smarks 
225503ad85cSMatthew Ahrens 	if (dmu_objset_hold(name, FTAG, &os) == 0) {
226745cd3c5Smaybee 		uint64_t zplversion;
2279e6eda55Smarks 
228503ad85cSMatthew Ahrens 		if (dmu_objset_type(os) != DMU_OST_ZFS) {
229503ad85cSMatthew Ahrens 			dmu_objset_rele(os, FTAG);
230503ad85cSMatthew Ahrens 			return (B_TRUE);
231503ad85cSMatthew Ahrens 		}
232503ad85cSMatthew Ahrens 		/* XXX reading from non-owned objset */
233745cd3c5Smaybee 		if (zfs_get_zplprop(os, ZFS_PROP_VERSION, &zplversion) == 0)
234745cd3c5Smaybee 			rc = zplversion < version;
235503ad85cSMatthew Ahrens 		dmu_objset_rele(os, FTAG);
2369e6eda55Smarks 	}
2379e6eda55Smarks 	return (rc);
2389e6eda55Smarks }
2399e6eda55Smarks 
240228975ccSek static void
241228975ccSek zfs_log_history(zfs_cmd_t *zc)
242228975ccSek {
243228975ccSek 	spa_t *spa;
244228975ccSek 	char *buf;
245ecd6cf80Smarks 
246228975ccSek 	if ((buf = history_str_get(zc)) == NULL)
247228975ccSek 		return;
248228975ccSek 
249228975ccSek 	if (spa_open(zc->zc_name, &spa, FTAG) == 0) {
250228975ccSek 		if (spa_version(spa) >= SPA_VERSION_ZPOOL_HISTORY)
251228975ccSek 			(void) spa_history_log(spa, buf, LOG_CMD_NORMAL);
252228975ccSek 		spa_close(spa, FTAG);
253228975ccSek 	}
254228975ccSek 	history_str_free(buf);
255ecd6cf80Smarks }
256ecd6cf80Smarks 
257fa9e4066Sahrens /*
258fa9e4066Sahrens  * Policy for top-level read operations (list pools).  Requires no privileges,
259fa9e4066Sahrens  * and can be used in the local zone, as there is no associated dataset.
260fa9e4066Sahrens  */
261fa9e4066Sahrens /* ARGSUSED */
262fa9e4066Sahrens static int
263ecd6cf80Smarks zfs_secpolicy_none(zfs_cmd_t *zc, cred_t *cr)
264fa9e4066Sahrens {
265fa9e4066Sahrens 	return (0);
266fa9e4066Sahrens }
267fa9e4066Sahrens 
268fa9e4066Sahrens /*
269fa9e4066Sahrens  * Policy for dataset read operations (list children, get statistics).  Requires
270fa9e4066Sahrens  * no privileges, but must be visible in the local zone.
271fa9e4066Sahrens  */
272fa9e4066Sahrens /* ARGSUSED */
273fa9e4066Sahrens static int
274ecd6cf80Smarks zfs_secpolicy_read(zfs_cmd_t *zc, cred_t *cr)
275fa9e4066Sahrens {
276fa9e4066Sahrens 	if (INGLOBALZONE(curproc) ||
277ecd6cf80Smarks 	    zone_dataset_visible(zc->zc_name, NULL))
278fa9e4066Sahrens 		return (0);
279fa9e4066Sahrens 
280fa9e4066Sahrens 	return (ENOENT);
281fa9e4066Sahrens }
282fa9e4066Sahrens 
283fa9e4066Sahrens static int
284fa9e4066Sahrens zfs_dozonecheck(const char *dataset, cred_t *cr)
285fa9e4066Sahrens {
286fa9e4066Sahrens 	uint64_t zoned;
287fa9e4066Sahrens 	int writable = 1;
288fa9e4066Sahrens 
289fa9e4066Sahrens 	/*
290fa9e4066Sahrens 	 * The dataset must be visible by this zone -- check this first
291fa9e4066Sahrens 	 * so they don't see EPERM on something they shouldn't know about.
292fa9e4066Sahrens 	 */
293fa9e4066Sahrens 	if (!INGLOBALZONE(curproc) &&
294fa9e4066Sahrens 	    !zone_dataset_visible(dataset, &writable))
295fa9e4066Sahrens 		return (ENOENT);
296fa9e4066Sahrens 
297fa9e4066Sahrens 	if (dsl_prop_get_integer(dataset, "zoned", &zoned, NULL))
298fa9e4066Sahrens 		return (ENOENT);
299fa9e4066Sahrens 
300fa9e4066Sahrens 	if (INGLOBALZONE(curproc)) {
301fa9e4066Sahrens 		/*
302fa9e4066Sahrens 		 * If the fs is zoned, only root can access it from the
303fa9e4066Sahrens 		 * global zone.
304fa9e4066Sahrens 		 */
305fa9e4066Sahrens 		if (secpolicy_zfs(cr) && zoned)
306fa9e4066Sahrens 			return (EPERM);
307fa9e4066Sahrens 	} else {
308fa9e4066Sahrens 		/*
309fa9e4066Sahrens 		 * If we are in a local zone, the 'zoned' property must be set.
310fa9e4066Sahrens 		 */
311fa9e4066Sahrens 		if (!zoned)
312fa9e4066Sahrens 			return (EPERM);
313fa9e4066Sahrens 
314fa9e4066Sahrens 		/* must be writable by this zone */
315fa9e4066Sahrens 		if (!writable)
316fa9e4066Sahrens 			return (EPERM);
317fa9e4066Sahrens 	}
318fa9e4066Sahrens 	return (0);
319fa9e4066Sahrens }
320fa9e4066Sahrens 
321fa9e4066Sahrens int
322ecd6cf80Smarks zfs_secpolicy_write_perms(const char *name, const char *perm, cred_t *cr)
323fa9e4066Sahrens {
324fa9e4066Sahrens 	int error;
325fa9e4066Sahrens 
326ecd6cf80Smarks 	error = zfs_dozonecheck(name, cr);
327ecd6cf80Smarks 	if (error == 0) {
328ecd6cf80Smarks 		error = secpolicy_zfs(cr);
329db870a07Sahrens 		if (error)
330ecd6cf80Smarks 			error = dsl_deleg_access(name, perm, cr);
331ecd6cf80Smarks 	}
332ecd6cf80Smarks 	return (error);
333ecd6cf80Smarks }
334ecd6cf80Smarks 
3354201a95eSRic Aleshire /*
3364201a95eSRic Aleshire  * Policy for setting the security label property.
3374201a95eSRic Aleshire  *
3384201a95eSRic Aleshire  * Returns 0 for success, non-zero for access and other errors.
3394201a95eSRic Aleshire  */
3404201a95eSRic Aleshire static int
34192241e0bSTom Erickson zfs_set_slabel_policy(const char *name, char *strval, cred_t *cr)
3424201a95eSRic Aleshire {
3434201a95eSRic Aleshire 	char		ds_hexsl[MAXNAMELEN];
3444201a95eSRic Aleshire 	bslabel_t	ds_sl, new_sl;
3454201a95eSRic Aleshire 	boolean_t	new_default = FALSE;
3464201a95eSRic Aleshire 	uint64_t	zoned;
3474201a95eSRic Aleshire 	int		needed_priv = -1;
3484201a95eSRic Aleshire 	int		error;
3494201a95eSRic Aleshire 
3504201a95eSRic Aleshire 	/* First get the existing dataset label. */
3514201a95eSRic Aleshire 	error = dsl_prop_get(name, zfs_prop_to_name(ZFS_PROP_MLSLABEL),
3524201a95eSRic Aleshire 	    1, sizeof (ds_hexsl), &ds_hexsl, NULL);
3534201a95eSRic Aleshire 	if (error)
3544201a95eSRic Aleshire 		return (EPERM);
3554201a95eSRic Aleshire 
3564201a95eSRic Aleshire 	if (strcasecmp(strval, ZFS_MLSLABEL_DEFAULT) == 0)
3574201a95eSRic Aleshire 		new_default = TRUE;
3584201a95eSRic Aleshire 
3594201a95eSRic Aleshire 	/* The label must be translatable */
3604201a95eSRic Aleshire 	if (!new_default && (hexstr_to_label(strval, &new_sl) != 0))
3614201a95eSRic Aleshire 		return (EINVAL);
3624201a95eSRic Aleshire 
3634201a95eSRic Aleshire 	/*
3644201a95eSRic Aleshire 	 * In a non-global zone, disallow attempts to set a label that
3654201a95eSRic Aleshire 	 * doesn't match that of the zone; otherwise no other checks
3664201a95eSRic Aleshire 	 * are needed.
3674201a95eSRic Aleshire 	 */
3684201a95eSRic Aleshire 	if (!INGLOBALZONE(curproc)) {
3694201a95eSRic Aleshire 		if (new_default || !blequal(&new_sl, CR_SL(CRED())))
3704201a95eSRic Aleshire 			return (EPERM);
3714201a95eSRic Aleshire 		return (0);
3724201a95eSRic Aleshire 	}
3734201a95eSRic Aleshire 
3744201a95eSRic Aleshire 	/*
3754201a95eSRic Aleshire 	 * For global-zone datasets (i.e., those whose zoned property is
3764201a95eSRic Aleshire 	 * "off", verify that the specified new label is valid for the
3774201a95eSRic Aleshire 	 * global zone.
3784201a95eSRic Aleshire 	 */
3794201a95eSRic Aleshire 	if (dsl_prop_get_integer(name,
3804201a95eSRic Aleshire 	    zfs_prop_to_name(ZFS_PROP_ZONED), &zoned, NULL))
3814201a95eSRic Aleshire 		return (EPERM);
3824201a95eSRic Aleshire 	if (!zoned) {
3834201a95eSRic Aleshire 		if (zfs_check_global_label(name, strval) != 0)
3844201a95eSRic Aleshire 			return (EPERM);
3854201a95eSRic Aleshire 	}
3864201a95eSRic Aleshire 
3874201a95eSRic Aleshire 	/*
3884201a95eSRic Aleshire 	 * If the existing dataset label is nondefault, check if the
3894201a95eSRic Aleshire 	 * dataset is mounted (label cannot be changed while mounted).
3904201a95eSRic Aleshire 	 * Get the zfsvfs; if there isn't one, then the dataset isn't
3914201a95eSRic Aleshire 	 * mounted (or isn't a dataset, doesn't exist, ...).
3924201a95eSRic Aleshire 	 */
3934201a95eSRic Aleshire 	if (strcasecmp(ds_hexsl, ZFS_MLSLABEL_DEFAULT) != 0) {
39492241e0bSTom Erickson 		objset_t *os;
39592241e0bSTom Erickson 		static char *setsl_tag = "setsl_tag";
39692241e0bSTom Erickson 
3974201a95eSRic Aleshire 		/*
3984201a95eSRic Aleshire 		 * Try to own the dataset; abort if there is any error,
3994201a95eSRic Aleshire 		 * (e.g., already mounted, in use, or other error).
4004201a95eSRic Aleshire 		 */
4014201a95eSRic Aleshire 		error = dmu_objset_own(name, DMU_OST_ZFS, B_TRUE,
40292241e0bSTom Erickson 		    setsl_tag, &os);
4034201a95eSRic Aleshire 		if (error)
4044201a95eSRic Aleshire 			return (EPERM);
4054201a95eSRic Aleshire 
40692241e0bSTom Erickson 		dmu_objset_disown(os, setsl_tag);
40792241e0bSTom Erickson 
4084201a95eSRic Aleshire 		if (new_default) {
4094201a95eSRic Aleshire 			needed_priv = PRIV_FILE_DOWNGRADE_SL;
4104201a95eSRic Aleshire 			goto out_check;
4114201a95eSRic Aleshire 		}
4124201a95eSRic Aleshire 
4134201a95eSRic Aleshire 		if (hexstr_to_label(strval, &new_sl) != 0)
4144201a95eSRic Aleshire 			return (EPERM);
4154201a95eSRic Aleshire 
4164201a95eSRic Aleshire 		if (blstrictdom(&ds_sl, &new_sl))
4174201a95eSRic Aleshire 			needed_priv = PRIV_FILE_DOWNGRADE_SL;
4184201a95eSRic Aleshire 		else if (blstrictdom(&new_sl, &ds_sl))
4194201a95eSRic Aleshire 			needed_priv = PRIV_FILE_UPGRADE_SL;
4204201a95eSRic Aleshire 	} else {
4214201a95eSRic Aleshire 		/* dataset currently has a default label */
4224201a95eSRic Aleshire 		if (!new_default)
4234201a95eSRic Aleshire 			needed_priv = PRIV_FILE_UPGRADE_SL;
4244201a95eSRic Aleshire 	}
4254201a95eSRic Aleshire 
4264201a95eSRic Aleshire out_check:
4274201a95eSRic Aleshire 	if (needed_priv != -1)
4284201a95eSRic Aleshire 		return (PRIV_POLICY(cr, needed_priv, B_FALSE, EPERM, NULL));
4294201a95eSRic Aleshire 	return (0);
4304201a95eSRic Aleshire }
4314201a95eSRic Aleshire 
432ecd6cf80Smarks static int
43392241e0bSTom Erickson zfs_secpolicy_setprop(const char *dsname, zfs_prop_t prop, nvpair_t *propval,
43492241e0bSTom Erickson     cred_t *cr)
435ecd6cf80Smarks {
43692241e0bSTom Erickson 	char *strval;
43792241e0bSTom Erickson 
438ecd6cf80Smarks 	/*
439ecd6cf80Smarks 	 * Check permissions for special properties.
440ecd6cf80Smarks 	 */
441ecd6cf80Smarks 	switch (prop) {
442ecd6cf80Smarks 	case ZFS_PROP_ZONED:
443ecd6cf80Smarks 		/*
444ecd6cf80Smarks 		 * Disallow setting of 'zoned' from within a local zone.
445ecd6cf80Smarks 		 */
446ecd6cf80Smarks 		if (!INGLOBALZONE(curproc))
447ecd6cf80Smarks 			return (EPERM);
448ecd6cf80Smarks 		break;
449ecd6cf80Smarks 
450ecd6cf80Smarks 	case ZFS_PROP_QUOTA:
451ecd6cf80Smarks 		if (!INGLOBALZONE(curproc)) {
452ecd6cf80Smarks 			uint64_t zoned;
453ecd6cf80Smarks 			char setpoint[MAXNAMELEN];
454ecd6cf80Smarks 			/*
455ecd6cf80Smarks 			 * Unprivileged users are allowed to modify the
456ecd6cf80Smarks 			 * quota on things *under* (ie. contained by)
457ecd6cf80Smarks 			 * the thing they own.
458ecd6cf80Smarks 			 */
45992241e0bSTom Erickson 			if (dsl_prop_get_integer(dsname, "zoned", &zoned,
460ecd6cf80Smarks 			    setpoint))
461ecd6cf80Smarks 				return (EPERM);
46292241e0bSTom Erickson 			if (!zoned || strlen(dsname) <= strlen(setpoint))
463ecd6cf80Smarks 				return (EPERM);
464ecd6cf80Smarks 		}
465db870a07Sahrens 		break;
4664201a95eSRic Aleshire 
4674201a95eSRic Aleshire 	case ZFS_PROP_MLSLABEL:
4684201a95eSRic Aleshire 		if (!is_system_labeled())
4694201a95eSRic Aleshire 			return (EPERM);
47092241e0bSTom Erickson 
47192241e0bSTom Erickson 		if (nvpair_value_string(propval, &strval) == 0) {
47292241e0bSTom Erickson 			int err;
47392241e0bSTom Erickson 
47492241e0bSTom Erickson 			err = zfs_set_slabel_policy(dsname, strval, CRED());
47592241e0bSTom Erickson 			if (err != 0)
47692241e0bSTom Erickson 				return (err);
47792241e0bSTom Erickson 		}
4784201a95eSRic Aleshire 		break;
479ecd6cf80Smarks 	}
480ecd6cf80Smarks 
48192241e0bSTom Erickson 	return (zfs_secpolicy_write_perms(dsname, zfs_prop_to_name(prop), cr));
482ecd6cf80Smarks }
483ecd6cf80Smarks 
484ecd6cf80Smarks int
485ecd6cf80Smarks zfs_secpolicy_fsacl(zfs_cmd_t *zc, cred_t *cr)
486ecd6cf80Smarks {
487ecd6cf80Smarks 	int error;
488ecd6cf80Smarks 
489ecd6cf80Smarks 	error = zfs_dozonecheck(zc->zc_name, cr);
490ecd6cf80Smarks 	if (error)
491fa9e4066Sahrens 		return (error);
492fa9e4066Sahrens 
493ecd6cf80Smarks 	/*
494ecd6cf80Smarks 	 * permission to set permissions will be evaluated later in
495ecd6cf80Smarks 	 * dsl_deleg_can_allow()
496ecd6cf80Smarks 	 */
497ecd6cf80Smarks 	return (0);
498ecd6cf80Smarks }
499ecd6cf80Smarks 
500ecd6cf80Smarks int
501ecd6cf80Smarks zfs_secpolicy_rollback(zfs_cmd_t *zc, cred_t *cr)
502ecd6cf80Smarks {
503681d9761SEric Taylor 	return (zfs_secpolicy_write_perms(zc->zc_name,
504681d9761SEric Taylor 	    ZFS_DELEG_PERM_ROLLBACK, cr));
505ecd6cf80Smarks }
506ecd6cf80Smarks 
507ecd6cf80Smarks int
508ecd6cf80Smarks zfs_secpolicy_send(zfs_cmd_t *zc, cred_t *cr)
509ecd6cf80Smarks {
510ecd6cf80Smarks 	return (zfs_secpolicy_write_perms(zc->zc_name,
511ecd6cf80Smarks 	    ZFS_DELEG_PERM_SEND, cr));
512ecd6cf80Smarks }
513ecd6cf80Smarks 
514743a77edSAlan Wright static int
515743a77edSAlan Wright zfs_secpolicy_deleg_share(zfs_cmd_t *zc, cred_t *cr)
516743a77edSAlan Wright {
517743a77edSAlan Wright 	vnode_t *vp;
518743a77edSAlan Wright 	int error;
519743a77edSAlan Wright 
520743a77edSAlan Wright 	if ((error = lookupname(zc->zc_value, UIO_SYSSPACE,
521743a77edSAlan Wright 	    NO_FOLLOW, NULL, &vp)) != 0)
522743a77edSAlan Wright 		return (error);
523743a77edSAlan Wright 
524743a77edSAlan Wright 	/* Now make sure mntpnt and dataset are ZFS */
525743a77edSAlan Wright 
526743a77edSAlan Wright 	if (vp->v_vfsp->vfs_fstype != zfsfstype ||
527743a77edSAlan Wright 	    (strcmp((char *)refstr_value(vp->v_vfsp->vfs_resource),
528743a77edSAlan Wright 	    zc->zc_name) != 0)) {
529743a77edSAlan Wright 		VN_RELE(vp);
530743a77edSAlan Wright 		return (EPERM);
531743a77edSAlan Wright 	}
532743a77edSAlan Wright 
533743a77edSAlan Wright 	VN_RELE(vp);
534743a77edSAlan Wright 	return (dsl_deleg_access(zc->zc_name,
535743a77edSAlan Wright 	    ZFS_DELEG_PERM_SHARE, cr));
536743a77edSAlan Wright }
537743a77edSAlan Wright 
538ecd6cf80Smarks int
539ecd6cf80Smarks zfs_secpolicy_share(zfs_cmd_t *zc, cred_t *cr)
540ecd6cf80Smarks {
541ecd6cf80Smarks 	if (!INGLOBALZONE(curproc))
542ecd6cf80Smarks 		return (EPERM);
543ecd6cf80Smarks 
5443cb34c60Sahrens 	if (secpolicy_nfs(cr) == 0) {
545ecd6cf80Smarks 		return (0);
546ecd6cf80Smarks 	} else {
547743a77edSAlan Wright 		return (zfs_secpolicy_deleg_share(zc, cr));
548743a77edSAlan Wright 	}
549743a77edSAlan Wright }
550ecd6cf80Smarks 
551743a77edSAlan Wright int
552743a77edSAlan Wright zfs_secpolicy_smb_acl(zfs_cmd_t *zc, cred_t *cr)
553743a77edSAlan Wright {
554743a77edSAlan Wright 	if (!INGLOBALZONE(curproc))
555743a77edSAlan Wright 		return (EPERM);
556ecd6cf80Smarks 
557743a77edSAlan Wright 	if (secpolicy_smb(cr) == 0) {
558743a77edSAlan Wright 		return (0);
559743a77edSAlan Wright 	} else {
560743a77edSAlan Wright 		return (zfs_secpolicy_deleg_share(zc, cr));
561ecd6cf80Smarks 	}
562fa9e4066Sahrens }
563fa9e4066Sahrens 
564fa9e4066Sahrens static int
565ecd6cf80Smarks zfs_get_parent(const char *datasetname, char *parent, int parentsize)
566fa9e4066Sahrens {
567fa9e4066Sahrens 	char *cp;
568fa9e4066Sahrens 
569fa9e4066Sahrens 	/*
570fa9e4066Sahrens 	 * Remove the @bla or /bla from the end of the name to get the parent.
571fa9e4066Sahrens 	 */
572ecd6cf80Smarks 	(void) strncpy(parent, datasetname, parentsize);
573ecd6cf80Smarks 	cp = strrchr(parent, '@');
574fa9e4066Sahrens 	if (cp != NULL) {
575fa9e4066Sahrens 		cp[0] = '\0';
576fa9e4066Sahrens 	} else {
577ecd6cf80Smarks 		cp = strrchr(parent, '/');
578fa9e4066Sahrens 		if (cp == NULL)
579fa9e4066Sahrens 			return (ENOENT);
580fa9e4066Sahrens 		cp[0] = '\0';
581ecd6cf80Smarks 	}
582ecd6cf80Smarks 
583ecd6cf80Smarks 	return (0);
584ecd6cf80Smarks }
585ecd6cf80Smarks 
586ecd6cf80Smarks int
587ecd6cf80Smarks zfs_secpolicy_destroy_perms(const char *name, cred_t *cr)
588ecd6cf80Smarks {
589ecd6cf80Smarks 	int error;
590ecd6cf80Smarks 
591ecd6cf80Smarks 	if ((error = zfs_secpolicy_write_perms(name,
592ecd6cf80Smarks 	    ZFS_DELEG_PERM_MOUNT, cr)) != 0)
593ecd6cf80Smarks 		return (error);
594ecd6cf80Smarks 
595ecd6cf80Smarks 	return (zfs_secpolicy_write_perms(name, ZFS_DELEG_PERM_DESTROY, cr));
596ecd6cf80Smarks }
597ecd6cf80Smarks 
598ecd6cf80Smarks static int
599ecd6cf80Smarks zfs_secpolicy_destroy(zfs_cmd_t *zc, cred_t *cr)
600ecd6cf80Smarks {
601ecd6cf80Smarks 	return (zfs_secpolicy_destroy_perms(zc->zc_name, cr));
602ecd6cf80Smarks }
603ecd6cf80Smarks 
604cbf6f6aaSWilliam Gorrell /*
605cbf6f6aaSWilliam Gorrell  * Destroying snapshots with delegated permissions requires
606cbf6f6aaSWilliam Gorrell  * descendent mount and destroy permissions.
607cbf6f6aaSWilliam Gorrell  * Reassemble the full filesystem@snap name so dsl_deleg_access()
608cbf6f6aaSWilliam Gorrell  * can do the correct permission check.
609cbf6f6aaSWilliam Gorrell  *
610cbf6f6aaSWilliam Gorrell  * Since this routine is used when doing a recursive destroy of snapshots
611cbf6f6aaSWilliam Gorrell  * and destroying snapshots requires descendent permissions, a successfull
612cbf6f6aaSWilliam Gorrell  * check of the top level snapshot applies to snapshots of all descendent
613cbf6f6aaSWilliam Gorrell  * datasets as well.
614cbf6f6aaSWilliam Gorrell  */
615cbf6f6aaSWilliam Gorrell static int
616cbf6f6aaSWilliam Gorrell zfs_secpolicy_destroy_snaps(zfs_cmd_t *zc, cred_t *cr)
617cbf6f6aaSWilliam Gorrell {
618cbf6f6aaSWilliam Gorrell 	int error;
619cbf6f6aaSWilliam Gorrell 	char *dsname;
620cbf6f6aaSWilliam Gorrell 
621cbf6f6aaSWilliam Gorrell 	dsname = kmem_asprintf("%s@%s", zc->zc_name, zc->zc_value);
622cbf6f6aaSWilliam Gorrell 
623cbf6f6aaSWilliam Gorrell 	error = zfs_secpolicy_destroy_perms(dsname, cr);
624cbf6f6aaSWilliam Gorrell 
625cbf6f6aaSWilliam Gorrell 	strfree(dsname);
626cbf6f6aaSWilliam Gorrell 	return (error);
627cbf6f6aaSWilliam Gorrell }
628cbf6f6aaSWilliam Gorrell 
629ecd6cf80Smarks int
630ecd6cf80Smarks zfs_secpolicy_rename_perms(const char *from, const char *to, cred_t *cr)
631ecd6cf80Smarks {
63292241e0bSTom Erickson 	char	parentname[MAXNAMELEN];
633ecd6cf80Smarks 	int	error;
634ecd6cf80Smarks 
635ecd6cf80Smarks 	if ((error = zfs_secpolicy_write_perms(from,
636ecd6cf80Smarks 	    ZFS_DELEG_PERM_RENAME, cr)) != 0)
637ecd6cf80Smarks 		return (error);
638ecd6cf80Smarks 
639ecd6cf80Smarks 	if ((error = zfs_secpolicy_write_perms(from,
640ecd6cf80Smarks 	    ZFS_DELEG_PERM_MOUNT, cr)) != 0)
641ecd6cf80Smarks 		return (error);
642ecd6cf80Smarks 
643ecd6cf80Smarks 	if ((error = zfs_get_parent(to, parentname,
644ecd6cf80Smarks 	    sizeof (parentname))) != 0)
645ecd6cf80Smarks 		return (error);
646ecd6cf80Smarks 
647ecd6cf80Smarks 	if ((error = zfs_secpolicy_write_perms(parentname,
648ecd6cf80Smarks 	    ZFS_DELEG_PERM_CREATE, cr)) != 0)
649ecd6cf80Smarks 		return (error);
650ecd6cf80Smarks 
651ecd6cf80Smarks 	if ((error = zfs_secpolicy_write_perms(parentname,
652ecd6cf80Smarks 	    ZFS_DELEG_PERM_MOUNT, cr)) != 0)
653ecd6cf80Smarks 		return (error);
654ecd6cf80Smarks 
655ecd6cf80Smarks 	return (error);
656ecd6cf80Smarks }
657ecd6cf80Smarks 
658ecd6cf80Smarks static int
659ecd6cf80Smarks zfs_secpolicy_rename(zfs_cmd_t *zc, cred_t *cr)
660ecd6cf80Smarks {
661ecd6cf80Smarks 	return (zfs_secpolicy_rename_perms(zc->zc_name, zc->zc_value, cr));
662ecd6cf80Smarks }
663ecd6cf80Smarks 
664ecd6cf80Smarks static int
665ecd6cf80Smarks zfs_secpolicy_promote(zfs_cmd_t *zc, cred_t *cr)
666ecd6cf80Smarks {
66792241e0bSTom Erickson 	char	parentname[MAXNAMELEN];
668ecd6cf80Smarks 	objset_t *clone;
669ecd6cf80Smarks 	int error;
670ecd6cf80Smarks 
671ecd6cf80Smarks 	error = zfs_secpolicy_write_perms(zc->zc_name,
672ecd6cf80Smarks 	    ZFS_DELEG_PERM_PROMOTE, cr);
673ecd6cf80Smarks 	if (error)
674ecd6cf80Smarks 		return (error);
675ecd6cf80Smarks 
676503ad85cSMatthew Ahrens 	error = dmu_objset_hold(zc->zc_name, FTAG, &clone);
677ecd6cf80Smarks 
678ecd6cf80Smarks 	if (error == 0) {
679ecd6cf80Smarks 		dsl_dataset_t *pclone = NULL;
680ecd6cf80Smarks 		dsl_dir_t *dd;
681503ad85cSMatthew Ahrens 		dd = clone->os_dsl_dataset->ds_dir;
682ecd6cf80Smarks 
683ecd6cf80Smarks 		rw_enter(&dd->dd_pool->dp_config_rwlock, RW_READER);
684745cd3c5Smaybee 		error = dsl_dataset_hold_obj(dd->dd_pool,
685745cd3c5Smaybee 		    dd->dd_phys->dd_origin_obj, FTAG, &pclone);
686ecd6cf80Smarks 		rw_exit(&dd->dd_pool->dp_config_rwlock);
687ecd6cf80Smarks 		if (error) {
688503ad85cSMatthew Ahrens 			dmu_objset_rele(clone, FTAG);
689ecd6cf80Smarks 			return (error);
690ecd6cf80Smarks 		}
691ecd6cf80Smarks 
692ecd6cf80Smarks 		error = zfs_secpolicy_write_perms(zc->zc_name,
693ecd6cf80Smarks 		    ZFS_DELEG_PERM_MOUNT, cr);
694ecd6cf80Smarks 
695ecd6cf80Smarks 		dsl_dataset_name(pclone, parentname);
696503ad85cSMatthew Ahrens 		dmu_objset_rele(clone, FTAG);
697745cd3c5Smaybee 		dsl_dataset_rele(pclone, FTAG);
698ecd6cf80Smarks 		if (error == 0)
699ecd6cf80Smarks 			error = zfs_secpolicy_write_perms(parentname,
700ecd6cf80Smarks 			    ZFS_DELEG_PERM_PROMOTE, cr);
701ecd6cf80Smarks 	}
702ecd6cf80Smarks 	return (error);
703ecd6cf80Smarks }
704ecd6cf80Smarks 
705ecd6cf80Smarks static int
706ecd6cf80Smarks zfs_secpolicy_receive(zfs_cmd_t *zc, cred_t *cr)
707ecd6cf80Smarks {
708ecd6cf80Smarks 	int error;
709ecd6cf80Smarks 
710ecd6cf80Smarks 	if ((error = zfs_secpolicy_write_perms(zc->zc_name,
711ecd6cf80Smarks 	    ZFS_DELEG_PERM_RECEIVE, cr)) != 0)
712ecd6cf80Smarks 		return (error);
713ecd6cf80Smarks 
714ecd6cf80Smarks 	if ((error = zfs_secpolicy_write_perms(zc->zc_name,
715ecd6cf80Smarks 	    ZFS_DELEG_PERM_MOUNT, cr)) != 0)
716ecd6cf80Smarks 		return (error);
717ecd6cf80Smarks 
718ecd6cf80Smarks 	return (zfs_secpolicy_write_perms(zc->zc_name,
719ecd6cf80Smarks 	    ZFS_DELEG_PERM_CREATE, cr));
720ecd6cf80Smarks }
721ecd6cf80Smarks 
722ecd6cf80Smarks int
723ecd6cf80Smarks zfs_secpolicy_snapshot_perms(const char *name, cred_t *cr)
724ecd6cf80Smarks {
725681d9761SEric Taylor 	return (zfs_secpolicy_write_perms(name,
726681d9761SEric Taylor 	    ZFS_DELEG_PERM_SNAPSHOT, cr));
727ecd6cf80Smarks }
728ecd6cf80Smarks 
729ecd6cf80Smarks static int
730ecd6cf80Smarks zfs_secpolicy_snapshot(zfs_cmd_t *zc, cred_t *cr)
731ecd6cf80Smarks {
732ecd6cf80Smarks 
733ecd6cf80Smarks 	return (zfs_secpolicy_snapshot_perms(zc->zc_name, cr));
734ecd6cf80Smarks }
735ecd6cf80Smarks 
736ecd6cf80Smarks static int
737ecd6cf80Smarks zfs_secpolicy_create(zfs_cmd_t *zc, cred_t *cr)
738ecd6cf80Smarks {
73992241e0bSTom Erickson 	char	parentname[MAXNAMELEN];
74092241e0bSTom Erickson 	int	error;
741ecd6cf80Smarks 
742ecd6cf80Smarks 	if ((error = zfs_get_parent(zc->zc_name, parentname,
743ecd6cf80Smarks 	    sizeof (parentname))) != 0)
744ecd6cf80Smarks 		return (error);
745fa9e4066Sahrens 
746ecd6cf80Smarks 	if (zc->zc_value[0] != '\0') {
747ecd6cf80Smarks 		if ((error = zfs_secpolicy_write_perms(zc->zc_value,
748ecd6cf80Smarks 		    ZFS_DELEG_PERM_CLONE, cr)) != 0)
749ecd6cf80Smarks 			return (error);
750fa9e4066Sahrens 	}
751fa9e4066Sahrens 
752ecd6cf80Smarks 	if ((error = zfs_secpolicy_write_perms(parentname,
753ecd6cf80Smarks 	    ZFS_DELEG_PERM_CREATE, cr)) != 0)
754ecd6cf80Smarks 		return (error);
755ecd6cf80Smarks 
756ecd6cf80Smarks 	error = zfs_secpolicy_write_perms(parentname,
757ecd6cf80Smarks 	    ZFS_DELEG_PERM_MOUNT, cr);
758ecd6cf80Smarks 
759ecd6cf80Smarks 	return (error);
760ecd6cf80Smarks }
761ecd6cf80Smarks 
762ecd6cf80Smarks static int
763ecd6cf80Smarks zfs_secpolicy_umount(zfs_cmd_t *zc, cred_t *cr)
764ecd6cf80Smarks {
765ecd6cf80Smarks 	int error;
766ecd6cf80Smarks 
767ecd6cf80Smarks 	error = secpolicy_fs_unmount(cr, NULL);
768ecd6cf80Smarks 	if (error) {
769ecd6cf80Smarks 		error = dsl_deleg_access(zc->zc_name, ZFS_DELEG_PERM_MOUNT, cr);
770ecd6cf80Smarks 	}
771ecd6cf80Smarks 	return (error);
772fa9e4066Sahrens }
773fa9e4066Sahrens 
774fa9e4066Sahrens /*
775fa9e4066Sahrens  * Policy for pool operations - create/destroy pools, add vdevs, etc.  Requires
776fa9e4066Sahrens  * SYS_CONFIG privilege, which is not available in a local zone.
777fa9e4066Sahrens  */
778fa9e4066Sahrens /* ARGSUSED */
779fa9e4066Sahrens static int
780ecd6cf80Smarks zfs_secpolicy_config(zfs_cmd_t *zc, cred_t *cr)
781fa9e4066Sahrens {
782fa9e4066Sahrens 	if (secpolicy_sys_config(cr, B_FALSE) != 0)
783fa9e4066Sahrens 		return (EPERM);
784fa9e4066Sahrens 
785fa9e4066Sahrens 	return (0);
786fa9e4066Sahrens }
787fa9e4066Sahrens 
788ea8dc4b6Seschrock /*
789ea8dc4b6Seschrock  * Policy for fault injection.  Requires all privileges.
790ea8dc4b6Seschrock  */
791ea8dc4b6Seschrock /* ARGSUSED */
792ea8dc4b6Seschrock static int
793ecd6cf80Smarks zfs_secpolicy_inject(zfs_cmd_t *zc, cred_t *cr)
794ea8dc4b6Seschrock {
795ea8dc4b6Seschrock 	return (secpolicy_zinject(cr));
796ea8dc4b6Seschrock }
797ea8dc4b6Seschrock 
798e45ce728Sahrens static int
799e45ce728Sahrens zfs_secpolicy_inherit(zfs_cmd_t *zc, cred_t *cr)
800e45ce728Sahrens {
801e45ce728Sahrens 	zfs_prop_t prop = zfs_name_to_prop(zc->zc_value);
802e45ce728Sahrens 
803990b4856Slling 	if (prop == ZPROP_INVAL) {
804e45ce728Sahrens 		if (!zfs_prop_user(zc->zc_value))
805e45ce728Sahrens 			return (EINVAL);
806e45ce728Sahrens 		return (zfs_secpolicy_write_perms(zc->zc_name,
807e45ce728Sahrens 		    ZFS_DELEG_PERM_USERPROP, cr));
808e45ce728Sahrens 	} else {
80992241e0bSTom Erickson 		return (zfs_secpolicy_setprop(zc->zc_name, prop,
81092241e0bSTom Erickson 		    NULL, cr));
811e45ce728Sahrens 	}
812e45ce728Sahrens }
813e45ce728Sahrens 
81414843421SMatthew Ahrens static int
81514843421SMatthew Ahrens zfs_secpolicy_userspace_one(zfs_cmd_t *zc, cred_t *cr)
81614843421SMatthew Ahrens {
81714843421SMatthew Ahrens 	int err = zfs_secpolicy_read(zc, cr);
81814843421SMatthew Ahrens 	if (err)
81914843421SMatthew Ahrens 		return (err);
82014843421SMatthew Ahrens 
82114843421SMatthew Ahrens 	if (zc->zc_objset_type >= ZFS_NUM_USERQUOTA_PROPS)
82214843421SMatthew Ahrens 		return (EINVAL);
82314843421SMatthew Ahrens 
82414843421SMatthew Ahrens 	if (zc->zc_value[0] == 0) {
82514843421SMatthew Ahrens 		/*
82614843421SMatthew Ahrens 		 * They are asking about a posix uid/gid.  If it's
82714843421SMatthew Ahrens 		 * themself, allow it.
82814843421SMatthew Ahrens 		 */
82914843421SMatthew Ahrens 		if (zc->zc_objset_type == ZFS_PROP_USERUSED ||
83014843421SMatthew Ahrens 		    zc->zc_objset_type == ZFS_PROP_USERQUOTA) {
83114843421SMatthew Ahrens 			if (zc->zc_guid == crgetuid(cr))
83214843421SMatthew Ahrens 				return (0);
83314843421SMatthew Ahrens 		} else {
83414843421SMatthew Ahrens 			if (groupmember(zc->zc_guid, cr))
83514843421SMatthew Ahrens 				return (0);
83614843421SMatthew Ahrens 		}
83714843421SMatthew Ahrens 	}
83814843421SMatthew Ahrens 
83914843421SMatthew Ahrens 	return (zfs_secpolicy_write_perms(zc->zc_name,
84014843421SMatthew Ahrens 	    userquota_perms[zc->zc_objset_type], cr));
84114843421SMatthew Ahrens }
84214843421SMatthew Ahrens 
84314843421SMatthew Ahrens static int
84414843421SMatthew Ahrens zfs_secpolicy_userspace_many(zfs_cmd_t *zc, cred_t *cr)
84514843421SMatthew Ahrens {
84614843421SMatthew Ahrens 	int err = zfs_secpolicy_read(zc, cr);
84714843421SMatthew Ahrens 	if (err)
84814843421SMatthew Ahrens 		return (err);
84914843421SMatthew Ahrens 
85014843421SMatthew Ahrens 	if (zc->zc_objset_type >= ZFS_NUM_USERQUOTA_PROPS)
85114843421SMatthew Ahrens 		return (EINVAL);
85214843421SMatthew Ahrens 
85314843421SMatthew Ahrens 	return (zfs_secpolicy_write_perms(zc->zc_name,
85414843421SMatthew Ahrens 	    userquota_perms[zc->zc_objset_type], cr));
85514843421SMatthew Ahrens }
85614843421SMatthew Ahrens 
85714843421SMatthew Ahrens static int
85814843421SMatthew Ahrens zfs_secpolicy_userspace_upgrade(zfs_cmd_t *zc, cred_t *cr)
85914843421SMatthew Ahrens {
86092241e0bSTom Erickson 	return (zfs_secpolicy_setprop(zc->zc_name, ZFS_PROP_VERSION,
86192241e0bSTom Erickson 	    NULL, cr));
86214843421SMatthew Ahrens }
86314843421SMatthew Ahrens 
864842727c2SChris Kirby static int
865842727c2SChris Kirby zfs_secpolicy_hold(zfs_cmd_t *zc, cred_t *cr)
866842727c2SChris Kirby {
867842727c2SChris Kirby 	return (zfs_secpolicy_write_perms(zc->zc_name,
868842727c2SChris Kirby 	    ZFS_DELEG_PERM_HOLD, cr));
869842727c2SChris Kirby }
870842727c2SChris Kirby 
871842727c2SChris Kirby static int
872842727c2SChris Kirby zfs_secpolicy_release(zfs_cmd_t *zc, cred_t *cr)
873842727c2SChris Kirby {
874842727c2SChris Kirby 	return (zfs_secpolicy_write_perms(zc->zc_name,
875842727c2SChris Kirby 	    ZFS_DELEG_PERM_RELEASE, cr));
876842727c2SChris Kirby }
877842727c2SChris Kirby 
878fa9e4066Sahrens /*
879fa9e4066Sahrens  * Returns the nvlist as specified by the user in the zfs_cmd_t.
880fa9e4066Sahrens  */
881fa9e4066Sahrens static int
882478ed9adSEric Taylor get_nvlist(uint64_t nvl, uint64_t size, int iflag, nvlist_t **nvp)
883fa9e4066Sahrens {
884fa9e4066Sahrens 	char *packed;
885fa9e4066Sahrens 	int error;
886990b4856Slling 	nvlist_t *list = NULL;
887fa9e4066Sahrens 
888fa9e4066Sahrens 	/*
889e9dbad6fSeschrock 	 * Read in and unpack the user-supplied nvlist.
890fa9e4066Sahrens 	 */
891990b4856Slling 	if (size == 0)
892fa9e4066Sahrens 		return (EINVAL);
893fa9e4066Sahrens 
894fa9e4066Sahrens 	packed = kmem_alloc(size, KM_SLEEP);
895fa9e4066Sahrens 
896478ed9adSEric Taylor 	if ((error = ddi_copyin((void *)(uintptr_t)nvl, packed, size,
897478ed9adSEric Taylor 	    iflag)) != 0) {
898fa9e4066Sahrens 		kmem_free(packed, size);
899fa9e4066Sahrens 		return (error);
900fa9e4066Sahrens 	}
901fa9e4066Sahrens 
902990b4856Slling 	if ((error = nvlist_unpack(packed, size, &list, 0)) != 0) {
903fa9e4066Sahrens 		kmem_free(packed, size);
904fa9e4066Sahrens 		return (error);
905fa9e4066Sahrens 	}
906fa9e4066Sahrens 
907fa9e4066Sahrens 	kmem_free(packed, size);
908fa9e4066Sahrens 
909990b4856Slling 	*nvp = list;
910fa9e4066Sahrens 	return (0);
911fa9e4066Sahrens }
912fa9e4066Sahrens 
91392241e0bSTom Erickson static int
91492241e0bSTom Erickson fit_error_list(zfs_cmd_t *zc, nvlist_t **errors)
91592241e0bSTom Erickson {
91692241e0bSTom Erickson 	size_t size;
91792241e0bSTom Erickson 
91892241e0bSTom Erickson 	VERIFY(nvlist_size(*errors, &size, NV_ENCODE_NATIVE) == 0);
91992241e0bSTom Erickson 
92092241e0bSTom Erickson 	if (size > zc->zc_nvlist_dst_size) {
92192241e0bSTom Erickson 		nvpair_t *more_errors;
92292241e0bSTom Erickson 		int n = 0;
92392241e0bSTom Erickson 
92492241e0bSTom Erickson 		if (zc->zc_nvlist_dst_size < 1024)
92592241e0bSTom Erickson 			return (ENOMEM);
92692241e0bSTom Erickson 
92792241e0bSTom Erickson 		VERIFY(nvlist_add_int32(*errors, ZPROP_N_MORE_ERRORS, 0) == 0);
92892241e0bSTom Erickson 		more_errors = nvlist_prev_nvpair(*errors, NULL);
92992241e0bSTom Erickson 
93092241e0bSTom Erickson 		do {
93192241e0bSTom Erickson 			nvpair_t *pair = nvlist_prev_nvpair(*errors,
93292241e0bSTom Erickson 			    more_errors);
93392241e0bSTom Erickson 			VERIFY(nvlist_remove_nvpair(*errors, pair) == 0);
93492241e0bSTom Erickson 			n++;
93592241e0bSTom Erickson 			VERIFY(nvlist_size(*errors, &size,
93692241e0bSTom Erickson 			    NV_ENCODE_NATIVE) == 0);
93792241e0bSTom Erickson 		} while (size > zc->zc_nvlist_dst_size);
93892241e0bSTom Erickson 
93992241e0bSTom Erickson 		VERIFY(nvlist_remove_nvpair(*errors, more_errors) == 0);
94092241e0bSTom Erickson 		VERIFY(nvlist_add_int32(*errors, ZPROP_N_MORE_ERRORS, n) == 0);
94192241e0bSTom Erickson 		ASSERT(nvlist_size(*errors, &size, NV_ENCODE_NATIVE) == 0);
94292241e0bSTom Erickson 		ASSERT(size <= zc->zc_nvlist_dst_size);
94392241e0bSTom Erickson 	}
94492241e0bSTom Erickson 
94592241e0bSTom Erickson 	return (0);
94692241e0bSTom Erickson }
94792241e0bSTom Erickson 
948e9dbad6fSeschrock static int
949e9dbad6fSeschrock put_nvlist(zfs_cmd_t *zc, nvlist_t *nvl)
950e9dbad6fSeschrock {
951e9dbad6fSeschrock 	char *packed = NULL;
9526e27f868SSam Falkner 	int error = 0;
953e9dbad6fSeschrock 	size_t size;
954e9dbad6fSeschrock 
955e9dbad6fSeschrock 	VERIFY(nvlist_size(nvl, &size, NV_ENCODE_NATIVE) == 0);
956e9dbad6fSeschrock 
957e9dbad6fSeschrock 	if (size > zc->zc_nvlist_dst_size) {
958e9dbad6fSeschrock 		error = ENOMEM;
959e9dbad6fSeschrock 	} else {
960da165920Smarks 		packed = kmem_alloc(size, KM_SLEEP);
961e9dbad6fSeschrock 		VERIFY(nvlist_pack(nvl, &packed, &size, NV_ENCODE_NATIVE,
962e9dbad6fSeschrock 		    KM_SLEEP) == 0);
9636e27f868SSam Falkner 		if (ddi_copyout(packed, (void *)(uintptr_t)zc->zc_nvlist_dst,
9646e27f868SSam Falkner 		    size, zc->zc_iflags) != 0)
9656e27f868SSam Falkner 			error = EFAULT;
966e9dbad6fSeschrock 		kmem_free(packed, size);
967e9dbad6fSeschrock 	}
968e9dbad6fSeschrock 
969e9dbad6fSeschrock 	zc->zc_nvlist_dst_size = size;
970e9dbad6fSeschrock 	return (error);
971e9dbad6fSeschrock }
972e9dbad6fSeschrock 
97314843421SMatthew Ahrens static int
974af4c679fSSean McEnroe getzfsvfs(const char *dsname, zfsvfs_t **zfvp)
97514843421SMatthew Ahrens {
97614843421SMatthew Ahrens 	objset_t *os;
97714843421SMatthew Ahrens 	int error;
97814843421SMatthew Ahrens 
979503ad85cSMatthew Ahrens 	error = dmu_objset_hold(dsname, FTAG, &os);
98014843421SMatthew Ahrens 	if (error)
98114843421SMatthew Ahrens 		return (error);
982503ad85cSMatthew Ahrens 	if (dmu_objset_type(os) != DMU_OST_ZFS) {
983503ad85cSMatthew Ahrens 		dmu_objset_rele(os, FTAG);
984503ad85cSMatthew Ahrens 		return (EINVAL);
985503ad85cSMatthew Ahrens 	}
98614843421SMatthew Ahrens 
987503ad85cSMatthew Ahrens 	mutex_enter(&os->os_user_ptr_lock);
988af4c679fSSean McEnroe 	*zfvp = dmu_objset_get_user(os);
989af4c679fSSean McEnroe 	if (*zfvp) {
990af4c679fSSean McEnroe 		VFS_HOLD((*zfvp)->z_vfs);
99114843421SMatthew Ahrens 	} else {
99214843421SMatthew Ahrens 		error = ESRCH;
99314843421SMatthew Ahrens 	}
994503ad85cSMatthew Ahrens 	mutex_exit(&os->os_user_ptr_lock);
995503ad85cSMatthew Ahrens 	dmu_objset_rele(os, FTAG);
99614843421SMatthew Ahrens 	return (error);
99714843421SMatthew Ahrens }
99814843421SMatthew Ahrens 
99914843421SMatthew Ahrens /*
100014843421SMatthew Ahrens  * Find a zfsvfs_t for a mounted filesystem, or create our own, in which
100114843421SMatthew Ahrens  * case its z_vfs will be NULL, and it will be opened as the owner.
100214843421SMatthew Ahrens  */
100314843421SMatthew Ahrens static int
1004af4c679fSSean McEnroe zfsvfs_hold(const char *name, void *tag, zfsvfs_t **zfvp)
100514843421SMatthew Ahrens {
100614843421SMatthew Ahrens 	int error = 0;
100714843421SMatthew Ahrens 
1008af4c679fSSean McEnroe 	if (getzfsvfs(name, zfvp) != 0)
1009af4c679fSSean McEnroe 		error = zfsvfs_create(name, zfvp);
101014843421SMatthew Ahrens 	if (error == 0) {
1011af4c679fSSean McEnroe 		rrw_enter(&(*zfvp)->z_teardown_lock, RW_READER, tag);
1012af4c679fSSean McEnroe 		if ((*zfvp)->z_unmounted) {
101314843421SMatthew Ahrens 			/*
101414843421SMatthew Ahrens 			 * XXX we could probably try again, since the unmounting
101514843421SMatthew Ahrens 			 * thread should be just about to disassociate the
101614843421SMatthew Ahrens 			 * objset from the zfsvfs.
101714843421SMatthew Ahrens 			 */
1018af4c679fSSean McEnroe 			rrw_exit(&(*zfvp)->z_teardown_lock, tag);
101914843421SMatthew Ahrens 			return (EBUSY);
102014843421SMatthew Ahrens 		}
102114843421SMatthew Ahrens 	}
102214843421SMatthew Ahrens 	return (error);
102314843421SMatthew Ahrens }
102414843421SMatthew Ahrens 
102514843421SMatthew Ahrens static void
102614843421SMatthew Ahrens zfsvfs_rele(zfsvfs_t *zfsvfs, void *tag)
102714843421SMatthew Ahrens {
102814843421SMatthew Ahrens 	rrw_exit(&zfsvfs->z_teardown_lock, tag);
102914843421SMatthew Ahrens 
103014843421SMatthew Ahrens 	if (zfsvfs->z_vfs) {
103114843421SMatthew Ahrens 		VFS_RELE(zfsvfs->z_vfs);
103214843421SMatthew Ahrens 	} else {
1033503ad85cSMatthew Ahrens 		dmu_objset_disown(zfsvfs->z_os, zfsvfs);
103414843421SMatthew Ahrens 		zfsvfs_free(zfsvfs);
103514843421SMatthew Ahrens 	}
103614843421SMatthew Ahrens }
103714843421SMatthew Ahrens 
1038fa9e4066Sahrens static int
1039fa9e4066Sahrens zfs_ioc_pool_create(zfs_cmd_t *zc)
1040fa9e4066Sahrens {
1041fa9e4066Sahrens 	int error;
1042990b4856Slling 	nvlist_t *config, *props = NULL;
10430a48a24eStimh 	nvlist_t *rootprops = NULL;
10440a48a24eStimh 	nvlist_t *zplprops = NULL;
1045228975ccSek 	char *buf;
1046fa9e4066Sahrens 
1047990b4856Slling 	if (error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size,
1048478ed9adSEric Taylor 	    zc->zc_iflags, &config))
1049fa9e4066Sahrens 		return (error);
10502a6b87f0Sek 
1051990b4856Slling 	if (zc->zc_nvlist_src_size != 0 && (error =
1052478ed9adSEric Taylor 	    get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
1053478ed9adSEric Taylor 	    zc->zc_iflags, &props))) {
1054990b4856Slling 		nvlist_free(config);
1055990b4856Slling 		return (error);
1056990b4856Slling 	}
1057990b4856Slling 
10580a48a24eStimh 	if (props) {
10590a48a24eStimh 		nvlist_t *nvl = NULL;
10600a48a24eStimh 		uint64_t version = SPA_VERSION;
10610a48a24eStimh 
10620a48a24eStimh 		(void) nvlist_lookup_uint64(props,
10630a48a24eStimh 		    zpool_prop_to_name(ZPOOL_PROP_VERSION), &version);
10640a48a24eStimh 		if (version < SPA_VERSION_INITIAL || version > SPA_VERSION) {
10650a48a24eStimh 			error = EINVAL;
10660a48a24eStimh 			goto pool_props_bad;
10670a48a24eStimh 		}
10680a48a24eStimh 		(void) nvlist_lookup_nvlist(props, ZPOOL_ROOTFS_PROPS, &nvl);
10690a48a24eStimh 		if (nvl) {
10700a48a24eStimh 			error = nvlist_dup(nvl, &rootprops, KM_SLEEP);
10710a48a24eStimh 			if (error != 0) {
10720a48a24eStimh 				nvlist_free(config);
10730a48a24eStimh 				nvlist_free(props);
10740a48a24eStimh 				return (error);
10750a48a24eStimh 			}
10760a48a24eStimh 			(void) nvlist_remove_all(props, ZPOOL_ROOTFS_PROPS);
10770a48a24eStimh 		}
10780a48a24eStimh 		VERIFY(nvlist_alloc(&zplprops, NV_UNIQUE_NAME, KM_SLEEP) == 0);
10790a48a24eStimh 		error = zfs_fill_zplprops_root(version, rootprops,
10800a48a24eStimh 		    zplprops, NULL);
10810a48a24eStimh 		if (error)
10820a48a24eStimh 			goto pool_props_bad;
10830a48a24eStimh 	}
10840a48a24eStimh 
10852a6b87f0Sek 	buf = history_str_get(zc);
1086fa9e4066Sahrens 
10870a48a24eStimh 	error = spa_create(zc->zc_name, config, props, buf, zplprops);
10880a48a24eStimh 
10890a48a24eStimh 	/*
10900a48a24eStimh 	 * Set the remaining root properties
10910a48a24eStimh 	 */
109292241e0bSTom Erickson 	if (!error && (error = zfs_set_prop_nvlist(zc->zc_name,
109392241e0bSTom Erickson 	    ZPROP_SRC_LOCAL, rootprops, NULL)) != 0)
10940a48a24eStimh 		(void) spa_destroy(zc->zc_name);
1095fa9e4066Sahrens 
10962a6b87f0Sek 	if (buf != NULL)
10972a6b87f0Sek 		history_str_free(buf);
1098990b4856Slling 
10990a48a24eStimh pool_props_bad:
11000a48a24eStimh 	nvlist_free(rootprops);
11010a48a24eStimh 	nvlist_free(zplprops);
1102fa9e4066Sahrens 	nvlist_free(config);
11030a48a24eStimh 	nvlist_free(props);
1104990b4856Slling 
1105fa9e4066Sahrens 	return (error);
1106fa9e4066Sahrens }
1107fa9e4066Sahrens 
1108fa9e4066Sahrens static int
1109fa9e4066Sahrens zfs_ioc_pool_destroy(zfs_cmd_t *zc)
1110fa9e4066Sahrens {
1111ecd6cf80Smarks 	int error;
1112ecd6cf80Smarks 	zfs_log_history(zc);
1113ecd6cf80Smarks 	error = spa_destroy(zc->zc_name);
1114681d9761SEric Taylor 	if (error == 0)
1115681d9761SEric Taylor 		zvol_remove_minors(zc->zc_name);
1116ecd6cf80Smarks 	return (error);
1117fa9e4066Sahrens }
1118fa9e4066Sahrens 
1119fa9e4066Sahrens static int
1120fa9e4066Sahrens zfs_ioc_pool_import(zfs_cmd_t *zc)
1121fa9e4066Sahrens {
1122990b4856Slling 	nvlist_t *config, *props = NULL;
1123fa9e4066Sahrens 	uint64_t guid;
1124468c413aSTim Haley 	int error;
1125fa9e4066Sahrens 
1126990b4856Slling 	if ((error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size,
1127478ed9adSEric Taylor 	    zc->zc_iflags, &config)) != 0)
1128990b4856Slling 		return (error);
1129990b4856Slling 
1130990b4856Slling 	if (zc->zc_nvlist_src_size != 0 && (error =
1131478ed9adSEric Taylor 	    get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
1132478ed9adSEric Taylor 	    zc->zc_iflags, &props))) {
1133990b4856Slling 		nvlist_free(config);
1134fa9e4066Sahrens 		return (error);
1135990b4856Slling 	}
1136fa9e4066Sahrens 
1137fa9e4066Sahrens 	if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID, &guid) != 0 ||
1138ea8dc4b6Seschrock 	    guid != zc->zc_guid)
1139fa9e4066Sahrens 		error = EINVAL;
1140c5904d13Seschrock 	else if (zc->zc_cookie)
1141468c413aSTim Haley 		error = spa_import_verbatim(zc->zc_name, config, props);
1142fa9e4066Sahrens 	else
1143990b4856Slling 		error = spa_import(zc->zc_name, config, props);
1144fa9e4066Sahrens 
1145468c413aSTim Haley 	if (zc->zc_nvlist_dst != 0)
1146468c413aSTim Haley 		(void) put_nvlist(zc, config);
1147468c413aSTim Haley 
1148fa9e4066Sahrens 	nvlist_free(config);
1149fa9e4066Sahrens 
1150990b4856Slling 	if (props)
1151990b4856Slling 		nvlist_free(props);
1152990b4856Slling 
1153fa9e4066Sahrens 	return (error);
1154fa9e4066Sahrens }
1155fa9e4066Sahrens 
1156fa9e4066Sahrens static int
1157fa9e4066Sahrens zfs_ioc_pool_export(zfs_cmd_t *zc)
1158fa9e4066Sahrens {
1159ecd6cf80Smarks 	int error;
116089a89ebfSlling 	boolean_t force = (boolean_t)zc->zc_cookie;
1161394ab0cbSGeorge Wilson 	boolean_t hardforce = (boolean_t)zc->zc_guid;
116289a89ebfSlling 
1163ecd6cf80Smarks 	zfs_log_history(zc);
1164394ab0cbSGeorge Wilson 	error = spa_export(zc->zc_name, NULL, force, hardforce);
1165681d9761SEric Taylor 	if (error == 0)
1166681d9761SEric Taylor 		zvol_remove_minors(zc->zc_name);
1167ecd6cf80Smarks 	return (error);
1168fa9e4066Sahrens }
1169fa9e4066Sahrens 
1170fa9e4066Sahrens static int
1171fa9e4066Sahrens zfs_ioc_pool_configs(zfs_cmd_t *zc)
1172fa9e4066Sahrens {
1173fa9e4066Sahrens 	nvlist_t *configs;
1174fa9e4066Sahrens 	int error;
1175fa9e4066Sahrens 
1176fa9e4066Sahrens 	if ((configs = spa_all_configs(&zc->zc_cookie)) == NULL)
1177fa9e4066Sahrens 		return (EEXIST);
1178fa9e4066Sahrens 
1179e9dbad6fSeschrock 	error = put_nvlist(zc, configs);
1180fa9e4066Sahrens 
1181fa9e4066Sahrens 	nvlist_free(configs);
1182fa9e4066Sahrens 
1183fa9e4066Sahrens 	return (error);
1184fa9e4066Sahrens }
1185fa9e4066Sahrens 
1186fa9e4066Sahrens static int
1187fa9e4066Sahrens zfs_ioc_pool_stats(zfs_cmd_t *zc)
1188fa9e4066Sahrens {
1189fa9e4066Sahrens 	nvlist_t *config;
1190fa9e4066Sahrens 	int error;
1191ea8dc4b6Seschrock 	int ret = 0;
1192fa9e4066Sahrens 
1193e9dbad6fSeschrock 	error = spa_get_stats(zc->zc_name, &config, zc->zc_value,
1194e9dbad6fSeschrock 	    sizeof (zc->zc_value));
1195fa9e4066Sahrens 
1196fa9e4066Sahrens 	if (config != NULL) {
1197e9dbad6fSeschrock 		ret = put_nvlist(zc, config);
1198fa9e4066Sahrens 		nvlist_free(config);
1199ea8dc4b6Seschrock 
1200ea8dc4b6Seschrock 		/*
1201ea8dc4b6Seschrock 		 * The config may be present even if 'error' is non-zero.
1202ea8dc4b6Seschrock 		 * In this case we return success, and preserve the real errno
1203ea8dc4b6Seschrock 		 * in 'zc_cookie'.
1204ea8dc4b6Seschrock 		 */
1205ea8dc4b6Seschrock 		zc->zc_cookie = error;
1206fa9e4066Sahrens 	} else {
1207ea8dc4b6Seschrock 		ret = error;
1208fa9e4066Sahrens 	}
1209fa9e4066Sahrens 
1210ea8dc4b6Seschrock 	return (ret);
1211fa9e4066Sahrens }
1212fa9e4066Sahrens 
1213fa9e4066Sahrens /*
1214fa9e4066Sahrens  * Try to import the given pool, returning pool stats as appropriate so that
1215fa9e4066Sahrens  * user land knows which devices are available and overall pool health.
1216fa9e4066Sahrens  */
1217fa9e4066Sahrens static int
1218fa9e4066Sahrens zfs_ioc_pool_tryimport(zfs_cmd_t *zc)
1219fa9e4066Sahrens {
1220fa9e4066Sahrens 	nvlist_t *tryconfig, *config;
1221fa9e4066Sahrens 	int error;
1222fa9e4066Sahrens 
1223990b4856Slling 	if ((error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size,
1224478ed9adSEric Taylor 	    zc->zc_iflags, &tryconfig)) != 0)
1225fa9e4066Sahrens 		return (error);
1226fa9e4066Sahrens 
1227fa9e4066Sahrens 	config = spa_tryimport(tryconfig);
1228fa9e4066Sahrens 
1229fa9e4066Sahrens 	nvlist_free(tryconfig);
1230fa9e4066Sahrens 
1231fa9e4066Sahrens 	if (config == NULL)
1232fa9e4066Sahrens 		return (EINVAL);
1233fa9e4066Sahrens 
1234e9dbad6fSeschrock 	error = put_nvlist(zc, config);
1235fa9e4066Sahrens 	nvlist_free(config);
1236fa9e4066Sahrens 
1237fa9e4066Sahrens 	return (error);
1238fa9e4066Sahrens }
1239fa9e4066Sahrens 
1240fa9e4066Sahrens static int
1241fa9e4066Sahrens zfs_ioc_pool_scrub(zfs_cmd_t *zc)
1242fa9e4066Sahrens {
1243fa9e4066Sahrens 	spa_t *spa;
1244fa9e4066Sahrens 	int error;
1245fa9e4066Sahrens 
124606eeb2adSek 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
124706eeb2adSek 		return (error);
124806eeb2adSek 
1249088f3894Sahrens 	error = spa_scrub(spa, zc->zc_cookie);
125006eeb2adSek 
125106eeb2adSek 	spa_close(spa, FTAG);
125206eeb2adSek 
1253fa9e4066Sahrens 	return (error);
1254fa9e4066Sahrens }
1255fa9e4066Sahrens 
1256fa9e4066Sahrens static int
1257fa9e4066Sahrens zfs_ioc_pool_freeze(zfs_cmd_t *zc)
1258fa9e4066Sahrens {
1259fa9e4066Sahrens 	spa_t *spa;
1260fa9e4066Sahrens 	int error;
1261fa9e4066Sahrens 
1262fa9e4066Sahrens 	error = spa_open(zc->zc_name, &spa, FTAG);
1263fa9e4066Sahrens 	if (error == 0) {
1264fa9e4066Sahrens 		spa_freeze(spa);
1265fa9e4066Sahrens 		spa_close(spa, FTAG);
1266fa9e4066Sahrens 	}
1267fa9e4066Sahrens 	return (error);
1268fa9e4066Sahrens }
1269fa9e4066Sahrens 
1270eaca9bbdSeschrock static int
1271eaca9bbdSeschrock zfs_ioc_pool_upgrade(zfs_cmd_t *zc)
1272eaca9bbdSeschrock {
1273eaca9bbdSeschrock 	spa_t *spa;
1274eaca9bbdSeschrock 	int error;
1275eaca9bbdSeschrock 
127606eeb2adSek 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
127706eeb2adSek 		return (error);
127806eeb2adSek 
1279558d2d50Slling 	if (zc->zc_cookie < spa_version(spa) || zc->zc_cookie > SPA_VERSION) {
1280558d2d50Slling 		spa_close(spa, FTAG);
1281558d2d50Slling 		return (EINVAL);
1282558d2d50Slling 	}
1283558d2d50Slling 
1284990b4856Slling 	spa_upgrade(spa, zc->zc_cookie);
128506eeb2adSek 	spa_close(spa, FTAG);
128606eeb2adSek 
128706eeb2adSek 	return (error);
128806eeb2adSek }
128906eeb2adSek 
129006eeb2adSek static int
129106eeb2adSek zfs_ioc_pool_get_history(zfs_cmd_t *zc)
129206eeb2adSek {
129306eeb2adSek 	spa_t *spa;
129406eeb2adSek 	char *hist_buf;
129506eeb2adSek 	uint64_t size;
129606eeb2adSek 	int error;
129706eeb2adSek 
129806eeb2adSek 	if ((size = zc->zc_history_len) == 0)
129906eeb2adSek 		return (EINVAL);
130006eeb2adSek 
130106eeb2adSek 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
130206eeb2adSek 		return (error);
130306eeb2adSek 
1304e7437265Sahrens 	if (spa_version(spa) < SPA_VERSION_ZPOOL_HISTORY) {
1305d7306b64Sek 		spa_close(spa, FTAG);
1306d7306b64Sek 		return (ENOTSUP);
1307d7306b64Sek 	}
1308d7306b64Sek 
130906eeb2adSek 	hist_buf = kmem_alloc(size, KM_SLEEP);
131006eeb2adSek 	if ((error = spa_history_get(spa, &zc->zc_history_offset,
131106eeb2adSek 	    &zc->zc_history_len, hist_buf)) == 0) {
1312478ed9adSEric Taylor 		error = ddi_copyout(hist_buf,
1313478ed9adSEric Taylor 		    (void *)(uintptr_t)zc->zc_history,
1314478ed9adSEric Taylor 		    zc->zc_history_len, zc->zc_iflags);
131506eeb2adSek 	}
131606eeb2adSek 
131706eeb2adSek 	spa_close(spa, FTAG);
131806eeb2adSek 	kmem_free(hist_buf, size);
131906eeb2adSek 	return (error);
132006eeb2adSek }
132106eeb2adSek 
132255434c77Sek static int
132355434c77Sek zfs_ioc_dsobj_to_dsname(zfs_cmd_t *zc)
132455434c77Sek {
132555434c77Sek 	int error;
132655434c77Sek 
1327b1b8ab34Slling 	if (error = dsl_dsobj_to_dsname(zc->zc_name, zc->zc_obj, zc->zc_value))
132855434c77Sek 		return (error);
132955434c77Sek 
133055434c77Sek 	return (0);
133155434c77Sek }
133255434c77Sek 
1333503ad85cSMatthew Ahrens /*
1334503ad85cSMatthew Ahrens  * inputs:
1335503ad85cSMatthew Ahrens  * zc_name		name of filesystem
1336503ad85cSMatthew Ahrens  * zc_obj		object to find
1337503ad85cSMatthew Ahrens  *
1338503ad85cSMatthew Ahrens  * outputs:
1339503ad85cSMatthew Ahrens  * zc_value		name of object
1340503ad85cSMatthew Ahrens  */
134155434c77Sek static int
134255434c77Sek zfs_ioc_obj_to_path(zfs_cmd_t *zc)
134355434c77Sek {
1344503ad85cSMatthew Ahrens 	objset_t *os;
134555434c77Sek 	int error;
134655434c77Sek 
1347503ad85cSMatthew Ahrens 	/* XXX reading from objset not owned */
1348503ad85cSMatthew Ahrens 	if ((error = dmu_objset_hold(zc->zc_name, FTAG, &os)) != 0)
134955434c77Sek 		return (error);
1350503ad85cSMatthew Ahrens 	if (dmu_objset_type(os) != DMU_OST_ZFS) {
1351503ad85cSMatthew Ahrens 		dmu_objset_rele(os, FTAG);
1352503ad85cSMatthew Ahrens 		return (EINVAL);
1353503ad85cSMatthew Ahrens 	}
1354503ad85cSMatthew Ahrens 	error = zfs_obj_to_path(os, zc->zc_obj, zc->zc_value,
135555434c77Sek 	    sizeof (zc->zc_value));
1356503ad85cSMatthew Ahrens 	dmu_objset_rele(os, FTAG);
135755434c77Sek 
135855434c77Sek 	return (error);
135955434c77Sek }
136055434c77Sek 
1361fa9e4066Sahrens static int
1362fa9e4066Sahrens zfs_ioc_vdev_add(zfs_cmd_t *zc)
1363fa9e4066Sahrens {
1364fa9e4066Sahrens 	spa_t *spa;
1365fa9e4066Sahrens 	int error;
1366e7cbe64fSgw 	nvlist_t *config, **l2cache, **spares;
1367e7cbe64fSgw 	uint_t nl2cache = 0, nspares = 0;
1368fa9e4066Sahrens 
1369fa9e4066Sahrens 	error = spa_open(zc->zc_name, &spa, FTAG);
1370fa9e4066Sahrens 	if (error != 0)
1371fa9e4066Sahrens 		return (error);
1372fa9e4066Sahrens 
1373fa94a07fSbrendan 	error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size,
1374478ed9adSEric Taylor 	    zc->zc_iflags, &config);
1375fa94a07fSbrendan 	(void) nvlist_lookup_nvlist_array(config, ZPOOL_CONFIG_L2CACHE,
1376fa94a07fSbrendan 	    &l2cache, &nl2cache);
1377fa94a07fSbrendan 
1378e7cbe64fSgw 	(void) nvlist_lookup_nvlist_array(config, ZPOOL_CONFIG_SPARES,
1379e7cbe64fSgw 	    &spares, &nspares);
1380e7cbe64fSgw 
1381b1b8ab34Slling 	/*
1382b1b8ab34Slling 	 * A root pool with concatenated devices is not supported.
1383e7cbe64fSgw 	 * Thus, can not add a device to a root pool.
1384e7cbe64fSgw 	 *
1385e7cbe64fSgw 	 * Intent log device can not be added to a rootpool because
1386e7cbe64fSgw 	 * during mountroot, zil is replayed, a seperated log device
1387e7cbe64fSgw 	 * can not be accessed during the mountroot time.
1388e7cbe64fSgw 	 *
1389e7cbe64fSgw 	 * l2cache and spare devices are ok to be added to a rootpool.
1390b1b8ab34Slling 	 */
1391b24ab676SJeff Bonwick 	if (spa_bootfs(spa) != 0 && nl2cache == 0 && nspares == 0) {
13921195e687SMark J Musante 		nvlist_free(config);
1393b1b8ab34Slling 		spa_close(spa, FTAG);
1394b1b8ab34Slling 		return (EDOM);
1395b1b8ab34Slling 	}
1396b1b8ab34Slling 
1397fa94a07fSbrendan 	if (error == 0) {
1398fa9e4066Sahrens 		error = spa_vdev_add(spa, config);
1399fa9e4066Sahrens 		nvlist_free(config);
1400fa9e4066Sahrens 	}
1401fa9e4066Sahrens 	spa_close(spa, FTAG);
1402fa9e4066Sahrens 	return (error);
1403fa9e4066Sahrens }
1404fa9e4066Sahrens 
1405fa9e4066Sahrens static int
1406fa9e4066Sahrens zfs_ioc_vdev_remove(zfs_cmd_t *zc)
1407fa9e4066Sahrens {
140899653d4eSeschrock 	spa_t *spa;
140999653d4eSeschrock 	int error;
141099653d4eSeschrock 
141199653d4eSeschrock 	error = spa_open(zc->zc_name, &spa, FTAG);
141299653d4eSeschrock 	if (error != 0)
141399653d4eSeschrock 		return (error);
141499653d4eSeschrock 	error = spa_vdev_remove(spa, zc->zc_guid, B_FALSE);
141599653d4eSeschrock 	spa_close(spa, FTAG);
141699653d4eSeschrock 	return (error);
1417fa9e4066Sahrens }
1418fa9e4066Sahrens 
1419fa9e4066Sahrens static int
14203d7072f8Seschrock zfs_ioc_vdev_set_state(zfs_cmd_t *zc)
1421fa9e4066Sahrens {
1422fa9e4066Sahrens 	spa_t *spa;
1423fa9e4066Sahrens 	int error;
14243d7072f8Seschrock 	vdev_state_t newstate = VDEV_STATE_UNKNOWN;
1425fa9e4066Sahrens 
142606eeb2adSek 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
1427fa9e4066Sahrens 		return (error);
14283d7072f8Seschrock 	switch (zc->zc_cookie) {
14293d7072f8Seschrock 	case VDEV_STATE_ONLINE:
14303d7072f8Seschrock 		error = vdev_online(spa, zc->zc_guid, zc->zc_obj, &newstate);
14313d7072f8Seschrock 		break;
1432fa9e4066Sahrens 
14333d7072f8Seschrock 	case VDEV_STATE_OFFLINE:
14343d7072f8Seschrock 		error = vdev_offline(spa, zc->zc_guid, zc->zc_obj);
14353d7072f8Seschrock 		break;
1436fa9e4066Sahrens 
14373d7072f8Seschrock 	case VDEV_STATE_FAULTED:
1438069f55e2SEric Schrock 		if (zc->zc_obj != VDEV_AUX_ERR_EXCEEDED &&
1439069f55e2SEric Schrock 		    zc->zc_obj != VDEV_AUX_EXTERNAL)
1440069f55e2SEric Schrock 			zc->zc_obj = VDEV_AUX_ERR_EXCEEDED;
1441069f55e2SEric Schrock 
1442069f55e2SEric Schrock 		error = vdev_fault(spa, zc->zc_guid, zc->zc_obj);
14433d7072f8Seschrock 		break;
14443d7072f8Seschrock 
14453d7072f8Seschrock 	case VDEV_STATE_DEGRADED:
1446069f55e2SEric Schrock 		if (zc->zc_obj != VDEV_AUX_ERR_EXCEEDED &&
1447069f55e2SEric Schrock 		    zc->zc_obj != VDEV_AUX_EXTERNAL)
1448069f55e2SEric Schrock 			zc->zc_obj = VDEV_AUX_ERR_EXCEEDED;
1449069f55e2SEric Schrock 
1450069f55e2SEric Schrock 		error = vdev_degrade(spa, zc->zc_guid, zc->zc_obj);
14513d7072f8Seschrock 		break;
14523d7072f8Seschrock 
14533d7072f8Seschrock 	default:
14543d7072f8Seschrock 		error = EINVAL;
14553d7072f8Seschrock 	}
14563d7072f8Seschrock 	zc->zc_cookie = newstate;
1457fa9e4066Sahrens 	spa_close(spa, FTAG);
1458fa9e4066Sahrens 	return (error);
1459fa9e4066Sahrens }
1460fa9e4066Sahrens 
1461fa9e4066Sahrens static int
1462fa9e4066Sahrens zfs_ioc_vdev_attach(zfs_cmd_t *zc)
1463fa9e4066Sahrens {
1464fa9e4066Sahrens 	spa_t *spa;
1465fa9e4066Sahrens 	int replacing = zc->zc_cookie;
1466fa9e4066Sahrens 	nvlist_t *config;
1467fa9e4066Sahrens 	int error;
1468fa9e4066Sahrens 
146906eeb2adSek 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
1470fa9e4066Sahrens 		return (error);
1471fa9e4066Sahrens 
1472990b4856Slling 	if ((error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size,
1473478ed9adSEric Taylor 	    zc->zc_iflags, &config)) == 0) {
1474ea8dc4b6Seschrock 		error = spa_vdev_attach(spa, zc->zc_guid, config, replacing);
1475fa9e4066Sahrens 		nvlist_free(config);
1476fa9e4066Sahrens 	}
1477fa9e4066Sahrens 
1478fa9e4066Sahrens 	spa_close(spa, FTAG);
1479fa9e4066Sahrens 	return (error);
1480fa9e4066Sahrens }
1481fa9e4066Sahrens 
1482fa9e4066Sahrens static int
1483fa9e4066Sahrens zfs_ioc_vdev_detach(zfs_cmd_t *zc)
1484fa9e4066Sahrens {
1485fa9e4066Sahrens 	spa_t *spa;
1486fa9e4066Sahrens 	int error;
1487fa9e4066Sahrens 
148806eeb2adSek 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
1489fa9e4066Sahrens 		return (error);
1490fa9e4066Sahrens 
14918ad4d6ddSJeff Bonwick 	error = spa_vdev_detach(spa, zc->zc_guid, 0, B_FALSE);
1492fa9e4066Sahrens 
1493fa9e4066Sahrens 	spa_close(spa, FTAG);
1494fa9e4066Sahrens 	return (error);
1495fa9e4066Sahrens }
1496fa9e4066Sahrens 
14971195e687SMark J Musante static int
14981195e687SMark J Musante zfs_ioc_vdev_split(zfs_cmd_t *zc)
14991195e687SMark J Musante {
15001195e687SMark J Musante 	spa_t *spa;
15011195e687SMark J Musante 	nvlist_t *config, *props = NULL;
15021195e687SMark J Musante 	int error;
15031195e687SMark J Musante 	boolean_t exp = !!(zc->zc_cookie & ZPOOL_EXPORT_AFTER_SPLIT);
15041195e687SMark J Musante 
15051195e687SMark J Musante 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
15061195e687SMark J Musante 		return (error);
15071195e687SMark J Musante 
15081195e687SMark J Musante 	if (error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size,
15091195e687SMark J Musante 	    zc->zc_iflags, &config)) {
15101195e687SMark J Musante 		spa_close(spa, FTAG);
15111195e687SMark J Musante 		return (error);
15121195e687SMark J Musante 	}
15131195e687SMark J Musante 
15141195e687SMark J Musante 	if (zc->zc_nvlist_src_size != 0 && (error =
15151195e687SMark J Musante 	    get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
15161195e687SMark J Musante 	    zc->zc_iflags, &props))) {
15171195e687SMark J Musante 		spa_close(spa, FTAG);
15181195e687SMark J Musante 		nvlist_free(config);
15191195e687SMark J Musante 		return (error);
15201195e687SMark J Musante 	}
15211195e687SMark J Musante 
15221195e687SMark J Musante 	error = spa_vdev_split_mirror(spa, zc->zc_string, config, props, exp);
15231195e687SMark J Musante 
15241195e687SMark J Musante 	spa_close(spa, FTAG);
15251195e687SMark J Musante 
15261195e687SMark J Musante 	nvlist_free(config);
15271195e687SMark J Musante 	nvlist_free(props);
15281195e687SMark J Musante 
15291195e687SMark J Musante 	return (error);
15301195e687SMark J Musante }
15311195e687SMark J Musante 
1532c67d9675Seschrock static int
1533c67d9675Seschrock zfs_ioc_vdev_setpath(zfs_cmd_t *zc)
1534c67d9675Seschrock {
1535c67d9675Seschrock 	spa_t *spa;
1536e9dbad6fSeschrock 	char *path = zc->zc_value;
1537ea8dc4b6Seschrock 	uint64_t guid = zc->zc_guid;
1538c67d9675Seschrock 	int error;
1539c67d9675Seschrock 
1540c67d9675Seschrock 	error = spa_open(zc->zc_name, &spa, FTAG);
1541c67d9675Seschrock 	if (error != 0)
1542c67d9675Seschrock 		return (error);
1543c67d9675Seschrock 
1544c67d9675Seschrock 	error = spa_vdev_setpath(spa, guid, path);
1545c67d9675Seschrock 	spa_close(spa, FTAG);
1546c67d9675Seschrock 	return (error);
1547c67d9675Seschrock }
1548c67d9675Seschrock 
15496809eb4eSEric Schrock static int
15506809eb4eSEric Schrock zfs_ioc_vdev_setfru(zfs_cmd_t *zc)
15516809eb4eSEric Schrock {
15526809eb4eSEric Schrock 	spa_t *spa;
15536809eb4eSEric Schrock 	char *fru = zc->zc_value;
15546809eb4eSEric Schrock 	uint64_t guid = zc->zc_guid;
15556809eb4eSEric Schrock 	int error;
15566809eb4eSEric Schrock 
15576809eb4eSEric Schrock 	error = spa_open(zc->zc_name, &spa, FTAG);
15586809eb4eSEric Schrock 	if (error != 0)
15596809eb4eSEric Schrock 		return (error);
15606809eb4eSEric Schrock 
15616809eb4eSEric Schrock 	error = spa_vdev_setfru(spa, guid, fru);
15626809eb4eSEric Schrock 	spa_close(spa, FTAG);
15636809eb4eSEric Schrock 	return (error);
15646809eb4eSEric Schrock }
15656809eb4eSEric Schrock 
15663cb34c60Sahrens /*
15673cb34c60Sahrens  * inputs:
15683cb34c60Sahrens  * zc_name		name of filesystem
15693cb34c60Sahrens  * zc_nvlist_dst_size	size of buffer for property nvlist
15703cb34c60Sahrens  *
15713cb34c60Sahrens  * outputs:
15723cb34c60Sahrens  * zc_objset_stats	stats
15733cb34c60Sahrens  * zc_nvlist_dst	property nvlist
15743cb34c60Sahrens  * zc_nvlist_dst_size	size of property nvlist
15753cb34c60Sahrens  */
1576fa9e4066Sahrens static int
1577fa9e4066Sahrens zfs_ioc_objset_stats(zfs_cmd_t *zc)
1578fa9e4066Sahrens {
1579fa9e4066Sahrens 	objset_t *os = NULL;
1580fa9e4066Sahrens 	int error;
15817f7322feSeschrock 	nvlist_t *nv;
1582fa9e4066Sahrens 
1583503ad85cSMatthew Ahrens 	if (error = dmu_objset_hold(zc->zc_name, FTAG, &os))
1584fa9e4066Sahrens 		return (error);
1585fa9e4066Sahrens 
1586a2eea2e1Sahrens 	dmu_objset_fast_stat(os, &zc->zc_objset_stats);
1587fa9e4066Sahrens 
15885ad82045Snd 	if (zc->zc_nvlist_dst != 0 &&
158992241e0bSTom Erickson 	    (error = dsl_prop_get_all(os, &nv)) == 0) {
1590a2eea2e1Sahrens 		dmu_objset_stats(os, nv);
1591432f72fdSahrens 		/*
1592bd00f61bSrm 		 * NB: zvol_get_stats() will read the objset contents,
1593432f72fdSahrens 		 * which we aren't supposed to do with a
1594745cd3c5Smaybee 		 * DS_MODE_USER hold, because it could be
1595432f72fdSahrens 		 * inconsistent.  So this is a bit of a workaround...
1596503ad85cSMatthew Ahrens 		 * XXX reading with out owning
1597432f72fdSahrens 		 */
1598e7437265Sahrens 		if (!zc->zc_objset_stats.dds_inconsistent) {
1599e7437265Sahrens 			if (dmu_objset_type(os) == DMU_OST_ZVOL)
1600e7437265Sahrens 				VERIFY(zvol_get_stats(os, nv) == 0);
1601e7437265Sahrens 		}
1602e9dbad6fSeschrock 		error = put_nvlist(zc, nv);
16037f7322feSeschrock 		nvlist_free(nv);
16047f7322feSeschrock 	}
1605fa9e4066Sahrens 
1606503ad85cSMatthew Ahrens 	dmu_objset_rele(os, FTAG);
1607fa9e4066Sahrens 	return (error);
1608fa9e4066Sahrens }
1609fa9e4066Sahrens 
161092241e0bSTom Erickson /*
161192241e0bSTom Erickson  * inputs:
161292241e0bSTom Erickson  * zc_name		name of filesystem
161392241e0bSTom Erickson  * zc_nvlist_dst_size	size of buffer for property nvlist
161492241e0bSTom Erickson  *
161592241e0bSTom Erickson  * outputs:
161692241e0bSTom Erickson  * zc_nvlist_dst	received property nvlist
161792241e0bSTom Erickson  * zc_nvlist_dst_size	size of received property nvlist
161892241e0bSTom Erickson  *
161992241e0bSTom Erickson  * Gets received properties (distinct from local properties on or after
162092241e0bSTom Erickson  * SPA_VERSION_RECVD_PROPS) for callers who want to differentiate received from
162192241e0bSTom Erickson  * local property values.
162292241e0bSTom Erickson  */
162392241e0bSTom Erickson static int
162492241e0bSTom Erickson zfs_ioc_objset_recvd_props(zfs_cmd_t *zc)
162592241e0bSTom Erickson {
162692241e0bSTom Erickson 	objset_t *os = NULL;
162792241e0bSTom Erickson 	int error;
162892241e0bSTom Erickson 	nvlist_t *nv;
162992241e0bSTom Erickson 
163092241e0bSTom Erickson 	if (error = dmu_objset_hold(zc->zc_name, FTAG, &os))
163192241e0bSTom Erickson 		return (error);
163292241e0bSTom Erickson 
163392241e0bSTom Erickson 	/*
163492241e0bSTom Erickson 	 * Without this check, we would return local property values if the
163592241e0bSTom Erickson 	 * caller has not already received properties on or after
163692241e0bSTom Erickson 	 * SPA_VERSION_RECVD_PROPS.
163792241e0bSTom Erickson 	 */
163892241e0bSTom Erickson 	if (!dsl_prop_get_hasrecvd(os)) {
163992241e0bSTom Erickson 		dmu_objset_rele(os, FTAG);
164092241e0bSTom Erickson 		return (ENOTSUP);
164192241e0bSTom Erickson 	}
164292241e0bSTom Erickson 
164392241e0bSTom Erickson 	if (zc->zc_nvlist_dst != 0 &&
164492241e0bSTom Erickson 	    (error = dsl_prop_get_received(os, &nv)) == 0) {
164592241e0bSTom Erickson 		error = put_nvlist(zc, nv);
164692241e0bSTom Erickson 		nvlist_free(nv);
164792241e0bSTom Erickson 	}
164892241e0bSTom Erickson 
164992241e0bSTom Erickson 	dmu_objset_rele(os, FTAG);
165092241e0bSTom Erickson 	return (error);
165192241e0bSTom Erickson }
165292241e0bSTom Erickson 
1653de8267e0Stimh static int
1654de8267e0Stimh nvl_add_zplprop(objset_t *os, nvlist_t *props, zfs_prop_t prop)
1655de8267e0Stimh {
1656de8267e0Stimh 	uint64_t value;
1657de8267e0Stimh 	int error;
1658de8267e0Stimh 
1659de8267e0Stimh 	/*
1660de8267e0Stimh 	 * zfs_get_zplprop() will either find a value or give us
1661de8267e0Stimh 	 * the default value (if there is one).
1662de8267e0Stimh 	 */
1663de8267e0Stimh 	if ((error = zfs_get_zplprop(os, prop, &value)) != 0)
1664de8267e0Stimh 		return (error);
1665de8267e0Stimh 	VERIFY(nvlist_add_uint64(props, zfs_prop_to_name(prop), value) == 0);
1666de8267e0Stimh 	return (0);
1667de8267e0Stimh }
1668de8267e0Stimh 
16693cb34c60Sahrens /*
16703cb34c60Sahrens  * inputs:
16713cb34c60Sahrens  * zc_name		name of filesystem
1672de8267e0Stimh  * zc_nvlist_dst_size	size of buffer for zpl property nvlist
16733cb34c60Sahrens  *
16743cb34c60Sahrens  * outputs:
1675de8267e0Stimh  * zc_nvlist_dst	zpl property nvlist
1676de8267e0Stimh  * zc_nvlist_dst_size	size of zpl property nvlist
16773cb34c60Sahrens  */
1678bd00f61bSrm static int
1679de8267e0Stimh zfs_ioc_objset_zplprops(zfs_cmd_t *zc)
1680bd00f61bSrm {
1681de8267e0Stimh 	objset_t *os;
1682de8267e0Stimh 	int err;
1683bd00f61bSrm 
1684503ad85cSMatthew Ahrens 	/* XXX reading without owning */
1685503ad85cSMatthew Ahrens 	if (err = dmu_objset_hold(zc->zc_name, FTAG, &os))
1686de8267e0Stimh 		return (err);
1687bd00f61bSrm 
1688bd00f61bSrm 	dmu_objset_fast_stat(os, &zc->zc_objset_stats);
1689bd00f61bSrm 
1690bd00f61bSrm 	/*
1691de8267e0Stimh 	 * NB: nvl_add_zplprop() will read the objset contents,
1692745cd3c5Smaybee 	 * which we aren't supposed to do with a DS_MODE_USER
1693745cd3c5Smaybee 	 * hold, because it could be inconsistent.
1694bd00f61bSrm 	 */
1695de8267e0Stimh 	if (zc->zc_nvlist_dst != NULL &&
1696de8267e0Stimh 	    !zc->zc_objset_stats.dds_inconsistent &&
1697de8267e0Stimh 	    dmu_objset_type(os) == DMU_OST_ZFS) {
1698de8267e0Stimh 		nvlist_t *nv;
1699de8267e0Stimh 
1700de8267e0Stimh 		VERIFY(nvlist_alloc(&nv, NV_UNIQUE_NAME, KM_SLEEP) == 0);
1701de8267e0Stimh 		if ((err = nvl_add_zplprop(os, nv, ZFS_PROP_VERSION)) == 0 &&
1702de8267e0Stimh 		    (err = nvl_add_zplprop(os, nv, ZFS_PROP_NORMALIZE)) == 0 &&
1703de8267e0Stimh 		    (err = nvl_add_zplprop(os, nv, ZFS_PROP_UTF8ONLY)) == 0 &&
1704de8267e0Stimh 		    (err = nvl_add_zplprop(os, nv, ZFS_PROP_CASE)) == 0)
1705de8267e0Stimh 			err = put_nvlist(zc, nv);
1706de8267e0Stimh 		nvlist_free(nv);
1707de8267e0Stimh 	} else {
1708de8267e0Stimh 		err = ENOENT;
1709de8267e0Stimh 	}
1710503ad85cSMatthew Ahrens 	dmu_objset_rele(os, FTAG);
1711de8267e0Stimh 	return (err);
1712bd00f61bSrm }
1713bd00f61bSrm 
171414843421SMatthew Ahrens static boolean_t
171514843421SMatthew Ahrens dataset_name_hidden(const char *name)
171614843421SMatthew Ahrens {
171714843421SMatthew Ahrens 	/*
171814843421SMatthew Ahrens 	 * Skip over datasets that are not visible in this zone,
171914843421SMatthew Ahrens 	 * internal datasets (which have a $ in their name), and
172014843421SMatthew Ahrens 	 * temporary datasets (which have a % in their name).
172114843421SMatthew Ahrens 	 */
172214843421SMatthew Ahrens 	if (strchr(name, '$') != NULL)
172314843421SMatthew Ahrens 		return (B_TRUE);
172414843421SMatthew Ahrens 	if (strchr(name, '%') != NULL)
172514843421SMatthew Ahrens 		return (B_TRUE);
172614843421SMatthew Ahrens 	if (!INGLOBALZONE(curproc) && !zone_dataset_visible(name, NULL))
172714843421SMatthew Ahrens 		return (B_TRUE);
172814843421SMatthew Ahrens 	return (B_FALSE);
172914843421SMatthew Ahrens }
173014843421SMatthew Ahrens 
1731de8267e0Stimh /*
1732de8267e0Stimh  * inputs:
1733de8267e0Stimh  * zc_name		name of filesystem
1734de8267e0Stimh  * zc_cookie		zap cursor
1735de8267e0Stimh  * zc_nvlist_dst_size	size of buffer for property nvlist
1736de8267e0Stimh  *
1737de8267e0Stimh  * outputs:
1738de8267e0Stimh  * zc_name		name of next filesystem
173914843421SMatthew Ahrens  * zc_cookie		zap cursor
1740de8267e0Stimh  * zc_objset_stats	stats
1741de8267e0Stimh  * zc_nvlist_dst	property nvlist
1742de8267e0Stimh  * zc_nvlist_dst_size	size of property nvlist
1743de8267e0Stimh  */
1744fa9e4066Sahrens static int
1745fa9e4066Sahrens zfs_ioc_dataset_list_next(zfs_cmd_t *zc)
1746fa9e4066Sahrens {
174787e5029aSahrens 	objset_t *os;
1748fa9e4066Sahrens 	int error;
1749fa9e4066Sahrens 	char *p;
1750620252bcSChris Kirby 	size_t orig_len = strlen(zc->zc_name);
1751fa9e4066Sahrens 
1752620252bcSChris Kirby top:
1753503ad85cSMatthew Ahrens 	if (error = dmu_objset_hold(zc->zc_name, FTAG, &os)) {
175487e5029aSahrens 		if (error == ENOENT)
175587e5029aSahrens 			error = ESRCH;
175687e5029aSahrens 		return (error);
1757fa9e4066Sahrens 	}
1758fa9e4066Sahrens 
1759fa9e4066Sahrens 	p = strrchr(zc->zc_name, '/');
1760fa9e4066Sahrens 	if (p == NULL || p[1] != '\0')
1761fa9e4066Sahrens 		(void) strlcat(zc->zc_name, "/", sizeof (zc->zc_name));
1762fa9e4066Sahrens 	p = zc->zc_name + strlen(zc->zc_name);
1763fa9e4066Sahrens 
17645c0b6a79SRich Morris 	/*
17655c0b6a79SRich Morris 	 * Pre-fetch the datasets.  dmu_objset_prefetch() always returns 0
17665c0b6a79SRich Morris 	 * but is not declared void because its called by dmu_objset_find().
17675c0b6a79SRich Morris 	 */
17687f73c863SRich Morris 	if (zc->zc_cookie == 0) {
17697f73c863SRich Morris 		uint64_t cookie = 0;
17707f73c863SRich Morris 		int len = sizeof (zc->zc_name) - (p - zc->zc_name);
17717f73c863SRich Morris 
17727f73c863SRich Morris 		while (dmu_dir_list_next(os, len, p, NULL, &cookie) == 0)
17735c0b6a79SRich Morris 			(void) dmu_objset_prefetch(p, NULL);
17747f73c863SRich Morris 	}
17757f73c863SRich Morris 
1776fa9e4066Sahrens 	do {
177787e5029aSahrens 		error = dmu_dir_list_next(os,
177887e5029aSahrens 		    sizeof (zc->zc_name) - (p - zc->zc_name), p,
177987e5029aSahrens 		    NULL, &zc->zc_cookie);
1780fa9e4066Sahrens 		if (error == ENOENT)
1781fa9e4066Sahrens 			error = ESRCH;
1782681d9761SEric Taylor 	} while (error == 0 && dataset_name_hidden(zc->zc_name) &&
1783681d9761SEric Taylor 	    !(zc->zc_iflags & FKIOCTL));
1784503ad85cSMatthew Ahrens 	dmu_objset_rele(os, FTAG);
1785fa9e4066Sahrens 
1786681d9761SEric Taylor 	/*
1787681d9761SEric Taylor 	 * If it's an internal dataset (ie. with a '$' in its name),
1788681d9761SEric Taylor 	 * don't try to get stats for it, otherwise we'll return ENOENT.
1789681d9761SEric Taylor 	 */
1790620252bcSChris Kirby 	if (error == 0 && strchr(zc->zc_name, '$') == NULL) {
179187e5029aSahrens 		error = zfs_ioc_objset_stats(zc); /* fill in the stats */
1792620252bcSChris Kirby 		if (error == ENOENT) {
1793620252bcSChris Kirby 			/* We lost a race with destroy, get the next one. */
1794620252bcSChris Kirby 			zc->zc_name[orig_len] = '\0';
1795620252bcSChris Kirby 			goto top;
1796620252bcSChris Kirby 		}
1797620252bcSChris Kirby 	}
1798fa9e4066Sahrens 	return (error);
1799fa9e4066Sahrens }
1800fa9e4066Sahrens 
18013cb34c60Sahrens /*
18023cb34c60Sahrens  * inputs:
18033cb34c60Sahrens  * zc_name		name of filesystem
18043cb34c60Sahrens  * zc_cookie		zap cursor
18053cb34c60Sahrens  * zc_nvlist_dst_size	size of buffer for property nvlist
18063cb34c60Sahrens  *
18073cb34c60Sahrens  * outputs:
18083cb34c60Sahrens  * zc_name		name of next snapshot
18093cb34c60Sahrens  * zc_objset_stats	stats
18103cb34c60Sahrens  * zc_nvlist_dst	property nvlist
18113cb34c60Sahrens  * zc_nvlist_dst_size	size of property nvlist
18123cb34c60Sahrens  */
1813fa9e4066Sahrens static int
1814fa9e4066Sahrens zfs_ioc_snapshot_list_next(zfs_cmd_t *zc)
1815fa9e4066Sahrens {
181687e5029aSahrens 	objset_t *os;
1817fa9e4066Sahrens 	int error;
1818fa9e4066Sahrens 
1819620252bcSChris Kirby top:
18207cbf8b43SRich Morris 	if (zc->zc_cookie == 0)
18217cbf8b43SRich Morris 		(void) dmu_objset_find(zc->zc_name, dmu_objset_prefetch,
18227cbf8b43SRich Morris 		    NULL, DS_FIND_SNAPSHOTS);
18237cbf8b43SRich Morris 
1824503ad85cSMatthew Ahrens 	error = dmu_objset_hold(zc->zc_name, FTAG, &os);
1825745cd3c5Smaybee 	if (error)
1826745cd3c5Smaybee 		return (error == ENOENT ? ESRCH : error);
1827fa9e4066Sahrens 
1828b81d61a6Slling 	/*
1829b81d61a6Slling 	 * A dataset name of maximum length cannot have any snapshots,
1830b81d61a6Slling 	 * so exit immediately.
1831b81d61a6Slling 	 */
1832b81d61a6Slling 	if (strlcat(zc->zc_name, "@", sizeof (zc->zc_name)) >= MAXNAMELEN) {
1833503ad85cSMatthew Ahrens 		dmu_objset_rele(os, FTAG);
1834b81d61a6Slling 		return (ESRCH);
1835fa9e4066Sahrens 	}
1836fa9e4066Sahrens 
183787e5029aSahrens 	error = dmu_snapshot_list_next(os,
183887e5029aSahrens 	    sizeof (zc->zc_name) - strlen(zc->zc_name),
1839b38f0970Sck 	    zc->zc_name + strlen(zc->zc_name), NULL, &zc->zc_cookie, NULL);
1840503ad85cSMatthew Ahrens 	dmu_objset_rele(os, FTAG);
1841620252bcSChris Kirby 	if (error == 0) {
184287e5029aSahrens 		error = zfs_ioc_objset_stats(zc); /* fill in the stats */
1843620252bcSChris Kirby 		if (error == ENOENT)  {
1844620252bcSChris Kirby 			/* We lost a race with destroy, get the next one. */
1845620252bcSChris Kirby 			*strchr(zc->zc_name, '@') = '\0';
1846620252bcSChris Kirby 			goto top;
1847620252bcSChris Kirby 		}
1848620252bcSChris Kirby 	} else if (error == ENOENT) {
1849745cd3c5Smaybee 		error = ESRCH;
1850620252bcSChris Kirby 	}
1851fa9e4066Sahrens 
18523cb34c60Sahrens 	/* if we failed, undo the @ that we tacked on to zc_name */
1853745cd3c5Smaybee 	if (error)
18543cb34c60Sahrens 		*strchr(zc->zc_name, '@') = '\0';
1855fa9e4066Sahrens 	return (error);
1856fa9e4066Sahrens }
1857fa9e4066Sahrens 
185892241e0bSTom Erickson static int
185992241e0bSTom Erickson zfs_prop_set_userquota(const char *dsname, nvpair_t *pair)
1860fa9e4066Sahrens {
186192241e0bSTom Erickson 	const char *propname = nvpair_name(pair);
186292241e0bSTom Erickson 	uint64_t *valary;
186392241e0bSTom Erickson 	unsigned int vallen;
186492241e0bSTom Erickson 	const char *domain;
1865eeb85002STim Haley 	char *dash;
186692241e0bSTom Erickson 	zfs_userquota_prop_t type;
186792241e0bSTom Erickson 	uint64_t rid;
186892241e0bSTom Erickson 	uint64_t quota;
186992241e0bSTom Erickson 	zfsvfs_t *zfsvfs;
187092241e0bSTom Erickson 	int err;
187192241e0bSTom Erickson 
187292241e0bSTom Erickson 	if (nvpair_type(pair) == DATA_TYPE_NVLIST) {
187392241e0bSTom Erickson 		nvlist_t *attrs;
187492241e0bSTom Erickson 		VERIFY(nvpair_value_nvlist(pair, &attrs) == 0);
1875eeb85002STim Haley 		if (nvlist_lookup_nvpair(attrs, ZPROP_VALUE,
1876eeb85002STim Haley 		    &pair) != 0)
1877eeb85002STim Haley 			return (EINVAL);
187892241e0bSTom Erickson 	}
1879e9dbad6fSeschrock 
1880ecd6cf80Smarks 	/*
1881eeb85002STim Haley 	 * A correctly constructed propname is encoded as
188292241e0bSTom Erickson 	 * userquota@<rid>-<domain>.
1883ecd6cf80Smarks 	 */
1884eeb85002STim Haley 	if ((dash = strchr(propname, '-')) == NULL ||
1885eeb85002STim Haley 	    nvpair_value_uint64_array(pair, &valary, &vallen) != 0 ||
1886eeb85002STim Haley 	    vallen != 3)
1887eeb85002STim Haley 		return (EINVAL);
1888eeb85002STim Haley 
1889eeb85002STim Haley 	domain = dash + 1;
1890eeb85002STim Haley 	type = valary[0];
1891eeb85002STim Haley 	rid = valary[1];
1892eeb85002STim Haley 	quota = valary[2];
1893e9dbad6fSeschrock 
189492241e0bSTom Erickson 	err = zfsvfs_hold(dsname, FTAG, &zfsvfs);
189592241e0bSTom Erickson 	if (err == 0) {
189692241e0bSTom Erickson 		err = zfs_set_userquota(zfsvfs, type, domain, rid, quota);
189792241e0bSTom Erickson 		zfsvfs_rele(zfsvfs, FTAG);
189892241e0bSTom Erickson 	}
1899e9dbad6fSeschrock 
190092241e0bSTom Erickson 	return (err);
190192241e0bSTom Erickson }
190214843421SMatthew Ahrens 
190392241e0bSTom Erickson /*
190492241e0bSTom Erickson  * If the named property is one that has a special function to set its value,
190592241e0bSTom Erickson  * return 0 on success and a positive error code on failure; otherwise if it is
190692241e0bSTom Erickson  * not one of the special properties handled by this function, return -1.
190792241e0bSTom Erickson  *
1908eeb85002STim Haley  * XXX: It would be better for callers of the property interface if we handled
190992241e0bSTom Erickson  * these special cases in dsl_prop.c (in the dsl layer).
191092241e0bSTom Erickson  */
191192241e0bSTom Erickson static int
191292241e0bSTom Erickson zfs_prop_set_special(const char *dsname, zprop_source_t source,
191392241e0bSTom Erickson     nvpair_t *pair)
191492241e0bSTom Erickson {
191592241e0bSTom Erickson 	const char *propname = nvpair_name(pair);
191692241e0bSTom Erickson 	zfs_prop_t prop = zfs_name_to_prop(propname);
191792241e0bSTom Erickson 	uint64_t intval;
191892241e0bSTom Erickson 	int err;
1919fa9e4066Sahrens 
192092241e0bSTom Erickson 	if (prop == ZPROP_INVAL) {
192192241e0bSTom Erickson 		if (zfs_prop_userquota(propname))
192292241e0bSTom Erickson 			return (zfs_prop_set_userquota(dsname, pair));
192392241e0bSTom Erickson 		return (-1);
192492241e0bSTom Erickson 	}
192514843421SMatthew Ahrens 
192692241e0bSTom Erickson 	if (nvpair_type(pair) == DATA_TYPE_NVLIST) {
192792241e0bSTom Erickson 		nvlist_t *attrs;
192892241e0bSTom Erickson 		VERIFY(nvpair_value_nvlist(pair, &attrs) == 0);
192992241e0bSTom Erickson 		VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE,
193092241e0bSTom Erickson 		    &pair) == 0);
193192241e0bSTom Erickson 	}
1932db870a07Sahrens 
193392241e0bSTom Erickson 	if (zfs_prop_get_type(prop) == PROP_TYPE_STRING)
193492241e0bSTom Erickson 		return (-1);
1935b24ab676SJeff Bonwick 
193692241e0bSTom Erickson 	VERIFY(0 == nvpair_value_uint64(pair, &intval));
193740feaa91Sahrens 
193892241e0bSTom Erickson 	switch (prop) {
193992241e0bSTom Erickson 	case ZFS_PROP_QUOTA:
194092241e0bSTom Erickson 		err = dsl_dir_set_quota(dsname, source, intval);
194192241e0bSTom Erickson 		break;
194292241e0bSTom Erickson 	case ZFS_PROP_REFQUOTA:
194392241e0bSTom Erickson 		err = dsl_dataset_set_quota(dsname, source, intval);
194492241e0bSTom Erickson 		break;
194592241e0bSTom Erickson 	case ZFS_PROP_RESERVATION:
194692241e0bSTom Erickson 		err = dsl_dir_set_reservation(dsname, source, intval);
194792241e0bSTom Erickson 		break;
194892241e0bSTom Erickson 	case ZFS_PROP_REFRESERVATION:
194992241e0bSTom Erickson 		err = dsl_dataset_set_reservation(dsname, source, intval);
195092241e0bSTom Erickson 		break;
195192241e0bSTom Erickson 	case ZFS_PROP_VOLSIZE:
195292241e0bSTom Erickson 		err = zvol_set_volsize(dsname, ddi_driver_major(zfs_dip),
195392241e0bSTom Erickson 		    intval);
195492241e0bSTom Erickson 		break;
195592241e0bSTom Erickson 	case ZFS_PROP_VERSION:
195692241e0bSTom Erickson 	{
195792241e0bSTom Erickson 		zfsvfs_t *zfsvfs;
19589e6eda55Smarks 
195992241e0bSTom Erickson 		if ((err = zfsvfs_hold(dsname, FTAG, &zfsvfs)) != 0)
1960b24ab676SJeff Bonwick 			break;
1961b24ab676SJeff Bonwick 
196292241e0bSTom Erickson 		err = zfs_set_version(zfsvfs, intval);
196392241e0bSTom Erickson 		zfsvfs_rele(zfsvfs, FTAG);
1964d0f3f37eSMark Shellenbaum 
196592241e0bSTom Erickson 		if (err == 0 && intval >= ZPL_VERSION_USERSPACE) {
1966b16da2e2SGeorge Wilson 			zfs_cmd_t *zc;
1967b16da2e2SGeorge Wilson 
1968b16da2e2SGeorge Wilson 			zc = kmem_zalloc(sizeof (zfs_cmd_t), KM_SLEEP);
1969b16da2e2SGeorge Wilson 			(void) strcpy(zc->zc_name, dsname);
1970b16da2e2SGeorge Wilson 			(void) zfs_ioc_userspace_upgrade(zc);
1971b16da2e2SGeorge Wilson 			kmem_free(zc, sizeof (zfs_cmd_t));
197240feaa91Sahrens 		}
197392241e0bSTom Erickson 		break;
1974ecd6cf80Smarks 	}
1975ecd6cf80Smarks 
197692241e0bSTom Erickson 	default:
197792241e0bSTom Erickson 		err = -1;
197892241e0bSTom Erickson 	}
1979e9dbad6fSeschrock 
198092241e0bSTom Erickson 	return (err);
198192241e0bSTom Erickson }
1982a9799022Sck 
198392241e0bSTom Erickson /*
198492241e0bSTom Erickson  * This function is best effort. If it fails to set any of the given properties,
198592241e0bSTom Erickson  * it continues to set as many as it can and returns the first error
198692241e0bSTom Erickson  * encountered. If the caller provides a non-NULL errlist, it also gives the
198792241e0bSTom Erickson  * complete list of names of all the properties it failed to set along with the
198892241e0bSTom Erickson  * corresponding error numbers. The caller is responsible for freeing the
198992241e0bSTom Erickson  * returned errlist.
199092241e0bSTom Erickson  *
199192241e0bSTom Erickson  * If every property is set successfully, zero is returned and the list pointed
199292241e0bSTom Erickson  * at by errlist is NULL.
199392241e0bSTom Erickson  */
199492241e0bSTom Erickson int
199592241e0bSTom Erickson zfs_set_prop_nvlist(const char *dsname, zprop_source_t source, nvlist_t *nvl,
199692241e0bSTom Erickson     nvlist_t **errlist)
199792241e0bSTom Erickson {
199892241e0bSTom Erickson 	nvpair_t *pair;
199992241e0bSTom Erickson 	nvpair_t *propval;
200002e383d1STom Erickson 	int rv = 0;
200192241e0bSTom Erickson 	uint64_t intval;
200292241e0bSTom Erickson 	char *strval;
200392241e0bSTom Erickson 	nvlist_t *genericnvl;
200492241e0bSTom Erickson 	nvlist_t *errors;
200592241e0bSTom Erickson 	nvlist_t *retrynvl;
2006e9dbad6fSeschrock 
200792241e0bSTom Erickson 	VERIFY(nvlist_alloc(&genericnvl, NV_UNIQUE_NAME, KM_SLEEP) == 0);
200892241e0bSTom Erickson 	VERIFY(nvlist_alloc(&errors, NV_UNIQUE_NAME, KM_SLEEP) == 0);
200992241e0bSTom Erickson 	VERIFY(nvlist_alloc(&retrynvl, NV_UNIQUE_NAME, KM_SLEEP) == 0);
2010a9799022Sck 
201192241e0bSTom Erickson retry:
201292241e0bSTom Erickson 	pair = NULL;
201392241e0bSTom Erickson 	while ((pair = nvlist_next_nvpair(nvl, pair)) != NULL) {
201492241e0bSTom Erickson 		const char *propname = nvpair_name(pair);
201592241e0bSTom Erickson 		zfs_prop_t prop = zfs_name_to_prop(propname);
2016cfa69fd2STom Erickson 		int err = 0;
2017e9dbad6fSeschrock 
201892241e0bSTom Erickson 		/* decode the property value */
201992241e0bSTom Erickson 		propval = pair;
202092241e0bSTom Erickson 		if (nvpair_type(pair) == DATA_TYPE_NVLIST) {
202192241e0bSTom Erickson 			nvlist_t *attrs;
202292241e0bSTom Erickson 			VERIFY(nvpair_value_nvlist(pair, &attrs) == 0);
2023eeb85002STim Haley 			if (nvlist_lookup_nvpair(attrs, ZPROP_VALUE,
2024eeb85002STim Haley 			    &propval) != 0)
2025eeb85002STim Haley 				err = EINVAL;
202614843421SMatthew Ahrens 		}
2027e9dbad6fSeschrock 
202892241e0bSTom Erickson 		/* Validate value type */
2029eeb85002STim Haley 		if (err == 0 && prop == ZPROP_INVAL) {
203092241e0bSTom Erickson 			if (zfs_prop_user(propname)) {
203192241e0bSTom Erickson 				if (nvpair_type(propval) != DATA_TYPE_STRING)
203292241e0bSTom Erickson 					err = EINVAL;
203392241e0bSTom Erickson 			} else if (zfs_prop_userquota(propname)) {
203492241e0bSTom Erickson 				if (nvpair_type(propval) !=
203592241e0bSTom Erickson 				    DATA_TYPE_UINT64_ARRAY)
203692241e0bSTom Erickson 					err = EINVAL;
20374201a95eSRic Aleshire 			}
2038eeb85002STim Haley 		} else if (err == 0) {
203992241e0bSTom Erickson 			if (nvpair_type(propval) == DATA_TYPE_STRING) {
204092241e0bSTom Erickson 				if (zfs_prop_get_type(prop) != PROP_TYPE_STRING)
204192241e0bSTom Erickson 					err = EINVAL;
204292241e0bSTom Erickson 			} else if (nvpair_type(propval) == DATA_TYPE_UINT64) {
2043a2eea2e1Sahrens 				const char *unused;
2044a2eea2e1Sahrens 
204592241e0bSTom Erickson 				VERIFY(nvpair_value_uint64(propval,
204692241e0bSTom Erickson 				    &intval) == 0);
2047e9dbad6fSeschrock 
2048e9dbad6fSeschrock 				switch (zfs_prop_get_type(prop)) {
204991ebeef5Sahrens 				case PROP_TYPE_NUMBER:
2050e9dbad6fSeschrock 					break;
205191ebeef5Sahrens 				case PROP_TYPE_STRING:
205292241e0bSTom Erickson 					err = EINVAL;
205392241e0bSTom Erickson 					break;
205491ebeef5Sahrens 				case PROP_TYPE_INDEX:
2055acd76fe5Seschrock 					if (zfs_prop_index_to_string(prop,
205692241e0bSTom Erickson 					    intval, &unused) != 0)
205792241e0bSTom Erickson 						err = EINVAL;
2058e9dbad6fSeschrock 					break;
2059e9dbad6fSeschrock 				default:
2060e7437265Sahrens 					cmn_err(CE_PANIC,
2061e7437265Sahrens 					    "unknown property type");
2062e9dbad6fSeschrock 				}
2063e9dbad6fSeschrock 			} else {
206492241e0bSTom Erickson 				err = EINVAL;
2065e9dbad6fSeschrock 			}
2066e9dbad6fSeschrock 		}
206792241e0bSTom Erickson 
206892241e0bSTom Erickson 		/* Validate permissions */
206992241e0bSTom Erickson 		if (err == 0)
207092241e0bSTom Erickson 			err = zfs_check_settable(dsname, pair, CRED());
207192241e0bSTom Erickson 
207292241e0bSTom Erickson 		if (err == 0) {
207392241e0bSTom Erickson 			err = zfs_prop_set_special(dsname, source, pair);
207492241e0bSTom Erickson 			if (err == -1) {
207592241e0bSTom Erickson 				/*
207692241e0bSTom Erickson 				 * For better performance we build up a list of
207792241e0bSTom Erickson 				 * properties to set in a single transaction.
207892241e0bSTom Erickson 				 */
207992241e0bSTom Erickson 				err = nvlist_add_nvpair(genericnvl, pair);
208092241e0bSTom Erickson 			} else if (err != 0 && nvl != retrynvl) {
208192241e0bSTom Erickson 				/*
208292241e0bSTom Erickson 				 * This may be a spurious error caused by
208392241e0bSTom Erickson 				 * receiving quota and reservation out of order.
208492241e0bSTom Erickson 				 * Try again in a second pass.
208592241e0bSTom Erickson 				 */
208692241e0bSTom Erickson 				err = nvlist_add_nvpair(retrynvl, pair);
208792241e0bSTom Erickson 			}
208892241e0bSTom Erickson 		}
208992241e0bSTom Erickson 
209092241e0bSTom Erickson 		if (err != 0)
209192241e0bSTom Erickson 			VERIFY(nvlist_add_int32(errors, propname, err) == 0);
2092e9dbad6fSeschrock 	}
2093e9dbad6fSeschrock 
209492241e0bSTom Erickson 	if (nvl != retrynvl && !nvlist_empty(retrynvl)) {
209592241e0bSTom Erickson 		nvl = retrynvl;
209692241e0bSTom Erickson 		goto retry;
209792241e0bSTom Erickson 	}
209892241e0bSTom Erickson 
209992241e0bSTom Erickson 	if (!nvlist_empty(genericnvl) &&
210092241e0bSTom Erickson 	    dsl_props_set(dsname, source, genericnvl) != 0) {
210192241e0bSTom Erickson 		/*
210292241e0bSTom Erickson 		 * If this fails, we still want to set as many properties as we
210392241e0bSTom Erickson 		 * can, so try setting them individually.
210492241e0bSTom Erickson 		 */
210592241e0bSTom Erickson 		pair = NULL;
210692241e0bSTom Erickson 		while ((pair = nvlist_next_nvpair(genericnvl, pair)) != NULL) {
210792241e0bSTom Erickson 			const char *propname = nvpair_name(pair);
2108cfa69fd2STom Erickson 			int err = 0;
210992241e0bSTom Erickson 
211092241e0bSTom Erickson 			propval = pair;
211192241e0bSTom Erickson 			if (nvpair_type(pair) == DATA_TYPE_NVLIST) {
211292241e0bSTom Erickson 				nvlist_t *attrs;
211392241e0bSTom Erickson 				VERIFY(nvpair_value_nvlist(pair, &attrs) == 0);
211492241e0bSTom Erickson 				VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE,
211592241e0bSTom Erickson 				    &propval) == 0);
211692241e0bSTom Erickson 			}
211792241e0bSTom Erickson 
211892241e0bSTom Erickson 			if (nvpair_type(propval) == DATA_TYPE_STRING) {
211992241e0bSTom Erickson 				VERIFY(nvpair_value_string(propval,
212092241e0bSTom Erickson 				    &strval) == 0);
212192241e0bSTom Erickson 				err = dsl_prop_set(dsname, propname, source, 1,
212292241e0bSTom Erickson 				    strlen(strval) + 1, strval);
212392241e0bSTom Erickson 			} else {
212492241e0bSTom Erickson 				VERIFY(nvpair_value_uint64(propval,
212592241e0bSTom Erickson 				    &intval) == 0);
212692241e0bSTom Erickson 				err = dsl_prop_set(dsname, propname, source, 8,
212792241e0bSTom Erickson 				    1, &intval);
212892241e0bSTom Erickson 			}
212992241e0bSTom Erickson 
213092241e0bSTom Erickson 			if (err != 0) {
213192241e0bSTom Erickson 				VERIFY(nvlist_add_int32(errors, propname,
213292241e0bSTom Erickson 				    err) == 0);
213392241e0bSTom Erickson 			}
213492241e0bSTom Erickson 		}
21355c0b6a79SRich Morris 	}
21365c0b6a79SRich Morris 	nvlist_free(genericnvl);
213792241e0bSTom Erickson 	nvlist_free(retrynvl);
213892241e0bSTom Erickson 
213992241e0bSTom Erickson 	if ((pair = nvlist_next_nvpair(errors, NULL)) == NULL) {
214092241e0bSTom Erickson 		nvlist_free(errors);
214192241e0bSTom Erickson 		errors = NULL;
214292241e0bSTom Erickson 	} else {
214392241e0bSTom Erickson 		VERIFY(nvpair_value_int32(pair, &rv) == 0);
214492241e0bSTom Erickson 	}
214592241e0bSTom Erickson 
214692241e0bSTom Erickson 	if (errlist == NULL)
214792241e0bSTom Erickson 		nvlist_free(errors);
214892241e0bSTom Erickson 	else
214992241e0bSTom Erickson 		*errlist = errors;
215092241e0bSTom Erickson 
215192241e0bSTom Erickson 	return (rv);
2152fa9e4066Sahrens }
2153fa9e4066Sahrens 
2154ea2f5b9eSMatthew Ahrens /*
2155ea2f5b9eSMatthew Ahrens  * Check that all the properties are valid user properties.
2156ea2f5b9eSMatthew Ahrens  */
2157ea2f5b9eSMatthew Ahrens static int
2158ea2f5b9eSMatthew Ahrens zfs_check_userprops(char *fsname, nvlist_t *nvl)
2159ea2f5b9eSMatthew Ahrens {
216092241e0bSTom Erickson 	nvpair_t *pair = NULL;
2161ea2f5b9eSMatthew Ahrens 	int error = 0;
2162ea2f5b9eSMatthew Ahrens 
216392241e0bSTom Erickson 	while ((pair = nvlist_next_nvpair(nvl, pair)) != NULL) {
216492241e0bSTom Erickson 		const char *propname = nvpair_name(pair);
2165ea2f5b9eSMatthew Ahrens 		char *valstr;
2166ea2f5b9eSMatthew Ahrens 
2167ea2f5b9eSMatthew Ahrens 		if (!zfs_prop_user(propname) ||
216892241e0bSTom Erickson 		    nvpair_type(pair) != DATA_TYPE_STRING)
2169ea2f5b9eSMatthew Ahrens 			return (EINVAL);
2170ea2f5b9eSMatthew Ahrens 
2171ea2f5b9eSMatthew Ahrens 		if (error = zfs_secpolicy_write_perms(fsname,
2172ea2f5b9eSMatthew Ahrens 		    ZFS_DELEG_PERM_USERPROP, CRED()))
2173ea2f5b9eSMatthew Ahrens 			return (error);
2174ea2f5b9eSMatthew Ahrens 
2175ea2f5b9eSMatthew Ahrens 		if (strlen(propname) >= ZAP_MAXNAMELEN)
2176ea2f5b9eSMatthew Ahrens 			return (ENAMETOOLONG);
2177ea2f5b9eSMatthew Ahrens 
217892241e0bSTom Erickson 		VERIFY(nvpair_value_string(pair, &valstr) == 0);
2179ea2f5b9eSMatthew Ahrens 		if (strlen(valstr) >= ZAP_MAXVALUELEN)
2180ea2f5b9eSMatthew Ahrens 			return (E2BIG);
2181ea2f5b9eSMatthew Ahrens 	}
2182ea2f5b9eSMatthew Ahrens 	return (0);
2183ea2f5b9eSMatthew Ahrens }
2184ea2f5b9eSMatthew Ahrens 
218592241e0bSTom Erickson static void
218692241e0bSTom Erickson props_skip(nvlist_t *props, nvlist_t *skipped, nvlist_t **newprops)
218792241e0bSTom Erickson {
218892241e0bSTom Erickson 	nvpair_t *pair;
218992241e0bSTom Erickson 
219092241e0bSTom Erickson 	VERIFY(nvlist_alloc(newprops, NV_UNIQUE_NAME, KM_SLEEP) == 0);
219192241e0bSTom Erickson 
219292241e0bSTom Erickson 	pair = NULL;
219392241e0bSTom Erickson 	while ((pair = nvlist_next_nvpair(props, pair)) != NULL) {
219492241e0bSTom Erickson 		if (nvlist_exists(skipped, nvpair_name(pair)))
219592241e0bSTom Erickson 			continue;
219692241e0bSTom Erickson 
219792241e0bSTom Erickson 		VERIFY(nvlist_add_nvpair(*newprops, pair) == 0);
219892241e0bSTom Erickson 	}
219992241e0bSTom Erickson }
220092241e0bSTom Erickson 
220192241e0bSTom Erickson static int
220292241e0bSTom Erickson clear_received_props(objset_t *os, const char *fs, nvlist_t *props,
220392241e0bSTom Erickson     nvlist_t *skipped)
220492241e0bSTom Erickson {
220592241e0bSTom Erickson 	int err = 0;
220692241e0bSTom Erickson 	nvlist_t *cleared_props = NULL;
220792241e0bSTom Erickson 	props_skip(props, skipped, &cleared_props);
220892241e0bSTom Erickson 	if (!nvlist_empty(cleared_props)) {
220992241e0bSTom Erickson 		/*
221092241e0bSTom Erickson 		 * Acts on local properties until the dataset has received
221192241e0bSTom Erickson 		 * properties at least once on or after SPA_VERSION_RECVD_PROPS.
221292241e0bSTom Erickson 		 */
221392241e0bSTom Erickson 		zprop_source_t flags = (ZPROP_SRC_NONE |
221492241e0bSTom Erickson 		    (dsl_prop_get_hasrecvd(os) ? ZPROP_SRC_RECEIVED : 0));
221592241e0bSTom Erickson 		err = zfs_set_prop_nvlist(fs, flags, cleared_props, NULL);
221692241e0bSTom Erickson 	}
221792241e0bSTom Erickson 	nvlist_free(cleared_props);
221892241e0bSTom Erickson 	return (err);
221992241e0bSTom Erickson }
222092241e0bSTom Erickson 
22213cb34c60Sahrens /*
22223cb34c60Sahrens  * inputs:
22233cb34c60Sahrens  * zc_name		name of filesystem
22245c0b6a79SRich Morris  * zc_value		name of property to set
22253cb34c60Sahrens  * zc_nvlist_src{_size}	nvlist of properties to apply
222692241e0bSTom Erickson  * zc_cookie		received properties flag
22273cb34c60Sahrens  *
222892241e0bSTom Erickson  * outputs:
222992241e0bSTom Erickson  * zc_nvlist_dst{_size} error for each unapplied received property
22303cb34c60Sahrens  */
2231fa9e4066Sahrens static int
2232e9dbad6fSeschrock zfs_ioc_set_prop(zfs_cmd_t *zc)
2233fa9e4066Sahrens {
2234e9dbad6fSeschrock 	nvlist_t *nvl;
223592241e0bSTom Erickson 	boolean_t received = zc->zc_cookie;
223692241e0bSTom Erickson 	zprop_source_t source = (received ? ZPROP_SRC_RECEIVED :
223792241e0bSTom Erickson 	    ZPROP_SRC_LOCAL);
223892241e0bSTom Erickson 	nvlist_t *errors = NULL;
2239e9dbad6fSeschrock 	int error;
2240e9dbad6fSeschrock 
2241990b4856Slling 	if ((error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
2242478ed9adSEric Taylor 	    zc->zc_iflags, &nvl)) != 0)
2243e9dbad6fSeschrock 		return (error);
2244e9dbad6fSeschrock 
224592241e0bSTom Erickson 	if (received) {
2246bb0ade09Sahrens 		nvlist_t *origprops;
2247bb0ade09Sahrens 		objset_t *os;
2248bb0ade09Sahrens 
2249503ad85cSMatthew Ahrens 		if (dmu_objset_hold(zc->zc_name, FTAG, &os) == 0) {
225092241e0bSTom Erickson 			if (dsl_prop_get_received(os, &origprops) == 0) {
225192241e0bSTom Erickson 				(void) clear_received_props(os,
225292241e0bSTom Erickson 				    zc->zc_name, origprops, nvl);
2253bb0ade09Sahrens 				nvlist_free(origprops);
2254bb0ade09Sahrens 			}
225592241e0bSTom Erickson 
225692241e0bSTom Erickson 			dsl_prop_set_hasrecvd(os);
2257503ad85cSMatthew Ahrens 			dmu_objset_rele(os, FTAG);
2258bb0ade09Sahrens 		}
2259bb0ade09Sahrens 	}
2260bb0ade09Sahrens 
226192241e0bSTom Erickson 	error = zfs_set_prop_nvlist(zc->zc_name, source, nvl, &errors);
226292241e0bSTom Erickson 
226392241e0bSTom Erickson 	if (zc->zc_nvlist_dst != NULL && errors != NULL) {
226492241e0bSTom Erickson 		(void) put_nvlist(zc, errors);
226592241e0bSTom Erickson 	}
2266ecd6cf80Smarks 
226792241e0bSTom Erickson 	nvlist_free(errors);
2268e9dbad6fSeschrock 	nvlist_free(nvl);
2269e9dbad6fSeschrock 	return (error);
2270fa9e4066Sahrens }
2271fa9e4066Sahrens 
22723cb34c60Sahrens /*
22733cb34c60Sahrens  * inputs:
22743cb34c60Sahrens  * zc_name		name of filesystem
22753cb34c60Sahrens  * zc_value		name of property to inherit
227692241e0bSTom Erickson  * zc_cookie		revert to received value if TRUE
22773cb34c60Sahrens  *
22783cb34c60Sahrens  * outputs:		none
22793cb34c60Sahrens  */
2280e45ce728Sahrens static int
2281e45ce728Sahrens zfs_ioc_inherit_prop(zfs_cmd_t *zc)
2282e45ce728Sahrens {
228392241e0bSTom Erickson 	const char *propname = zc->zc_value;
228492241e0bSTom Erickson 	zfs_prop_t prop = zfs_name_to_prop(propname);
228592241e0bSTom Erickson 	boolean_t received = zc->zc_cookie;
228692241e0bSTom Erickson 	zprop_source_t source = (received
228792241e0bSTom Erickson 	    ? ZPROP_SRC_NONE		/* revert to received value, if any */
228892241e0bSTom Erickson 	    : ZPROP_SRC_INHERITED);	/* explicitly inherit */
228992241e0bSTom Erickson 
229092241e0bSTom Erickson 	if (received) {
229192241e0bSTom Erickson 		nvlist_t *dummy;
229292241e0bSTom Erickson 		nvpair_t *pair;
229392241e0bSTom Erickson 		zprop_type_t type;
229492241e0bSTom Erickson 		int err;
229592241e0bSTom Erickson 
229692241e0bSTom Erickson 		/*
229792241e0bSTom Erickson 		 * zfs_prop_set_special() expects properties in the form of an
229892241e0bSTom Erickson 		 * nvpair with type info.
229992241e0bSTom Erickson 		 */
230092241e0bSTom Erickson 		if (prop == ZPROP_INVAL) {
230192241e0bSTom Erickson 			if (!zfs_prop_user(propname))
230292241e0bSTom Erickson 				return (EINVAL);
230392241e0bSTom Erickson 
230492241e0bSTom Erickson 			type = PROP_TYPE_STRING;
2305a79992aaSTom Erickson 		} else if (prop == ZFS_PROP_VOLSIZE ||
2306a79992aaSTom Erickson 		    prop == ZFS_PROP_VERSION) {
2307a79992aaSTom Erickson 			return (EINVAL);
230892241e0bSTom Erickson 		} else {
230992241e0bSTom Erickson 			type = zfs_prop_get_type(prop);
231092241e0bSTom Erickson 		}
231192241e0bSTom Erickson 
231292241e0bSTom Erickson 		VERIFY(nvlist_alloc(&dummy, NV_UNIQUE_NAME, KM_SLEEP) == 0);
231392241e0bSTom Erickson 
231492241e0bSTom Erickson 		switch (type) {
231592241e0bSTom Erickson 		case PROP_TYPE_STRING:
231692241e0bSTom Erickson 			VERIFY(0 == nvlist_add_string(dummy, propname, ""));
231792241e0bSTom Erickson 			break;
231892241e0bSTom Erickson 		case PROP_TYPE_NUMBER:
231992241e0bSTom Erickson 		case PROP_TYPE_INDEX:
232092241e0bSTom Erickson 			VERIFY(0 == nvlist_add_uint64(dummy, propname, 0));
232192241e0bSTom Erickson 			break;
232292241e0bSTom Erickson 		default:
232392241e0bSTom Erickson 			nvlist_free(dummy);
232492241e0bSTom Erickson 			return (EINVAL);
232592241e0bSTom Erickson 		}
232692241e0bSTom Erickson 
232792241e0bSTom Erickson 		pair = nvlist_next_nvpair(dummy, NULL);
232892241e0bSTom Erickson 		err = zfs_prop_set_special(zc->zc_name, source, pair);
232992241e0bSTom Erickson 		nvlist_free(dummy);
233092241e0bSTom Erickson 		if (err != -1)
233192241e0bSTom Erickson 			return (err); /* special property already handled */
233292241e0bSTom Erickson 	} else {
233392241e0bSTom Erickson 		/*
233492241e0bSTom Erickson 		 * Only check this in the non-received case. We want to allow
233592241e0bSTom Erickson 		 * 'inherit -S' to revert non-inheritable properties like quota
233692241e0bSTom Erickson 		 * and reservation to the received or default values even though
233792241e0bSTom Erickson 		 * they are not considered inheritable.
233892241e0bSTom Erickson 		 */
233992241e0bSTom Erickson 		if (prop != ZPROP_INVAL && !zfs_prop_inheritable(prop))
234092241e0bSTom Erickson 			return (EINVAL);
234192241e0bSTom Erickson 	}
234292241e0bSTom Erickson 
2343e45ce728Sahrens 	/* the property name has been validated by zfs_secpolicy_inherit() */
234492241e0bSTom Erickson 	return (dsl_prop_set(zc->zc_name, zc->zc_value, source, 0, 0, NULL));
2345e45ce728Sahrens }
2346e45ce728Sahrens 
2347b1b8ab34Slling static int
234811a41203Slling zfs_ioc_pool_set_props(zfs_cmd_t *zc)
2349b1b8ab34Slling {
2350990b4856Slling 	nvlist_t *props;
2351b1b8ab34Slling 	spa_t *spa;
2352990b4856Slling 	int error;
235392241e0bSTom Erickson 	nvpair_t *pair;
2354b1b8ab34Slling 
235592241e0bSTom Erickson 	if (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
235692241e0bSTom Erickson 	    zc->zc_iflags, &props))
2357b1b8ab34Slling 		return (error);
2358b1b8ab34Slling 
2359379c004dSEric Schrock 	/*
2360379c004dSEric Schrock 	 * If the only property is the configfile, then just do a spa_lookup()
2361379c004dSEric Schrock 	 * to handle the faulted case.
2362379c004dSEric Schrock 	 */
236392241e0bSTom Erickson 	pair = nvlist_next_nvpair(props, NULL);
236492241e0bSTom Erickson 	if (pair != NULL && strcmp(nvpair_name(pair),
2365379c004dSEric Schrock 	    zpool_prop_to_name(ZPOOL_PROP_CACHEFILE)) == 0 &&
236692241e0bSTom Erickson 	    nvlist_next_nvpair(props, pair) == NULL) {
2367379c004dSEric Schrock 		mutex_enter(&spa_namespace_lock);
2368379c004dSEric Schrock 		if ((spa = spa_lookup(zc->zc_name)) != NULL) {
2369379c004dSEric Schrock 			spa_configfile_set(spa, props, B_FALSE);
2370379c004dSEric Schrock 			spa_config_sync(spa, B_FALSE, B_TRUE);
2371379c004dSEric Schrock 		}
2372379c004dSEric Schrock 		mutex_exit(&spa_namespace_lock);
2373b693757aSEric Schrock 		if (spa != NULL) {
2374b693757aSEric Schrock 			nvlist_free(props);
2375379c004dSEric Schrock 			return (0);
2376b693757aSEric Schrock 		}
2377379c004dSEric Schrock 	}
2378379c004dSEric Schrock 
2379b1b8ab34Slling 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) {
2380990b4856Slling 		nvlist_free(props);
2381b1b8ab34Slling 		return (error);
2382b1b8ab34Slling 	}
2383b1b8ab34Slling 
2384990b4856Slling 	error = spa_prop_set(spa, props);
2385b1b8ab34Slling 
2386990b4856Slling 	nvlist_free(props);
2387b1b8ab34Slling 	spa_close(spa, FTAG);
2388b1b8ab34Slling 
2389b1b8ab34Slling 	return (error);
2390b1b8ab34Slling }
2391b1b8ab34Slling 
2392b1b8ab34Slling static int
239311a41203Slling zfs_ioc_pool_get_props(zfs_cmd_t *zc)
2394b1b8ab34Slling {
2395b1b8ab34Slling 	spa_t *spa;
2396b1b8ab34Slling 	int error;
2397b1b8ab34Slling 	nvlist_t *nvp = NULL;
2398b1b8ab34Slling 
2399379c004dSEric Schrock 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) {
2400379c004dSEric Schrock 		/*
2401379c004dSEric Schrock 		 * If the pool is faulted, there may be properties we can still
2402379c004dSEric Schrock 		 * get (such as altroot and cachefile), so attempt to get them
2403379c004dSEric Schrock 		 * anyway.
2404379c004dSEric Schrock 		 */
2405379c004dSEric Schrock 		mutex_enter(&spa_namespace_lock);
2406379c004dSEric Schrock 		if ((spa = spa_lookup(zc->zc_name)) != NULL)
2407379c004dSEric Schrock 			error = spa_prop_get(spa, &nvp);
2408379c004dSEric Schrock 		mutex_exit(&spa_namespace_lock);
2409379c004dSEric Schrock 	} else {
2410379c004dSEric Schrock 		error = spa_prop_get(spa, &nvp);
2411379c004dSEric Schrock 		spa_close(spa, FTAG);
2412379c004dSEric Schrock 	}
2413b1b8ab34Slling 
2414b1b8ab34Slling 	if (error == 0 && zc->zc_nvlist_dst != NULL)
2415b1b8ab34Slling 		error = put_nvlist(zc, nvp);
2416b1b8ab34Slling 	else
2417b1b8ab34Slling 		error = EFAULT;
2418b1b8ab34Slling 
2419379c004dSEric Schrock 	nvlist_free(nvp);
2420b1b8ab34Slling 	return (error);
2421b1b8ab34Slling }
2422b1b8ab34Slling 
24233cb34c60Sahrens /*
24243cb34c60Sahrens  * inputs:
24253cb34c60Sahrens  * zc_name		name of filesystem
24263cb34c60Sahrens  * zc_nvlist_src{_size}	nvlist of delegated permissions
24273cb34c60Sahrens  * zc_perm_action	allow/unallow flag
24283cb34c60Sahrens  *
24293cb34c60Sahrens  * outputs:		none
24303cb34c60Sahrens  */
2431ecd6cf80Smarks static int
2432ecd6cf80Smarks zfs_ioc_set_fsacl(zfs_cmd_t *zc)
2433ecd6cf80Smarks {
2434ecd6cf80Smarks 	int error;
2435ecd6cf80Smarks 	nvlist_t *fsaclnv = NULL;
2436ecd6cf80Smarks 
2437990b4856Slling 	if ((error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
2438478ed9adSEric Taylor 	    zc->zc_iflags, &fsaclnv)) != 0)
2439ecd6cf80Smarks 		return (error);
2440ecd6cf80Smarks 
2441ecd6cf80Smarks 	/*
2442ecd6cf80Smarks 	 * Verify nvlist is constructed correctly
2443ecd6cf80Smarks 	 */
2444ecd6cf80Smarks 	if ((error = zfs_deleg_verify_nvlist(fsaclnv)) != 0) {
2445ecd6cf80Smarks 		nvlist_free(fsaclnv);
2446ecd6cf80Smarks 		return (EINVAL);
2447ecd6cf80Smarks 	}
2448ecd6cf80Smarks 
2449ecd6cf80Smarks 	/*
2450ecd6cf80Smarks 	 * If we don't have PRIV_SYS_MOUNT, then validate
2451ecd6cf80Smarks 	 * that user is allowed to hand out each permission in
2452ecd6cf80Smarks 	 * the nvlist(s)
2453ecd6cf80Smarks 	 */
2454ecd6cf80Smarks 
245591ebeef5Sahrens 	error = secpolicy_zfs(CRED());
2456ecd6cf80Smarks 	if (error) {
245791ebeef5Sahrens 		if (zc->zc_perm_action == B_FALSE) {
245891ebeef5Sahrens 			error = dsl_deleg_can_allow(zc->zc_name,
245991ebeef5Sahrens 			    fsaclnv, CRED());
246091ebeef5Sahrens 		} else {
246191ebeef5Sahrens 			error = dsl_deleg_can_unallow(zc->zc_name,
246291ebeef5Sahrens 			    fsaclnv, CRED());
246391ebeef5Sahrens 		}
2464ecd6cf80Smarks 	}
2465ecd6cf80Smarks 
2466ecd6cf80Smarks 	if (error == 0)
2467ecd6cf80Smarks 		error = dsl_deleg_set(zc->zc_name, fsaclnv, zc->zc_perm_action);
2468ecd6cf80Smarks 
2469ecd6cf80Smarks 	nvlist_free(fsaclnv);
2470ecd6cf80Smarks 	return (error);
2471ecd6cf80Smarks }
2472ecd6cf80Smarks 
24733cb34c60Sahrens /*
24743cb34c60Sahrens  * inputs:
24753cb34c60Sahrens  * zc_name		name of filesystem
24763cb34c60Sahrens  *
24773cb34c60Sahrens  * outputs:
24783cb34c60Sahrens  * zc_nvlist_src{_size}	nvlist of delegated permissions
24793cb34c60Sahrens  */
2480ecd6cf80Smarks static int
2481ecd6cf80Smarks zfs_ioc_get_fsacl(zfs_cmd_t *zc)
2482ecd6cf80Smarks {
2483ecd6cf80Smarks 	nvlist_t *nvp;
2484ecd6cf80Smarks 	int error;
2485ecd6cf80Smarks 
2486ecd6cf80Smarks 	if ((error = dsl_deleg_get(zc->zc_name, &nvp)) == 0) {
2487ecd6cf80Smarks 		error = put_nvlist(zc, nvp);
2488ecd6cf80Smarks 		nvlist_free(nvp);
2489ecd6cf80Smarks 	}
2490ecd6cf80Smarks 
2491ecd6cf80Smarks 	return (error);
2492ecd6cf80Smarks }
2493ecd6cf80Smarks 
2494fa9e4066Sahrens /*
2495fa9e4066Sahrens  * Search the vfs list for a specified resource.  Returns a pointer to it
2496fa9e4066Sahrens  * or NULL if no suitable entry is found. The caller of this routine
2497fa9e4066Sahrens  * is responsible for releasing the returned vfs pointer.
2498fa9e4066Sahrens  */
2499fa9e4066Sahrens static vfs_t *
2500fa9e4066Sahrens zfs_get_vfs(const char *resource)
2501fa9e4066Sahrens {
2502fa9e4066Sahrens 	struct vfs *vfsp;
2503fa9e4066Sahrens 	struct vfs *vfs_found = NULL;
2504fa9e4066Sahrens 
2505fa9e4066Sahrens 	vfs_list_read_lock();
2506fa9e4066Sahrens 	vfsp = rootvfs;
2507fa9e4066Sahrens 	do {
2508fa9e4066Sahrens 		if (strcmp(refstr_value(vfsp->vfs_resource), resource) == 0) {
2509fa9e4066Sahrens 			VFS_HOLD(vfsp);
2510fa9e4066Sahrens 			vfs_found = vfsp;
2511fa9e4066Sahrens 			break;
2512fa9e4066Sahrens 		}
2513fa9e4066Sahrens 		vfsp = vfsp->vfs_next;
2514fa9e4066Sahrens 	} while (vfsp != rootvfs);
2515fa9e4066Sahrens 	vfs_list_unlock();
2516fa9e4066Sahrens 	return (vfs_found);
2517fa9e4066Sahrens }
2518fa9e4066Sahrens 
2519ecd6cf80Smarks /* ARGSUSED */
2520fa9e4066Sahrens static void
2521ecd6cf80Smarks zfs_create_cb(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx)
2522fa9e4066Sahrens {
2523da6c28aaSamw 	zfs_creat_t *zct = arg;
2524da6c28aaSamw 
2525de8267e0Stimh 	zfs_create_fs(os, cr, zct->zct_zplprops, tx);
2526da6c28aaSamw }
2527da6c28aaSamw 
2528de8267e0Stimh #define	ZFS_PROP_UNDEFINED	((uint64_t)-1)
2529da6c28aaSamw 
2530da6c28aaSamw /*
2531de8267e0Stimh  * inputs:
25320a48a24eStimh  * createprops		list of properties requested by creator
25330a48a24eStimh  * default_zplver	zpl version to use if unspecified in createprops
25340a48a24eStimh  * fuids_ok		fuids allowed in this version of the spa?
25350a48a24eStimh  * os			parent objset pointer (NULL if root fs)
2536de8267e0Stimh  *
2537de8267e0Stimh  * outputs:
2538de8267e0Stimh  * zplprops	values for the zplprops we attach to the master node object
25390a48a24eStimh  * is_ci	true if requested file system will be purely case-insensitive
2540da6c28aaSamw  *
2541de8267e0Stimh  * Determine the settings for utf8only, normalization and
2542de8267e0Stimh  * casesensitivity.  Specific values may have been requested by the
2543de8267e0Stimh  * creator and/or we can inherit values from the parent dataset.  If
2544de8267e0Stimh  * the file system is of too early a vintage, a creator can not
2545de8267e0Stimh  * request settings for these properties, even if the requested
2546de8267e0Stimh  * setting is the default value.  We don't actually want to create dsl
2547de8267e0Stimh  * properties for these, so remove them from the source nvlist after
2548de8267e0Stimh  * processing.
2549da6c28aaSamw  */
2550da6c28aaSamw static int
255114843421SMatthew Ahrens zfs_fill_zplprops_impl(objset_t *os, uint64_t zplver,
2552*0a586ceaSMark Shellenbaum     boolean_t fuids_ok, boolean_t sa_ok, nvlist_t *createprops,
2553*0a586ceaSMark Shellenbaum     nvlist_t *zplprops, boolean_t *is_ci)
2554da6c28aaSamw {
2555de8267e0Stimh 	uint64_t sense = ZFS_PROP_UNDEFINED;
2556de8267e0Stimh 	uint64_t norm = ZFS_PROP_UNDEFINED;
2557de8267e0Stimh 	uint64_t u8 = ZFS_PROP_UNDEFINED;
2558da6c28aaSamw 
2559de8267e0Stimh 	ASSERT(zplprops != NULL);
2560da6c28aaSamw 
2561de8267e0Stimh 	/*
2562de8267e0Stimh 	 * Pull out creator prop choices, if any.
2563de8267e0Stimh 	 */
2564de8267e0Stimh 	if (createprops) {
25650a48a24eStimh 		(void) nvlist_lookup_uint64(createprops,
25660a48a24eStimh 		    zfs_prop_to_name(ZFS_PROP_VERSION), &zplver);
2567de8267e0Stimh 		(void) nvlist_lookup_uint64(createprops,
2568de8267e0Stimh 		    zfs_prop_to_name(ZFS_PROP_NORMALIZE), &norm);
2569de8267e0Stimh 		(void) nvlist_remove_all(createprops,
2570de8267e0Stimh 		    zfs_prop_to_name(ZFS_PROP_NORMALIZE));
2571de8267e0Stimh 		(void) nvlist_lookup_uint64(createprops,
2572de8267e0Stimh 		    zfs_prop_to_name(ZFS_PROP_UTF8ONLY), &u8);
2573de8267e0Stimh 		(void) nvlist_remove_all(createprops,
2574de8267e0Stimh 		    zfs_prop_to_name(ZFS_PROP_UTF8ONLY));
2575de8267e0Stimh 		(void) nvlist_lookup_uint64(createprops,
2576de8267e0Stimh 		    zfs_prop_to_name(ZFS_PROP_CASE), &sense);
2577de8267e0Stimh 		(void) nvlist_remove_all(createprops,
2578de8267e0Stimh 		    zfs_prop_to_name(ZFS_PROP_CASE));
2579de8267e0Stimh 	}
2580da6c28aaSamw 
2581c2a93d44Stimh 	/*
25820a48a24eStimh 	 * If the zpl version requested is whacky or the file system
25830a48a24eStimh 	 * or pool is version is too "young" to support normalization
25840a48a24eStimh 	 * and the creator tried to set a value for one of the props,
25850a48a24eStimh 	 * error out.
2586c2a93d44Stimh 	 */
25870a48a24eStimh 	if ((zplver < ZPL_VERSION_INITIAL || zplver > ZPL_VERSION) ||
25880a48a24eStimh 	    (zplver >= ZPL_VERSION_FUID && !fuids_ok) ||
2589*0a586ceaSMark Shellenbaum 	    (zplver >= ZPL_VERSION_SA && !sa_ok) ||
25900a48a24eStimh 	    (zplver < ZPL_VERSION_NORMALIZATION &&
2591de8267e0Stimh 	    (norm != ZFS_PROP_UNDEFINED || u8 != ZFS_PROP_UNDEFINED ||
25920a48a24eStimh 	    sense != ZFS_PROP_UNDEFINED)))
2593de8267e0Stimh 		return (ENOTSUP);
2594c2a93d44Stimh 
2595de8267e0Stimh 	/*
2596de8267e0Stimh 	 * Put the version in the zplprops
2597de8267e0Stimh 	 */
2598de8267e0Stimh 	VERIFY(nvlist_add_uint64(zplprops,
2599de8267e0Stimh 	    zfs_prop_to_name(ZFS_PROP_VERSION), zplver) == 0);
2600da6c28aaSamw 
2601de8267e0Stimh 	if (norm == ZFS_PROP_UNDEFINED)
2602de8267e0Stimh 		VERIFY(zfs_get_zplprop(os, ZFS_PROP_NORMALIZE, &norm) == 0);
2603de8267e0Stimh 	VERIFY(nvlist_add_uint64(zplprops,
2604de8267e0Stimh 	    zfs_prop_to_name(ZFS_PROP_NORMALIZE), norm) == 0);
2605da6c28aaSamw 
2606c2a93d44Stimh 	/*
2607de8267e0Stimh 	 * If we're normalizing, names must always be valid UTF-8 strings.
2608c2a93d44Stimh 	 */
2609de8267e0Stimh 	if (norm)
2610de8267e0Stimh 		u8 = 1;
2611de8267e0Stimh 	if (u8 == ZFS_PROP_UNDEFINED)
2612de8267e0Stimh 		VERIFY(zfs_get_zplprop(os, ZFS_PROP_UTF8ONLY, &u8) == 0);
2613de8267e0Stimh 	VERIFY(nvlist_add_uint64(zplprops,
2614de8267e0Stimh 	    zfs_prop_to_name(ZFS_PROP_UTF8ONLY), u8) == 0);
2615de8267e0Stimh 
2616de8267e0Stimh 	if (sense == ZFS_PROP_UNDEFINED)
2617de8267e0Stimh 		VERIFY(zfs_get_zplprop(os, ZFS_PROP_CASE, &sense) == 0);
2618de8267e0Stimh 	VERIFY(nvlist_add_uint64(zplprops,
2619de8267e0Stimh 	    zfs_prop_to_name(ZFS_PROP_CASE), sense) == 0);
2620c2a93d44Stimh 
2621ab04eb8eStimh 	if (is_ci)
2622ab04eb8eStimh 		*is_ci = (sense == ZFS_CASE_INSENSITIVE);
2623ab04eb8eStimh 
2624da6c28aaSamw 	return (0);
2625fa9e4066Sahrens }
2626fa9e4066Sahrens 
26270a48a24eStimh static int
26280a48a24eStimh zfs_fill_zplprops(const char *dataset, nvlist_t *createprops,
26290a48a24eStimh     nvlist_t *zplprops, boolean_t *is_ci)
26300a48a24eStimh {
2631*0a586ceaSMark Shellenbaum 	boolean_t fuids_ok, sa_ok;
26320a48a24eStimh 	uint64_t zplver = ZPL_VERSION;
26330a48a24eStimh 	objset_t *os = NULL;
26340a48a24eStimh 	char parentname[MAXNAMELEN];
26350a48a24eStimh 	char *cp;
2636*0a586ceaSMark Shellenbaum 	spa_t *spa;
2637*0a586ceaSMark Shellenbaum 	uint64_t spa_vers;
26380a48a24eStimh 	int error;
26390a48a24eStimh 
26400a48a24eStimh 	(void) strlcpy(parentname, dataset, sizeof (parentname));
26410a48a24eStimh 	cp = strrchr(parentname, '/');
26420a48a24eStimh 	ASSERT(cp != NULL);
26430a48a24eStimh 	cp[0] = '\0';
26440a48a24eStimh 
2645*0a586ceaSMark Shellenbaum 	if ((error = spa_open(dataset, &spa, FTAG)) != 0)
2646*0a586ceaSMark Shellenbaum 		return (error);
2647*0a586ceaSMark Shellenbaum 
2648*0a586ceaSMark Shellenbaum 	spa_vers = spa_version(spa);
2649*0a586ceaSMark Shellenbaum 	spa_close(spa, FTAG);
2650*0a586ceaSMark Shellenbaum 
2651*0a586ceaSMark Shellenbaum 	zplver = zfs_zpl_version_map(spa_vers);
2652*0a586ceaSMark Shellenbaum 	fuids_ok = (zplver >= ZPL_VERSION_FUID);
2653*0a586ceaSMark Shellenbaum 	sa_ok = (zplver >= ZPL_VERSION_SA);
26540a48a24eStimh 
26550a48a24eStimh 	/*
26560a48a24eStimh 	 * Open parent object set so we can inherit zplprop values.
26570a48a24eStimh 	 */
2658503ad85cSMatthew Ahrens 	if ((error = dmu_objset_hold(parentname, FTAG, &os)) != 0)
26590a48a24eStimh 		return (error);
26600a48a24eStimh 
2661*0a586ceaSMark Shellenbaum 	error = zfs_fill_zplprops_impl(os, zplver, fuids_ok, sa_ok, createprops,
26620a48a24eStimh 	    zplprops, is_ci);
2663503ad85cSMatthew Ahrens 	dmu_objset_rele(os, FTAG);
26640a48a24eStimh 	return (error);
26650a48a24eStimh }
26660a48a24eStimh 
26670a48a24eStimh static int
26680a48a24eStimh zfs_fill_zplprops_root(uint64_t spa_vers, nvlist_t *createprops,
26690a48a24eStimh     nvlist_t *zplprops, boolean_t *is_ci)
26700a48a24eStimh {
2671*0a586ceaSMark Shellenbaum 	boolean_t fuids_ok;
2672*0a586ceaSMark Shellenbaum 	boolean_t sa_ok;
26730a48a24eStimh 	uint64_t zplver = ZPL_VERSION;
26740a48a24eStimh 	int error;
26750a48a24eStimh 
2676*0a586ceaSMark Shellenbaum 	zplver = zfs_zpl_version_map(spa_vers);
2677*0a586ceaSMark Shellenbaum 	fuids_ok = (zplver >= ZPL_VERSION_FUID);
2678*0a586ceaSMark Shellenbaum 	sa_ok = (zplver >= ZPL_VERSION_SA);
26790a48a24eStimh 
2680*0a586ceaSMark Shellenbaum 	error = zfs_fill_zplprops_impl(NULL, zplver, fuids_ok, sa_ok,
2681*0a586ceaSMark Shellenbaum 	    createprops, zplprops, is_ci);
26820a48a24eStimh 	return (error);
26830a48a24eStimh }
26840a48a24eStimh 
26853cb34c60Sahrens /*
26863cb34c60Sahrens  * inputs:
26873cb34c60Sahrens  * zc_objset_type	type of objset to create (fs vs zvol)
26883cb34c60Sahrens  * zc_name		name of new objset
26893cb34c60Sahrens  * zc_value		name of snapshot to clone from (may be empty)
26903cb34c60Sahrens  * zc_nvlist_src{_size}	nvlist of properties to apply
26913cb34c60Sahrens  *
2692de8267e0Stimh  * outputs: none
26933cb34c60Sahrens  */
2694fa9e4066Sahrens static int
2695fa9e4066Sahrens zfs_ioc_create(zfs_cmd_t *zc)
2696fa9e4066Sahrens {
2697fa9e4066Sahrens 	objset_t *clone;
2698fa9e4066Sahrens 	int error = 0;
2699da6c28aaSamw 	zfs_creat_t zct;
2700ecd6cf80Smarks 	nvlist_t *nvprops = NULL;
2701ecd6cf80Smarks 	void (*cbfunc)(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx);
2702fa9e4066Sahrens 	dmu_objset_type_t type = zc->zc_objset_type;
2703fa9e4066Sahrens 
2704fa9e4066Sahrens 	switch (type) {
2705fa9e4066Sahrens 
2706fa9e4066Sahrens 	case DMU_OST_ZFS:
2707fa9e4066Sahrens 		cbfunc = zfs_create_cb;
2708fa9e4066Sahrens 		break;
2709fa9e4066Sahrens 
2710fa9e4066Sahrens 	case DMU_OST_ZVOL:
2711fa9e4066Sahrens 		cbfunc = zvol_create_cb;
2712fa9e4066Sahrens 		break;
2713fa9e4066Sahrens 
2714fa9e4066Sahrens 	default:
27151d452cf5Sahrens 		cbfunc = NULL;
2716e7cbe64fSgw 		break;
2717fa9e4066Sahrens 	}
2718f18faf3fSek 	if (strchr(zc->zc_name, '@') ||
2719f18faf3fSek 	    strchr(zc->zc_name, '%'))
27201d452cf5Sahrens 		return (EINVAL);
2721fa9e4066Sahrens 
2722e9dbad6fSeschrock 	if (zc->zc_nvlist_src != NULL &&
2723990b4856Slling 	    (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
2724478ed9adSEric Taylor 	    zc->zc_iflags, &nvprops)) != 0)
2725e9dbad6fSeschrock 		return (error);
2726e9dbad6fSeschrock 
2727de8267e0Stimh 	zct.zct_zplprops = NULL;
2728da6c28aaSamw 	zct.zct_props = nvprops;
2729da6c28aaSamw 
2730e9dbad6fSeschrock 	if (zc->zc_value[0] != '\0') {
2731fa9e4066Sahrens 		/*
2732fa9e4066Sahrens 		 * We're creating a clone of an existing snapshot.
2733fa9e4066Sahrens 		 */
2734e9dbad6fSeschrock 		zc->zc_value[sizeof (zc->zc_value) - 1] = '\0';
2735e9dbad6fSeschrock 		if (dataset_namecheck(zc->zc_value, NULL, NULL) != 0) {
2736ecd6cf80Smarks 			nvlist_free(nvprops);
2737fa9e4066Sahrens 			return (EINVAL);
2738e9dbad6fSeschrock 		}
2739fa9e4066Sahrens 
2740503ad85cSMatthew Ahrens 		error = dmu_objset_hold(zc->zc_value, FTAG, &clone);
2741e9dbad6fSeschrock 		if (error) {
2742ecd6cf80Smarks 			nvlist_free(nvprops);
2743fa9e4066Sahrens 			return (error);
2744e9dbad6fSeschrock 		}
2745ab04eb8eStimh 
2746ae46e4c7SMatthew Ahrens 		error = dmu_objset_clone(zc->zc_name, dmu_objset_ds(clone), 0);
2747503ad85cSMatthew Ahrens 		dmu_objset_rele(clone, FTAG);
2748da6c28aaSamw 		if (error) {
2749da6c28aaSamw 			nvlist_free(nvprops);
2750da6c28aaSamw 			return (error);
2751da6c28aaSamw 		}
2752fa9e4066Sahrens 	} else {
2753ab04eb8eStimh 		boolean_t is_insensitive = B_FALSE;
2754ab04eb8eStimh 
2755e9dbad6fSeschrock 		if (cbfunc == NULL) {
2756ecd6cf80Smarks 			nvlist_free(nvprops);
27571d452cf5Sahrens 			return (EINVAL);
2758e9dbad6fSeschrock 		}
27595c5460e9Seschrock 
2760e9dbad6fSeschrock 		if (type == DMU_OST_ZVOL) {
2761e9dbad6fSeschrock 			uint64_t volsize, volblocksize;
2762e9dbad6fSeschrock 
2763ecd6cf80Smarks 			if (nvprops == NULL ||
2764ecd6cf80Smarks 			    nvlist_lookup_uint64(nvprops,
2765e9dbad6fSeschrock 			    zfs_prop_to_name(ZFS_PROP_VOLSIZE),
2766e9dbad6fSeschrock 			    &volsize) != 0) {
2767ecd6cf80Smarks 				nvlist_free(nvprops);
2768e9dbad6fSeschrock 				return (EINVAL);
2769e9dbad6fSeschrock 			}
2770e9dbad6fSeschrock 
2771ecd6cf80Smarks 			if ((error = nvlist_lookup_uint64(nvprops,
2772e9dbad6fSeschrock 			    zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE),
2773e9dbad6fSeschrock 			    &volblocksize)) != 0 && error != ENOENT) {
2774ecd6cf80Smarks 				nvlist_free(nvprops);
2775e9dbad6fSeschrock 				return (EINVAL);
2776e9dbad6fSeschrock 			}
2777e9dbad6fSeschrock 
2778e9dbad6fSeschrock 			if (error != 0)
2779e9dbad6fSeschrock 				volblocksize = zfs_prop_default_numeric(
2780e9dbad6fSeschrock 				    ZFS_PROP_VOLBLOCKSIZE);
2781e9dbad6fSeschrock 
2782e9dbad6fSeschrock 			if ((error = zvol_check_volblocksize(
2783e9dbad6fSeschrock 			    volblocksize)) != 0 ||
2784e9dbad6fSeschrock 			    (error = zvol_check_volsize(volsize,
2785e9dbad6fSeschrock 			    volblocksize)) != 0) {
2786ecd6cf80Smarks 				nvlist_free(nvprops);
27875c5460e9Seschrock 				return (error);
2788e9dbad6fSeschrock 			}
2789e7437265Sahrens 		} else if (type == DMU_OST_ZFS) {
2790da6c28aaSamw 			int error;
2791da6c28aaSamw 
2792da6c28aaSamw 			/*
2793da6c28aaSamw 			 * We have to have normalization and
2794da6c28aaSamw 			 * case-folding flags correct when we do the
2795da6c28aaSamw 			 * file system creation, so go figure them out
2796de8267e0Stimh 			 * now.
2797da6c28aaSamw 			 */
2798de8267e0Stimh 			VERIFY(nvlist_alloc(&zct.zct_zplprops,
2799de8267e0Stimh 			    NV_UNIQUE_NAME, KM_SLEEP) == 0);
2800de8267e0Stimh 			error = zfs_fill_zplprops(zc->zc_name, nvprops,
28010a48a24eStimh 			    zct.zct_zplprops, &is_insensitive);
2802da6c28aaSamw 			if (error != 0) {
2803da6c28aaSamw 				nvlist_free(nvprops);
2804de8267e0Stimh 				nvlist_free(zct.zct_zplprops);
2805da6c28aaSamw 				return (error);
2806da6c28aaSamw 			}
2807da6c28aaSamw 		}
2808ae46e4c7SMatthew Ahrens 		error = dmu_objset_create(zc->zc_name, type,
2809ab04eb8eStimh 		    is_insensitive ? DS_FLAG_CI_DATASET : 0, cbfunc, &zct);
2810de8267e0Stimh 		nvlist_free(zct.zct_zplprops);
2811fa9e4066Sahrens 	}
2812e9dbad6fSeschrock 
2813e9dbad6fSeschrock 	/*
2814e9dbad6fSeschrock 	 * It would be nice to do this atomically.
2815e9dbad6fSeschrock 	 */
2816e9dbad6fSeschrock 	if (error == 0) {
281792241e0bSTom Erickson 		error = zfs_set_prop_nvlist(zc->zc_name, ZPROP_SRC_LOCAL,
281892241e0bSTom Erickson 		    nvprops, NULL);
281992241e0bSTom Erickson 		if (error != 0)
2820842727c2SChris Kirby 			(void) dmu_objset_destroy(zc->zc_name, B_FALSE);
2821e9dbad6fSeschrock 	}
2822ecd6cf80Smarks 	nvlist_free(nvprops);
2823fa9e4066Sahrens 	return (error);
2824fa9e4066Sahrens }
2825fa9e4066Sahrens 
28263cb34c60Sahrens /*
28273cb34c60Sahrens  * inputs:
28283cb34c60Sahrens  * zc_name	name of filesystem
28293cb34c60Sahrens  * zc_value	short name of snapshot
28303cb34c60Sahrens  * zc_cookie	recursive flag
283114843421SMatthew Ahrens  * zc_nvlist_src[_size] property list
28323cb34c60Sahrens  *
2833681d9761SEric Taylor  * outputs:
2834681d9761SEric Taylor  * zc_value	short snapname (i.e. part after the '@')
28353cb34c60Sahrens  */
2836fa9e4066Sahrens static int
28371d452cf5Sahrens zfs_ioc_snapshot(zfs_cmd_t *zc)
2838fa9e4066Sahrens {
2839bb0ade09Sahrens 	nvlist_t *nvprops = NULL;
2840bb0ade09Sahrens 	int error;
2841bb0ade09Sahrens 	boolean_t recursive = zc->zc_cookie;
2842bb0ade09Sahrens 
2843e9dbad6fSeschrock 	if (snapshot_namecheck(zc->zc_value, NULL, NULL) != 0)
28441d452cf5Sahrens 		return (EINVAL);
2845bb0ade09Sahrens 
2846bb0ade09Sahrens 	if (zc->zc_nvlist_src != NULL &&
2847bb0ade09Sahrens 	    (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
2848478ed9adSEric Taylor 	    zc->zc_iflags, &nvprops)) != 0)
2849bb0ade09Sahrens 		return (error);
2850bb0ade09Sahrens 
2851ea2f5b9eSMatthew Ahrens 	error = zfs_check_userprops(zc->zc_name, nvprops);
2852ea2f5b9eSMatthew Ahrens 	if (error)
2853ea2f5b9eSMatthew Ahrens 		goto out;
2854bb0ade09Sahrens 
285592241e0bSTom Erickson 	if (!nvlist_empty(nvprops) &&
2856ea2f5b9eSMatthew Ahrens 	    zfs_earlier_version(zc->zc_name, SPA_VERSION_SNAP_PROPS)) {
2857ea2f5b9eSMatthew Ahrens 		error = ENOTSUP;
2858ea2f5b9eSMatthew Ahrens 		goto out;
2859bb0ade09Sahrens 	}
2860ea2f5b9eSMatthew Ahrens 
2861ea2f5b9eSMatthew Ahrens 	error = dmu_objset_snapshot(zc->zc_name, zc->zc_value,
2862ea2f5b9eSMatthew Ahrens 	    nvprops, recursive);
2863ea2f5b9eSMatthew Ahrens 
2864ea2f5b9eSMatthew Ahrens out:
2865bb0ade09Sahrens 	nvlist_free(nvprops);
2866bb0ade09Sahrens 	return (error);
28671d452cf5Sahrens }
2868fa9e4066Sahrens 
2869cdf5b4caSmmusante int
2870fd136879SMatthew Ahrens zfs_unmount_snap(const char *name, void *arg)
28711d452cf5Sahrens {
28720b69c2f0Sahrens 	vfs_t *vfsp = NULL;
28731d452cf5Sahrens 
2874745cd3c5Smaybee 	if (arg) {
2875745cd3c5Smaybee 		char *snapname = arg;
2876fd136879SMatthew Ahrens 		char *fullname = kmem_asprintf("%s@%s", name, snapname);
2877fd136879SMatthew Ahrens 		vfsp = zfs_get_vfs(fullname);
2878fd136879SMatthew Ahrens 		strfree(fullname);
28790b69c2f0Sahrens 	} else if (strchr(name, '@')) {
28801d452cf5Sahrens 		vfsp = zfs_get_vfs(name);
28811d452cf5Sahrens 	}
28821d452cf5Sahrens 
28831d452cf5Sahrens 	if (vfsp) {
2884fa9e4066Sahrens 		/*
28851d452cf5Sahrens 		 * Always force the unmount for snapshots.
2886fa9e4066Sahrens 		 */
28871d452cf5Sahrens 		int flag = MS_FORCE;
28881d452cf5Sahrens 		int err;
28891d452cf5Sahrens 
28901d452cf5Sahrens 		if ((err = vn_vfswlock(vfsp->vfs_vnodecovered)) != 0) {
2891fa9e4066Sahrens 			VFS_RELE(vfsp);
28921d452cf5Sahrens 			return (err);
2893fa9e4066Sahrens 		}
28941d452cf5Sahrens 		VFS_RELE(vfsp);
28951d452cf5Sahrens 		if ((err = dounmount(vfsp, flag, kcred)) != 0)
28961d452cf5Sahrens 			return (err);
28971d452cf5Sahrens 	}
28981d452cf5Sahrens 	return (0);
28991d452cf5Sahrens }
29001d452cf5Sahrens 
29013cb34c60Sahrens /*
29023cb34c60Sahrens  * inputs:
2903842727c2SChris Kirby  * zc_name		name of filesystem
2904842727c2SChris Kirby  * zc_value		short name of snapshot
2905842727c2SChris Kirby  * zc_defer_destroy	mark for deferred destroy
29063cb34c60Sahrens  *
29073cb34c60Sahrens  * outputs:	none
29083cb34c60Sahrens  */
29091d452cf5Sahrens static int
29101d452cf5Sahrens zfs_ioc_destroy_snaps(zfs_cmd_t *zc)
29111d452cf5Sahrens {
29121d452cf5Sahrens 	int err;
29131d452cf5Sahrens 
2914e9dbad6fSeschrock 	if (snapshot_namecheck(zc->zc_value, NULL, NULL) != 0)
29151d452cf5Sahrens 		return (EINVAL);
29161d452cf5Sahrens 	err = dmu_objset_find(zc->zc_name,
2917e9dbad6fSeschrock 	    zfs_unmount_snap, zc->zc_value, DS_FIND_CHILDREN);
29181d452cf5Sahrens 	if (err)
29191d452cf5Sahrens 		return (err);
2920842727c2SChris Kirby 	return (dmu_snapshots_destroy(zc->zc_name, zc->zc_value,
2921842727c2SChris Kirby 	    zc->zc_defer_destroy));
29221d452cf5Sahrens }
29231d452cf5Sahrens 
29243cb34c60Sahrens /*
29253cb34c60Sahrens  * inputs:
29263cb34c60Sahrens  * zc_name		name of dataset to destroy
29273cb34c60Sahrens  * zc_objset_type	type of objset
2928842727c2SChris Kirby  * zc_defer_destroy	mark for deferred destroy
29293cb34c60Sahrens  *
29303cb34c60Sahrens  * outputs:		none
29313cb34c60Sahrens  */
29321d452cf5Sahrens static int
29331d452cf5Sahrens zfs_ioc_destroy(zfs_cmd_t *zc)
29341d452cf5Sahrens {
2935681d9761SEric Taylor 	int err;
29361d452cf5Sahrens 	if (strchr(zc->zc_name, '@') && zc->zc_objset_type == DMU_OST_ZFS) {
2937681d9761SEric Taylor 		err = zfs_unmount_snap(zc->zc_name, NULL);
29381d452cf5Sahrens 		if (err)
29391d452cf5Sahrens 			return (err);
2940fa9e4066Sahrens 	}
2941fa9e4066Sahrens 
2942681d9761SEric Taylor 	err = dmu_objset_destroy(zc->zc_name, zc->zc_defer_destroy);
2943681d9761SEric Taylor 	if (zc->zc_objset_type == DMU_OST_ZVOL && err == 0)
29445c987a37SChris Kirby 		(void) zvol_remove_minor(zc->zc_name);
2945681d9761SEric Taylor 	return (err);
2946fa9e4066Sahrens }
2947fa9e4066Sahrens 
29483cb34c60Sahrens /*
29493cb34c60Sahrens  * inputs:
29504ccbb6e7Sahrens  * zc_name	name of dataset to rollback (to most recent snapshot)
29513cb34c60Sahrens  *
29523cb34c60Sahrens  * outputs:	none
29533cb34c60Sahrens  */
2954fa9e4066Sahrens static int
2955fa9e4066Sahrens zfs_ioc_rollback(zfs_cmd_t *zc)
2956fa9e4066Sahrens {
2957ae46e4c7SMatthew Ahrens 	dsl_dataset_t *ds, *clone;
29584ccbb6e7Sahrens 	int error;
2959ae46e4c7SMatthew Ahrens 	zfsvfs_t *zfsvfs;
2960ae46e4c7SMatthew Ahrens 	char *clone_name;
2961ae46e4c7SMatthew Ahrens 
2962ae46e4c7SMatthew Ahrens 	error = dsl_dataset_hold(zc->zc_name, FTAG, &ds);
2963ae46e4c7SMatthew Ahrens 	if (error)
2964ae46e4c7SMatthew Ahrens 		return (error);
2965ae46e4c7SMatthew Ahrens 
2966ae46e4c7SMatthew Ahrens 	/* must not be a snapshot */
2967ae46e4c7SMatthew Ahrens 	if (dsl_dataset_is_snapshot(ds)) {
2968ae46e4c7SMatthew Ahrens 		dsl_dataset_rele(ds, FTAG);
2969ae46e4c7SMatthew Ahrens 		return (EINVAL);
2970ae46e4c7SMatthew Ahrens 	}
2971ae46e4c7SMatthew Ahrens 
2972ae46e4c7SMatthew Ahrens 	/* must have a most recent snapshot */
2973ae46e4c7SMatthew Ahrens 	if (ds->ds_phys->ds_prev_snap_txg < TXG_INITIAL) {
2974ae46e4c7SMatthew Ahrens 		dsl_dataset_rele(ds, FTAG);
2975ae46e4c7SMatthew Ahrens 		return (EINVAL);
2976ae46e4c7SMatthew Ahrens 	}
29774ccbb6e7Sahrens 
29784ccbb6e7Sahrens 	/*
2979ae46e4c7SMatthew Ahrens 	 * Create clone of most recent snapshot.
29804ccbb6e7Sahrens 	 */
2981ae46e4c7SMatthew Ahrens 	clone_name = kmem_asprintf("%s/%%rollback", zc->zc_name);
2982ae46e4c7SMatthew Ahrens 	error = dmu_objset_clone(clone_name, ds->ds_prev, DS_FLAG_INCONSISTENT);
29834ccbb6e7Sahrens 	if (error)
2984ae46e4c7SMatthew Ahrens 		goto out;
29854ccbb6e7Sahrens 
2986503ad85cSMatthew Ahrens 	error = dsl_dataset_own(clone_name, B_TRUE, FTAG, &clone);
2987ae46e4c7SMatthew Ahrens 	if (error)
2988ae46e4c7SMatthew Ahrens 		goto out;
2989ae46e4c7SMatthew Ahrens 
2990ae46e4c7SMatthew Ahrens 	/*
2991ae46e4c7SMatthew Ahrens 	 * Do clone swap.
2992ae46e4c7SMatthew Ahrens 	 */
299314843421SMatthew Ahrens 	if (getzfsvfs(zc->zc_name, &zfsvfs) == 0) {
2994503ad85cSMatthew Ahrens 		error = zfs_suspend_fs(zfsvfs);
299547f263f4Sek 		if (error == 0) {
299647f263f4Sek 			int resume_err;
29974ccbb6e7Sahrens 
2998ae46e4c7SMatthew Ahrens 			if (dsl_dataset_tryown(ds, B_FALSE, FTAG)) {
2999ae46e4c7SMatthew Ahrens 				error = dsl_dataset_clone_swap(clone, ds,
3000ae46e4c7SMatthew Ahrens 				    B_TRUE);
3001ae46e4c7SMatthew Ahrens 				dsl_dataset_disown(ds, FTAG);
3002ae46e4c7SMatthew Ahrens 				ds = NULL;
3003ae46e4c7SMatthew Ahrens 			} else {
3004ae46e4c7SMatthew Ahrens 				error = EBUSY;
3005ae46e4c7SMatthew Ahrens 			}
3006503ad85cSMatthew Ahrens 			resume_err = zfs_resume_fs(zfsvfs, zc->zc_name);
300747f263f4Sek 			error = error ? error : resume_err;
300847f263f4Sek 		}
30094ccbb6e7Sahrens 		VFS_RELE(zfsvfs->z_vfs);
30104ccbb6e7Sahrens 	} else {
3011ae46e4c7SMatthew Ahrens 		if (dsl_dataset_tryown(ds, B_FALSE, FTAG)) {
3012ae46e4c7SMatthew Ahrens 			error = dsl_dataset_clone_swap(clone, ds, B_TRUE);
3013ae46e4c7SMatthew Ahrens 			dsl_dataset_disown(ds, FTAG);
3014ae46e4c7SMatthew Ahrens 			ds = NULL;
3015ae46e4c7SMatthew Ahrens 		} else {
3016ae46e4c7SMatthew Ahrens 			error = EBUSY;
3017ae46e4c7SMatthew Ahrens 		}
30184ccbb6e7Sahrens 	}
30194ccbb6e7Sahrens 
3020ae46e4c7SMatthew Ahrens 	/*
3021ae46e4c7SMatthew Ahrens 	 * Destroy clone (which also closes it).
3022ae46e4c7SMatthew Ahrens 	 */
3023ae46e4c7SMatthew Ahrens 	(void) dsl_dataset_destroy(clone, FTAG, B_FALSE);
3024ae46e4c7SMatthew Ahrens 
3025ae46e4c7SMatthew Ahrens out:
3026ae46e4c7SMatthew Ahrens 	strfree(clone_name);
3027ae46e4c7SMatthew Ahrens 	if (ds)
3028ae46e4c7SMatthew Ahrens 		dsl_dataset_rele(ds, FTAG);
30294ccbb6e7Sahrens 	return (error);
3030fa9e4066Sahrens }
3031fa9e4066Sahrens 
30323cb34c60Sahrens /*
30333cb34c60Sahrens  * inputs:
30343cb34c60Sahrens  * zc_name	old name of dataset
30353cb34c60Sahrens  * zc_value	new name of dataset
30363cb34c60Sahrens  * zc_cookie	recursive flag (only valid for snapshots)
30373cb34c60Sahrens  *
30383cb34c60Sahrens  * outputs:	none
30393cb34c60Sahrens  */
3040fa9e4066Sahrens static int
3041fa9e4066Sahrens zfs_ioc_rename(zfs_cmd_t *zc)
3042fa9e4066Sahrens {
30437f1f55eaSvb 	boolean_t recursive = zc->zc_cookie & 1;
3044cdf5b4caSmmusante 
3045e9dbad6fSeschrock 	zc->zc_value[sizeof (zc->zc_value) - 1] = '\0';
3046f18faf3fSek 	if (dataset_namecheck(zc->zc_value, NULL, NULL) != 0 ||
3047f18faf3fSek 	    strchr(zc->zc_value, '%'))
3048fa9e4066Sahrens 		return (EINVAL);
3049fa9e4066Sahrens 
3050cdf5b4caSmmusante 	/*
3051cdf5b4caSmmusante 	 * Unmount snapshot unless we're doing a recursive rename,
3052cdf5b4caSmmusante 	 * in which case the dataset code figures out which snapshots
3053cdf5b4caSmmusante 	 * to unmount.
3054cdf5b4caSmmusante 	 */
3055cdf5b4caSmmusante 	if (!recursive && strchr(zc->zc_name, '@') != NULL &&
3056fa9e4066Sahrens 	    zc->zc_objset_type == DMU_OST_ZFS) {
30571d452cf5Sahrens 		int err = zfs_unmount_snap(zc->zc_name, NULL);
30581d452cf5Sahrens 		if (err)
30591d452cf5Sahrens 			return (err);
3060fa9e4066Sahrens 	}
3061681d9761SEric Taylor 	if (zc->zc_objset_type == DMU_OST_ZVOL)
3062681d9761SEric Taylor 		(void) zvol_remove_minor(zc->zc_name);
3063cdf5b4caSmmusante 	return (dmu_objset_rename(zc->zc_name, zc->zc_value, recursive));
3064fa9e4066Sahrens }
3065fa9e4066Sahrens 
306692241e0bSTom Erickson static int
306792241e0bSTom Erickson zfs_check_settable(const char *dsname, nvpair_t *pair, cred_t *cr)
306892241e0bSTom Erickson {
306992241e0bSTom Erickson 	const char *propname = nvpair_name(pair);
307092241e0bSTom Erickson 	boolean_t issnap = (strchr(dsname, '@') != NULL);
307192241e0bSTom Erickson 	zfs_prop_t prop = zfs_name_to_prop(propname);
307292241e0bSTom Erickson 	uint64_t intval;
307392241e0bSTom Erickson 	int err;
307492241e0bSTom Erickson 
307592241e0bSTom Erickson 	if (prop == ZPROP_INVAL) {
307692241e0bSTom Erickson 		if (zfs_prop_user(propname)) {
307792241e0bSTom Erickson 			if (err = zfs_secpolicy_write_perms(dsname,
307892241e0bSTom Erickson 			    ZFS_DELEG_PERM_USERPROP, cr))
307992241e0bSTom Erickson 				return (err);
308092241e0bSTom Erickson 			return (0);
308192241e0bSTom Erickson 		}
308292241e0bSTom Erickson 
308392241e0bSTom Erickson 		if (!issnap && zfs_prop_userquota(propname)) {
308492241e0bSTom Erickson 			const char *perm = NULL;
308592241e0bSTom Erickson 			const char *uq_prefix =
308692241e0bSTom Erickson 			    zfs_userquota_prop_prefixes[ZFS_PROP_USERQUOTA];
308792241e0bSTom Erickson 			const char *gq_prefix =
308892241e0bSTom Erickson 			    zfs_userquota_prop_prefixes[ZFS_PROP_GROUPQUOTA];
308992241e0bSTom Erickson 
309092241e0bSTom Erickson 			if (strncmp(propname, uq_prefix,
309192241e0bSTom Erickson 			    strlen(uq_prefix)) == 0) {
309292241e0bSTom Erickson 				perm = ZFS_DELEG_PERM_USERQUOTA;
309392241e0bSTom Erickson 			} else if (strncmp(propname, gq_prefix,
309492241e0bSTom Erickson 			    strlen(gq_prefix)) == 0) {
309592241e0bSTom Erickson 				perm = ZFS_DELEG_PERM_GROUPQUOTA;
309692241e0bSTom Erickson 			} else {
309792241e0bSTom Erickson 				/* USERUSED and GROUPUSED are read-only */
309892241e0bSTom Erickson 				return (EINVAL);
309992241e0bSTom Erickson 			}
310092241e0bSTom Erickson 
310192241e0bSTom Erickson 			if (err = zfs_secpolicy_write_perms(dsname, perm, cr))
310292241e0bSTom Erickson 				return (err);
310392241e0bSTom Erickson 			return (0);
310492241e0bSTom Erickson 		}
310592241e0bSTom Erickson 
310692241e0bSTom Erickson 		return (EINVAL);
310792241e0bSTom Erickson 	}
310892241e0bSTom Erickson 
310992241e0bSTom Erickson 	if (issnap)
311092241e0bSTom Erickson 		return (EINVAL);
311192241e0bSTom Erickson 
311292241e0bSTom Erickson 	if (nvpair_type(pair) == DATA_TYPE_NVLIST) {
311392241e0bSTom Erickson 		/*
311492241e0bSTom Erickson 		 * dsl_prop_get_all_impl() returns properties in this
311592241e0bSTom Erickson 		 * format.
311692241e0bSTom Erickson 		 */
311792241e0bSTom Erickson 		nvlist_t *attrs;
311892241e0bSTom Erickson 		VERIFY(nvpair_value_nvlist(pair, &attrs) == 0);
311992241e0bSTom Erickson 		VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE,
312092241e0bSTom Erickson 		    &pair) == 0);
312192241e0bSTom Erickson 	}
312292241e0bSTom Erickson 
312392241e0bSTom Erickson 	/*
312492241e0bSTom Erickson 	 * Check that this value is valid for this pool version
312592241e0bSTom Erickson 	 */
312692241e0bSTom Erickson 	switch (prop) {
312792241e0bSTom Erickson 	case ZFS_PROP_COMPRESSION:
312892241e0bSTom Erickson 		/*
312992241e0bSTom Erickson 		 * If the user specified gzip compression, make sure
313092241e0bSTom Erickson 		 * the SPA supports it. We ignore any errors here since
313192241e0bSTom Erickson 		 * we'll catch them later.
313292241e0bSTom Erickson 		 */
313392241e0bSTom Erickson 		if (nvpair_type(pair) == DATA_TYPE_UINT64 &&
313492241e0bSTom Erickson 		    nvpair_value_uint64(pair, &intval) == 0) {
313592241e0bSTom Erickson 			if (intval >= ZIO_COMPRESS_GZIP_1 &&
313692241e0bSTom Erickson 			    intval <= ZIO_COMPRESS_GZIP_9 &&
313792241e0bSTom Erickson 			    zfs_earlier_version(dsname,
313892241e0bSTom Erickson 			    SPA_VERSION_GZIP_COMPRESSION)) {
313992241e0bSTom Erickson 				return (ENOTSUP);
314092241e0bSTom Erickson 			}
314192241e0bSTom Erickson 
314292241e0bSTom Erickson 			if (intval == ZIO_COMPRESS_ZLE &&
314392241e0bSTom Erickson 			    zfs_earlier_version(dsname,
314492241e0bSTom Erickson 			    SPA_VERSION_ZLE_COMPRESSION))
314592241e0bSTom Erickson 				return (ENOTSUP);
314692241e0bSTom Erickson 
314792241e0bSTom Erickson 			/*
314892241e0bSTom Erickson 			 * If this is a bootable dataset then
314992241e0bSTom Erickson 			 * verify that the compression algorithm
315092241e0bSTom Erickson 			 * is supported for booting. We must return
315192241e0bSTom Erickson 			 * something other than ENOTSUP since it
315292241e0bSTom Erickson 			 * implies a downrev pool version.
315392241e0bSTom Erickson 			 */
315492241e0bSTom Erickson 			if (zfs_is_bootfs(dsname) &&
315592241e0bSTom Erickson 			    !BOOTFS_COMPRESS_VALID(intval)) {
315692241e0bSTom Erickson 				return (ERANGE);
315792241e0bSTom Erickson 			}
315892241e0bSTom Erickson 		}
315992241e0bSTom Erickson 		break;
316092241e0bSTom Erickson 
316192241e0bSTom Erickson 	case ZFS_PROP_COPIES:
316292241e0bSTom Erickson 		if (zfs_earlier_version(dsname, SPA_VERSION_DITTO_BLOCKS))
316392241e0bSTom Erickson 			return (ENOTSUP);
316492241e0bSTom Erickson 		break;
316592241e0bSTom Erickson 
316692241e0bSTom Erickson 	case ZFS_PROP_DEDUP:
316792241e0bSTom Erickson 		if (zfs_earlier_version(dsname, SPA_VERSION_DEDUP))
316892241e0bSTom Erickson 			return (ENOTSUP);
316992241e0bSTom Erickson 		break;
317092241e0bSTom Erickson 
317192241e0bSTom Erickson 	case ZFS_PROP_SHARESMB:
317292241e0bSTom Erickson 		if (zpl_earlier_version(dsname, ZPL_VERSION_FUID))
317392241e0bSTom Erickson 			return (ENOTSUP);
317492241e0bSTom Erickson 		break;
317592241e0bSTom Erickson 
317692241e0bSTom Erickson 	case ZFS_PROP_ACLINHERIT:
317792241e0bSTom Erickson 		if (nvpair_type(pair) == DATA_TYPE_UINT64 &&
317892241e0bSTom Erickson 		    nvpair_value_uint64(pair, &intval) == 0) {
317992241e0bSTom Erickson 			if (intval == ZFS_ACL_PASSTHROUGH_X &&
318092241e0bSTom Erickson 			    zfs_earlier_version(dsname,
318192241e0bSTom Erickson 			    SPA_VERSION_PASSTHROUGH_X))
318292241e0bSTom Erickson 				return (ENOTSUP);
318392241e0bSTom Erickson 		}
318492241e0bSTom Erickson 		break;
318592241e0bSTom Erickson 	}
318692241e0bSTom Erickson 
318792241e0bSTom Erickson 	return (zfs_secpolicy_setprop(dsname, prop, pair, CRED()));
318892241e0bSTom Erickson }
318992241e0bSTom Erickson 
319092241e0bSTom Erickson /*
319192241e0bSTom Erickson  * Removes properties from the given props list that fail permission checks
319292241e0bSTom Erickson  * needed to clear them and to restore them in case of a receive error. For each
319392241e0bSTom Erickson  * property, make sure we have both set and inherit permissions.
319492241e0bSTom Erickson  *
319592241e0bSTom Erickson  * Returns the first error encountered if any permission checks fail. If the
319692241e0bSTom Erickson  * caller provides a non-NULL errlist, it also gives the complete list of names
319792241e0bSTom Erickson  * of all the properties that failed a permission check along with the
319892241e0bSTom Erickson  * corresponding error numbers. The caller is responsible for freeing the
319992241e0bSTom Erickson  * returned errlist.
320092241e0bSTom Erickson  *
320192241e0bSTom Erickson  * If every property checks out successfully, zero is returned and the list
320292241e0bSTom Erickson  * pointed at by errlist is NULL.
320392241e0bSTom Erickson  */
320492241e0bSTom Erickson static int
320592241e0bSTom Erickson zfs_check_clearable(char *dataset, nvlist_t *props, nvlist_t **errlist)
3206745cd3c5Smaybee {
3207745cd3c5Smaybee 	zfs_cmd_t *zc;
320892241e0bSTom Erickson 	nvpair_t *pair, *next_pair;
320992241e0bSTom Erickson 	nvlist_t *errors;
321092241e0bSTom Erickson 	int err, rv = 0;
3211745cd3c5Smaybee 
3212745cd3c5Smaybee 	if (props == NULL)
321392241e0bSTom Erickson 		return (0);
321492241e0bSTom Erickson 
321592241e0bSTom Erickson 	VERIFY(nvlist_alloc(&errors, NV_UNIQUE_NAME, KM_SLEEP) == 0);
321692241e0bSTom Erickson 
3217745cd3c5Smaybee 	zc = kmem_alloc(sizeof (zfs_cmd_t), KM_SLEEP);
3218745cd3c5Smaybee 	(void) strcpy(zc->zc_name, dataset);
321992241e0bSTom Erickson 	pair = nvlist_next_nvpair(props, NULL);
322092241e0bSTom Erickson 	while (pair != NULL) {
322192241e0bSTom Erickson 		next_pair = nvlist_next_nvpair(props, pair);
322292241e0bSTom Erickson 
322392241e0bSTom Erickson 		(void) strcpy(zc->zc_value, nvpair_name(pair));
322492241e0bSTom Erickson 		if ((err = zfs_check_settable(dataset, pair, CRED())) != 0 ||
322592241e0bSTom Erickson 		    (err = zfs_secpolicy_inherit(zc, CRED())) != 0) {
322692241e0bSTom Erickson 			VERIFY(nvlist_remove_nvpair(props, pair) == 0);
322792241e0bSTom Erickson 			VERIFY(nvlist_add_int32(errors,
322892241e0bSTom Erickson 			    zc->zc_value, err) == 0);
322992241e0bSTom Erickson 		}
323092241e0bSTom Erickson 		pair = next_pair;
3231745cd3c5Smaybee 	}
3232745cd3c5Smaybee 	kmem_free(zc, sizeof (zfs_cmd_t));
323392241e0bSTom Erickson 
323492241e0bSTom Erickson 	if ((pair = nvlist_next_nvpair(errors, NULL)) == NULL) {
323592241e0bSTom Erickson 		nvlist_free(errors);
323692241e0bSTom Erickson 		errors = NULL;
323792241e0bSTom Erickson 	} else {
323892241e0bSTom Erickson 		VERIFY(nvpair_value_int32(pair, &rv) == 0);
323992241e0bSTom Erickson 	}
324092241e0bSTom Erickson 
324192241e0bSTom Erickson 	if (errlist == NULL)
324292241e0bSTom Erickson 		nvlist_free(errors);
324392241e0bSTom Erickson 	else
324492241e0bSTom Erickson 		*errlist = errors;
324592241e0bSTom Erickson 
324692241e0bSTom Erickson 	return (rv);
324792241e0bSTom Erickson }
324892241e0bSTom Erickson 
324992241e0bSTom Erickson static boolean_t
325092241e0bSTom Erickson propval_equals(nvpair_t *p1, nvpair_t *p2)
325192241e0bSTom Erickson {
325292241e0bSTom Erickson 	if (nvpair_type(p1) == DATA_TYPE_NVLIST) {
325392241e0bSTom Erickson 		/* dsl_prop_get_all_impl() format */
325492241e0bSTom Erickson 		nvlist_t *attrs;
325592241e0bSTom Erickson 		VERIFY(nvpair_value_nvlist(p1, &attrs) == 0);
325692241e0bSTom Erickson 		VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE,
325792241e0bSTom Erickson 		    &p1) == 0);
325892241e0bSTom Erickson 	}
325992241e0bSTom Erickson 
326092241e0bSTom Erickson 	if (nvpair_type(p2) == DATA_TYPE_NVLIST) {
326192241e0bSTom Erickson 		nvlist_t *attrs;
326292241e0bSTom Erickson 		VERIFY(nvpair_value_nvlist(p2, &attrs) == 0);
326392241e0bSTom Erickson 		VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE,
326492241e0bSTom Erickson 		    &p2) == 0);
326592241e0bSTom Erickson 	}
326692241e0bSTom Erickson 
326792241e0bSTom Erickson 	if (nvpair_type(p1) != nvpair_type(p2))
326892241e0bSTom Erickson 		return (B_FALSE);
326992241e0bSTom Erickson 
327092241e0bSTom Erickson 	if (nvpair_type(p1) == DATA_TYPE_STRING) {
327192241e0bSTom Erickson 		char *valstr1, *valstr2;
327292241e0bSTom Erickson 
327392241e0bSTom Erickson 		VERIFY(nvpair_value_string(p1, (char **)&valstr1) == 0);
327492241e0bSTom Erickson 		VERIFY(nvpair_value_string(p2, (char **)&valstr2) == 0);
327592241e0bSTom Erickson 		return (strcmp(valstr1, valstr2) == 0);
327692241e0bSTom Erickson 	} else {
327792241e0bSTom Erickson 		uint64_t intval1, intval2;
327892241e0bSTom Erickson 
327992241e0bSTom Erickson 		VERIFY(nvpair_value_uint64(p1, &intval1) == 0);
328092241e0bSTom Erickson 		VERIFY(nvpair_value_uint64(p2, &intval2) == 0);
328192241e0bSTom Erickson 		return (intval1 == intval2);
328292241e0bSTom Erickson 	}
3283745cd3c5Smaybee }
3284745cd3c5Smaybee 
328592241e0bSTom Erickson /*
328692241e0bSTom Erickson  * Remove properties from props if they are not going to change (as determined
328792241e0bSTom Erickson  * by comparison with origprops). Remove them from origprops as well, since we
328892241e0bSTom Erickson  * do not need to clear or restore properties that won't change.
328992241e0bSTom Erickson  */
329092241e0bSTom Erickson static void
329192241e0bSTom Erickson props_reduce(nvlist_t *props, nvlist_t *origprops)
329292241e0bSTom Erickson {
329392241e0bSTom Erickson 	nvpair_t *pair, *next_pair;
329492241e0bSTom Erickson 
329592241e0bSTom Erickson 	if (origprops == NULL)
329692241e0bSTom Erickson 		return; /* all props need to be received */
329792241e0bSTom Erickson 
329892241e0bSTom Erickson 	pair = nvlist_next_nvpair(props, NULL);
329992241e0bSTom Erickson 	while (pair != NULL) {
330092241e0bSTom Erickson 		const char *propname = nvpair_name(pair);
330192241e0bSTom Erickson 		nvpair_t *match;
330292241e0bSTom Erickson 
330392241e0bSTom Erickson 		next_pair = nvlist_next_nvpair(props, pair);
330492241e0bSTom Erickson 
330592241e0bSTom Erickson 		if ((nvlist_lookup_nvpair(origprops, propname,
330692241e0bSTom Erickson 		    &match) != 0) || !propval_equals(pair, match))
330792241e0bSTom Erickson 			goto next; /* need to set received value */
330892241e0bSTom Erickson 
330992241e0bSTom Erickson 		/* don't clear the existing received value */
331092241e0bSTom Erickson 		(void) nvlist_remove_nvpair(origprops, match);
331192241e0bSTom Erickson 		/* don't bother receiving the property */
331292241e0bSTom Erickson 		(void) nvlist_remove_nvpair(props, pair);
331392241e0bSTom Erickson next:
331492241e0bSTom Erickson 		pair = next_pair;
331592241e0bSTom Erickson 	}
331692241e0bSTom Erickson }
331792241e0bSTom Erickson 
331892241e0bSTom Erickson #ifdef	DEBUG
331992241e0bSTom Erickson static boolean_t zfs_ioc_recv_inject_err;
332092241e0bSTom Erickson #endif
332192241e0bSTom Erickson 
33223cb34c60Sahrens /*
33233cb34c60Sahrens  * inputs:
33243cb34c60Sahrens  * zc_name		name of containing filesystem
33253cb34c60Sahrens  * zc_nvlist_src{_size}	nvlist of properties to apply
33263cb34c60Sahrens  * zc_value		name of snapshot to create
33273cb34c60Sahrens  * zc_string		name of clone origin (if DRR_FLAG_CLONE)
33283cb34c60Sahrens  * zc_cookie		file descriptor to recv from
33293cb34c60Sahrens  * zc_begin_record	the BEGIN record of the stream (not byteswapped)
33303cb34c60Sahrens  * zc_guid		force flag
33313cb34c60Sahrens  *
33323cb34c60Sahrens  * outputs:
33333cb34c60Sahrens  * zc_cookie		number of bytes read
333492241e0bSTom Erickson  * zc_nvlist_dst{_size} error for each unapplied received property
333592241e0bSTom Erickson  * zc_obj		zprop_errflags_t
33363cb34c60Sahrens  */
3337fa9e4066Sahrens static int
33383cb34c60Sahrens zfs_ioc_recv(zfs_cmd_t *zc)
3339fa9e4066Sahrens {
3340fa9e4066Sahrens 	file_t *fp;
3341f18faf3fSek 	objset_t *os;
33423cb34c60Sahrens 	dmu_recv_cookie_t drc;
3343f18faf3fSek 	boolean_t force = (boolean_t)zc->zc_guid;
334492241e0bSTom Erickson 	int fd;
334592241e0bSTom Erickson 	int error = 0;
334692241e0bSTom Erickson 	int props_error = 0;
334792241e0bSTom Erickson 	nvlist_t *errors;
33483cb34c60Sahrens 	offset_t off;
334992241e0bSTom Erickson 	nvlist_t *props = NULL; /* sent properties */
335092241e0bSTom Erickson 	nvlist_t *origprops = NULL; /* existing properties */
33513cb34c60Sahrens 	objset_t *origin = NULL;
33523cb34c60Sahrens 	char *tosnap;
33533cb34c60Sahrens 	char tofs[ZFS_MAXNAMELEN];
335492241e0bSTom Erickson 	boolean_t first_recvd_props = B_FALSE;
3355fa9e4066Sahrens 
33563ccfa83cSahrens 	if (dataset_namecheck(zc->zc_value, NULL, NULL) != 0 ||
3357f18faf3fSek 	    strchr(zc->zc_value, '@') == NULL ||
3358f18faf3fSek 	    strchr(zc->zc_value, '%'))
33593ccfa83cSahrens 		return (EINVAL);
33603ccfa83cSahrens 
33613cb34c60Sahrens 	(void) strcpy(tofs, zc->zc_value);
33623cb34c60Sahrens 	tosnap = strchr(tofs, '@');
336392241e0bSTom Erickson 	*tosnap++ = '\0';
33643cb34c60Sahrens 
33653cb34c60Sahrens 	if (zc->zc_nvlist_src != NULL &&
33663cb34c60Sahrens 	    (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
3367478ed9adSEric Taylor 	    zc->zc_iflags, &props)) != 0)
33683cb34c60Sahrens 		return (error);
33693cb34c60Sahrens 
3370fa9e4066Sahrens 	fd = zc->zc_cookie;
3371fa9e4066Sahrens 	fp = getf(fd);
33723cb34c60Sahrens 	if (fp == NULL) {
33733cb34c60Sahrens 		nvlist_free(props);
3374fa9e4066Sahrens 		return (EBADF);
33753cb34c60Sahrens 	}
3376f18faf3fSek 
337792241e0bSTom Erickson 	VERIFY(nvlist_alloc(&errors, NV_UNIQUE_NAME, KM_SLEEP) == 0);
337892241e0bSTom Erickson 
3379503ad85cSMatthew Ahrens 	if (props && dmu_objset_hold(tofs, FTAG, &os) == 0) {
338092241e0bSTom Erickson 		if ((spa_version(os->os_spa) >= SPA_VERSION_RECVD_PROPS) &&
338192241e0bSTom Erickson 		    !dsl_prop_get_hasrecvd(os)) {
338292241e0bSTom Erickson 			first_recvd_props = B_TRUE;
338392241e0bSTom Erickson 		}
338492241e0bSTom Erickson 
3385745cd3c5Smaybee 		/*
338692241e0bSTom Erickson 		 * If new received properties are supplied, they are to
338792241e0bSTom Erickson 		 * completely replace the existing received properties, so stash
338892241e0bSTom Erickson 		 * away the existing ones.
3389745cd3c5Smaybee 		 */
339092241e0bSTom Erickson 		if (dsl_prop_get_received(os, &origprops) == 0) {
339192241e0bSTom Erickson 			nvlist_t *errlist = NULL;
339292241e0bSTom Erickson 			/*
339392241e0bSTom Erickson 			 * Don't bother writing a property if its value won't
339492241e0bSTom Erickson 			 * change (and avoid the unnecessary security checks).
339592241e0bSTom Erickson 			 *
339692241e0bSTom Erickson 			 * The first receive after SPA_VERSION_RECVD_PROPS is a
339792241e0bSTom Erickson 			 * special case where we blow away all local properties
339892241e0bSTom Erickson 			 * regardless.
339992241e0bSTom Erickson 			 */
340092241e0bSTom Erickson 			if (!first_recvd_props)
340192241e0bSTom Erickson 				props_reduce(props, origprops);
340292241e0bSTom Erickson 			if (zfs_check_clearable(tofs, origprops,
340392241e0bSTom Erickson 			    &errlist) != 0)
340492241e0bSTom Erickson 				(void) nvlist_merge(errors, errlist, 0);
340592241e0bSTom Erickson 			nvlist_free(errlist);
340692241e0bSTom Erickson 		}
3407745cd3c5Smaybee 
3408503ad85cSMatthew Ahrens 		dmu_objset_rele(os, FTAG);
3409f18faf3fSek 	}
3410f18faf3fSek 
34113cb34c60Sahrens 	if (zc->zc_string[0]) {
3412503ad85cSMatthew Ahrens 		error = dmu_objset_hold(zc->zc_string, FTAG, &origin);
3413745cd3c5Smaybee 		if (error)
3414745cd3c5Smaybee 			goto out;
34153cb34c60Sahrens 	}
34163cb34c60Sahrens 
34179e69d7d0SLori Alt 	error = dmu_recv_begin(tofs, tosnap, zc->zc_top_ds,
34189e69d7d0SLori Alt 	    &zc->zc_begin_record, force, origin, &drc);
34193cb34c60Sahrens 	if (origin)
3420503ad85cSMatthew Ahrens 		dmu_objset_rele(origin, FTAG);
3421745cd3c5Smaybee 	if (error)
3422745cd3c5Smaybee 		goto out;
3423f18faf3fSek 
3424f18faf3fSek 	/*
342592241e0bSTom Erickson 	 * Set properties before we receive the stream so that they are applied
342692241e0bSTom Erickson 	 * to the new data. Note that we must call dmu_recv_stream() if
342792241e0bSTom Erickson 	 * dmu_recv_begin() succeeds.
3428f18faf3fSek 	 */
34293cb34c60Sahrens 	if (props) {
343092241e0bSTom Erickson 		nvlist_t *errlist;
343192241e0bSTom Erickson 
343292241e0bSTom Erickson 		if (dmu_objset_from_ds(drc.drc_logical_ds, &os) == 0) {
343392241e0bSTom Erickson 			if (drc.drc_newfs) {
343492241e0bSTom Erickson 				if (spa_version(os->os_spa) >=
343592241e0bSTom Erickson 				    SPA_VERSION_RECVD_PROPS)
343692241e0bSTom Erickson 					first_recvd_props = B_TRUE;
343792241e0bSTom Erickson 			} else if (origprops != NULL) {
343892241e0bSTom Erickson 				if (clear_received_props(os, tofs, origprops,
343992241e0bSTom Erickson 				    first_recvd_props ? NULL : props) != 0)
344092241e0bSTom Erickson 					zc->zc_obj |= ZPROP_ERR_NOCLEAR;
344192241e0bSTom Erickson 			} else {
344292241e0bSTom Erickson 				zc->zc_obj |= ZPROP_ERR_NOCLEAR;
344392241e0bSTom Erickson 			}
344492241e0bSTom Erickson 			dsl_prop_set_hasrecvd(os);
344592241e0bSTom Erickson 		} else if (!drc.drc_newfs) {
344692241e0bSTom Erickson 			zc->zc_obj |= ZPROP_ERR_NOCLEAR;
344792241e0bSTom Erickson 		}
344892241e0bSTom Erickson 
344992241e0bSTom Erickson 		(void) zfs_set_prop_nvlist(tofs, ZPROP_SRC_RECEIVED,
345092241e0bSTom Erickson 		    props, &errlist);
345192241e0bSTom Erickson 		(void) nvlist_merge(errors, errlist, 0);
345292241e0bSTom Erickson 		nvlist_free(errlist);
345392241e0bSTom Erickson 	}
345492241e0bSTom Erickson 
345592241e0bSTom Erickson 	if (fit_error_list(zc, &errors) != 0 || put_nvlist(zc, errors) != 0) {
3456745cd3c5Smaybee 		/*
345792241e0bSTom Erickson 		 * Caller made zc->zc_nvlist_dst less than the minimum expected
345892241e0bSTom Erickson 		 * size or supplied an invalid address.
3459745cd3c5Smaybee 		 */
346092241e0bSTom Erickson 		props_error = EINVAL;
34613cb34c60Sahrens 	}
34623cb34c60Sahrens 
34633cb34c60Sahrens 	off = fp->f_offset;
34643cb34c60Sahrens 	error = dmu_recv_stream(&drc, fp->f_vnode, &off);
3465a2eea2e1Sahrens 
3466f4b94bdeSMatthew Ahrens 	if (error == 0) {
3467f4b94bdeSMatthew Ahrens 		zfsvfs_t *zfsvfs = NULL;
3468745cd3c5Smaybee 
3469f4b94bdeSMatthew Ahrens 		if (getzfsvfs(tofs, &zfsvfs) == 0) {
3470f4b94bdeSMatthew Ahrens 			/* online recv */
3471f4b94bdeSMatthew Ahrens 			int end_err;
3472745cd3c5Smaybee 
3473503ad85cSMatthew Ahrens 			error = zfs_suspend_fs(zfsvfs);
3474f4b94bdeSMatthew Ahrens 			/*
3475f4b94bdeSMatthew Ahrens 			 * If the suspend fails, then the recv_end will
3476f4b94bdeSMatthew Ahrens 			 * likely also fail, and clean up after itself.
3477f4b94bdeSMatthew Ahrens 			 */
3478f4b94bdeSMatthew Ahrens 			end_err = dmu_recv_end(&drc);
34795c703fceSGeorge Wilson 			if (error == 0)
34805c703fceSGeorge Wilson 				error = zfs_resume_fs(zfsvfs, tofs);
3481f4b94bdeSMatthew Ahrens 			error = error ? error : end_err;
3482f4b94bdeSMatthew Ahrens 			VFS_RELE(zfsvfs->z_vfs);
3483745cd3c5Smaybee 		} else {
3484f4b94bdeSMatthew Ahrens 			error = dmu_recv_end(&drc);
34853cb34c60Sahrens 		}
348647f263f4Sek 	}
34873cb34c60Sahrens 
34883cb34c60Sahrens 	zc->zc_cookie = off - fp->f_offset;
34893cb34c60Sahrens 	if (VOP_SEEK(fp->f_vnode, fp->f_offset, &off, NULL) == 0)
34903cb34c60Sahrens 		fp->f_offset = off;
3491a2eea2e1Sahrens 
349292241e0bSTom Erickson #ifdef	DEBUG
349392241e0bSTom Erickson 	if (zfs_ioc_recv_inject_err) {
349492241e0bSTom Erickson 		zfs_ioc_recv_inject_err = B_FALSE;
349592241e0bSTom Erickson 		error = 1;
349692241e0bSTom Erickson 	}
349792241e0bSTom Erickson #endif
3498745cd3c5Smaybee 	/*
3499745cd3c5Smaybee 	 * On error, restore the original props.
3500745cd3c5Smaybee 	 */
3501745cd3c5Smaybee 	if (error && props) {
350292241e0bSTom Erickson 		if (dmu_objset_hold(tofs, FTAG, &os) == 0) {
350392241e0bSTom Erickson 			if (clear_received_props(os, tofs, props, NULL) != 0) {
350492241e0bSTom Erickson 				/*
350592241e0bSTom Erickson 				 * We failed to clear the received properties.
350692241e0bSTom Erickson 				 * Since we may have left a $recvd value on the
350792241e0bSTom Erickson 				 * system, we can't clear the $hasrecvd flag.
350892241e0bSTom Erickson 				 */
350992241e0bSTom Erickson 				zc->zc_obj |= ZPROP_ERR_NORESTORE;
351092241e0bSTom Erickson 			} else if (first_recvd_props) {
351192241e0bSTom Erickson 				dsl_prop_unset_hasrecvd(os);
351292241e0bSTom Erickson 			}
351392241e0bSTom Erickson 			dmu_objset_rele(os, FTAG);
351492241e0bSTom Erickson 		} else if (!drc.drc_newfs) {
351592241e0bSTom Erickson 			/* We failed to clear the received properties. */
351692241e0bSTom Erickson 			zc->zc_obj |= ZPROP_ERR_NORESTORE;
351792241e0bSTom Erickson 		}
351892241e0bSTom Erickson 
351992241e0bSTom Erickson 		if (origprops == NULL && !drc.drc_newfs) {
352092241e0bSTom Erickson 			/* We failed to stash the original properties. */
352192241e0bSTom Erickson 			zc->zc_obj |= ZPROP_ERR_NORESTORE;
352292241e0bSTom Erickson 		}
352392241e0bSTom Erickson 
352492241e0bSTom Erickson 		/*
352592241e0bSTom Erickson 		 * dsl_props_set() will not convert RECEIVED to LOCAL on or
352692241e0bSTom Erickson 		 * after SPA_VERSION_RECVD_PROPS, so we need to specify LOCAL
352792241e0bSTom Erickson 		 * explictly if we're restoring local properties cleared in the
352892241e0bSTom Erickson 		 * first new-style receive.
352992241e0bSTom Erickson 		 */
353092241e0bSTom Erickson 		if (origprops != NULL &&
353192241e0bSTom Erickson 		    zfs_set_prop_nvlist(tofs, (first_recvd_props ?
353292241e0bSTom Erickson 		    ZPROP_SRC_LOCAL : ZPROP_SRC_RECEIVED),
353392241e0bSTom Erickson 		    origprops, NULL) != 0) {
353492241e0bSTom Erickson 			/*
353592241e0bSTom Erickson 			 * We stashed the original properties but failed to
353692241e0bSTom Erickson 			 * restore them.
353792241e0bSTom Erickson 			 */
353892241e0bSTom Erickson 			zc->zc_obj |= ZPROP_ERR_NORESTORE;
353992241e0bSTom Erickson 		}
3540745cd3c5Smaybee 	}
3541745cd3c5Smaybee out:
3542745cd3c5Smaybee 	nvlist_free(props);
3543745cd3c5Smaybee 	nvlist_free(origprops);
354492241e0bSTom Erickson 	nvlist_free(errors);
3545fa9e4066Sahrens 	releasef(fd);
354692241e0bSTom Erickson 
354792241e0bSTom Erickson 	if (error == 0)
354892241e0bSTom Erickson 		error = props_error;
354992241e0bSTom Erickson 
3550fa9e4066Sahrens 	return (error);
3551fa9e4066Sahrens }
3552fa9e4066Sahrens 
35533cb34c60Sahrens /*
35543cb34c60Sahrens  * inputs:
35553cb34c60Sahrens  * zc_name	name of snapshot to send
35563cb34c60Sahrens  * zc_value	short name of incremental fromsnap (may be empty)
35573cb34c60Sahrens  * zc_cookie	file descriptor to send stream to
35583cb34c60Sahrens  * zc_obj	fromorigin flag (mutually exclusive with zc_value)
35593cb34c60Sahrens  *
35603cb34c60Sahrens  * outputs: none
35613cb34c60Sahrens  */
3562fa9e4066Sahrens static int
35633cb34c60Sahrens zfs_ioc_send(zfs_cmd_t *zc)
3564fa9e4066Sahrens {
3565fa9e4066Sahrens 	objset_t *fromsnap = NULL;
3566fa9e4066Sahrens 	objset_t *tosnap;
3567fa9e4066Sahrens 	file_t *fp;
3568fa9e4066Sahrens 	int error;
35693cb34c60Sahrens 	offset_t off;
3570fa9e4066Sahrens 
3571503ad85cSMatthew Ahrens 	error = dmu_objset_hold(zc->zc_name, FTAG, &tosnap);
3572fa9e4066Sahrens 	if (error)
3573fa9e4066Sahrens 		return (error);
3574fa9e4066Sahrens 
3575e9dbad6fSeschrock 	if (zc->zc_value[0] != '\0') {
35766a0f0066SEric Taylor 		char *buf;
3577a2eea2e1Sahrens 		char *cp;
3578a2eea2e1Sahrens 
35796a0f0066SEric Taylor 		buf = kmem_alloc(MAXPATHLEN, KM_SLEEP);
35806a0f0066SEric Taylor 		(void) strncpy(buf, zc->zc_name, MAXPATHLEN);
3581a2eea2e1Sahrens 		cp = strchr(buf, '@');
3582a2eea2e1Sahrens 		if (cp)
3583a2eea2e1Sahrens 			*(cp+1) = 0;
35846a0f0066SEric Taylor 		(void) strncat(buf, zc->zc_value, MAXPATHLEN);
3585503ad85cSMatthew Ahrens 		error = dmu_objset_hold(buf, FTAG, &fromsnap);
35866a0f0066SEric Taylor 		kmem_free(buf, MAXPATHLEN);
3587fa9e4066Sahrens 		if (error) {
3588503ad85cSMatthew Ahrens 			dmu_objset_rele(tosnap, FTAG);
3589fa9e4066Sahrens 			return (error);
3590fa9e4066Sahrens 		}
3591fa9e4066Sahrens 	}
3592fa9e4066Sahrens 
3593fa9e4066Sahrens 	fp = getf(zc->zc_cookie);
3594fa9e4066Sahrens 	if (fp == NULL) {
3595503ad85cSMatthew Ahrens 		dmu_objset_rele(tosnap, FTAG);
3596fa9e4066Sahrens 		if (fromsnap)
3597503ad85cSMatthew Ahrens 			dmu_objset_rele(fromsnap, FTAG);
3598fa9e4066Sahrens 		return (EBADF);
3599fa9e4066Sahrens 	}
3600fa9e4066Sahrens 
36013cb34c60Sahrens 	off = fp->f_offset;
36023cb34c60Sahrens 	error = dmu_sendbackup(tosnap, fromsnap, zc->zc_obj, fp->f_vnode, &off);
3603fa9e4066Sahrens 
36043cb34c60Sahrens 	if (VOP_SEEK(fp->f_vnode, fp->f_offset, &off, NULL) == 0)
36053cb34c60Sahrens 		fp->f_offset = off;
3606fa9e4066Sahrens 	releasef(zc->zc_cookie);
3607fa9e4066Sahrens 	if (fromsnap)
3608503ad85cSMatthew Ahrens 		dmu_objset_rele(fromsnap, FTAG);
3609503ad85cSMatthew Ahrens 	dmu_objset_rele(tosnap, FTAG);
3610fa9e4066Sahrens 	return (error);
3611fa9e4066Sahrens }
3612fa9e4066Sahrens 
3613ea8dc4b6Seschrock static int
3614ea8dc4b6Seschrock zfs_ioc_inject_fault(zfs_cmd_t *zc)
3615ea8dc4b6Seschrock {
3616ea8dc4b6Seschrock 	int id, error;
3617ea8dc4b6Seschrock 
3618ea8dc4b6Seschrock 	error = zio_inject_fault(zc->zc_name, (int)zc->zc_guid, &id,
3619ea8dc4b6Seschrock 	    &zc->zc_inject_record);
3620ea8dc4b6Seschrock 
3621ea8dc4b6Seschrock 	if (error == 0)
3622ea8dc4b6Seschrock 		zc->zc_guid = (uint64_t)id;
3623ea8dc4b6Seschrock 
3624ea8dc4b6Seschrock 	return (error);
3625ea8dc4b6Seschrock }
3626ea8dc4b6Seschrock 
3627ea8dc4b6Seschrock static int
3628ea8dc4b6Seschrock zfs_ioc_clear_fault(zfs_cmd_t *zc)
3629ea8dc4b6Seschrock {
3630ea8dc4b6Seschrock 	return (zio_clear_fault((int)zc->zc_guid));
3631ea8dc4b6Seschrock }
3632ea8dc4b6Seschrock 
3633ea8dc4b6Seschrock static int
3634ea8dc4b6Seschrock zfs_ioc_inject_list_next(zfs_cmd_t *zc)
3635ea8dc4b6Seschrock {
3636ea8dc4b6Seschrock 	int id = (int)zc->zc_guid;
3637ea8dc4b6Seschrock 	int error;
3638ea8dc4b6Seschrock 
3639ea8dc4b6Seschrock 	error = zio_inject_list_next(&id, zc->zc_name, sizeof (zc->zc_name),
3640ea8dc4b6Seschrock 	    &zc->zc_inject_record);
3641ea8dc4b6Seschrock 
3642ea8dc4b6Seschrock 	zc->zc_guid = id;
3643ea8dc4b6Seschrock 
3644ea8dc4b6Seschrock 	return (error);
3645ea8dc4b6Seschrock }
3646ea8dc4b6Seschrock 
3647ea8dc4b6Seschrock static int
3648ea8dc4b6Seschrock zfs_ioc_error_log(zfs_cmd_t *zc)
3649ea8dc4b6Seschrock {
3650ea8dc4b6Seschrock 	spa_t *spa;
3651ea8dc4b6Seschrock 	int error;
3652e9dbad6fSeschrock 	size_t count = (size_t)zc->zc_nvlist_dst_size;
3653ea8dc4b6Seschrock 
3654ea8dc4b6Seschrock 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
3655ea8dc4b6Seschrock 		return (error);
3656ea8dc4b6Seschrock 
3657e9dbad6fSeschrock 	error = spa_get_errlog(spa, (void *)(uintptr_t)zc->zc_nvlist_dst,
3658ea8dc4b6Seschrock 	    &count);
3659ea8dc4b6Seschrock 	if (error == 0)
3660e9dbad6fSeschrock 		zc->zc_nvlist_dst_size = count;
3661ea8dc4b6Seschrock 	else
3662e9dbad6fSeschrock 		zc->zc_nvlist_dst_size = spa_get_errlog_size(spa);
3663ea8dc4b6Seschrock 
3664ea8dc4b6Seschrock 	spa_close(spa, FTAG);
3665ea8dc4b6Seschrock 
3666ea8dc4b6Seschrock 	return (error);
3667ea8dc4b6Seschrock }
3668ea8dc4b6Seschrock 
3669ea8dc4b6Seschrock static int
3670ea8dc4b6Seschrock zfs_ioc_clear(zfs_cmd_t *zc)
3671ea8dc4b6Seschrock {
3672ea8dc4b6Seschrock 	spa_t *spa;
3673ea8dc4b6Seschrock 	vdev_t *vd;
3674bb8b5132Sek 	int error;
3675ea8dc4b6Seschrock 
3676b87f3af3Sperrin 	/*
3677b87f3af3Sperrin 	 * On zpool clear we also fix up missing slogs
3678b87f3af3Sperrin 	 */
3679b87f3af3Sperrin 	mutex_enter(&spa_namespace_lock);
3680b87f3af3Sperrin 	spa = spa_lookup(zc->zc_name);
3681b87f3af3Sperrin 	if (spa == NULL) {
3682b87f3af3Sperrin 		mutex_exit(&spa_namespace_lock);
3683b87f3af3Sperrin 		return (EIO);
3684b87f3af3Sperrin 	}
3685b24ab676SJeff Bonwick 	if (spa_get_log_state(spa) == SPA_LOG_MISSING) {
3686b87f3af3Sperrin 		/* we need to let spa_open/spa_load clear the chains */
3687b24ab676SJeff Bonwick 		spa_set_log_state(spa, SPA_LOG_CLEAR);
3688b87f3af3Sperrin 	}
3689468c413aSTim Haley 	spa->spa_last_open_failed = 0;
3690b87f3af3Sperrin 	mutex_exit(&spa_namespace_lock);
3691b87f3af3Sperrin 
3692c8ee1847SVictor Latushkin 	if (zc->zc_cookie & ZPOOL_NO_REWIND) {
3693468c413aSTim Haley 		error = spa_open(zc->zc_name, &spa, FTAG);
3694468c413aSTim Haley 	} else {
3695468c413aSTim Haley 		nvlist_t *policy;
3696468c413aSTim Haley 		nvlist_t *config = NULL;
3697468c413aSTim Haley 
3698468c413aSTim Haley 		if (zc->zc_nvlist_src == NULL)
3699468c413aSTim Haley 			return (EINVAL);
3700468c413aSTim Haley 
3701468c413aSTim Haley 		if ((error = get_nvlist(zc->zc_nvlist_src,
3702468c413aSTim Haley 		    zc->zc_nvlist_src_size, zc->zc_iflags, &policy)) == 0) {
3703468c413aSTim Haley 			error = spa_open_rewind(zc->zc_name, &spa, FTAG,
3704468c413aSTim Haley 			    policy, &config);
3705468c413aSTim Haley 			if (config != NULL) {
3706468c413aSTim Haley 				(void) put_nvlist(zc, config);
3707468c413aSTim Haley 				nvlist_free(config);
3708468c413aSTim Haley 			}
3709468c413aSTim Haley 			nvlist_free(policy);
3710468c413aSTim Haley 		}
3711468c413aSTim Haley 	}
3712468c413aSTim Haley 
3713468c413aSTim Haley 	if (error)
3714ea8dc4b6Seschrock 		return (error);
3715ea8dc4b6Seschrock 
37168f18d1faSGeorge Wilson 	spa_vdev_state_enter(spa, SCL_NONE);
3717ea8dc4b6Seschrock 
3718e9dbad6fSeschrock 	if (zc->zc_guid == 0) {
3719ea8dc4b6Seschrock 		vd = NULL;
3720c5904d13Seschrock 	} else {
3721c5904d13Seschrock 		vd = spa_lookup_by_guid(spa, zc->zc_guid, B_TRUE);
3722fa94a07fSbrendan 		if (vd == NULL) {
3723e14bb325SJeff Bonwick 			(void) spa_vdev_state_exit(spa, NULL, ENODEV);
3724fa94a07fSbrendan 			spa_close(spa, FTAG);
3725fa94a07fSbrendan 			return (ENODEV);
3726fa94a07fSbrendan 		}
3727ea8dc4b6Seschrock 	}
3728ea8dc4b6Seschrock 
3729e14bb325SJeff Bonwick 	vdev_clear(spa, vd);
3730e14bb325SJeff Bonwick 
3731e14bb325SJeff Bonwick 	(void) spa_vdev_state_exit(spa, NULL, 0);
3732ea8dc4b6Seschrock 
3733e14bb325SJeff Bonwick 	/*
3734e14bb325SJeff Bonwick 	 * Resume any suspended I/Os.
3735e14bb325SJeff Bonwick 	 */
373654d692b7SGeorge Wilson 	if (zio_resume(spa) != 0)
373754d692b7SGeorge Wilson 		error = EIO;
3738ea8dc4b6Seschrock 
3739ea8dc4b6Seschrock 	spa_close(spa, FTAG);
3740ea8dc4b6Seschrock 
374154d692b7SGeorge Wilson 	return (error);
3742ea8dc4b6Seschrock }
3743ea8dc4b6Seschrock 
37443cb34c60Sahrens /*
37453cb34c60Sahrens  * inputs:
37463cb34c60Sahrens  * zc_name	name of filesystem
37473cb34c60Sahrens  * zc_value	name of origin snapshot
37483cb34c60Sahrens  *
3749681d9761SEric Taylor  * outputs:
3750681d9761SEric Taylor  * zc_string	name of conflicting snapshot, if there is one
37513cb34c60Sahrens  */
375299653d4eSeschrock static int
375399653d4eSeschrock zfs_ioc_promote(zfs_cmd_t *zc)
375499653d4eSeschrock {
37550b69c2f0Sahrens 	char *cp;
37560b69c2f0Sahrens 
37570b69c2f0Sahrens 	/*
37580b69c2f0Sahrens 	 * We don't need to unmount *all* the origin fs's snapshots, but
37590b69c2f0Sahrens 	 * it's easier.
37600b69c2f0Sahrens 	 */
3761e9dbad6fSeschrock 	cp = strchr(zc->zc_value, '@');
37620b69c2f0Sahrens 	if (cp)
37630b69c2f0Sahrens 		*cp = '\0';
3764e9dbad6fSeschrock 	(void) dmu_objset_find(zc->zc_value,
37650b69c2f0Sahrens 	    zfs_unmount_snap, NULL, DS_FIND_SNAPSHOTS);
3766681d9761SEric Taylor 	return (dsl_dataset_promote(zc->zc_name, zc->zc_string));
376799653d4eSeschrock }
376899653d4eSeschrock 
376914843421SMatthew Ahrens /*
377014843421SMatthew Ahrens  * Retrieve a single {user|group}{used|quota}@... property.
377114843421SMatthew Ahrens  *
377214843421SMatthew Ahrens  * inputs:
377314843421SMatthew Ahrens  * zc_name	name of filesystem
377414843421SMatthew Ahrens  * zc_objset_type zfs_userquota_prop_t
377514843421SMatthew Ahrens  * zc_value	domain name (eg. "S-1-234-567-89")
377614843421SMatthew Ahrens  * zc_guid	RID/UID/GID
377714843421SMatthew Ahrens  *
377814843421SMatthew Ahrens  * outputs:
377914843421SMatthew Ahrens  * zc_cookie	property value
378014843421SMatthew Ahrens  */
378114843421SMatthew Ahrens static int
378214843421SMatthew Ahrens zfs_ioc_userspace_one(zfs_cmd_t *zc)
378314843421SMatthew Ahrens {
378414843421SMatthew Ahrens 	zfsvfs_t *zfsvfs;
378514843421SMatthew Ahrens 	int error;
378614843421SMatthew Ahrens 
378714843421SMatthew Ahrens 	if (zc->zc_objset_type >= ZFS_NUM_USERQUOTA_PROPS)
378814843421SMatthew Ahrens 		return (EINVAL);
378914843421SMatthew Ahrens 
3790503ad85cSMatthew Ahrens 	error = zfsvfs_hold(zc->zc_name, FTAG, &zfsvfs);
379114843421SMatthew Ahrens 	if (error)
379214843421SMatthew Ahrens 		return (error);
379314843421SMatthew Ahrens 
379414843421SMatthew Ahrens 	error = zfs_userspace_one(zfsvfs,
379514843421SMatthew Ahrens 	    zc->zc_objset_type, zc->zc_value, zc->zc_guid, &zc->zc_cookie);
379614843421SMatthew Ahrens 	zfsvfs_rele(zfsvfs, FTAG);
379714843421SMatthew Ahrens 
379814843421SMatthew Ahrens 	return (error);
379914843421SMatthew Ahrens }
380014843421SMatthew Ahrens 
380114843421SMatthew Ahrens /*
380214843421SMatthew Ahrens  * inputs:
380314843421SMatthew Ahrens  * zc_name		name of filesystem
380414843421SMatthew Ahrens  * zc_cookie		zap cursor
380514843421SMatthew Ahrens  * zc_objset_type	zfs_userquota_prop_t
380614843421SMatthew Ahrens  * zc_nvlist_dst[_size] buffer to fill (not really an nvlist)
380714843421SMatthew Ahrens  *
380814843421SMatthew Ahrens  * outputs:
380914843421SMatthew Ahrens  * zc_nvlist_dst[_size]	data buffer (array of zfs_useracct_t)
381014843421SMatthew Ahrens  * zc_cookie	zap cursor
381114843421SMatthew Ahrens  */
381214843421SMatthew Ahrens static int
381314843421SMatthew Ahrens zfs_ioc_userspace_many(zfs_cmd_t *zc)
381414843421SMatthew Ahrens {
381514843421SMatthew Ahrens 	zfsvfs_t *zfsvfs;
3816eeb85002STim Haley 	int bufsize = zc->zc_nvlist_dst_size;
381714843421SMatthew Ahrens 
3818eeb85002STim Haley 	if (bufsize <= 0)
3819eeb85002STim Haley 		return (ENOMEM);
3820eeb85002STim Haley 
3821eeb85002STim Haley 	int error = zfsvfs_hold(zc->zc_name, FTAG, &zfsvfs);
382214843421SMatthew Ahrens 	if (error)
382314843421SMatthew Ahrens 		return (error);
382414843421SMatthew Ahrens 
382514843421SMatthew Ahrens 	void *buf = kmem_alloc(bufsize, KM_SLEEP);
382614843421SMatthew Ahrens 
382714843421SMatthew Ahrens 	error = zfs_userspace_many(zfsvfs, zc->zc_objset_type, &zc->zc_cookie,
382814843421SMatthew Ahrens 	    buf, &zc->zc_nvlist_dst_size);
382914843421SMatthew Ahrens 
383014843421SMatthew Ahrens 	if (error == 0) {
383114843421SMatthew Ahrens 		error = xcopyout(buf,
383214843421SMatthew Ahrens 		    (void *)(uintptr_t)zc->zc_nvlist_dst,
383314843421SMatthew Ahrens 		    zc->zc_nvlist_dst_size);
383414843421SMatthew Ahrens 	}
383514843421SMatthew Ahrens 	kmem_free(buf, bufsize);
383614843421SMatthew Ahrens 	zfsvfs_rele(zfsvfs, FTAG);
383714843421SMatthew Ahrens 
383814843421SMatthew Ahrens 	return (error);
383914843421SMatthew Ahrens }
384014843421SMatthew Ahrens 
384114843421SMatthew Ahrens /*
384214843421SMatthew Ahrens  * inputs:
384314843421SMatthew Ahrens  * zc_name		name of filesystem
384414843421SMatthew Ahrens  *
384514843421SMatthew Ahrens  * outputs:
384614843421SMatthew Ahrens  * none
384714843421SMatthew Ahrens  */
384814843421SMatthew Ahrens static int
384914843421SMatthew Ahrens zfs_ioc_userspace_upgrade(zfs_cmd_t *zc)
385014843421SMatthew Ahrens {
385114843421SMatthew Ahrens 	objset_t *os;
38521195e687SMark J Musante 	int error = 0;
385314843421SMatthew Ahrens 	zfsvfs_t *zfsvfs;
385414843421SMatthew Ahrens 
385514843421SMatthew Ahrens 	if (getzfsvfs(zc->zc_name, &zfsvfs) == 0) {
3856503ad85cSMatthew Ahrens 		if (!dmu_objset_userused_enabled(zfsvfs->z_os)) {
385714843421SMatthew Ahrens 			/*
385814843421SMatthew Ahrens 			 * If userused is not enabled, it may be because the
385914843421SMatthew Ahrens 			 * objset needs to be closed & reopened (to grow the
386014843421SMatthew Ahrens 			 * objset_phys_t).  Suspend/resume the fs will do that.
386114843421SMatthew Ahrens 			 */
3862503ad85cSMatthew Ahrens 			error = zfs_suspend_fs(zfsvfs);
3863503ad85cSMatthew Ahrens 			if (error == 0)
3864503ad85cSMatthew Ahrens 				error = zfs_resume_fs(zfsvfs, zc->zc_name);
386514843421SMatthew Ahrens 		}
386614843421SMatthew Ahrens 		if (error == 0)
386714843421SMatthew Ahrens 			error = dmu_objset_userspace_upgrade(zfsvfs->z_os);
386814843421SMatthew Ahrens 		VFS_RELE(zfsvfs->z_vfs);
386914843421SMatthew Ahrens 	} else {
3870503ad85cSMatthew Ahrens 		/* XXX kind of reading contents without owning */
3871503ad85cSMatthew Ahrens 		error = dmu_objset_hold(zc->zc_name, FTAG, &os);
387214843421SMatthew Ahrens 		if (error)
387314843421SMatthew Ahrens 			return (error);
387414843421SMatthew Ahrens 
387514843421SMatthew Ahrens 		error = dmu_objset_userspace_upgrade(os);
3876503ad85cSMatthew Ahrens 		dmu_objset_rele(os, FTAG);
387714843421SMatthew Ahrens 	}
387814843421SMatthew Ahrens 
387914843421SMatthew Ahrens 	return (error);
388014843421SMatthew Ahrens }
388114843421SMatthew Ahrens 
3882ecd6cf80Smarks /*
3883ecd6cf80Smarks  * We don't want to have a hard dependency
3884ecd6cf80Smarks  * against some special symbols in sharefs
3885da6c28aaSamw  * nfs, and smbsrv.  Determine them if needed when
3886ecd6cf80Smarks  * the first file system is shared.
3887da6c28aaSamw  * Neither sharefs, nfs or smbsrv are unloadable modules.
3888ecd6cf80Smarks  */
3889da6c28aaSamw int (*znfsexport_fs)(void *arg);
3890ecd6cf80Smarks int (*zshare_fs)(enum sharefs_sys_op, share_t *, uint32_t);
3891da6c28aaSamw int (*zsmbexport_fs)(void *arg, boolean_t add_share);
3892da6c28aaSamw 
3893da6c28aaSamw int zfs_nfsshare_inited;
3894da6c28aaSamw int zfs_smbshare_inited;
3895ecd6cf80Smarks 
3896ecd6cf80Smarks ddi_modhandle_t nfs_mod;
3897ecd6cf80Smarks ddi_modhandle_t sharefs_mod;
3898da6c28aaSamw ddi_modhandle_t smbsrv_mod;
3899ecd6cf80Smarks kmutex_t zfs_share_lock;
3900ecd6cf80Smarks 
3901da6c28aaSamw static int
3902da6c28aaSamw zfs_init_sharefs()
3903da6c28aaSamw {
3904da6c28aaSamw 	int error;
3905da6c28aaSamw 
3906da6c28aaSamw 	ASSERT(MUTEX_HELD(&zfs_share_lock));
3907da6c28aaSamw 	/* Both NFS and SMB shares also require sharetab support. */
3908da6c28aaSamw 	if (sharefs_mod == NULL && ((sharefs_mod =
3909da6c28aaSamw 	    ddi_modopen("fs/sharefs",
3910da6c28aaSamw 	    KRTLD_MODE_FIRST, &error)) == NULL)) {
3911da6c28aaSamw 		return (ENOSYS);
3912da6c28aaSamw 	}
3913da6c28aaSamw 	if (zshare_fs == NULL && ((zshare_fs =
3914da6c28aaSamw 	    (int (*)(enum sharefs_sys_op, share_t *, uint32_t))
3915da6c28aaSamw 	    ddi_modsym(sharefs_mod, "sharefs_impl", &error)) == NULL)) {
3916da6c28aaSamw 		return (ENOSYS);
3917da6c28aaSamw 	}
3918da6c28aaSamw 	return (0);
3919da6c28aaSamw }
3920da6c28aaSamw 
3921ecd6cf80Smarks static int
3922ecd6cf80Smarks zfs_ioc_share(zfs_cmd_t *zc)
3923ecd6cf80Smarks {
3924ecd6cf80Smarks 	int error;
3925ecd6cf80Smarks 	int opcode;
3926ecd6cf80Smarks 
3927da6c28aaSamw 	switch (zc->zc_share.z_sharetype) {
3928da6c28aaSamw 	case ZFS_SHARE_NFS:
3929da6c28aaSamw 	case ZFS_UNSHARE_NFS:
3930da6c28aaSamw 		if (zfs_nfsshare_inited == 0) {
3931da6c28aaSamw 			mutex_enter(&zfs_share_lock);
3932da6c28aaSamw 			if (nfs_mod == NULL && ((nfs_mod = ddi_modopen("fs/nfs",
3933da6c28aaSamw 			    KRTLD_MODE_FIRST, &error)) == NULL)) {
3934da6c28aaSamw 				mutex_exit(&zfs_share_lock);
3935da6c28aaSamw 				return (ENOSYS);
3936da6c28aaSamw 			}
3937da6c28aaSamw 			if (znfsexport_fs == NULL &&
3938da6c28aaSamw 			    ((znfsexport_fs = (int (*)(void *))
3939da6c28aaSamw 			    ddi_modsym(nfs_mod,
3940da6c28aaSamw 			    "nfs_export", &error)) == NULL)) {
3941da6c28aaSamw 				mutex_exit(&zfs_share_lock);
3942da6c28aaSamw 				return (ENOSYS);
3943da6c28aaSamw 			}
3944da6c28aaSamw 			error = zfs_init_sharefs();
3945da6c28aaSamw 			if (error) {
3946da6c28aaSamw 				mutex_exit(&zfs_share_lock);
3947da6c28aaSamw 				return (ENOSYS);
3948da6c28aaSamw 			}
3949da6c28aaSamw 			zfs_nfsshare_inited = 1;
3950ecd6cf80Smarks 			mutex_exit(&zfs_share_lock);
3951ecd6cf80Smarks 		}
3952da6c28aaSamw 		break;
3953da6c28aaSamw 	case ZFS_SHARE_SMB:
3954da6c28aaSamw 	case ZFS_UNSHARE_SMB:
3955da6c28aaSamw 		if (zfs_smbshare_inited == 0) {
3956da6c28aaSamw 			mutex_enter(&zfs_share_lock);
3957da6c28aaSamw 			if (smbsrv_mod == NULL && ((smbsrv_mod =
3958da6c28aaSamw 			    ddi_modopen("drv/smbsrv",
3959da6c28aaSamw 			    KRTLD_MODE_FIRST, &error)) == NULL)) {
3960da6c28aaSamw 				mutex_exit(&zfs_share_lock);
3961da6c28aaSamw 				return (ENOSYS);
3962da6c28aaSamw 			}
3963da6c28aaSamw 			if (zsmbexport_fs == NULL && ((zsmbexport_fs =
3964da6c28aaSamw 			    (int (*)(void *, boolean_t))ddi_modsym(smbsrv_mod,
3965faa1795aSjb 			    "smb_server_share", &error)) == NULL)) {
3966da6c28aaSamw 				mutex_exit(&zfs_share_lock);
3967da6c28aaSamw 				return (ENOSYS);
3968da6c28aaSamw 			}
3969da6c28aaSamw 			error = zfs_init_sharefs();
3970da6c28aaSamw 			if (error) {
3971da6c28aaSamw 				mutex_exit(&zfs_share_lock);
3972da6c28aaSamw 				return (ENOSYS);
3973da6c28aaSamw 			}
3974da6c28aaSamw 			zfs_smbshare_inited = 1;
3975ecd6cf80Smarks 			mutex_exit(&zfs_share_lock);
3976ecd6cf80Smarks 		}
3977da6c28aaSamw 		break;
3978da6c28aaSamw 	default:
3979da6c28aaSamw 		return (EINVAL);
3980da6c28aaSamw 	}
3981ecd6cf80Smarks 
3982da6c28aaSamw 	switch (zc->zc_share.z_sharetype) {
3983da6c28aaSamw 	case ZFS_SHARE_NFS:
3984da6c28aaSamw 	case ZFS_UNSHARE_NFS:
3985da6c28aaSamw 		if (error =
3986da6c28aaSamw 		    znfsexport_fs((void *)
3987da6c28aaSamw 		    (uintptr_t)zc->zc_share.z_exportdata))
3988da6c28aaSamw 			return (error);
3989da6c28aaSamw 		break;
3990da6c28aaSamw 	case ZFS_SHARE_SMB:
3991da6c28aaSamw 	case ZFS_UNSHARE_SMB:
3992da6c28aaSamw 		if (error = zsmbexport_fs((void *)
3993da6c28aaSamw 		    (uintptr_t)zc->zc_share.z_exportdata,
3994da6c28aaSamw 		    zc->zc_share.z_sharetype == ZFS_SHARE_SMB ?
3995743a77edSAlan Wright 		    B_TRUE: B_FALSE)) {
3996da6c28aaSamw 			return (error);
3997ecd6cf80Smarks 		}
3998da6c28aaSamw 		break;
3999ecd6cf80Smarks 	}
4000ecd6cf80Smarks 
4001da6c28aaSamw 	opcode = (zc->zc_share.z_sharetype == ZFS_SHARE_NFS ||
4002da6c28aaSamw 	    zc->zc_share.z_sharetype == ZFS_SHARE_SMB) ?
4003ecd6cf80Smarks 	    SHAREFS_ADD : SHAREFS_REMOVE;
4004ecd6cf80Smarks 
4005da6c28aaSamw 	/*
4006da6c28aaSamw 	 * Add or remove share from sharetab
4007da6c28aaSamw 	 */
4008ecd6cf80Smarks 	error = zshare_fs(opcode,
4009ecd6cf80Smarks 	    (void *)(uintptr_t)zc->zc_share.z_sharedata,
4010ecd6cf80Smarks 	    zc->zc_share.z_sharemax);
4011ecd6cf80Smarks 
4012ecd6cf80Smarks 	return (error);
4013ecd6cf80Smarks 
4014ecd6cf80Smarks }
4015ecd6cf80Smarks 
4016743a77edSAlan Wright ace_t full_access[] = {
4017743a77edSAlan Wright 	{(uid_t)-1, ACE_ALL_PERMS, ACE_EVERYONE, 0}
4018743a77edSAlan Wright };
4019743a77edSAlan Wright 
4020743a77edSAlan Wright /*
4021743a77edSAlan Wright  * Remove all ACL files in shares dir
4022743a77edSAlan Wright  */
4023743a77edSAlan Wright static int
4024743a77edSAlan Wright zfs_smb_acl_purge(znode_t *dzp)
4025743a77edSAlan Wright {
4026743a77edSAlan Wright 	zap_cursor_t	zc;
4027743a77edSAlan Wright 	zap_attribute_t	zap;
4028743a77edSAlan Wright 	zfsvfs_t *zfsvfs = dzp->z_zfsvfs;
4029743a77edSAlan Wright 	int error;
4030743a77edSAlan Wright 
4031743a77edSAlan Wright 	for (zap_cursor_init(&zc, zfsvfs->z_os, dzp->z_id);
4032743a77edSAlan Wright 	    (error = zap_cursor_retrieve(&zc, &zap)) == 0;
4033743a77edSAlan Wright 	    zap_cursor_advance(&zc)) {
4034743a77edSAlan Wright 		if ((error = VOP_REMOVE(ZTOV(dzp), zap.za_name, kcred,
4035743a77edSAlan Wright 		    NULL, 0)) != 0)
4036743a77edSAlan Wright 			break;
4037743a77edSAlan Wright 	}
4038743a77edSAlan Wright 	zap_cursor_fini(&zc);
4039743a77edSAlan Wright 	return (error);
4040743a77edSAlan Wright }
4041743a77edSAlan Wright 
4042743a77edSAlan Wright static int
4043743a77edSAlan Wright zfs_ioc_smb_acl(zfs_cmd_t *zc)
4044743a77edSAlan Wright {
4045743a77edSAlan Wright 	vnode_t *vp;
4046743a77edSAlan Wright 	znode_t *dzp;
4047743a77edSAlan Wright 	vnode_t *resourcevp = NULL;
4048743a77edSAlan Wright 	znode_t *sharedir;
4049743a77edSAlan Wright 	zfsvfs_t *zfsvfs;
4050743a77edSAlan Wright 	nvlist_t *nvlist;
4051743a77edSAlan Wright 	char *src, *target;
4052743a77edSAlan Wright 	vattr_t vattr;
4053743a77edSAlan Wright 	vsecattr_t vsec;
4054743a77edSAlan Wright 	int error = 0;
4055743a77edSAlan Wright 
4056743a77edSAlan Wright 	if ((error = lookupname(zc->zc_value, UIO_SYSSPACE,
4057743a77edSAlan Wright 	    NO_FOLLOW, NULL, &vp)) != 0)
4058743a77edSAlan Wright 		return (error);
4059743a77edSAlan Wright 
4060743a77edSAlan Wright 	/* Now make sure mntpnt and dataset are ZFS */
4061743a77edSAlan Wright 
4062743a77edSAlan Wright 	if (vp->v_vfsp->vfs_fstype != zfsfstype ||
4063743a77edSAlan Wright 	    (strcmp((char *)refstr_value(vp->v_vfsp->vfs_resource),
4064743a77edSAlan Wright 	    zc->zc_name) != 0)) {
4065743a77edSAlan Wright 		VN_RELE(vp);
4066743a77edSAlan Wright 		return (EINVAL);
4067743a77edSAlan Wright 	}
4068743a77edSAlan Wright 
4069743a77edSAlan Wright 	dzp = VTOZ(vp);
4070743a77edSAlan Wright 	zfsvfs = dzp->z_zfsvfs;
4071743a77edSAlan Wright 	ZFS_ENTER(zfsvfs);
4072743a77edSAlan Wright 
40739e1320c0SMark Shellenbaum 	/*
40749e1320c0SMark Shellenbaum 	 * Create share dir if its missing.
40759e1320c0SMark Shellenbaum 	 */
40769e1320c0SMark Shellenbaum 	mutex_enter(&zfsvfs->z_lock);
40779e1320c0SMark Shellenbaum 	if (zfsvfs->z_shares_dir == 0) {
40789e1320c0SMark Shellenbaum 		dmu_tx_t *tx;
40799e1320c0SMark Shellenbaum 
40809e1320c0SMark Shellenbaum 		tx = dmu_tx_create(zfsvfs->z_os);
40819e1320c0SMark Shellenbaum 		dmu_tx_hold_zap(tx, MASTER_NODE_OBJ, TRUE,
40829e1320c0SMark Shellenbaum 		    ZFS_SHARES_DIR);
40839e1320c0SMark Shellenbaum 		dmu_tx_hold_zap(tx, DMU_NEW_OBJECT, FALSE, NULL);
40849e1320c0SMark Shellenbaum 		error = dmu_tx_assign(tx, TXG_WAIT);
40859e1320c0SMark Shellenbaum 		if (error) {
40869e1320c0SMark Shellenbaum 			dmu_tx_abort(tx);
40879e1320c0SMark Shellenbaum 		} else {
40889e1320c0SMark Shellenbaum 			error = zfs_create_share_dir(zfsvfs, tx);
40899e1320c0SMark Shellenbaum 			dmu_tx_commit(tx);
40909e1320c0SMark Shellenbaum 		}
40919e1320c0SMark Shellenbaum 		if (error) {
40929e1320c0SMark Shellenbaum 			mutex_exit(&zfsvfs->z_lock);
40939e1320c0SMark Shellenbaum 			VN_RELE(vp);
40949e1320c0SMark Shellenbaum 			ZFS_EXIT(zfsvfs);
40959e1320c0SMark Shellenbaum 			return (error);
40969e1320c0SMark Shellenbaum 		}
40979e1320c0SMark Shellenbaum 	}
40989e1320c0SMark Shellenbaum 	mutex_exit(&zfsvfs->z_lock);
40999e1320c0SMark Shellenbaum 
41009e1320c0SMark Shellenbaum 	ASSERT(zfsvfs->z_shares_dir);
4101743a77edSAlan Wright 	if ((error = zfs_zget(zfsvfs, zfsvfs->z_shares_dir, &sharedir)) != 0) {
41029e1320c0SMark Shellenbaum 		VN_RELE(vp);
4103743a77edSAlan Wright 		ZFS_EXIT(zfsvfs);
4104743a77edSAlan Wright 		return (error);
4105743a77edSAlan Wright 	}
4106743a77edSAlan Wright 
4107743a77edSAlan Wright 	switch (zc->zc_cookie) {
4108743a77edSAlan Wright 	case ZFS_SMB_ACL_ADD:
4109743a77edSAlan Wright 		vattr.va_mask = AT_MODE|AT_UID|AT_GID|AT_TYPE;
4110743a77edSAlan Wright 		vattr.va_type = VREG;
4111743a77edSAlan Wright 		vattr.va_mode = S_IFREG|0777;
4112743a77edSAlan Wright 		vattr.va_uid = 0;
4113743a77edSAlan Wright 		vattr.va_gid = 0;
4114743a77edSAlan Wright 
4115743a77edSAlan Wright 		vsec.vsa_mask = VSA_ACE;
4116743a77edSAlan Wright 		vsec.vsa_aclentp = &full_access;
4117743a77edSAlan Wright 		vsec.vsa_aclentsz = sizeof (full_access);
4118743a77edSAlan Wright 		vsec.vsa_aclcnt = 1;
4119743a77edSAlan Wright 
4120743a77edSAlan Wright 		error = VOP_CREATE(ZTOV(sharedir), zc->zc_string,
4121743a77edSAlan Wright 		    &vattr, EXCL, 0, &resourcevp, kcred, 0, NULL, &vsec);
4122743a77edSAlan Wright 		if (resourcevp)
4123743a77edSAlan Wright 			VN_RELE(resourcevp);
4124743a77edSAlan Wright 		break;
4125743a77edSAlan Wright 
4126743a77edSAlan Wright 	case ZFS_SMB_ACL_REMOVE:
4127743a77edSAlan Wright 		error = VOP_REMOVE(ZTOV(sharedir), zc->zc_string, kcred,
4128743a77edSAlan Wright 		    NULL, 0);
4129743a77edSAlan Wright 		break;
4130743a77edSAlan Wright 
4131743a77edSAlan Wright 	case ZFS_SMB_ACL_RENAME:
4132743a77edSAlan Wright 		if ((error = get_nvlist(zc->zc_nvlist_src,
4133478ed9adSEric Taylor 		    zc->zc_nvlist_src_size, zc->zc_iflags, &nvlist)) != 0) {
4134743a77edSAlan Wright 			VN_RELE(vp);
4135743a77edSAlan Wright 			ZFS_EXIT(zfsvfs);
4136743a77edSAlan Wright 			return (error);
4137743a77edSAlan Wright 		}
4138743a77edSAlan Wright 		if (nvlist_lookup_string(nvlist, ZFS_SMB_ACL_SRC, &src) ||
4139743a77edSAlan Wright 		    nvlist_lookup_string(nvlist, ZFS_SMB_ACL_TARGET,
4140743a77edSAlan Wright 		    &target)) {
4141743a77edSAlan Wright 			VN_RELE(vp);
414289459e17SMark Shellenbaum 			VN_RELE(ZTOV(sharedir));
4143743a77edSAlan Wright 			ZFS_EXIT(zfsvfs);
41441195e687SMark J Musante 			nvlist_free(nvlist);
4145743a77edSAlan Wright 			return (error);
4146743a77edSAlan Wright 		}
4147743a77edSAlan Wright 		error = VOP_RENAME(ZTOV(sharedir), src, ZTOV(sharedir), target,
4148743a77edSAlan Wright 		    kcred, NULL, 0);
4149743a77edSAlan Wright 		nvlist_free(nvlist);
4150743a77edSAlan Wright 		break;
4151743a77edSAlan Wright 
4152743a77edSAlan Wright 	case ZFS_SMB_ACL_PURGE:
4153743a77edSAlan Wright 		error = zfs_smb_acl_purge(sharedir);
4154743a77edSAlan Wright 		break;
4155743a77edSAlan Wright 
4156743a77edSAlan Wright 	default:
4157743a77edSAlan Wright 		error = EINVAL;
4158743a77edSAlan Wright 		break;
4159743a77edSAlan Wright 	}
4160743a77edSAlan Wright 
4161743a77edSAlan Wright 	VN_RELE(vp);
4162743a77edSAlan Wright 	VN_RELE(ZTOV(sharedir));
4163743a77edSAlan Wright 
4164743a77edSAlan Wright 	ZFS_EXIT(zfsvfs);
4165743a77edSAlan Wright 
4166743a77edSAlan Wright 	return (error);
4167743a77edSAlan Wright }
4168743a77edSAlan Wright 
4169842727c2SChris Kirby /*
4170842727c2SChris Kirby  * inputs:
4171842727c2SChris Kirby  * zc_name	name of filesystem
4172842727c2SChris Kirby  * zc_value	short name of snap
4173842727c2SChris Kirby  * zc_string	user-supplied tag for this reference
4174842727c2SChris Kirby  * zc_cookie	recursive flag
4175ca45db41SChris Kirby  * zc_temphold	set if hold is temporary
4176842727c2SChris Kirby  *
4177842727c2SChris Kirby  * outputs:		none
4178842727c2SChris Kirby  */
4179842727c2SChris Kirby static int
4180842727c2SChris Kirby zfs_ioc_hold(zfs_cmd_t *zc)
4181842727c2SChris Kirby {
4182842727c2SChris Kirby 	boolean_t recursive = zc->zc_cookie;
4183842727c2SChris Kirby 
4184842727c2SChris Kirby 	if (snapshot_namecheck(zc->zc_value, NULL, NULL) != 0)
4185842727c2SChris Kirby 		return (EINVAL);
4186842727c2SChris Kirby 
4187842727c2SChris Kirby 	return (dsl_dataset_user_hold(zc->zc_name, zc->zc_value,
4188ca45db41SChris Kirby 	    zc->zc_string, recursive, zc->zc_temphold));
4189842727c2SChris Kirby }
4190842727c2SChris Kirby 
4191842727c2SChris Kirby /*
4192842727c2SChris Kirby  * inputs:
4193842727c2SChris Kirby  * zc_name	name of dataset from which we're releasing a user reference
4194842727c2SChris Kirby  * zc_value	short name of snap
4195842727c2SChris Kirby  * zc_string	user-supplied tag for this reference
4196842727c2SChris Kirby  * zc_cookie	recursive flag
4197842727c2SChris Kirby  *
4198842727c2SChris Kirby  * outputs:		none
4199842727c2SChris Kirby  */
4200842727c2SChris Kirby static int
4201842727c2SChris Kirby zfs_ioc_release(zfs_cmd_t *zc)
4202842727c2SChris Kirby {
4203842727c2SChris Kirby 	boolean_t recursive = zc->zc_cookie;
4204842727c2SChris Kirby 
4205842727c2SChris Kirby 	if (snapshot_namecheck(zc->zc_value, NULL, NULL) != 0)
4206842727c2SChris Kirby 		return (EINVAL);
4207842727c2SChris Kirby 
4208842727c2SChris Kirby 	return (dsl_dataset_user_release(zc->zc_name, zc->zc_value,
4209842727c2SChris Kirby 	    zc->zc_string, recursive));
4210842727c2SChris Kirby }
4211842727c2SChris Kirby 
4212842727c2SChris Kirby /*
4213842727c2SChris Kirby  * inputs:
4214842727c2SChris Kirby  * zc_name		name of filesystem
4215842727c2SChris Kirby  *
4216842727c2SChris Kirby  * outputs:
4217842727c2SChris Kirby  * zc_nvlist_src{_size}	nvlist of snapshot holds
4218842727c2SChris Kirby  */
4219842727c2SChris Kirby static int
4220842727c2SChris Kirby zfs_ioc_get_holds(zfs_cmd_t *zc)
4221842727c2SChris Kirby {
4222842727c2SChris Kirby 	nvlist_t *nvp;
4223842727c2SChris Kirby 	int error;
4224842727c2SChris Kirby 
4225842727c2SChris Kirby 	if ((error = dsl_dataset_get_holds(zc->zc_name, &nvp)) == 0) {
4226842727c2SChris Kirby 		error = put_nvlist(zc, nvp);
4227842727c2SChris Kirby 		nvlist_free(nvp);
4228842727c2SChris Kirby 	}
4229842727c2SChris Kirby 
4230842727c2SChris Kirby 	return (error);
4231842727c2SChris Kirby }
4232842727c2SChris Kirby 
4233ecd6cf80Smarks /*
42342a6b87f0Sek  * pool create, destroy, and export don't log the history as part of
42352a6b87f0Sek  * zfsdev_ioctl, but rather zfs_ioc_pool_create, and zfs_ioc_pool_export
42362a6b87f0Sek  * do the logging of those commands.
4237ecd6cf80Smarks  */
4238fa9e4066Sahrens static zfs_ioc_vec_t zfs_ioc_vec[] = {
423954d692b7SGeorge Wilson 	{ zfs_ioc_pool_create, zfs_secpolicy_config, POOL_NAME, B_FALSE,
424054d692b7SGeorge Wilson 	    B_FALSE },
424154d692b7SGeorge Wilson 	{ zfs_ioc_pool_destroy,	zfs_secpolicy_config, POOL_NAME, B_FALSE,
424254d692b7SGeorge Wilson 	    B_FALSE },
424354d692b7SGeorge Wilson 	{ zfs_ioc_pool_import, zfs_secpolicy_config, POOL_NAME, B_TRUE,
424454d692b7SGeorge Wilson 	    B_FALSE },
424554d692b7SGeorge Wilson 	{ zfs_ioc_pool_export, zfs_secpolicy_config, POOL_NAME, B_FALSE,
424654d692b7SGeorge Wilson 	    B_FALSE },
424754d692b7SGeorge Wilson 	{ zfs_ioc_pool_configs,	zfs_secpolicy_none, NO_NAME, B_FALSE,
424854d692b7SGeorge Wilson 	    B_FALSE },
424954d692b7SGeorge Wilson 	{ zfs_ioc_pool_stats, zfs_secpolicy_read, POOL_NAME, B_FALSE,
425054d692b7SGeorge Wilson 	    B_FALSE },
425154d692b7SGeorge Wilson 	{ zfs_ioc_pool_tryimport, zfs_secpolicy_config, NO_NAME, B_FALSE,
425254d692b7SGeorge Wilson 	    B_FALSE },
425354d692b7SGeorge Wilson 	{ zfs_ioc_pool_scrub, zfs_secpolicy_config, POOL_NAME, B_TRUE,
425454d692b7SGeorge Wilson 	    B_TRUE },
425554d692b7SGeorge Wilson 	{ zfs_ioc_pool_freeze, zfs_secpolicy_config, NO_NAME, B_FALSE,
425654d692b7SGeorge Wilson 	    B_FALSE },
425754d692b7SGeorge Wilson 	{ zfs_ioc_pool_upgrade,	zfs_secpolicy_config, POOL_NAME, B_TRUE,
425854d692b7SGeorge Wilson 	    B_TRUE },
425954d692b7SGeorge Wilson 	{ zfs_ioc_pool_get_history, zfs_secpolicy_config, POOL_NAME, B_FALSE,
426054d692b7SGeorge Wilson 	    B_FALSE },
426154d692b7SGeorge Wilson 	{ zfs_ioc_vdev_add, zfs_secpolicy_config, POOL_NAME, B_TRUE,
426254d692b7SGeorge Wilson 	    B_TRUE },
426354d692b7SGeorge Wilson 	{ zfs_ioc_vdev_remove, zfs_secpolicy_config, POOL_NAME, B_TRUE,
426454d692b7SGeorge Wilson 	    B_TRUE },
426554d692b7SGeorge Wilson 	{ zfs_ioc_vdev_set_state, zfs_secpolicy_config,	POOL_NAME, B_TRUE,
426654d692b7SGeorge Wilson 	    B_FALSE },
426754d692b7SGeorge Wilson 	{ zfs_ioc_vdev_attach, zfs_secpolicy_config, POOL_NAME, B_TRUE,
426854d692b7SGeorge Wilson 	    B_TRUE },
426954d692b7SGeorge Wilson 	{ zfs_ioc_vdev_detach, zfs_secpolicy_config, POOL_NAME, B_TRUE,
427054d692b7SGeorge Wilson 	    B_TRUE },
427154d692b7SGeorge Wilson 	{ zfs_ioc_vdev_setpath,	zfs_secpolicy_config, POOL_NAME, B_FALSE,
427254d692b7SGeorge Wilson 	    B_TRUE },
42736809eb4eSEric Schrock 	{ zfs_ioc_vdev_setfru,	zfs_secpolicy_config, POOL_NAME, B_FALSE,
42746809eb4eSEric Schrock 	    B_TRUE },
427554d692b7SGeorge Wilson 	{ zfs_ioc_objset_stats,	zfs_secpolicy_read, DATASET_NAME, B_FALSE,
4276cf2859fcSSanjeev Bagewadi 	    B_TRUE },
427754d692b7SGeorge Wilson 	{ zfs_ioc_objset_zplprops, zfs_secpolicy_read, DATASET_NAME, B_FALSE,
427854d692b7SGeorge Wilson 	    B_FALSE },
427954d692b7SGeorge Wilson 	{ zfs_ioc_dataset_list_next, zfs_secpolicy_read, DATASET_NAME, B_FALSE,
4280cf2859fcSSanjeev Bagewadi 	    B_TRUE },
428154d692b7SGeorge Wilson 	{ zfs_ioc_snapshot_list_next, zfs_secpolicy_read, DATASET_NAME, B_FALSE,
4282cf2859fcSSanjeev Bagewadi 	    B_TRUE },
428354d692b7SGeorge Wilson 	{ zfs_ioc_set_prop, zfs_secpolicy_none, DATASET_NAME, B_TRUE, B_TRUE },
428454d692b7SGeorge Wilson 	{ zfs_ioc_create, zfs_secpolicy_create, DATASET_NAME, B_TRUE, B_TRUE },
428554d692b7SGeorge Wilson 	{ zfs_ioc_destroy, zfs_secpolicy_destroy, DATASET_NAME, B_TRUE,
428654d692b7SGeorge Wilson 	    B_TRUE},
428754d692b7SGeorge Wilson 	{ zfs_ioc_rollback, zfs_secpolicy_rollback, DATASET_NAME, B_TRUE,
428854d692b7SGeorge Wilson 	    B_TRUE },
428954d692b7SGeorge Wilson 	{ zfs_ioc_rename, zfs_secpolicy_rename,	DATASET_NAME, B_TRUE, B_TRUE },
429054d692b7SGeorge Wilson 	{ zfs_ioc_recv, zfs_secpolicy_receive, DATASET_NAME, B_TRUE, B_TRUE },
429154d692b7SGeorge Wilson 	{ zfs_ioc_send, zfs_secpolicy_send, DATASET_NAME, B_TRUE, B_FALSE },
429254d692b7SGeorge Wilson 	{ zfs_ioc_inject_fault,	zfs_secpolicy_inject, NO_NAME, B_FALSE,
429354d692b7SGeorge Wilson 	    B_FALSE },
429454d692b7SGeorge Wilson 	{ zfs_ioc_clear_fault, zfs_secpolicy_inject, NO_NAME, B_FALSE,
429554d692b7SGeorge Wilson 	    B_FALSE },
429654d692b7SGeorge Wilson 	{ zfs_ioc_inject_list_next, zfs_secpolicy_inject, NO_NAME, B_FALSE,
429754d692b7SGeorge Wilson 	    B_FALSE },
429854d692b7SGeorge Wilson 	{ zfs_ioc_error_log, zfs_secpolicy_inject, POOL_NAME, B_FALSE,
429954d692b7SGeorge Wilson 	    B_FALSE },
430054d692b7SGeorge Wilson 	{ zfs_ioc_clear, zfs_secpolicy_config, POOL_NAME, B_TRUE, B_FALSE },
430154d692b7SGeorge Wilson 	{ zfs_ioc_promote, zfs_secpolicy_promote, DATASET_NAME, B_TRUE,
430254d692b7SGeorge Wilson 	    B_TRUE },
4303cbf6f6aaSWilliam Gorrell 	{ zfs_ioc_destroy_snaps, zfs_secpolicy_destroy_snaps, DATASET_NAME,
4304cbf6f6aaSWilliam Gorrell 	    B_TRUE, B_TRUE },
430554d692b7SGeorge Wilson 	{ zfs_ioc_snapshot, zfs_secpolicy_snapshot, DATASET_NAME, B_TRUE,
430654d692b7SGeorge Wilson 	    B_TRUE },
430754d692b7SGeorge Wilson 	{ zfs_ioc_dsobj_to_dsname, zfs_secpolicy_config, POOL_NAME, B_FALSE,
430854d692b7SGeorge Wilson 	    B_FALSE },
43096e8a0f56SGeorge Wilson 	{ zfs_ioc_obj_to_path, zfs_secpolicy_config, DATASET_NAME, B_FALSE,
43106e8a0f56SGeorge Wilson 	    B_TRUE },
431154d692b7SGeorge Wilson 	{ zfs_ioc_pool_set_props, zfs_secpolicy_config,	POOL_NAME, B_TRUE,
431254d692b7SGeorge Wilson 	    B_TRUE },
431354d692b7SGeorge Wilson 	{ zfs_ioc_pool_get_props, zfs_secpolicy_read, POOL_NAME, B_FALSE,
431454d692b7SGeorge Wilson 	    B_FALSE },
431554d692b7SGeorge Wilson 	{ zfs_ioc_set_fsacl, zfs_secpolicy_fsacl, DATASET_NAME, B_TRUE,
431654d692b7SGeorge Wilson 	    B_TRUE },
431754d692b7SGeorge Wilson 	{ zfs_ioc_get_fsacl, zfs_secpolicy_read, DATASET_NAME, B_FALSE,
431854d692b7SGeorge Wilson 	    B_FALSE },
431954d692b7SGeorge Wilson 	{ zfs_ioc_share, zfs_secpolicy_share, DATASET_NAME, B_FALSE, B_FALSE },
432054d692b7SGeorge Wilson 	{ zfs_ioc_inherit_prop, zfs_secpolicy_inherit, DATASET_NAME, B_TRUE,
432154d692b7SGeorge Wilson 	    B_TRUE },
432254d692b7SGeorge Wilson 	{ zfs_ioc_smb_acl, zfs_secpolicy_smb_acl, DATASET_NAME, B_FALSE,
432314843421SMatthew Ahrens 	    B_FALSE },
432414843421SMatthew Ahrens 	{ zfs_ioc_userspace_one, zfs_secpolicy_userspace_one,
432514843421SMatthew Ahrens 	    DATASET_NAME, B_FALSE, B_FALSE },
432614843421SMatthew Ahrens 	{ zfs_ioc_userspace_many, zfs_secpolicy_userspace_many,
432714843421SMatthew Ahrens 	    DATASET_NAME, B_FALSE, B_FALSE },
432814843421SMatthew Ahrens 	{ zfs_ioc_userspace_upgrade, zfs_secpolicy_userspace_upgrade,
432914843421SMatthew Ahrens 	    DATASET_NAME, B_FALSE, B_TRUE },
4330842727c2SChris Kirby 	{ zfs_ioc_hold, zfs_secpolicy_hold, DATASET_NAME, B_TRUE, B_TRUE },
4331842727c2SChris Kirby 	{ zfs_ioc_release, zfs_secpolicy_release, DATASET_NAME, B_TRUE,
4332842727c2SChris Kirby 	    B_TRUE },
4333842727c2SChris Kirby 	{ zfs_ioc_get_holds, zfs_secpolicy_read, DATASET_NAME, B_FALSE,
433492241e0bSTom Erickson 	    B_TRUE },
433592241e0bSTom Erickson 	{ zfs_ioc_objset_recvd_props, zfs_secpolicy_read, DATASET_NAME, B_FALSE,
43361195e687SMark J Musante 	    B_FALSE },
43371195e687SMark J Musante 	{ zfs_ioc_vdev_split, zfs_secpolicy_config, POOL_NAME, B_TRUE,
43381195e687SMark J Musante 	    B_TRUE }
4339fa9e4066Sahrens };
4340fa9e4066Sahrens 
434154d692b7SGeorge Wilson int
434254d692b7SGeorge Wilson pool_status_check(const char *name, zfs_ioc_namecheck_t type)
434354d692b7SGeorge Wilson {
434454d692b7SGeorge Wilson 	spa_t *spa;
434554d692b7SGeorge Wilson 	int error;
434654d692b7SGeorge Wilson 
434754d692b7SGeorge Wilson 	ASSERT(type == POOL_NAME || type == DATASET_NAME);
434854d692b7SGeorge Wilson 
434914843421SMatthew Ahrens 	error = spa_open(name, &spa, FTAG);
435054d692b7SGeorge Wilson 	if (error == 0) {
435154d692b7SGeorge Wilson 		if (spa_suspended(spa))
435254d692b7SGeorge Wilson 			error = EAGAIN;
435354d692b7SGeorge Wilson 		spa_close(spa, FTAG);
435454d692b7SGeorge Wilson 	}
435554d692b7SGeorge Wilson 	return (error);
435654d692b7SGeorge Wilson }
435754d692b7SGeorge Wilson 
4358fa9e4066Sahrens static int
4359fa9e4066Sahrens zfsdev_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *cr, int *rvalp)
4360fa9e4066Sahrens {
4361fa9e4066Sahrens 	zfs_cmd_t *zc;
4362fa9e4066Sahrens 	uint_t vec;
43631d452cf5Sahrens 	int error, rc;
4364fa9e4066Sahrens 
4365fa9e4066Sahrens 	if (getminor(dev) != 0)
4366fa9e4066Sahrens 		return (zvol_ioctl(dev, cmd, arg, flag, cr, rvalp));
4367fa9e4066Sahrens 
4368fa9e4066Sahrens 	vec = cmd - ZFS_IOC;
436991ebeef5Sahrens 	ASSERT3U(getmajor(dev), ==, ddi_driver_major(zfs_dip));
4370fa9e4066Sahrens 
4371fa9e4066Sahrens 	if (vec >= sizeof (zfs_ioc_vec) / sizeof (zfs_ioc_vec[0]))
4372fa9e4066Sahrens 		return (EINVAL);
4373fa9e4066Sahrens 
4374fa9e4066Sahrens 	zc = kmem_zalloc(sizeof (zfs_cmd_t), KM_SLEEP);
4375fa9e4066Sahrens 
4376478ed9adSEric Taylor 	error = ddi_copyin((void *)arg, zc, sizeof (zfs_cmd_t), flag);
43776e27f868SSam Falkner 	if (error != 0)
43786e27f868SSam Falkner 		error = EFAULT;
4379fa9e4066Sahrens 
4380681d9761SEric Taylor 	if ((error == 0) && !(flag & FKIOCTL))
4381ecd6cf80Smarks 		error = zfs_ioc_vec[vec].zvec_secpolicy(zc, cr);
4382fa9e4066Sahrens 
4383fa9e4066Sahrens 	/*
4384fa9e4066Sahrens 	 * Ensure that all pool/dataset names are valid before we pass down to
4385fa9e4066Sahrens 	 * the lower layers.
4386fa9e4066Sahrens 	 */
4387fa9e4066Sahrens 	if (error == 0) {
4388fa9e4066Sahrens 		zc->zc_name[sizeof (zc->zc_name) - 1] = '\0';
4389478ed9adSEric Taylor 		zc->zc_iflags = flag & FKIOCTL;
4390fa9e4066Sahrens 		switch (zfs_ioc_vec[vec].zvec_namecheck) {
4391e7437265Sahrens 		case POOL_NAME:
4392fa9e4066Sahrens 			if (pool_namecheck(zc->zc_name, NULL, NULL) != 0)
4393fa9e4066Sahrens 				error = EINVAL;
439454d692b7SGeorge Wilson 			if (zfs_ioc_vec[vec].zvec_pool_check)
439554d692b7SGeorge Wilson 				error = pool_status_check(zc->zc_name,
439654d692b7SGeorge Wilson 				    zfs_ioc_vec[vec].zvec_namecheck);
4397fa9e4066Sahrens 			break;
4398fa9e4066Sahrens 
4399e7437265Sahrens 		case DATASET_NAME:
4400fa9e4066Sahrens 			if (dataset_namecheck(zc->zc_name, NULL, NULL) != 0)
4401fa9e4066Sahrens 				error = EINVAL;
440254d692b7SGeorge Wilson 			if (zfs_ioc_vec[vec].zvec_pool_check)
440354d692b7SGeorge Wilson 				error = pool_status_check(zc->zc_name,
440454d692b7SGeorge Wilson 				    zfs_ioc_vec[vec].zvec_namecheck);
4405fa9e4066Sahrens 			break;
44065ad82045Snd 
4407e7437265Sahrens 		case NO_NAME:
44085ad82045Snd 			break;
4409fa9e4066Sahrens 		}
4410fa9e4066Sahrens 	}
4411fa9e4066Sahrens 
4412fa9e4066Sahrens 	if (error == 0)
4413fa9e4066Sahrens 		error = zfs_ioc_vec[vec].zvec_func(zc);
4414fa9e4066Sahrens 
4415478ed9adSEric Taylor 	rc = ddi_copyout(zc, (void *)arg, sizeof (zfs_cmd_t), flag);
4416ecd6cf80Smarks 	if (error == 0) {
44176e27f868SSam Falkner 		if (rc != 0)
44186e27f868SSam Falkner 			error = EFAULT;
441914843421SMatthew Ahrens 		if (zfs_ioc_vec[vec].zvec_his_log)
4420ecd6cf80Smarks 			zfs_log_history(zc);
4421ecd6cf80Smarks 	}
4422fa9e4066Sahrens 
4423fa9e4066Sahrens 	kmem_free(zc, sizeof (zfs_cmd_t));
4424fa9e4066Sahrens 	return (error);
4425fa9e4066Sahrens }
4426fa9e4066Sahrens 
4427fa9e4066Sahrens static int
4428fa9e4066Sahrens zfs_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
4429fa9e4066Sahrens {
4430fa9e4066Sahrens 	if (cmd != DDI_ATTACH)
4431fa9e4066Sahrens 		return (DDI_FAILURE);
4432fa9e4066Sahrens 
4433fa9e4066Sahrens 	if (ddi_create_minor_node(dip, "zfs", S_IFCHR, 0,
4434fa9e4066Sahrens 	    DDI_PSEUDO, 0) == DDI_FAILURE)
4435fa9e4066Sahrens 		return (DDI_FAILURE);
4436fa9e4066Sahrens 
4437fa9e4066Sahrens 	zfs_dip = dip;
4438fa9e4066Sahrens 
4439fa9e4066Sahrens 	ddi_report_dev(dip);
4440fa9e4066Sahrens 
4441fa9e4066Sahrens 	return (DDI_SUCCESS);
4442fa9e4066Sahrens }
4443fa9e4066Sahrens 
4444fa9e4066Sahrens static int
4445fa9e4066Sahrens zfs_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
4446fa9e4066Sahrens {
4447fa9e4066Sahrens 	if (spa_busy() || zfs_busy() || zvol_busy())
4448fa9e4066Sahrens 		return (DDI_FAILURE);
4449fa9e4066Sahrens 
4450fa9e4066Sahrens 	if (cmd != DDI_DETACH)
4451fa9e4066Sahrens 		return (DDI_FAILURE);
4452fa9e4066Sahrens 
4453fa9e4066Sahrens 	zfs_dip = NULL;
4454fa9e4066Sahrens 
4455fa9e4066Sahrens 	ddi_prop_remove_all(dip);
4456fa9e4066Sahrens 	ddi_remove_minor_node(dip, NULL);
4457fa9e4066Sahrens 
4458fa9e4066Sahrens 	return (DDI_SUCCESS);
4459fa9e4066Sahrens }
4460fa9e4066Sahrens 
4461fa9e4066Sahrens /*ARGSUSED*/
4462fa9e4066Sahrens static int
4463fa9e4066Sahrens zfs_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
4464fa9e4066Sahrens {
4465fa9e4066Sahrens 	switch (infocmd) {
4466fa9e4066Sahrens 	case DDI_INFO_DEVT2DEVINFO:
4467fa9e4066Sahrens 		*result = zfs_dip;
4468fa9e4066Sahrens 		return (DDI_SUCCESS);
4469fa9e4066Sahrens 
4470fa9e4066Sahrens 	case DDI_INFO_DEVT2INSTANCE:
4471a0965f35Sbonwick 		*result = (void *)0;
4472fa9e4066Sahrens 		return (DDI_SUCCESS);
4473fa9e4066Sahrens 	}
4474fa9e4066Sahrens 
4475fa9e4066Sahrens 	return (DDI_FAILURE);
4476fa9e4066Sahrens }
4477fa9e4066Sahrens 
4478fa9e4066Sahrens /*
4479fa9e4066Sahrens  * OK, so this is a little weird.
4480fa9e4066Sahrens  *
4481fa9e4066Sahrens  * /dev/zfs is the control node, i.e. minor 0.
4482fa9e4066Sahrens  * /dev/zvol/[r]dsk/pool/dataset are the zvols, minor > 0.
4483fa9e4066Sahrens  *
4484fa9e4066Sahrens  * /dev/zfs has basically nothing to do except serve up ioctls,
4485fa9e4066Sahrens  * so most of the standard driver entry points are in zvol.c.
4486fa9e4066Sahrens  */
4487fa9e4066Sahrens static struct cb_ops zfs_cb_ops = {
4488fa9e4066Sahrens 	zvol_open,	/* open */
4489fa9e4066Sahrens 	zvol_close,	/* close */
4490fa9e4066Sahrens 	zvol_strategy,	/* strategy */
4491fa9e4066Sahrens 	nodev,		/* print */
4492e7cbe64fSgw 	zvol_dump,	/* dump */
4493fa9e4066Sahrens 	zvol_read,	/* read */
4494fa9e4066Sahrens 	zvol_write,	/* write */
4495fa9e4066Sahrens 	zfsdev_ioctl,	/* ioctl */
4496fa9e4066Sahrens 	nodev,		/* devmap */
4497fa9e4066Sahrens 	nodev,		/* mmap */
4498fa9e4066Sahrens 	nodev,		/* segmap */
4499fa9e4066Sahrens 	nochpoll,	/* poll */
4500fa9e4066Sahrens 	ddi_prop_op,	/* prop_op */
4501fa9e4066Sahrens 	NULL,		/* streamtab */
4502fa9e4066Sahrens 	D_NEW | D_MP | D_64BIT,		/* Driver compatibility flag */
4503fa9e4066Sahrens 	CB_REV,		/* version */
4504feb08c6bSbillm 	nodev,		/* async read */
4505feb08c6bSbillm 	nodev,		/* async write */
4506fa9e4066Sahrens };
4507fa9e4066Sahrens 
4508fa9e4066Sahrens static struct dev_ops zfs_dev_ops = {
4509fa9e4066Sahrens 	DEVO_REV,	/* version */
4510fa9e4066Sahrens 	0,		/* refcnt */
4511fa9e4066Sahrens 	zfs_info,	/* info */
4512fa9e4066Sahrens 	nulldev,	/* identify */
4513fa9e4066Sahrens 	nulldev,	/* probe */
4514fa9e4066Sahrens 	zfs_attach,	/* attach */
4515fa9e4066Sahrens 	zfs_detach,	/* detach */
4516fa9e4066Sahrens 	nodev,		/* reset */
4517fa9e4066Sahrens 	&zfs_cb_ops,	/* driver operations */
451819397407SSherry Moore 	NULL,		/* no bus operations */
451919397407SSherry Moore 	NULL,		/* power */
452019397407SSherry Moore 	ddi_quiesce_not_needed,	/* quiesce */
4521fa9e4066Sahrens };
4522fa9e4066Sahrens 
4523fa9e4066Sahrens static struct modldrv zfs_modldrv = {
452419397407SSherry Moore 	&mod_driverops,
452519397407SSherry Moore 	"ZFS storage pool",
452619397407SSherry Moore 	&zfs_dev_ops
4527fa9e4066Sahrens };
4528fa9e4066Sahrens 
4529fa9e4066Sahrens static struct modlinkage modlinkage = {
4530fa9e4066Sahrens 	MODREV_1,
4531fa9e4066Sahrens 	(void *)&zfs_modlfs,
4532fa9e4066Sahrens 	(void *)&zfs_modldrv,
4533fa9e4066Sahrens 	NULL
4534fa9e4066Sahrens };
4535fa9e4066Sahrens 
4536ec533521Sfr 
4537ec533521Sfr uint_t zfs_fsyncer_key;
4538f18faf3fSek extern uint_t rrw_tsd_key;
4539ec533521Sfr 
4540fa9e4066Sahrens int
4541fa9e4066Sahrens _init(void)
4542fa9e4066Sahrens {
4543fa9e4066Sahrens 	int error;
4544fa9e4066Sahrens 
4545a0965f35Sbonwick 	spa_init(FREAD | FWRITE);
4546a0965f35Sbonwick 	zfs_init();
4547a0965f35Sbonwick 	zvol_init();
4548a0965f35Sbonwick 
4549a0965f35Sbonwick 	if ((error = mod_install(&modlinkage)) != 0) {
4550a0965f35Sbonwick 		zvol_fini();
4551a0965f35Sbonwick 		zfs_fini();
4552a0965f35Sbonwick 		spa_fini();
4553fa9e4066Sahrens 		return (error);
4554a0965f35Sbonwick 	}
4555fa9e4066Sahrens 
4556ec533521Sfr 	tsd_create(&zfs_fsyncer_key, NULL);
4557f18faf3fSek 	tsd_create(&rrw_tsd_key, NULL);
4558ec533521Sfr 
4559fa9e4066Sahrens 	error = ldi_ident_from_mod(&modlinkage, &zfs_li);
4560fa9e4066Sahrens 	ASSERT(error == 0);
4561ecd6cf80Smarks 	mutex_init(&zfs_share_lock, NULL, MUTEX_DEFAULT, NULL);
4562fa9e4066Sahrens 
4563fa9e4066Sahrens 	return (0);
4564fa9e4066Sahrens }
4565fa9e4066Sahrens 
4566fa9e4066Sahrens int
4567fa9e4066Sahrens _fini(void)
4568fa9e4066Sahrens {
4569fa9e4066Sahrens 	int error;
4570fa9e4066Sahrens 
4571ea8dc4b6Seschrock 	if (spa_busy() || zfs_busy() || zvol_busy() || zio_injection_enabled)
4572fa9e4066Sahrens 		return (EBUSY);
4573fa9e4066Sahrens 
4574fa9e4066Sahrens 	if ((error = mod_remove(&modlinkage)) != 0)
4575fa9e4066Sahrens 		return (error);
4576fa9e4066Sahrens 
4577fa9e4066Sahrens 	zvol_fini();
4578fa9e4066Sahrens 	zfs_fini();
4579fa9e4066Sahrens 	spa_fini();
4580da6c28aaSamw 	if (zfs_nfsshare_inited)
4581ecd6cf80Smarks 		(void) ddi_modclose(nfs_mod);
4582da6c28aaSamw 	if (zfs_smbshare_inited)
4583da6c28aaSamw 		(void) ddi_modclose(smbsrv_mod);
4584da6c28aaSamw 	if (zfs_nfsshare_inited || zfs_smbshare_inited)
4585ecd6cf80Smarks 		(void) ddi_modclose(sharefs_mod);
4586fa9e4066Sahrens 
4587ec533521Sfr 	tsd_destroy(&zfs_fsyncer_key);
4588fa9e4066Sahrens 	ldi_ident_release(zfs_li);
4589fa9e4066Sahrens 	zfs_li = NULL;
4590ecd6cf80Smarks 	mutex_destroy(&zfs_share_lock);
4591fa9e4066Sahrens 
4592fa9e4066Sahrens 	return (error);
4593fa9e4066Sahrens }
4594fa9e4066Sahrens 
4595fa9e4066Sahrens int
4596fa9e4066Sahrens _info(struct modinfo *modinfop)
4597fa9e4066Sahrens {
4598fa9e4066Sahrens 	return (mod_info(&modlinkage, modinfop));
4599fa9e4066Sahrens }
4600