xref: /illumos-gate/usr/src/uts/common/fs/zfs/zfs_ioctl.c (revision 3f9d6ad73e45c6823b409f93b0c8d4f62861d2d5)
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 /*
22*3f9d6ad7SLin Ling  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
23fa9e4066Sahrens  */
24fa9e4066Sahrens 
25fa9e4066Sahrens #include <sys/types.h>
26fa9e4066Sahrens #include <sys/param.h>
27fa9e4066Sahrens #include <sys/errno.h>
28fa9e4066Sahrens #include <sys/uio.h>
29fa9e4066Sahrens #include <sys/buf.h>
30fa9e4066Sahrens #include <sys/modctl.h>
31fa9e4066Sahrens #include <sys/open.h>
32fa9e4066Sahrens #include <sys/file.h>
33fa9e4066Sahrens #include <sys/kmem.h>
34fa9e4066Sahrens #include <sys/conf.h>
35fa9e4066Sahrens #include <sys/cmn_err.h>
36fa9e4066Sahrens #include <sys/stat.h>
37fa9e4066Sahrens #include <sys/zfs_ioctl.h>
384201a95eSRic Aleshire #include <sys/zfs_vfsops.h>
39da6c28aaSamw #include <sys/zfs_znode.h>
40fa9e4066Sahrens #include <sys/zap.h>
41fa9e4066Sahrens #include <sys/spa.h>
42b1b8ab34Slling #include <sys/spa_impl.h>
43fa9e4066Sahrens #include <sys/vdev.h>
444201a95eSRic Aleshire #include <sys/priv_impl.h>
45fa9e4066Sahrens #include <sys/dmu.h>
46fa9e4066Sahrens #include <sys/dsl_dir.h>
47fa9e4066Sahrens #include <sys/dsl_dataset.h>
48fa9e4066Sahrens #include <sys/dsl_prop.h>
49ecd6cf80Smarks #include <sys/dsl_deleg.h>
50ecd6cf80Smarks #include <sys/dmu_objset.h>
51fa9e4066Sahrens #include <sys/ddi.h>
52fa9e4066Sahrens #include <sys/sunddi.h>
53fa9e4066Sahrens #include <sys/sunldi.h>
54fa9e4066Sahrens #include <sys/policy.h>
55fa9e4066Sahrens #include <sys/zone.h>
56fa9e4066Sahrens #include <sys/nvpair.h>
57fa9e4066Sahrens #include <sys/pathname.h>
58fa9e4066Sahrens #include <sys/mount.h>
59fa9e4066Sahrens #include <sys/sdt.h>
60fa9e4066Sahrens #include <sys/fs/zfs.h>
61fa9e4066Sahrens #include <sys/zfs_ctldir.h>
62da6c28aaSamw #include <sys/zfs_dir.h>
63a2eea2e1Sahrens #include <sys/zvol.h>
64*3f9d6ad7SLin Ling #include <sys/dsl_scan.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"
710a586ceaSMark 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;
120*3f9d6ad7SLin Ling 	char buf[512];
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 
1240*3f9d6ad7SLin Ling /*
1241*3f9d6ad7SLin Ling  * inputs:
1242*3f9d6ad7SLin Ling  * zc_name              name of the pool
1243*3f9d6ad7SLin Ling  * zc_cookie            scan func (pool_scan_func_t)
1244*3f9d6ad7SLin Ling  */
1245fa9e4066Sahrens static int
1246*3f9d6ad7SLin Ling zfs_ioc_pool_scan(zfs_cmd_t *zc)
1247fa9e4066Sahrens {
1248fa9e4066Sahrens 	spa_t *spa;
1249fa9e4066Sahrens 	int error;
1250fa9e4066Sahrens 
125106eeb2adSek 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
125206eeb2adSek 		return (error);
125306eeb2adSek 
1254*3f9d6ad7SLin Ling 	if (zc->zc_cookie == POOL_SCAN_NONE)
1255*3f9d6ad7SLin Ling 		error = spa_scan_stop(spa);
1256*3f9d6ad7SLin Ling 	else
1257*3f9d6ad7SLin Ling 		error = spa_scan(spa, zc->zc_cookie);
125806eeb2adSek 
125906eeb2adSek 	spa_close(spa, FTAG);
126006eeb2adSek 
1261fa9e4066Sahrens 	return (error);
1262fa9e4066Sahrens }
1263fa9e4066Sahrens 
1264fa9e4066Sahrens static int
1265fa9e4066Sahrens zfs_ioc_pool_freeze(zfs_cmd_t *zc)
1266fa9e4066Sahrens {
1267fa9e4066Sahrens 	spa_t *spa;
1268fa9e4066Sahrens 	int error;
1269fa9e4066Sahrens 
1270fa9e4066Sahrens 	error = spa_open(zc->zc_name, &spa, FTAG);
1271fa9e4066Sahrens 	if (error == 0) {
1272fa9e4066Sahrens 		spa_freeze(spa);
1273fa9e4066Sahrens 		spa_close(spa, FTAG);
1274fa9e4066Sahrens 	}
1275fa9e4066Sahrens 	return (error);
1276fa9e4066Sahrens }
1277fa9e4066Sahrens 
1278eaca9bbdSeschrock static int
1279eaca9bbdSeschrock zfs_ioc_pool_upgrade(zfs_cmd_t *zc)
1280eaca9bbdSeschrock {
1281eaca9bbdSeschrock 	spa_t *spa;
1282eaca9bbdSeschrock 	int error;
1283eaca9bbdSeschrock 
128406eeb2adSek 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
128506eeb2adSek 		return (error);
128606eeb2adSek 
1287558d2d50Slling 	if (zc->zc_cookie < spa_version(spa) || zc->zc_cookie > SPA_VERSION) {
1288558d2d50Slling 		spa_close(spa, FTAG);
1289558d2d50Slling 		return (EINVAL);
1290558d2d50Slling 	}
1291558d2d50Slling 
1292990b4856Slling 	spa_upgrade(spa, zc->zc_cookie);
129306eeb2adSek 	spa_close(spa, FTAG);
129406eeb2adSek 
129506eeb2adSek 	return (error);
129606eeb2adSek }
129706eeb2adSek 
129806eeb2adSek static int
129906eeb2adSek zfs_ioc_pool_get_history(zfs_cmd_t *zc)
130006eeb2adSek {
130106eeb2adSek 	spa_t *spa;
130206eeb2adSek 	char *hist_buf;
130306eeb2adSek 	uint64_t size;
130406eeb2adSek 	int error;
130506eeb2adSek 
130606eeb2adSek 	if ((size = zc->zc_history_len) == 0)
130706eeb2adSek 		return (EINVAL);
130806eeb2adSek 
130906eeb2adSek 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
131006eeb2adSek 		return (error);
131106eeb2adSek 
1312e7437265Sahrens 	if (spa_version(spa) < SPA_VERSION_ZPOOL_HISTORY) {
1313d7306b64Sek 		spa_close(spa, FTAG);
1314d7306b64Sek 		return (ENOTSUP);
1315d7306b64Sek 	}
1316d7306b64Sek 
131706eeb2adSek 	hist_buf = kmem_alloc(size, KM_SLEEP);
131806eeb2adSek 	if ((error = spa_history_get(spa, &zc->zc_history_offset,
131906eeb2adSek 	    &zc->zc_history_len, hist_buf)) == 0) {
1320478ed9adSEric Taylor 		error = ddi_copyout(hist_buf,
1321478ed9adSEric Taylor 		    (void *)(uintptr_t)zc->zc_history,
1322478ed9adSEric Taylor 		    zc->zc_history_len, zc->zc_iflags);
132306eeb2adSek 	}
132406eeb2adSek 
132506eeb2adSek 	spa_close(spa, FTAG);
132606eeb2adSek 	kmem_free(hist_buf, size);
132706eeb2adSek 	return (error);
132806eeb2adSek }
132906eeb2adSek 
133055434c77Sek static int
133155434c77Sek zfs_ioc_dsobj_to_dsname(zfs_cmd_t *zc)
133255434c77Sek {
133355434c77Sek 	int error;
133455434c77Sek 
1335b1b8ab34Slling 	if (error = dsl_dsobj_to_dsname(zc->zc_name, zc->zc_obj, zc->zc_value))
133655434c77Sek 		return (error);
133755434c77Sek 
133855434c77Sek 	return (0);
133955434c77Sek }
134055434c77Sek 
1341503ad85cSMatthew Ahrens /*
1342503ad85cSMatthew Ahrens  * inputs:
1343503ad85cSMatthew Ahrens  * zc_name		name of filesystem
1344503ad85cSMatthew Ahrens  * zc_obj		object to find
1345503ad85cSMatthew Ahrens  *
1346503ad85cSMatthew Ahrens  * outputs:
1347503ad85cSMatthew Ahrens  * zc_value		name of object
1348503ad85cSMatthew Ahrens  */
134955434c77Sek static int
135055434c77Sek zfs_ioc_obj_to_path(zfs_cmd_t *zc)
135155434c77Sek {
1352503ad85cSMatthew Ahrens 	objset_t *os;
135355434c77Sek 	int error;
135455434c77Sek 
1355503ad85cSMatthew Ahrens 	/* XXX reading from objset not owned */
1356503ad85cSMatthew Ahrens 	if ((error = dmu_objset_hold(zc->zc_name, FTAG, &os)) != 0)
135755434c77Sek 		return (error);
1358503ad85cSMatthew Ahrens 	if (dmu_objset_type(os) != DMU_OST_ZFS) {
1359503ad85cSMatthew Ahrens 		dmu_objset_rele(os, FTAG);
1360503ad85cSMatthew Ahrens 		return (EINVAL);
1361503ad85cSMatthew Ahrens 	}
1362503ad85cSMatthew Ahrens 	error = zfs_obj_to_path(os, zc->zc_obj, zc->zc_value,
136355434c77Sek 	    sizeof (zc->zc_value));
1364503ad85cSMatthew Ahrens 	dmu_objset_rele(os, FTAG);
136555434c77Sek 
136655434c77Sek 	return (error);
136755434c77Sek }
136855434c77Sek 
1369fa9e4066Sahrens static int
1370fa9e4066Sahrens zfs_ioc_vdev_add(zfs_cmd_t *zc)
1371fa9e4066Sahrens {
1372fa9e4066Sahrens 	spa_t *spa;
1373fa9e4066Sahrens 	int error;
1374e7cbe64fSgw 	nvlist_t *config, **l2cache, **spares;
1375e7cbe64fSgw 	uint_t nl2cache = 0, nspares = 0;
1376fa9e4066Sahrens 
1377fa9e4066Sahrens 	error = spa_open(zc->zc_name, &spa, FTAG);
1378fa9e4066Sahrens 	if (error != 0)
1379fa9e4066Sahrens 		return (error);
1380fa9e4066Sahrens 
1381fa94a07fSbrendan 	error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size,
1382478ed9adSEric Taylor 	    zc->zc_iflags, &config);
1383fa94a07fSbrendan 	(void) nvlist_lookup_nvlist_array(config, ZPOOL_CONFIG_L2CACHE,
1384fa94a07fSbrendan 	    &l2cache, &nl2cache);
1385fa94a07fSbrendan 
1386e7cbe64fSgw 	(void) nvlist_lookup_nvlist_array(config, ZPOOL_CONFIG_SPARES,
1387e7cbe64fSgw 	    &spares, &nspares);
1388e7cbe64fSgw 
1389b1b8ab34Slling 	/*
1390b1b8ab34Slling 	 * A root pool with concatenated devices is not supported.
1391e7cbe64fSgw 	 * Thus, can not add a device to a root pool.
1392e7cbe64fSgw 	 *
1393e7cbe64fSgw 	 * Intent log device can not be added to a rootpool because
1394e7cbe64fSgw 	 * during mountroot, zil is replayed, a seperated log device
1395e7cbe64fSgw 	 * can not be accessed during the mountroot time.
1396e7cbe64fSgw 	 *
1397e7cbe64fSgw 	 * l2cache and spare devices are ok to be added to a rootpool.
1398b1b8ab34Slling 	 */
1399b24ab676SJeff Bonwick 	if (spa_bootfs(spa) != 0 && nl2cache == 0 && nspares == 0) {
14001195e687SMark J Musante 		nvlist_free(config);
1401b1b8ab34Slling 		spa_close(spa, FTAG);
1402b1b8ab34Slling 		return (EDOM);
1403b1b8ab34Slling 	}
1404b1b8ab34Slling 
1405fa94a07fSbrendan 	if (error == 0) {
1406fa9e4066Sahrens 		error = spa_vdev_add(spa, config);
1407fa9e4066Sahrens 		nvlist_free(config);
1408fa9e4066Sahrens 	}
1409fa9e4066Sahrens 	spa_close(spa, FTAG);
1410fa9e4066Sahrens 	return (error);
1411fa9e4066Sahrens }
1412fa9e4066Sahrens 
1413*3f9d6ad7SLin Ling /*
1414*3f9d6ad7SLin Ling  * inputs:
1415*3f9d6ad7SLin Ling  * zc_name		name of the pool
1416*3f9d6ad7SLin Ling  * zc_nvlist_conf	nvlist of devices to remove
1417*3f9d6ad7SLin Ling  * zc_cookie		to stop the remove?
1418*3f9d6ad7SLin Ling  */
1419fa9e4066Sahrens static int
1420fa9e4066Sahrens zfs_ioc_vdev_remove(zfs_cmd_t *zc)
1421fa9e4066Sahrens {
142299653d4eSeschrock 	spa_t *spa;
142399653d4eSeschrock 	int error;
142499653d4eSeschrock 
142599653d4eSeschrock 	error = spa_open(zc->zc_name, &spa, FTAG);
142699653d4eSeschrock 	if (error != 0)
142799653d4eSeschrock 		return (error);
142899653d4eSeschrock 	error = spa_vdev_remove(spa, zc->zc_guid, B_FALSE);
142999653d4eSeschrock 	spa_close(spa, FTAG);
143099653d4eSeschrock 	return (error);
1431fa9e4066Sahrens }
1432fa9e4066Sahrens 
1433fa9e4066Sahrens static int
14343d7072f8Seschrock zfs_ioc_vdev_set_state(zfs_cmd_t *zc)
1435fa9e4066Sahrens {
1436fa9e4066Sahrens 	spa_t *spa;
1437fa9e4066Sahrens 	int error;
14383d7072f8Seschrock 	vdev_state_t newstate = VDEV_STATE_UNKNOWN;
1439fa9e4066Sahrens 
144006eeb2adSek 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
1441fa9e4066Sahrens 		return (error);
14423d7072f8Seschrock 	switch (zc->zc_cookie) {
14433d7072f8Seschrock 	case VDEV_STATE_ONLINE:
14443d7072f8Seschrock 		error = vdev_online(spa, zc->zc_guid, zc->zc_obj, &newstate);
14453d7072f8Seschrock 		break;
1446fa9e4066Sahrens 
14473d7072f8Seschrock 	case VDEV_STATE_OFFLINE:
14483d7072f8Seschrock 		error = vdev_offline(spa, zc->zc_guid, zc->zc_obj);
14493d7072f8Seschrock 		break;
1450fa9e4066Sahrens 
14513d7072f8Seschrock 	case VDEV_STATE_FAULTED:
1452069f55e2SEric Schrock 		if (zc->zc_obj != VDEV_AUX_ERR_EXCEEDED &&
1453069f55e2SEric Schrock 		    zc->zc_obj != VDEV_AUX_EXTERNAL)
1454069f55e2SEric Schrock 			zc->zc_obj = VDEV_AUX_ERR_EXCEEDED;
1455069f55e2SEric Schrock 
1456069f55e2SEric Schrock 		error = vdev_fault(spa, zc->zc_guid, zc->zc_obj);
14573d7072f8Seschrock 		break;
14583d7072f8Seschrock 
14593d7072f8Seschrock 	case VDEV_STATE_DEGRADED:
1460069f55e2SEric Schrock 		if (zc->zc_obj != VDEV_AUX_ERR_EXCEEDED &&
1461069f55e2SEric Schrock 		    zc->zc_obj != VDEV_AUX_EXTERNAL)
1462069f55e2SEric Schrock 			zc->zc_obj = VDEV_AUX_ERR_EXCEEDED;
1463069f55e2SEric Schrock 
1464069f55e2SEric Schrock 		error = vdev_degrade(spa, zc->zc_guid, zc->zc_obj);
14653d7072f8Seschrock 		break;
14663d7072f8Seschrock 
14673d7072f8Seschrock 	default:
14683d7072f8Seschrock 		error = EINVAL;
14693d7072f8Seschrock 	}
14703d7072f8Seschrock 	zc->zc_cookie = newstate;
1471fa9e4066Sahrens 	spa_close(spa, FTAG);
1472fa9e4066Sahrens 	return (error);
1473fa9e4066Sahrens }
1474fa9e4066Sahrens 
1475fa9e4066Sahrens static int
1476fa9e4066Sahrens zfs_ioc_vdev_attach(zfs_cmd_t *zc)
1477fa9e4066Sahrens {
1478fa9e4066Sahrens 	spa_t *spa;
1479fa9e4066Sahrens 	int replacing = zc->zc_cookie;
1480fa9e4066Sahrens 	nvlist_t *config;
1481fa9e4066Sahrens 	int error;
1482fa9e4066Sahrens 
148306eeb2adSek 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
1484fa9e4066Sahrens 		return (error);
1485fa9e4066Sahrens 
1486990b4856Slling 	if ((error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size,
1487478ed9adSEric Taylor 	    zc->zc_iflags, &config)) == 0) {
1488ea8dc4b6Seschrock 		error = spa_vdev_attach(spa, zc->zc_guid, config, replacing);
1489fa9e4066Sahrens 		nvlist_free(config);
1490fa9e4066Sahrens 	}
1491fa9e4066Sahrens 
1492fa9e4066Sahrens 	spa_close(spa, FTAG);
1493fa9e4066Sahrens 	return (error);
1494fa9e4066Sahrens }
1495fa9e4066Sahrens 
1496fa9e4066Sahrens static int
1497fa9e4066Sahrens zfs_ioc_vdev_detach(zfs_cmd_t *zc)
1498fa9e4066Sahrens {
1499fa9e4066Sahrens 	spa_t *spa;
1500fa9e4066Sahrens 	int error;
1501fa9e4066Sahrens 
150206eeb2adSek 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
1503fa9e4066Sahrens 		return (error);
1504fa9e4066Sahrens 
15058ad4d6ddSJeff Bonwick 	error = spa_vdev_detach(spa, zc->zc_guid, 0, B_FALSE);
1506fa9e4066Sahrens 
1507fa9e4066Sahrens 	spa_close(spa, FTAG);
1508fa9e4066Sahrens 	return (error);
1509fa9e4066Sahrens }
1510fa9e4066Sahrens 
15111195e687SMark J Musante static int
15121195e687SMark J Musante zfs_ioc_vdev_split(zfs_cmd_t *zc)
15131195e687SMark J Musante {
15141195e687SMark J Musante 	spa_t *spa;
15151195e687SMark J Musante 	nvlist_t *config, *props = NULL;
15161195e687SMark J Musante 	int error;
15171195e687SMark J Musante 	boolean_t exp = !!(zc->zc_cookie & ZPOOL_EXPORT_AFTER_SPLIT);
15181195e687SMark J Musante 
15191195e687SMark J Musante 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
15201195e687SMark J Musante 		return (error);
15211195e687SMark J Musante 
15221195e687SMark J Musante 	if (error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size,
15231195e687SMark J Musante 	    zc->zc_iflags, &config)) {
15241195e687SMark J Musante 		spa_close(spa, FTAG);
15251195e687SMark J Musante 		return (error);
15261195e687SMark J Musante 	}
15271195e687SMark J Musante 
15281195e687SMark J Musante 	if (zc->zc_nvlist_src_size != 0 && (error =
15291195e687SMark J Musante 	    get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
15301195e687SMark J Musante 	    zc->zc_iflags, &props))) {
15311195e687SMark J Musante 		spa_close(spa, FTAG);
15321195e687SMark J Musante 		nvlist_free(config);
15331195e687SMark J Musante 		return (error);
15341195e687SMark J Musante 	}
15351195e687SMark J Musante 
15361195e687SMark J Musante 	error = spa_vdev_split_mirror(spa, zc->zc_string, config, props, exp);
15371195e687SMark J Musante 
15381195e687SMark J Musante 	spa_close(spa, FTAG);
15391195e687SMark J Musante 
15401195e687SMark J Musante 	nvlist_free(config);
15411195e687SMark J Musante 	nvlist_free(props);
15421195e687SMark J Musante 
15431195e687SMark J Musante 	return (error);
15441195e687SMark J Musante }
15451195e687SMark J Musante 
1546c67d9675Seschrock static int
1547c67d9675Seschrock zfs_ioc_vdev_setpath(zfs_cmd_t *zc)
1548c67d9675Seschrock {
1549c67d9675Seschrock 	spa_t *spa;
1550e9dbad6fSeschrock 	char *path = zc->zc_value;
1551ea8dc4b6Seschrock 	uint64_t guid = zc->zc_guid;
1552c67d9675Seschrock 	int error;
1553c67d9675Seschrock 
1554c67d9675Seschrock 	error = spa_open(zc->zc_name, &spa, FTAG);
1555c67d9675Seschrock 	if (error != 0)
1556c67d9675Seschrock 		return (error);
1557c67d9675Seschrock 
1558c67d9675Seschrock 	error = spa_vdev_setpath(spa, guid, path);
1559c67d9675Seschrock 	spa_close(spa, FTAG);
1560c67d9675Seschrock 	return (error);
1561c67d9675Seschrock }
1562c67d9675Seschrock 
15636809eb4eSEric Schrock static int
15646809eb4eSEric Schrock zfs_ioc_vdev_setfru(zfs_cmd_t *zc)
15656809eb4eSEric Schrock {
15666809eb4eSEric Schrock 	spa_t *spa;
15676809eb4eSEric Schrock 	char *fru = zc->zc_value;
15686809eb4eSEric Schrock 	uint64_t guid = zc->zc_guid;
15696809eb4eSEric Schrock 	int error;
15706809eb4eSEric Schrock 
15716809eb4eSEric Schrock 	error = spa_open(zc->zc_name, &spa, FTAG);
15726809eb4eSEric Schrock 	if (error != 0)
15736809eb4eSEric Schrock 		return (error);
15746809eb4eSEric Schrock 
15756809eb4eSEric Schrock 	error = spa_vdev_setfru(spa, guid, fru);
15766809eb4eSEric Schrock 	spa_close(spa, FTAG);
15776809eb4eSEric Schrock 	return (error);
15786809eb4eSEric Schrock }
15796809eb4eSEric Schrock 
15803cb34c60Sahrens /*
15813cb34c60Sahrens  * inputs:
15823cb34c60Sahrens  * zc_name		name of filesystem
15833cb34c60Sahrens  * zc_nvlist_dst_size	size of buffer for property nvlist
15843cb34c60Sahrens  *
15853cb34c60Sahrens  * outputs:
15863cb34c60Sahrens  * zc_objset_stats	stats
15873cb34c60Sahrens  * zc_nvlist_dst	property nvlist
15883cb34c60Sahrens  * zc_nvlist_dst_size	size of property nvlist
15893cb34c60Sahrens  */
1590fa9e4066Sahrens static int
1591fa9e4066Sahrens zfs_ioc_objset_stats(zfs_cmd_t *zc)
1592fa9e4066Sahrens {
1593fa9e4066Sahrens 	objset_t *os = NULL;
1594fa9e4066Sahrens 	int error;
15957f7322feSeschrock 	nvlist_t *nv;
1596fa9e4066Sahrens 
1597503ad85cSMatthew Ahrens 	if (error = dmu_objset_hold(zc->zc_name, FTAG, &os))
1598fa9e4066Sahrens 		return (error);
1599fa9e4066Sahrens 
1600a2eea2e1Sahrens 	dmu_objset_fast_stat(os, &zc->zc_objset_stats);
1601fa9e4066Sahrens 
16025ad82045Snd 	if (zc->zc_nvlist_dst != 0 &&
160392241e0bSTom Erickson 	    (error = dsl_prop_get_all(os, &nv)) == 0) {
1604a2eea2e1Sahrens 		dmu_objset_stats(os, nv);
1605432f72fdSahrens 		/*
1606bd00f61bSrm 		 * NB: zvol_get_stats() will read the objset contents,
1607432f72fdSahrens 		 * which we aren't supposed to do with a
1608745cd3c5Smaybee 		 * DS_MODE_USER hold, because it could be
1609432f72fdSahrens 		 * inconsistent.  So this is a bit of a workaround...
1610503ad85cSMatthew Ahrens 		 * XXX reading with out owning
1611432f72fdSahrens 		 */
1612e7437265Sahrens 		if (!zc->zc_objset_stats.dds_inconsistent) {
1613e7437265Sahrens 			if (dmu_objset_type(os) == DMU_OST_ZVOL)
1614e7437265Sahrens 				VERIFY(zvol_get_stats(os, nv) == 0);
1615e7437265Sahrens 		}
1616e9dbad6fSeschrock 		error = put_nvlist(zc, nv);
16177f7322feSeschrock 		nvlist_free(nv);
16187f7322feSeschrock 	}
1619fa9e4066Sahrens 
1620503ad85cSMatthew Ahrens 	dmu_objset_rele(os, FTAG);
1621fa9e4066Sahrens 	return (error);
1622fa9e4066Sahrens }
1623fa9e4066Sahrens 
162492241e0bSTom Erickson /*
162592241e0bSTom Erickson  * inputs:
162692241e0bSTom Erickson  * zc_name		name of filesystem
162792241e0bSTom Erickson  * zc_nvlist_dst_size	size of buffer for property nvlist
162892241e0bSTom Erickson  *
162992241e0bSTom Erickson  * outputs:
163092241e0bSTom Erickson  * zc_nvlist_dst	received property nvlist
163192241e0bSTom Erickson  * zc_nvlist_dst_size	size of received property nvlist
163292241e0bSTom Erickson  *
163392241e0bSTom Erickson  * Gets received properties (distinct from local properties on or after
163492241e0bSTom Erickson  * SPA_VERSION_RECVD_PROPS) for callers who want to differentiate received from
163592241e0bSTom Erickson  * local property values.
163692241e0bSTom Erickson  */
163792241e0bSTom Erickson static int
163892241e0bSTom Erickson zfs_ioc_objset_recvd_props(zfs_cmd_t *zc)
163992241e0bSTom Erickson {
164092241e0bSTom Erickson 	objset_t *os = NULL;
164192241e0bSTom Erickson 	int error;
164292241e0bSTom Erickson 	nvlist_t *nv;
164392241e0bSTom Erickson 
164492241e0bSTom Erickson 	if (error = dmu_objset_hold(zc->zc_name, FTAG, &os))
164592241e0bSTom Erickson 		return (error);
164692241e0bSTom Erickson 
164792241e0bSTom Erickson 	/*
164892241e0bSTom Erickson 	 * Without this check, we would return local property values if the
164992241e0bSTom Erickson 	 * caller has not already received properties on or after
165092241e0bSTom Erickson 	 * SPA_VERSION_RECVD_PROPS.
165192241e0bSTom Erickson 	 */
165292241e0bSTom Erickson 	if (!dsl_prop_get_hasrecvd(os)) {
165392241e0bSTom Erickson 		dmu_objset_rele(os, FTAG);
165492241e0bSTom Erickson 		return (ENOTSUP);
165592241e0bSTom Erickson 	}
165692241e0bSTom Erickson 
165792241e0bSTom Erickson 	if (zc->zc_nvlist_dst != 0 &&
165892241e0bSTom Erickson 	    (error = dsl_prop_get_received(os, &nv)) == 0) {
165992241e0bSTom Erickson 		error = put_nvlist(zc, nv);
166092241e0bSTom Erickson 		nvlist_free(nv);
166192241e0bSTom Erickson 	}
166292241e0bSTom Erickson 
166392241e0bSTom Erickson 	dmu_objset_rele(os, FTAG);
166492241e0bSTom Erickson 	return (error);
166592241e0bSTom Erickson }
166692241e0bSTom Erickson 
1667de8267e0Stimh static int
1668de8267e0Stimh nvl_add_zplprop(objset_t *os, nvlist_t *props, zfs_prop_t prop)
1669de8267e0Stimh {
1670de8267e0Stimh 	uint64_t value;
1671de8267e0Stimh 	int error;
1672de8267e0Stimh 
1673de8267e0Stimh 	/*
1674de8267e0Stimh 	 * zfs_get_zplprop() will either find a value or give us
1675de8267e0Stimh 	 * the default value (if there is one).
1676de8267e0Stimh 	 */
1677de8267e0Stimh 	if ((error = zfs_get_zplprop(os, prop, &value)) != 0)
1678de8267e0Stimh 		return (error);
1679de8267e0Stimh 	VERIFY(nvlist_add_uint64(props, zfs_prop_to_name(prop), value) == 0);
1680de8267e0Stimh 	return (0);
1681de8267e0Stimh }
1682de8267e0Stimh 
16833cb34c60Sahrens /*
16843cb34c60Sahrens  * inputs:
16853cb34c60Sahrens  * zc_name		name of filesystem
1686de8267e0Stimh  * zc_nvlist_dst_size	size of buffer for zpl property nvlist
16873cb34c60Sahrens  *
16883cb34c60Sahrens  * outputs:
1689de8267e0Stimh  * zc_nvlist_dst	zpl property nvlist
1690de8267e0Stimh  * zc_nvlist_dst_size	size of zpl property nvlist
16913cb34c60Sahrens  */
1692bd00f61bSrm static int
1693de8267e0Stimh zfs_ioc_objset_zplprops(zfs_cmd_t *zc)
1694bd00f61bSrm {
1695de8267e0Stimh 	objset_t *os;
1696de8267e0Stimh 	int err;
1697bd00f61bSrm 
1698503ad85cSMatthew Ahrens 	/* XXX reading without owning */
1699503ad85cSMatthew Ahrens 	if (err = dmu_objset_hold(zc->zc_name, FTAG, &os))
1700de8267e0Stimh 		return (err);
1701bd00f61bSrm 
1702bd00f61bSrm 	dmu_objset_fast_stat(os, &zc->zc_objset_stats);
1703bd00f61bSrm 
1704bd00f61bSrm 	/*
1705de8267e0Stimh 	 * NB: nvl_add_zplprop() will read the objset contents,
1706745cd3c5Smaybee 	 * which we aren't supposed to do with a DS_MODE_USER
1707745cd3c5Smaybee 	 * hold, because it could be inconsistent.
1708bd00f61bSrm 	 */
1709de8267e0Stimh 	if (zc->zc_nvlist_dst != NULL &&
1710de8267e0Stimh 	    !zc->zc_objset_stats.dds_inconsistent &&
1711de8267e0Stimh 	    dmu_objset_type(os) == DMU_OST_ZFS) {
1712de8267e0Stimh 		nvlist_t *nv;
1713de8267e0Stimh 
1714de8267e0Stimh 		VERIFY(nvlist_alloc(&nv, NV_UNIQUE_NAME, KM_SLEEP) == 0);
1715de8267e0Stimh 		if ((err = nvl_add_zplprop(os, nv, ZFS_PROP_VERSION)) == 0 &&
1716de8267e0Stimh 		    (err = nvl_add_zplprop(os, nv, ZFS_PROP_NORMALIZE)) == 0 &&
1717de8267e0Stimh 		    (err = nvl_add_zplprop(os, nv, ZFS_PROP_UTF8ONLY)) == 0 &&
1718de8267e0Stimh 		    (err = nvl_add_zplprop(os, nv, ZFS_PROP_CASE)) == 0)
1719de8267e0Stimh 			err = put_nvlist(zc, nv);
1720de8267e0Stimh 		nvlist_free(nv);
1721de8267e0Stimh 	} else {
1722de8267e0Stimh 		err = ENOENT;
1723de8267e0Stimh 	}
1724503ad85cSMatthew Ahrens 	dmu_objset_rele(os, FTAG);
1725de8267e0Stimh 	return (err);
1726bd00f61bSrm }
1727bd00f61bSrm 
172814843421SMatthew Ahrens static boolean_t
172914843421SMatthew Ahrens dataset_name_hidden(const char *name)
173014843421SMatthew Ahrens {
173114843421SMatthew Ahrens 	/*
173214843421SMatthew Ahrens 	 * Skip over datasets that are not visible in this zone,
173314843421SMatthew Ahrens 	 * internal datasets (which have a $ in their name), and
173414843421SMatthew Ahrens 	 * temporary datasets (which have a % in their name).
173514843421SMatthew Ahrens 	 */
173614843421SMatthew Ahrens 	if (strchr(name, '$') != NULL)
173714843421SMatthew Ahrens 		return (B_TRUE);
173814843421SMatthew Ahrens 	if (strchr(name, '%') != NULL)
173914843421SMatthew Ahrens 		return (B_TRUE);
174014843421SMatthew Ahrens 	if (!INGLOBALZONE(curproc) && !zone_dataset_visible(name, NULL))
174114843421SMatthew Ahrens 		return (B_TRUE);
174214843421SMatthew Ahrens 	return (B_FALSE);
174314843421SMatthew Ahrens }
174414843421SMatthew Ahrens 
1745de8267e0Stimh /*
1746de8267e0Stimh  * inputs:
1747de8267e0Stimh  * zc_name		name of filesystem
1748de8267e0Stimh  * zc_cookie		zap cursor
1749de8267e0Stimh  * zc_nvlist_dst_size	size of buffer for property nvlist
1750de8267e0Stimh  *
1751de8267e0Stimh  * outputs:
1752de8267e0Stimh  * zc_name		name of next filesystem
175314843421SMatthew Ahrens  * zc_cookie		zap cursor
1754de8267e0Stimh  * zc_objset_stats	stats
1755de8267e0Stimh  * zc_nvlist_dst	property nvlist
1756de8267e0Stimh  * zc_nvlist_dst_size	size of property nvlist
1757de8267e0Stimh  */
1758fa9e4066Sahrens static int
1759fa9e4066Sahrens zfs_ioc_dataset_list_next(zfs_cmd_t *zc)
1760fa9e4066Sahrens {
176187e5029aSahrens 	objset_t *os;
1762fa9e4066Sahrens 	int error;
1763fa9e4066Sahrens 	char *p;
1764620252bcSChris Kirby 	size_t orig_len = strlen(zc->zc_name);
1765fa9e4066Sahrens 
1766620252bcSChris Kirby top:
1767503ad85cSMatthew Ahrens 	if (error = dmu_objset_hold(zc->zc_name, FTAG, &os)) {
176887e5029aSahrens 		if (error == ENOENT)
176987e5029aSahrens 			error = ESRCH;
177087e5029aSahrens 		return (error);
1771fa9e4066Sahrens 	}
1772fa9e4066Sahrens 
1773fa9e4066Sahrens 	p = strrchr(zc->zc_name, '/');
1774fa9e4066Sahrens 	if (p == NULL || p[1] != '\0')
1775fa9e4066Sahrens 		(void) strlcat(zc->zc_name, "/", sizeof (zc->zc_name));
1776fa9e4066Sahrens 	p = zc->zc_name + strlen(zc->zc_name);
1777fa9e4066Sahrens 
17785c0b6a79SRich Morris 	/*
17795c0b6a79SRich Morris 	 * Pre-fetch the datasets.  dmu_objset_prefetch() always returns 0
17805c0b6a79SRich Morris 	 * but is not declared void because its called by dmu_objset_find().
17815c0b6a79SRich Morris 	 */
17827f73c863SRich Morris 	if (zc->zc_cookie == 0) {
17837f73c863SRich Morris 		uint64_t cookie = 0;
17847f73c863SRich Morris 		int len = sizeof (zc->zc_name) - (p - zc->zc_name);
17857f73c863SRich Morris 
17867f73c863SRich Morris 		while (dmu_dir_list_next(os, len, p, NULL, &cookie) == 0)
17875c0b6a79SRich Morris 			(void) dmu_objset_prefetch(p, NULL);
17887f73c863SRich Morris 	}
17897f73c863SRich Morris 
1790fa9e4066Sahrens 	do {
179187e5029aSahrens 		error = dmu_dir_list_next(os,
179287e5029aSahrens 		    sizeof (zc->zc_name) - (p - zc->zc_name), p,
179387e5029aSahrens 		    NULL, &zc->zc_cookie);
1794fa9e4066Sahrens 		if (error == ENOENT)
1795fa9e4066Sahrens 			error = ESRCH;
1796681d9761SEric Taylor 	} while (error == 0 && dataset_name_hidden(zc->zc_name) &&
1797681d9761SEric Taylor 	    !(zc->zc_iflags & FKIOCTL));
1798503ad85cSMatthew Ahrens 	dmu_objset_rele(os, FTAG);
1799fa9e4066Sahrens 
1800681d9761SEric Taylor 	/*
1801681d9761SEric Taylor 	 * If it's an internal dataset (ie. with a '$' in its name),
1802681d9761SEric Taylor 	 * don't try to get stats for it, otherwise we'll return ENOENT.
1803681d9761SEric Taylor 	 */
1804620252bcSChris Kirby 	if (error == 0 && strchr(zc->zc_name, '$') == NULL) {
180587e5029aSahrens 		error = zfs_ioc_objset_stats(zc); /* fill in the stats */
1806620252bcSChris Kirby 		if (error == ENOENT) {
1807620252bcSChris Kirby 			/* We lost a race with destroy, get the next one. */
1808620252bcSChris Kirby 			zc->zc_name[orig_len] = '\0';
1809620252bcSChris Kirby 			goto top;
1810620252bcSChris Kirby 		}
1811620252bcSChris Kirby 	}
1812fa9e4066Sahrens 	return (error);
1813fa9e4066Sahrens }
1814fa9e4066Sahrens 
18153cb34c60Sahrens /*
18163cb34c60Sahrens  * inputs:
18173cb34c60Sahrens  * zc_name		name of filesystem
18183cb34c60Sahrens  * zc_cookie		zap cursor
18193cb34c60Sahrens  * zc_nvlist_dst_size	size of buffer for property nvlist
18203cb34c60Sahrens  *
18213cb34c60Sahrens  * outputs:
18223cb34c60Sahrens  * zc_name		name of next snapshot
18233cb34c60Sahrens  * zc_objset_stats	stats
18243cb34c60Sahrens  * zc_nvlist_dst	property nvlist
18253cb34c60Sahrens  * zc_nvlist_dst_size	size of property nvlist
18263cb34c60Sahrens  */
1827fa9e4066Sahrens static int
1828fa9e4066Sahrens zfs_ioc_snapshot_list_next(zfs_cmd_t *zc)
1829fa9e4066Sahrens {
183087e5029aSahrens 	objset_t *os;
1831fa9e4066Sahrens 	int error;
1832fa9e4066Sahrens 
1833620252bcSChris Kirby top:
18347cbf8b43SRich Morris 	if (zc->zc_cookie == 0)
18357cbf8b43SRich Morris 		(void) dmu_objset_find(zc->zc_name, dmu_objset_prefetch,
18367cbf8b43SRich Morris 		    NULL, DS_FIND_SNAPSHOTS);
18377cbf8b43SRich Morris 
1838503ad85cSMatthew Ahrens 	error = dmu_objset_hold(zc->zc_name, FTAG, &os);
1839745cd3c5Smaybee 	if (error)
1840745cd3c5Smaybee 		return (error == ENOENT ? ESRCH : error);
1841fa9e4066Sahrens 
1842b81d61a6Slling 	/*
1843b81d61a6Slling 	 * A dataset name of maximum length cannot have any snapshots,
1844b81d61a6Slling 	 * so exit immediately.
1845b81d61a6Slling 	 */
1846b81d61a6Slling 	if (strlcat(zc->zc_name, "@", sizeof (zc->zc_name)) >= MAXNAMELEN) {
1847503ad85cSMatthew Ahrens 		dmu_objset_rele(os, FTAG);
1848b81d61a6Slling 		return (ESRCH);
1849fa9e4066Sahrens 	}
1850fa9e4066Sahrens 
185187e5029aSahrens 	error = dmu_snapshot_list_next(os,
185287e5029aSahrens 	    sizeof (zc->zc_name) - strlen(zc->zc_name),
1853b38f0970Sck 	    zc->zc_name + strlen(zc->zc_name), NULL, &zc->zc_cookie, NULL);
1854503ad85cSMatthew Ahrens 	dmu_objset_rele(os, FTAG);
1855620252bcSChris Kirby 	if (error == 0) {
185687e5029aSahrens 		error = zfs_ioc_objset_stats(zc); /* fill in the stats */
1857620252bcSChris Kirby 		if (error == ENOENT)  {
1858620252bcSChris Kirby 			/* We lost a race with destroy, get the next one. */
1859620252bcSChris Kirby 			*strchr(zc->zc_name, '@') = '\0';
1860620252bcSChris Kirby 			goto top;
1861620252bcSChris Kirby 		}
1862620252bcSChris Kirby 	} else if (error == ENOENT) {
1863745cd3c5Smaybee 		error = ESRCH;
1864620252bcSChris Kirby 	}
1865fa9e4066Sahrens 
18663cb34c60Sahrens 	/* if we failed, undo the @ that we tacked on to zc_name */
1867745cd3c5Smaybee 	if (error)
18683cb34c60Sahrens 		*strchr(zc->zc_name, '@') = '\0';
1869fa9e4066Sahrens 	return (error);
1870fa9e4066Sahrens }
1871fa9e4066Sahrens 
187292241e0bSTom Erickson static int
187392241e0bSTom Erickson zfs_prop_set_userquota(const char *dsname, nvpair_t *pair)
1874fa9e4066Sahrens {
187592241e0bSTom Erickson 	const char *propname = nvpair_name(pair);
187692241e0bSTom Erickson 	uint64_t *valary;
187792241e0bSTom Erickson 	unsigned int vallen;
187892241e0bSTom Erickson 	const char *domain;
1879eeb85002STim Haley 	char *dash;
188092241e0bSTom Erickson 	zfs_userquota_prop_t type;
188192241e0bSTom Erickson 	uint64_t rid;
188292241e0bSTom Erickson 	uint64_t quota;
188392241e0bSTom Erickson 	zfsvfs_t *zfsvfs;
188492241e0bSTom Erickson 	int err;
188592241e0bSTom Erickson 
188692241e0bSTom Erickson 	if (nvpair_type(pair) == DATA_TYPE_NVLIST) {
188792241e0bSTom Erickson 		nvlist_t *attrs;
188892241e0bSTom Erickson 		VERIFY(nvpair_value_nvlist(pair, &attrs) == 0);
1889eeb85002STim Haley 		if (nvlist_lookup_nvpair(attrs, ZPROP_VALUE,
1890eeb85002STim Haley 		    &pair) != 0)
1891eeb85002STim Haley 			return (EINVAL);
189292241e0bSTom Erickson 	}
1893e9dbad6fSeschrock 
1894ecd6cf80Smarks 	/*
1895eeb85002STim Haley 	 * A correctly constructed propname is encoded as
189692241e0bSTom Erickson 	 * userquota@<rid>-<domain>.
1897ecd6cf80Smarks 	 */
1898eeb85002STim Haley 	if ((dash = strchr(propname, '-')) == NULL ||
1899eeb85002STim Haley 	    nvpair_value_uint64_array(pair, &valary, &vallen) != 0 ||
1900eeb85002STim Haley 	    vallen != 3)
1901eeb85002STim Haley 		return (EINVAL);
1902eeb85002STim Haley 
1903eeb85002STim Haley 	domain = dash + 1;
1904eeb85002STim Haley 	type = valary[0];
1905eeb85002STim Haley 	rid = valary[1];
1906eeb85002STim Haley 	quota = valary[2];
1907e9dbad6fSeschrock 
190892241e0bSTom Erickson 	err = zfsvfs_hold(dsname, FTAG, &zfsvfs);
190992241e0bSTom Erickson 	if (err == 0) {
191092241e0bSTom Erickson 		err = zfs_set_userquota(zfsvfs, type, domain, rid, quota);
191192241e0bSTom Erickson 		zfsvfs_rele(zfsvfs, FTAG);
191292241e0bSTom Erickson 	}
1913e9dbad6fSeschrock 
191492241e0bSTom Erickson 	return (err);
191592241e0bSTom Erickson }
191614843421SMatthew Ahrens 
191792241e0bSTom Erickson /*
191892241e0bSTom Erickson  * If the named property is one that has a special function to set its value,
191992241e0bSTom Erickson  * return 0 on success and a positive error code on failure; otherwise if it is
192092241e0bSTom Erickson  * not one of the special properties handled by this function, return -1.
192192241e0bSTom Erickson  *
1922eeb85002STim Haley  * XXX: It would be better for callers of the property interface if we handled
192392241e0bSTom Erickson  * these special cases in dsl_prop.c (in the dsl layer).
192492241e0bSTom Erickson  */
192592241e0bSTom Erickson static int
192692241e0bSTom Erickson zfs_prop_set_special(const char *dsname, zprop_source_t source,
192792241e0bSTom Erickson     nvpair_t *pair)
192892241e0bSTom Erickson {
192992241e0bSTom Erickson 	const char *propname = nvpair_name(pair);
193092241e0bSTom Erickson 	zfs_prop_t prop = zfs_name_to_prop(propname);
193192241e0bSTom Erickson 	uint64_t intval;
193292241e0bSTom Erickson 	int err;
1933fa9e4066Sahrens 
193492241e0bSTom Erickson 	if (prop == ZPROP_INVAL) {
193592241e0bSTom Erickson 		if (zfs_prop_userquota(propname))
193692241e0bSTom Erickson 			return (zfs_prop_set_userquota(dsname, pair));
193792241e0bSTom Erickson 		return (-1);
193892241e0bSTom Erickson 	}
193914843421SMatthew Ahrens 
194092241e0bSTom Erickson 	if (nvpair_type(pair) == DATA_TYPE_NVLIST) {
194192241e0bSTom Erickson 		nvlist_t *attrs;
194292241e0bSTom Erickson 		VERIFY(nvpair_value_nvlist(pair, &attrs) == 0);
194392241e0bSTom Erickson 		VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE,
194492241e0bSTom Erickson 		    &pair) == 0);
194592241e0bSTom Erickson 	}
1946db870a07Sahrens 
194792241e0bSTom Erickson 	if (zfs_prop_get_type(prop) == PROP_TYPE_STRING)
194892241e0bSTom Erickson 		return (-1);
1949b24ab676SJeff Bonwick 
195092241e0bSTom Erickson 	VERIFY(0 == nvpair_value_uint64(pair, &intval));
195140feaa91Sahrens 
195292241e0bSTom Erickson 	switch (prop) {
195392241e0bSTom Erickson 	case ZFS_PROP_QUOTA:
195492241e0bSTom Erickson 		err = dsl_dir_set_quota(dsname, source, intval);
195592241e0bSTom Erickson 		break;
195692241e0bSTom Erickson 	case ZFS_PROP_REFQUOTA:
195792241e0bSTom Erickson 		err = dsl_dataset_set_quota(dsname, source, intval);
195892241e0bSTom Erickson 		break;
195992241e0bSTom Erickson 	case ZFS_PROP_RESERVATION:
196092241e0bSTom Erickson 		err = dsl_dir_set_reservation(dsname, source, intval);
196192241e0bSTom Erickson 		break;
196292241e0bSTom Erickson 	case ZFS_PROP_REFRESERVATION:
196392241e0bSTom Erickson 		err = dsl_dataset_set_reservation(dsname, source, intval);
196492241e0bSTom Erickson 		break;
196592241e0bSTom Erickson 	case ZFS_PROP_VOLSIZE:
196692241e0bSTom Erickson 		err = zvol_set_volsize(dsname, ddi_driver_major(zfs_dip),
196792241e0bSTom Erickson 		    intval);
196892241e0bSTom Erickson 		break;
196992241e0bSTom Erickson 	case ZFS_PROP_VERSION:
197092241e0bSTom Erickson 	{
197192241e0bSTom Erickson 		zfsvfs_t *zfsvfs;
19729e6eda55Smarks 
197392241e0bSTom Erickson 		if ((err = zfsvfs_hold(dsname, FTAG, &zfsvfs)) != 0)
1974b24ab676SJeff Bonwick 			break;
1975b24ab676SJeff Bonwick 
197692241e0bSTom Erickson 		err = zfs_set_version(zfsvfs, intval);
197792241e0bSTom Erickson 		zfsvfs_rele(zfsvfs, FTAG);
1978d0f3f37eSMark Shellenbaum 
197992241e0bSTom Erickson 		if (err == 0 && intval >= ZPL_VERSION_USERSPACE) {
1980b16da2e2SGeorge Wilson 			zfs_cmd_t *zc;
1981b16da2e2SGeorge Wilson 
1982b16da2e2SGeorge Wilson 			zc = kmem_zalloc(sizeof (zfs_cmd_t), KM_SLEEP);
1983b16da2e2SGeorge Wilson 			(void) strcpy(zc->zc_name, dsname);
1984b16da2e2SGeorge Wilson 			(void) zfs_ioc_userspace_upgrade(zc);
1985b16da2e2SGeorge Wilson 			kmem_free(zc, sizeof (zfs_cmd_t));
198640feaa91Sahrens 		}
198792241e0bSTom Erickson 		break;
1988ecd6cf80Smarks 	}
1989ecd6cf80Smarks 
199092241e0bSTom Erickson 	default:
199192241e0bSTom Erickson 		err = -1;
199292241e0bSTom Erickson 	}
1993e9dbad6fSeschrock 
199492241e0bSTom Erickson 	return (err);
199592241e0bSTom Erickson }
1996a9799022Sck 
199792241e0bSTom Erickson /*
199892241e0bSTom Erickson  * This function is best effort. If it fails to set any of the given properties,
199992241e0bSTom Erickson  * it continues to set as many as it can and returns the first error
200092241e0bSTom Erickson  * encountered. If the caller provides a non-NULL errlist, it also gives the
200192241e0bSTom Erickson  * complete list of names of all the properties it failed to set along with the
200292241e0bSTom Erickson  * corresponding error numbers. The caller is responsible for freeing the
200392241e0bSTom Erickson  * returned errlist.
200492241e0bSTom Erickson  *
200592241e0bSTom Erickson  * If every property is set successfully, zero is returned and the list pointed
200692241e0bSTom Erickson  * at by errlist is NULL.
200792241e0bSTom Erickson  */
200892241e0bSTom Erickson int
200992241e0bSTom Erickson zfs_set_prop_nvlist(const char *dsname, zprop_source_t source, nvlist_t *nvl,
201092241e0bSTom Erickson     nvlist_t **errlist)
201192241e0bSTom Erickson {
201292241e0bSTom Erickson 	nvpair_t *pair;
201392241e0bSTom Erickson 	nvpair_t *propval;
201402e383d1STom Erickson 	int rv = 0;
201592241e0bSTom Erickson 	uint64_t intval;
201692241e0bSTom Erickson 	char *strval;
201792241e0bSTom Erickson 	nvlist_t *genericnvl;
201892241e0bSTom Erickson 	nvlist_t *errors;
201992241e0bSTom Erickson 	nvlist_t *retrynvl;
2020e9dbad6fSeschrock 
202192241e0bSTom Erickson 	VERIFY(nvlist_alloc(&genericnvl, NV_UNIQUE_NAME, KM_SLEEP) == 0);
202292241e0bSTom Erickson 	VERIFY(nvlist_alloc(&errors, NV_UNIQUE_NAME, KM_SLEEP) == 0);
202392241e0bSTom Erickson 	VERIFY(nvlist_alloc(&retrynvl, NV_UNIQUE_NAME, KM_SLEEP) == 0);
2024a9799022Sck 
202592241e0bSTom Erickson retry:
202692241e0bSTom Erickson 	pair = NULL;
202792241e0bSTom Erickson 	while ((pair = nvlist_next_nvpair(nvl, pair)) != NULL) {
202892241e0bSTom Erickson 		const char *propname = nvpair_name(pair);
202992241e0bSTom Erickson 		zfs_prop_t prop = zfs_name_to_prop(propname);
2030cfa69fd2STom Erickson 		int err = 0;
2031e9dbad6fSeschrock 
203292241e0bSTom Erickson 		/* decode the property value */
203392241e0bSTom Erickson 		propval = pair;
203492241e0bSTom Erickson 		if (nvpair_type(pair) == DATA_TYPE_NVLIST) {
203592241e0bSTom Erickson 			nvlist_t *attrs;
203692241e0bSTom Erickson 			VERIFY(nvpair_value_nvlist(pair, &attrs) == 0);
2037eeb85002STim Haley 			if (nvlist_lookup_nvpair(attrs, ZPROP_VALUE,
2038eeb85002STim Haley 			    &propval) != 0)
2039eeb85002STim Haley 				err = EINVAL;
204014843421SMatthew Ahrens 		}
2041e9dbad6fSeschrock 
204292241e0bSTom Erickson 		/* Validate value type */
2043eeb85002STim Haley 		if (err == 0 && prop == ZPROP_INVAL) {
204492241e0bSTom Erickson 			if (zfs_prop_user(propname)) {
204592241e0bSTom Erickson 				if (nvpair_type(propval) != DATA_TYPE_STRING)
204692241e0bSTom Erickson 					err = EINVAL;
204792241e0bSTom Erickson 			} else if (zfs_prop_userquota(propname)) {
204892241e0bSTom Erickson 				if (nvpair_type(propval) !=
204992241e0bSTom Erickson 				    DATA_TYPE_UINT64_ARRAY)
205092241e0bSTom Erickson 					err = EINVAL;
20514201a95eSRic Aleshire 			}
2052eeb85002STim Haley 		} else if (err == 0) {
205392241e0bSTom Erickson 			if (nvpair_type(propval) == DATA_TYPE_STRING) {
205492241e0bSTom Erickson 				if (zfs_prop_get_type(prop) != PROP_TYPE_STRING)
205592241e0bSTom Erickson 					err = EINVAL;
205692241e0bSTom Erickson 			} else if (nvpair_type(propval) == DATA_TYPE_UINT64) {
2057a2eea2e1Sahrens 				const char *unused;
2058a2eea2e1Sahrens 
205992241e0bSTom Erickson 				VERIFY(nvpair_value_uint64(propval,
206092241e0bSTom Erickson 				    &intval) == 0);
2061e9dbad6fSeschrock 
2062e9dbad6fSeschrock 				switch (zfs_prop_get_type(prop)) {
206391ebeef5Sahrens 				case PROP_TYPE_NUMBER:
2064e9dbad6fSeschrock 					break;
206591ebeef5Sahrens 				case PROP_TYPE_STRING:
206692241e0bSTom Erickson 					err = EINVAL;
206792241e0bSTom Erickson 					break;
206891ebeef5Sahrens 				case PROP_TYPE_INDEX:
2069acd76fe5Seschrock 					if (zfs_prop_index_to_string(prop,
207092241e0bSTom Erickson 					    intval, &unused) != 0)
207192241e0bSTom Erickson 						err = EINVAL;
2072e9dbad6fSeschrock 					break;
2073e9dbad6fSeschrock 				default:
2074e7437265Sahrens 					cmn_err(CE_PANIC,
2075e7437265Sahrens 					    "unknown property type");
2076e9dbad6fSeschrock 				}
2077e9dbad6fSeschrock 			} else {
207892241e0bSTom Erickson 				err = EINVAL;
2079e9dbad6fSeschrock 			}
2080e9dbad6fSeschrock 		}
208192241e0bSTom Erickson 
208292241e0bSTom Erickson 		/* Validate permissions */
208392241e0bSTom Erickson 		if (err == 0)
208492241e0bSTom Erickson 			err = zfs_check_settable(dsname, pair, CRED());
208592241e0bSTom Erickson 
208692241e0bSTom Erickson 		if (err == 0) {
208792241e0bSTom Erickson 			err = zfs_prop_set_special(dsname, source, pair);
208892241e0bSTom Erickson 			if (err == -1) {
208992241e0bSTom Erickson 				/*
209092241e0bSTom Erickson 				 * For better performance we build up a list of
209192241e0bSTom Erickson 				 * properties to set in a single transaction.
209292241e0bSTom Erickson 				 */
209392241e0bSTom Erickson 				err = nvlist_add_nvpair(genericnvl, pair);
209492241e0bSTom Erickson 			} else if (err != 0 && nvl != retrynvl) {
209592241e0bSTom Erickson 				/*
209692241e0bSTom Erickson 				 * This may be a spurious error caused by
209792241e0bSTom Erickson 				 * receiving quota and reservation out of order.
209892241e0bSTom Erickson 				 * Try again in a second pass.
209992241e0bSTom Erickson 				 */
210092241e0bSTom Erickson 				err = nvlist_add_nvpair(retrynvl, pair);
210192241e0bSTom Erickson 			}
210292241e0bSTom Erickson 		}
210392241e0bSTom Erickson 
210492241e0bSTom Erickson 		if (err != 0)
210592241e0bSTom Erickson 			VERIFY(nvlist_add_int32(errors, propname, err) == 0);
2106e9dbad6fSeschrock 	}
2107e9dbad6fSeschrock 
210892241e0bSTom Erickson 	if (nvl != retrynvl && !nvlist_empty(retrynvl)) {
210992241e0bSTom Erickson 		nvl = retrynvl;
211092241e0bSTom Erickson 		goto retry;
211192241e0bSTom Erickson 	}
211292241e0bSTom Erickson 
211392241e0bSTom Erickson 	if (!nvlist_empty(genericnvl) &&
211492241e0bSTom Erickson 	    dsl_props_set(dsname, source, genericnvl) != 0) {
211592241e0bSTom Erickson 		/*
211692241e0bSTom Erickson 		 * If this fails, we still want to set as many properties as we
211792241e0bSTom Erickson 		 * can, so try setting them individually.
211892241e0bSTom Erickson 		 */
211992241e0bSTom Erickson 		pair = NULL;
212092241e0bSTom Erickson 		while ((pair = nvlist_next_nvpair(genericnvl, pair)) != NULL) {
212192241e0bSTom Erickson 			const char *propname = nvpair_name(pair);
2122cfa69fd2STom Erickson 			int err = 0;
212392241e0bSTom Erickson 
212492241e0bSTom Erickson 			propval = pair;
212592241e0bSTom Erickson 			if (nvpair_type(pair) == DATA_TYPE_NVLIST) {
212692241e0bSTom Erickson 				nvlist_t *attrs;
212792241e0bSTom Erickson 				VERIFY(nvpair_value_nvlist(pair, &attrs) == 0);
212892241e0bSTom Erickson 				VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE,
212992241e0bSTom Erickson 				    &propval) == 0);
213092241e0bSTom Erickson 			}
213192241e0bSTom Erickson 
213292241e0bSTom Erickson 			if (nvpair_type(propval) == DATA_TYPE_STRING) {
213392241e0bSTom Erickson 				VERIFY(nvpair_value_string(propval,
213492241e0bSTom Erickson 				    &strval) == 0);
213592241e0bSTom Erickson 				err = dsl_prop_set(dsname, propname, source, 1,
213692241e0bSTom Erickson 				    strlen(strval) + 1, strval);
213792241e0bSTom Erickson 			} else {
213892241e0bSTom Erickson 				VERIFY(nvpair_value_uint64(propval,
213992241e0bSTom Erickson 				    &intval) == 0);
214092241e0bSTom Erickson 				err = dsl_prop_set(dsname, propname, source, 8,
214192241e0bSTom Erickson 				    1, &intval);
214292241e0bSTom Erickson 			}
214392241e0bSTom Erickson 
214492241e0bSTom Erickson 			if (err != 0) {
214592241e0bSTom Erickson 				VERIFY(nvlist_add_int32(errors, propname,
214692241e0bSTom Erickson 				    err) == 0);
214792241e0bSTom Erickson 			}
214892241e0bSTom Erickson 		}
21495c0b6a79SRich Morris 	}
21505c0b6a79SRich Morris 	nvlist_free(genericnvl);
215192241e0bSTom Erickson 	nvlist_free(retrynvl);
215292241e0bSTom Erickson 
215392241e0bSTom Erickson 	if ((pair = nvlist_next_nvpair(errors, NULL)) == NULL) {
215492241e0bSTom Erickson 		nvlist_free(errors);
215592241e0bSTom Erickson 		errors = NULL;
215692241e0bSTom Erickson 	} else {
215792241e0bSTom Erickson 		VERIFY(nvpair_value_int32(pair, &rv) == 0);
215892241e0bSTom Erickson 	}
215992241e0bSTom Erickson 
216092241e0bSTom Erickson 	if (errlist == NULL)
216192241e0bSTom Erickson 		nvlist_free(errors);
216292241e0bSTom Erickson 	else
216392241e0bSTom Erickson 		*errlist = errors;
216492241e0bSTom Erickson 
216592241e0bSTom Erickson 	return (rv);
2166fa9e4066Sahrens }
2167fa9e4066Sahrens 
2168ea2f5b9eSMatthew Ahrens /*
2169ea2f5b9eSMatthew Ahrens  * Check that all the properties are valid user properties.
2170ea2f5b9eSMatthew Ahrens  */
2171ea2f5b9eSMatthew Ahrens static int
2172ea2f5b9eSMatthew Ahrens zfs_check_userprops(char *fsname, nvlist_t *nvl)
2173ea2f5b9eSMatthew Ahrens {
217492241e0bSTom Erickson 	nvpair_t *pair = NULL;
2175ea2f5b9eSMatthew Ahrens 	int error = 0;
2176ea2f5b9eSMatthew Ahrens 
217792241e0bSTom Erickson 	while ((pair = nvlist_next_nvpair(nvl, pair)) != NULL) {
217892241e0bSTom Erickson 		const char *propname = nvpair_name(pair);
2179ea2f5b9eSMatthew Ahrens 		char *valstr;
2180ea2f5b9eSMatthew Ahrens 
2181ea2f5b9eSMatthew Ahrens 		if (!zfs_prop_user(propname) ||
218292241e0bSTom Erickson 		    nvpair_type(pair) != DATA_TYPE_STRING)
2183ea2f5b9eSMatthew Ahrens 			return (EINVAL);
2184ea2f5b9eSMatthew Ahrens 
2185ea2f5b9eSMatthew Ahrens 		if (error = zfs_secpolicy_write_perms(fsname,
2186ea2f5b9eSMatthew Ahrens 		    ZFS_DELEG_PERM_USERPROP, CRED()))
2187ea2f5b9eSMatthew Ahrens 			return (error);
2188ea2f5b9eSMatthew Ahrens 
2189ea2f5b9eSMatthew Ahrens 		if (strlen(propname) >= ZAP_MAXNAMELEN)
2190ea2f5b9eSMatthew Ahrens 			return (ENAMETOOLONG);
2191ea2f5b9eSMatthew Ahrens 
219292241e0bSTom Erickson 		VERIFY(nvpair_value_string(pair, &valstr) == 0);
2193ea2f5b9eSMatthew Ahrens 		if (strlen(valstr) >= ZAP_MAXVALUELEN)
2194ea2f5b9eSMatthew Ahrens 			return (E2BIG);
2195ea2f5b9eSMatthew Ahrens 	}
2196ea2f5b9eSMatthew Ahrens 	return (0);
2197ea2f5b9eSMatthew Ahrens }
2198ea2f5b9eSMatthew Ahrens 
219992241e0bSTom Erickson static void
220092241e0bSTom Erickson props_skip(nvlist_t *props, nvlist_t *skipped, nvlist_t **newprops)
220192241e0bSTom Erickson {
220292241e0bSTom Erickson 	nvpair_t *pair;
220392241e0bSTom Erickson 
220492241e0bSTom Erickson 	VERIFY(nvlist_alloc(newprops, NV_UNIQUE_NAME, KM_SLEEP) == 0);
220592241e0bSTom Erickson 
220692241e0bSTom Erickson 	pair = NULL;
220792241e0bSTom Erickson 	while ((pair = nvlist_next_nvpair(props, pair)) != NULL) {
220892241e0bSTom Erickson 		if (nvlist_exists(skipped, nvpair_name(pair)))
220992241e0bSTom Erickson 			continue;
221092241e0bSTom Erickson 
221192241e0bSTom Erickson 		VERIFY(nvlist_add_nvpair(*newprops, pair) == 0);
221292241e0bSTom Erickson 	}
221392241e0bSTom Erickson }
221492241e0bSTom Erickson 
221592241e0bSTom Erickson static int
221692241e0bSTom Erickson clear_received_props(objset_t *os, const char *fs, nvlist_t *props,
221792241e0bSTom Erickson     nvlist_t *skipped)
221892241e0bSTom Erickson {
221992241e0bSTom Erickson 	int err = 0;
222092241e0bSTom Erickson 	nvlist_t *cleared_props = NULL;
222192241e0bSTom Erickson 	props_skip(props, skipped, &cleared_props);
222292241e0bSTom Erickson 	if (!nvlist_empty(cleared_props)) {
222392241e0bSTom Erickson 		/*
222492241e0bSTom Erickson 		 * Acts on local properties until the dataset has received
222592241e0bSTom Erickson 		 * properties at least once on or after SPA_VERSION_RECVD_PROPS.
222692241e0bSTom Erickson 		 */
222792241e0bSTom Erickson 		zprop_source_t flags = (ZPROP_SRC_NONE |
222892241e0bSTom Erickson 		    (dsl_prop_get_hasrecvd(os) ? ZPROP_SRC_RECEIVED : 0));
222992241e0bSTom Erickson 		err = zfs_set_prop_nvlist(fs, flags, cleared_props, NULL);
223092241e0bSTom Erickson 	}
223192241e0bSTom Erickson 	nvlist_free(cleared_props);
223292241e0bSTom Erickson 	return (err);
223392241e0bSTom Erickson }
223492241e0bSTom Erickson 
22353cb34c60Sahrens /*
22363cb34c60Sahrens  * inputs:
22373cb34c60Sahrens  * zc_name		name of filesystem
22385c0b6a79SRich Morris  * zc_value		name of property to set
22393cb34c60Sahrens  * zc_nvlist_src{_size}	nvlist of properties to apply
224092241e0bSTom Erickson  * zc_cookie		received properties flag
22413cb34c60Sahrens  *
224292241e0bSTom Erickson  * outputs:
224392241e0bSTom Erickson  * zc_nvlist_dst{_size} error for each unapplied received property
22443cb34c60Sahrens  */
2245fa9e4066Sahrens static int
2246e9dbad6fSeschrock zfs_ioc_set_prop(zfs_cmd_t *zc)
2247fa9e4066Sahrens {
2248e9dbad6fSeschrock 	nvlist_t *nvl;
224992241e0bSTom Erickson 	boolean_t received = zc->zc_cookie;
225092241e0bSTom Erickson 	zprop_source_t source = (received ? ZPROP_SRC_RECEIVED :
225192241e0bSTom Erickson 	    ZPROP_SRC_LOCAL);
225292241e0bSTom Erickson 	nvlist_t *errors = NULL;
2253e9dbad6fSeschrock 	int error;
2254e9dbad6fSeschrock 
2255990b4856Slling 	if ((error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
2256478ed9adSEric Taylor 	    zc->zc_iflags, &nvl)) != 0)
2257e9dbad6fSeschrock 		return (error);
2258e9dbad6fSeschrock 
225992241e0bSTom Erickson 	if (received) {
2260bb0ade09Sahrens 		nvlist_t *origprops;
2261bb0ade09Sahrens 		objset_t *os;
2262bb0ade09Sahrens 
2263503ad85cSMatthew Ahrens 		if (dmu_objset_hold(zc->zc_name, FTAG, &os) == 0) {
226492241e0bSTom Erickson 			if (dsl_prop_get_received(os, &origprops) == 0) {
226592241e0bSTom Erickson 				(void) clear_received_props(os,
226692241e0bSTom Erickson 				    zc->zc_name, origprops, nvl);
2267bb0ade09Sahrens 				nvlist_free(origprops);
2268bb0ade09Sahrens 			}
226992241e0bSTom Erickson 
227092241e0bSTom Erickson 			dsl_prop_set_hasrecvd(os);
2271503ad85cSMatthew Ahrens 			dmu_objset_rele(os, FTAG);
2272bb0ade09Sahrens 		}
2273bb0ade09Sahrens 	}
2274bb0ade09Sahrens 
227592241e0bSTom Erickson 	error = zfs_set_prop_nvlist(zc->zc_name, source, nvl, &errors);
227692241e0bSTom Erickson 
227792241e0bSTom Erickson 	if (zc->zc_nvlist_dst != NULL && errors != NULL) {
227892241e0bSTom Erickson 		(void) put_nvlist(zc, errors);
227992241e0bSTom Erickson 	}
2280ecd6cf80Smarks 
228192241e0bSTom Erickson 	nvlist_free(errors);
2282e9dbad6fSeschrock 	nvlist_free(nvl);
2283e9dbad6fSeschrock 	return (error);
2284fa9e4066Sahrens }
2285fa9e4066Sahrens 
22863cb34c60Sahrens /*
22873cb34c60Sahrens  * inputs:
22883cb34c60Sahrens  * zc_name		name of filesystem
22893cb34c60Sahrens  * zc_value		name of property to inherit
229092241e0bSTom Erickson  * zc_cookie		revert to received value if TRUE
22913cb34c60Sahrens  *
22923cb34c60Sahrens  * outputs:		none
22933cb34c60Sahrens  */
2294e45ce728Sahrens static int
2295e45ce728Sahrens zfs_ioc_inherit_prop(zfs_cmd_t *zc)
2296e45ce728Sahrens {
229792241e0bSTom Erickson 	const char *propname = zc->zc_value;
229892241e0bSTom Erickson 	zfs_prop_t prop = zfs_name_to_prop(propname);
229992241e0bSTom Erickson 	boolean_t received = zc->zc_cookie;
230092241e0bSTom Erickson 	zprop_source_t source = (received
230192241e0bSTom Erickson 	    ? ZPROP_SRC_NONE		/* revert to received value, if any */
230292241e0bSTom Erickson 	    : ZPROP_SRC_INHERITED);	/* explicitly inherit */
230392241e0bSTom Erickson 
230492241e0bSTom Erickson 	if (received) {
230592241e0bSTom Erickson 		nvlist_t *dummy;
230692241e0bSTom Erickson 		nvpair_t *pair;
230792241e0bSTom Erickson 		zprop_type_t type;
230892241e0bSTom Erickson 		int err;
230992241e0bSTom Erickson 
231092241e0bSTom Erickson 		/*
231192241e0bSTom Erickson 		 * zfs_prop_set_special() expects properties in the form of an
231292241e0bSTom Erickson 		 * nvpair with type info.
231392241e0bSTom Erickson 		 */
231492241e0bSTom Erickson 		if (prop == ZPROP_INVAL) {
231592241e0bSTom Erickson 			if (!zfs_prop_user(propname))
231692241e0bSTom Erickson 				return (EINVAL);
231792241e0bSTom Erickson 
231892241e0bSTom Erickson 			type = PROP_TYPE_STRING;
2319a79992aaSTom Erickson 		} else if (prop == ZFS_PROP_VOLSIZE ||
2320a79992aaSTom Erickson 		    prop == ZFS_PROP_VERSION) {
2321a79992aaSTom Erickson 			return (EINVAL);
232292241e0bSTom Erickson 		} else {
232392241e0bSTom Erickson 			type = zfs_prop_get_type(prop);
232492241e0bSTom Erickson 		}
232592241e0bSTom Erickson 
232692241e0bSTom Erickson 		VERIFY(nvlist_alloc(&dummy, NV_UNIQUE_NAME, KM_SLEEP) == 0);
232792241e0bSTom Erickson 
232892241e0bSTom Erickson 		switch (type) {
232992241e0bSTom Erickson 		case PROP_TYPE_STRING:
233092241e0bSTom Erickson 			VERIFY(0 == nvlist_add_string(dummy, propname, ""));
233192241e0bSTom Erickson 			break;
233292241e0bSTom Erickson 		case PROP_TYPE_NUMBER:
233392241e0bSTom Erickson 		case PROP_TYPE_INDEX:
233492241e0bSTom Erickson 			VERIFY(0 == nvlist_add_uint64(dummy, propname, 0));
233592241e0bSTom Erickson 			break;
233692241e0bSTom Erickson 		default:
233792241e0bSTom Erickson 			nvlist_free(dummy);
233892241e0bSTom Erickson 			return (EINVAL);
233992241e0bSTom Erickson 		}
234092241e0bSTom Erickson 
234192241e0bSTom Erickson 		pair = nvlist_next_nvpair(dummy, NULL);
234292241e0bSTom Erickson 		err = zfs_prop_set_special(zc->zc_name, source, pair);
234392241e0bSTom Erickson 		nvlist_free(dummy);
234492241e0bSTom Erickson 		if (err != -1)
234592241e0bSTom Erickson 			return (err); /* special property already handled */
234692241e0bSTom Erickson 	} else {
234792241e0bSTom Erickson 		/*
234892241e0bSTom Erickson 		 * Only check this in the non-received case. We want to allow
234992241e0bSTom Erickson 		 * 'inherit -S' to revert non-inheritable properties like quota
235092241e0bSTom Erickson 		 * and reservation to the received or default values even though
235192241e0bSTom Erickson 		 * they are not considered inheritable.
235292241e0bSTom Erickson 		 */
235392241e0bSTom Erickson 		if (prop != ZPROP_INVAL && !zfs_prop_inheritable(prop))
235492241e0bSTom Erickson 			return (EINVAL);
235592241e0bSTom Erickson 	}
235692241e0bSTom Erickson 
2357e45ce728Sahrens 	/* the property name has been validated by zfs_secpolicy_inherit() */
235892241e0bSTom Erickson 	return (dsl_prop_set(zc->zc_name, zc->zc_value, source, 0, 0, NULL));
2359e45ce728Sahrens }
2360e45ce728Sahrens 
2361b1b8ab34Slling static int
236211a41203Slling zfs_ioc_pool_set_props(zfs_cmd_t *zc)
2363b1b8ab34Slling {
2364990b4856Slling 	nvlist_t *props;
2365b1b8ab34Slling 	spa_t *spa;
2366990b4856Slling 	int error;
236792241e0bSTom Erickson 	nvpair_t *pair;
2368b1b8ab34Slling 
236992241e0bSTom Erickson 	if (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
237092241e0bSTom Erickson 	    zc->zc_iflags, &props))
2371b1b8ab34Slling 		return (error);
2372b1b8ab34Slling 
2373379c004dSEric Schrock 	/*
2374379c004dSEric Schrock 	 * If the only property is the configfile, then just do a spa_lookup()
2375379c004dSEric Schrock 	 * to handle the faulted case.
2376379c004dSEric Schrock 	 */
237792241e0bSTom Erickson 	pair = nvlist_next_nvpair(props, NULL);
237892241e0bSTom Erickson 	if (pair != NULL && strcmp(nvpair_name(pair),
2379379c004dSEric Schrock 	    zpool_prop_to_name(ZPOOL_PROP_CACHEFILE)) == 0 &&
238092241e0bSTom Erickson 	    nvlist_next_nvpair(props, pair) == NULL) {
2381379c004dSEric Schrock 		mutex_enter(&spa_namespace_lock);
2382379c004dSEric Schrock 		if ((spa = spa_lookup(zc->zc_name)) != NULL) {
2383379c004dSEric Schrock 			spa_configfile_set(spa, props, B_FALSE);
2384379c004dSEric Schrock 			spa_config_sync(spa, B_FALSE, B_TRUE);
2385379c004dSEric Schrock 		}
2386379c004dSEric Schrock 		mutex_exit(&spa_namespace_lock);
2387b693757aSEric Schrock 		if (spa != NULL) {
2388b693757aSEric Schrock 			nvlist_free(props);
2389379c004dSEric Schrock 			return (0);
2390b693757aSEric Schrock 		}
2391379c004dSEric Schrock 	}
2392379c004dSEric Schrock 
2393b1b8ab34Slling 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) {
2394990b4856Slling 		nvlist_free(props);
2395b1b8ab34Slling 		return (error);
2396b1b8ab34Slling 	}
2397b1b8ab34Slling 
2398990b4856Slling 	error = spa_prop_set(spa, props);
2399b1b8ab34Slling 
2400990b4856Slling 	nvlist_free(props);
2401b1b8ab34Slling 	spa_close(spa, FTAG);
2402b1b8ab34Slling 
2403b1b8ab34Slling 	return (error);
2404b1b8ab34Slling }
2405b1b8ab34Slling 
2406b1b8ab34Slling static int
240711a41203Slling zfs_ioc_pool_get_props(zfs_cmd_t *zc)
2408b1b8ab34Slling {
2409b1b8ab34Slling 	spa_t *spa;
2410b1b8ab34Slling 	int error;
2411b1b8ab34Slling 	nvlist_t *nvp = NULL;
2412b1b8ab34Slling 
2413379c004dSEric Schrock 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) {
2414379c004dSEric Schrock 		/*
2415379c004dSEric Schrock 		 * If the pool is faulted, there may be properties we can still
2416379c004dSEric Schrock 		 * get (such as altroot and cachefile), so attempt to get them
2417379c004dSEric Schrock 		 * anyway.
2418379c004dSEric Schrock 		 */
2419379c004dSEric Schrock 		mutex_enter(&spa_namespace_lock);
2420379c004dSEric Schrock 		if ((spa = spa_lookup(zc->zc_name)) != NULL)
2421379c004dSEric Schrock 			error = spa_prop_get(spa, &nvp);
2422379c004dSEric Schrock 		mutex_exit(&spa_namespace_lock);
2423379c004dSEric Schrock 	} else {
2424379c004dSEric Schrock 		error = spa_prop_get(spa, &nvp);
2425379c004dSEric Schrock 		spa_close(spa, FTAG);
2426379c004dSEric Schrock 	}
2427b1b8ab34Slling 
2428b1b8ab34Slling 	if (error == 0 && zc->zc_nvlist_dst != NULL)
2429b1b8ab34Slling 		error = put_nvlist(zc, nvp);
2430b1b8ab34Slling 	else
2431b1b8ab34Slling 		error = EFAULT;
2432b1b8ab34Slling 
2433379c004dSEric Schrock 	nvlist_free(nvp);
2434b1b8ab34Slling 	return (error);
2435b1b8ab34Slling }
2436b1b8ab34Slling 
24373cb34c60Sahrens /*
24383cb34c60Sahrens  * inputs:
24393cb34c60Sahrens  * zc_name		name of filesystem
24403cb34c60Sahrens  * zc_nvlist_src{_size}	nvlist of delegated permissions
24413cb34c60Sahrens  * zc_perm_action	allow/unallow flag
24423cb34c60Sahrens  *
24433cb34c60Sahrens  * outputs:		none
24443cb34c60Sahrens  */
2445ecd6cf80Smarks static int
2446ecd6cf80Smarks zfs_ioc_set_fsacl(zfs_cmd_t *zc)
2447ecd6cf80Smarks {
2448ecd6cf80Smarks 	int error;
2449ecd6cf80Smarks 	nvlist_t *fsaclnv = NULL;
2450ecd6cf80Smarks 
2451990b4856Slling 	if ((error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
2452478ed9adSEric Taylor 	    zc->zc_iflags, &fsaclnv)) != 0)
2453ecd6cf80Smarks 		return (error);
2454ecd6cf80Smarks 
2455ecd6cf80Smarks 	/*
2456ecd6cf80Smarks 	 * Verify nvlist is constructed correctly
2457ecd6cf80Smarks 	 */
2458ecd6cf80Smarks 	if ((error = zfs_deleg_verify_nvlist(fsaclnv)) != 0) {
2459ecd6cf80Smarks 		nvlist_free(fsaclnv);
2460ecd6cf80Smarks 		return (EINVAL);
2461ecd6cf80Smarks 	}
2462ecd6cf80Smarks 
2463ecd6cf80Smarks 	/*
2464ecd6cf80Smarks 	 * If we don't have PRIV_SYS_MOUNT, then validate
2465ecd6cf80Smarks 	 * that user is allowed to hand out each permission in
2466ecd6cf80Smarks 	 * the nvlist(s)
2467ecd6cf80Smarks 	 */
2468ecd6cf80Smarks 
246991ebeef5Sahrens 	error = secpolicy_zfs(CRED());
2470ecd6cf80Smarks 	if (error) {
247191ebeef5Sahrens 		if (zc->zc_perm_action == B_FALSE) {
247291ebeef5Sahrens 			error = dsl_deleg_can_allow(zc->zc_name,
247391ebeef5Sahrens 			    fsaclnv, CRED());
247491ebeef5Sahrens 		} else {
247591ebeef5Sahrens 			error = dsl_deleg_can_unallow(zc->zc_name,
247691ebeef5Sahrens 			    fsaclnv, CRED());
247791ebeef5Sahrens 		}
2478ecd6cf80Smarks 	}
2479ecd6cf80Smarks 
2480ecd6cf80Smarks 	if (error == 0)
2481ecd6cf80Smarks 		error = dsl_deleg_set(zc->zc_name, fsaclnv, zc->zc_perm_action);
2482ecd6cf80Smarks 
2483ecd6cf80Smarks 	nvlist_free(fsaclnv);
2484ecd6cf80Smarks 	return (error);
2485ecd6cf80Smarks }
2486ecd6cf80Smarks 
24873cb34c60Sahrens /*
24883cb34c60Sahrens  * inputs:
24893cb34c60Sahrens  * zc_name		name of filesystem
24903cb34c60Sahrens  *
24913cb34c60Sahrens  * outputs:
24923cb34c60Sahrens  * zc_nvlist_src{_size}	nvlist of delegated permissions
24933cb34c60Sahrens  */
2494ecd6cf80Smarks static int
2495ecd6cf80Smarks zfs_ioc_get_fsacl(zfs_cmd_t *zc)
2496ecd6cf80Smarks {
2497ecd6cf80Smarks 	nvlist_t *nvp;
2498ecd6cf80Smarks 	int error;
2499ecd6cf80Smarks 
2500ecd6cf80Smarks 	if ((error = dsl_deleg_get(zc->zc_name, &nvp)) == 0) {
2501ecd6cf80Smarks 		error = put_nvlist(zc, nvp);
2502ecd6cf80Smarks 		nvlist_free(nvp);
2503ecd6cf80Smarks 	}
2504ecd6cf80Smarks 
2505ecd6cf80Smarks 	return (error);
2506ecd6cf80Smarks }
2507ecd6cf80Smarks 
2508fa9e4066Sahrens /*
2509fa9e4066Sahrens  * Search the vfs list for a specified resource.  Returns a pointer to it
2510fa9e4066Sahrens  * or NULL if no suitable entry is found. The caller of this routine
2511fa9e4066Sahrens  * is responsible for releasing the returned vfs pointer.
2512fa9e4066Sahrens  */
2513fa9e4066Sahrens static vfs_t *
2514fa9e4066Sahrens zfs_get_vfs(const char *resource)
2515fa9e4066Sahrens {
2516fa9e4066Sahrens 	struct vfs *vfsp;
2517fa9e4066Sahrens 	struct vfs *vfs_found = NULL;
2518fa9e4066Sahrens 
2519fa9e4066Sahrens 	vfs_list_read_lock();
2520fa9e4066Sahrens 	vfsp = rootvfs;
2521fa9e4066Sahrens 	do {
2522fa9e4066Sahrens 		if (strcmp(refstr_value(vfsp->vfs_resource), resource) == 0) {
2523fa9e4066Sahrens 			VFS_HOLD(vfsp);
2524fa9e4066Sahrens 			vfs_found = vfsp;
2525fa9e4066Sahrens 			break;
2526fa9e4066Sahrens 		}
2527fa9e4066Sahrens 		vfsp = vfsp->vfs_next;
2528fa9e4066Sahrens 	} while (vfsp != rootvfs);
2529fa9e4066Sahrens 	vfs_list_unlock();
2530fa9e4066Sahrens 	return (vfs_found);
2531fa9e4066Sahrens }
2532fa9e4066Sahrens 
2533ecd6cf80Smarks /* ARGSUSED */
2534fa9e4066Sahrens static void
2535ecd6cf80Smarks zfs_create_cb(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx)
2536fa9e4066Sahrens {
2537da6c28aaSamw 	zfs_creat_t *zct = arg;
2538da6c28aaSamw 
2539de8267e0Stimh 	zfs_create_fs(os, cr, zct->zct_zplprops, tx);
2540da6c28aaSamw }
2541da6c28aaSamw 
2542de8267e0Stimh #define	ZFS_PROP_UNDEFINED	((uint64_t)-1)
2543da6c28aaSamw 
2544da6c28aaSamw /*
2545de8267e0Stimh  * inputs:
25460a48a24eStimh  * createprops		list of properties requested by creator
25470a48a24eStimh  * default_zplver	zpl version to use if unspecified in createprops
25480a48a24eStimh  * fuids_ok		fuids allowed in this version of the spa?
25490a48a24eStimh  * os			parent objset pointer (NULL if root fs)
2550de8267e0Stimh  *
2551de8267e0Stimh  * outputs:
2552de8267e0Stimh  * zplprops	values for the zplprops we attach to the master node object
25530a48a24eStimh  * is_ci	true if requested file system will be purely case-insensitive
2554da6c28aaSamw  *
2555de8267e0Stimh  * Determine the settings for utf8only, normalization and
2556de8267e0Stimh  * casesensitivity.  Specific values may have been requested by the
2557de8267e0Stimh  * creator and/or we can inherit values from the parent dataset.  If
2558de8267e0Stimh  * the file system is of too early a vintage, a creator can not
2559de8267e0Stimh  * request settings for these properties, even if the requested
2560de8267e0Stimh  * setting is the default value.  We don't actually want to create dsl
2561de8267e0Stimh  * properties for these, so remove them from the source nvlist after
2562de8267e0Stimh  * processing.
2563da6c28aaSamw  */
2564da6c28aaSamw static int
256514843421SMatthew Ahrens zfs_fill_zplprops_impl(objset_t *os, uint64_t zplver,
25660a586ceaSMark Shellenbaum     boolean_t fuids_ok, boolean_t sa_ok, nvlist_t *createprops,
25670a586ceaSMark Shellenbaum     nvlist_t *zplprops, boolean_t *is_ci)
2568da6c28aaSamw {
2569de8267e0Stimh 	uint64_t sense = ZFS_PROP_UNDEFINED;
2570de8267e0Stimh 	uint64_t norm = ZFS_PROP_UNDEFINED;
2571de8267e0Stimh 	uint64_t u8 = ZFS_PROP_UNDEFINED;
2572da6c28aaSamw 
2573de8267e0Stimh 	ASSERT(zplprops != NULL);
2574da6c28aaSamw 
2575de8267e0Stimh 	/*
2576de8267e0Stimh 	 * Pull out creator prop choices, if any.
2577de8267e0Stimh 	 */
2578de8267e0Stimh 	if (createprops) {
25790a48a24eStimh 		(void) nvlist_lookup_uint64(createprops,
25800a48a24eStimh 		    zfs_prop_to_name(ZFS_PROP_VERSION), &zplver);
2581de8267e0Stimh 		(void) nvlist_lookup_uint64(createprops,
2582de8267e0Stimh 		    zfs_prop_to_name(ZFS_PROP_NORMALIZE), &norm);
2583de8267e0Stimh 		(void) nvlist_remove_all(createprops,
2584de8267e0Stimh 		    zfs_prop_to_name(ZFS_PROP_NORMALIZE));
2585de8267e0Stimh 		(void) nvlist_lookup_uint64(createprops,
2586de8267e0Stimh 		    zfs_prop_to_name(ZFS_PROP_UTF8ONLY), &u8);
2587de8267e0Stimh 		(void) nvlist_remove_all(createprops,
2588de8267e0Stimh 		    zfs_prop_to_name(ZFS_PROP_UTF8ONLY));
2589de8267e0Stimh 		(void) nvlist_lookup_uint64(createprops,
2590de8267e0Stimh 		    zfs_prop_to_name(ZFS_PROP_CASE), &sense);
2591de8267e0Stimh 		(void) nvlist_remove_all(createprops,
2592de8267e0Stimh 		    zfs_prop_to_name(ZFS_PROP_CASE));
2593de8267e0Stimh 	}
2594da6c28aaSamw 
2595c2a93d44Stimh 	/*
25960a48a24eStimh 	 * If the zpl version requested is whacky or the file system
25970a48a24eStimh 	 * or pool is version is too "young" to support normalization
25980a48a24eStimh 	 * and the creator tried to set a value for one of the props,
25990a48a24eStimh 	 * error out.
2600c2a93d44Stimh 	 */
26010a48a24eStimh 	if ((zplver < ZPL_VERSION_INITIAL || zplver > ZPL_VERSION) ||
26020a48a24eStimh 	    (zplver >= ZPL_VERSION_FUID && !fuids_ok) ||
26030a586ceaSMark Shellenbaum 	    (zplver >= ZPL_VERSION_SA && !sa_ok) ||
26040a48a24eStimh 	    (zplver < ZPL_VERSION_NORMALIZATION &&
2605de8267e0Stimh 	    (norm != ZFS_PROP_UNDEFINED || u8 != ZFS_PROP_UNDEFINED ||
26060a48a24eStimh 	    sense != ZFS_PROP_UNDEFINED)))
2607de8267e0Stimh 		return (ENOTSUP);
2608c2a93d44Stimh 
2609de8267e0Stimh 	/*
2610de8267e0Stimh 	 * Put the version in the zplprops
2611de8267e0Stimh 	 */
2612de8267e0Stimh 	VERIFY(nvlist_add_uint64(zplprops,
2613de8267e0Stimh 	    zfs_prop_to_name(ZFS_PROP_VERSION), zplver) == 0);
2614da6c28aaSamw 
2615de8267e0Stimh 	if (norm == ZFS_PROP_UNDEFINED)
2616de8267e0Stimh 		VERIFY(zfs_get_zplprop(os, ZFS_PROP_NORMALIZE, &norm) == 0);
2617de8267e0Stimh 	VERIFY(nvlist_add_uint64(zplprops,
2618de8267e0Stimh 	    zfs_prop_to_name(ZFS_PROP_NORMALIZE), norm) == 0);
2619da6c28aaSamw 
2620c2a93d44Stimh 	/*
2621de8267e0Stimh 	 * If we're normalizing, names must always be valid UTF-8 strings.
2622c2a93d44Stimh 	 */
2623de8267e0Stimh 	if (norm)
2624de8267e0Stimh 		u8 = 1;
2625de8267e0Stimh 	if (u8 == ZFS_PROP_UNDEFINED)
2626de8267e0Stimh 		VERIFY(zfs_get_zplprop(os, ZFS_PROP_UTF8ONLY, &u8) == 0);
2627de8267e0Stimh 	VERIFY(nvlist_add_uint64(zplprops,
2628de8267e0Stimh 	    zfs_prop_to_name(ZFS_PROP_UTF8ONLY), u8) == 0);
2629de8267e0Stimh 
2630de8267e0Stimh 	if (sense == ZFS_PROP_UNDEFINED)
2631de8267e0Stimh 		VERIFY(zfs_get_zplprop(os, ZFS_PROP_CASE, &sense) == 0);
2632de8267e0Stimh 	VERIFY(nvlist_add_uint64(zplprops,
2633de8267e0Stimh 	    zfs_prop_to_name(ZFS_PROP_CASE), sense) == 0);
2634c2a93d44Stimh 
2635ab04eb8eStimh 	if (is_ci)
2636ab04eb8eStimh 		*is_ci = (sense == ZFS_CASE_INSENSITIVE);
2637ab04eb8eStimh 
2638da6c28aaSamw 	return (0);
2639fa9e4066Sahrens }
2640fa9e4066Sahrens 
26410a48a24eStimh static int
26420a48a24eStimh zfs_fill_zplprops(const char *dataset, nvlist_t *createprops,
26430a48a24eStimh     nvlist_t *zplprops, boolean_t *is_ci)
26440a48a24eStimh {
26450a586ceaSMark Shellenbaum 	boolean_t fuids_ok, sa_ok;
26460a48a24eStimh 	uint64_t zplver = ZPL_VERSION;
26470a48a24eStimh 	objset_t *os = NULL;
26480a48a24eStimh 	char parentname[MAXNAMELEN];
26490a48a24eStimh 	char *cp;
26500a586ceaSMark Shellenbaum 	spa_t *spa;
26510a586ceaSMark Shellenbaum 	uint64_t spa_vers;
26520a48a24eStimh 	int error;
26530a48a24eStimh 
26540a48a24eStimh 	(void) strlcpy(parentname, dataset, sizeof (parentname));
26550a48a24eStimh 	cp = strrchr(parentname, '/');
26560a48a24eStimh 	ASSERT(cp != NULL);
26570a48a24eStimh 	cp[0] = '\0';
26580a48a24eStimh 
26590a586ceaSMark Shellenbaum 	if ((error = spa_open(dataset, &spa, FTAG)) != 0)
26600a586ceaSMark Shellenbaum 		return (error);
26610a586ceaSMark Shellenbaum 
26620a586ceaSMark Shellenbaum 	spa_vers = spa_version(spa);
26630a586ceaSMark Shellenbaum 	spa_close(spa, FTAG);
26640a586ceaSMark Shellenbaum 
26650a586ceaSMark Shellenbaum 	zplver = zfs_zpl_version_map(spa_vers);
26660a586ceaSMark Shellenbaum 	fuids_ok = (zplver >= ZPL_VERSION_FUID);
26670a586ceaSMark Shellenbaum 	sa_ok = (zplver >= ZPL_VERSION_SA);
26680a48a24eStimh 
26690a48a24eStimh 	/*
26700a48a24eStimh 	 * Open parent object set so we can inherit zplprop values.
26710a48a24eStimh 	 */
2672503ad85cSMatthew Ahrens 	if ((error = dmu_objset_hold(parentname, FTAG, &os)) != 0)
26730a48a24eStimh 		return (error);
26740a48a24eStimh 
26750a586ceaSMark Shellenbaum 	error = zfs_fill_zplprops_impl(os, zplver, fuids_ok, sa_ok, createprops,
26760a48a24eStimh 	    zplprops, is_ci);
2677503ad85cSMatthew Ahrens 	dmu_objset_rele(os, FTAG);
26780a48a24eStimh 	return (error);
26790a48a24eStimh }
26800a48a24eStimh 
26810a48a24eStimh static int
26820a48a24eStimh zfs_fill_zplprops_root(uint64_t spa_vers, nvlist_t *createprops,
26830a48a24eStimh     nvlist_t *zplprops, boolean_t *is_ci)
26840a48a24eStimh {
26850a586ceaSMark Shellenbaum 	boolean_t fuids_ok;
26860a586ceaSMark Shellenbaum 	boolean_t sa_ok;
26870a48a24eStimh 	uint64_t zplver = ZPL_VERSION;
26880a48a24eStimh 	int error;
26890a48a24eStimh 
26900a586ceaSMark Shellenbaum 	zplver = zfs_zpl_version_map(spa_vers);
26910a586ceaSMark Shellenbaum 	fuids_ok = (zplver >= ZPL_VERSION_FUID);
26920a586ceaSMark Shellenbaum 	sa_ok = (zplver >= ZPL_VERSION_SA);
26930a48a24eStimh 
26940a586ceaSMark Shellenbaum 	error = zfs_fill_zplprops_impl(NULL, zplver, fuids_ok, sa_ok,
26950a586ceaSMark Shellenbaum 	    createprops, zplprops, is_ci);
26960a48a24eStimh 	return (error);
26970a48a24eStimh }
26980a48a24eStimh 
26993cb34c60Sahrens /*
27003cb34c60Sahrens  * inputs:
27013cb34c60Sahrens  * zc_objset_type	type of objset to create (fs vs zvol)
27023cb34c60Sahrens  * zc_name		name of new objset
27033cb34c60Sahrens  * zc_value		name of snapshot to clone from (may be empty)
27043cb34c60Sahrens  * zc_nvlist_src{_size}	nvlist of properties to apply
27053cb34c60Sahrens  *
2706de8267e0Stimh  * outputs: none
27073cb34c60Sahrens  */
2708fa9e4066Sahrens static int
2709fa9e4066Sahrens zfs_ioc_create(zfs_cmd_t *zc)
2710fa9e4066Sahrens {
2711fa9e4066Sahrens 	objset_t *clone;
2712fa9e4066Sahrens 	int error = 0;
2713da6c28aaSamw 	zfs_creat_t zct;
2714ecd6cf80Smarks 	nvlist_t *nvprops = NULL;
2715ecd6cf80Smarks 	void (*cbfunc)(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx);
2716fa9e4066Sahrens 	dmu_objset_type_t type = zc->zc_objset_type;
2717fa9e4066Sahrens 
2718fa9e4066Sahrens 	switch (type) {
2719fa9e4066Sahrens 
2720fa9e4066Sahrens 	case DMU_OST_ZFS:
2721fa9e4066Sahrens 		cbfunc = zfs_create_cb;
2722fa9e4066Sahrens 		break;
2723fa9e4066Sahrens 
2724fa9e4066Sahrens 	case DMU_OST_ZVOL:
2725fa9e4066Sahrens 		cbfunc = zvol_create_cb;
2726fa9e4066Sahrens 		break;
2727fa9e4066Sahrens 
2728fa9e4066Sahrens 	default:
27291d452cf5Sahrens 		cbfunc = NULL;
2730e7cbe64fSgw 		break;
2731fa9e4066Sahrens 	}
2732f18faf3fSek 	if (strchr(zc->zc_name, '@') ||
2733f18faf3fSek 	    strchr(zc->zc_name, '%'))
27341d452cf5Sahrens 		return (EINVAL);
2735fa9e4066Sahrens 
2736e9dbad6fSeschrock 	if (zc->zc_nvlist_src != NULL &&
2737990b4856Slling 	    (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
2738478ed9adSEric Taylor 	    zc->zc_iflags, &nvprops)) != 0)
2739e9dbad6fSeschrock 		return (error);
2740e9dbad6fSeschrock 
2741de8267e0Stimh 	zct.zct_zplprops = NULL;
2742da6c28aaSamw 	zct.zct_props = nvprops;
2743da6c28aaSamw 
2744e9dbad6fSeschrock 	if (zc->zc_value[0] != '\0') {
2745fa9e4066Sahrens 		/*
2746fa9e4066Sahrens 		 * We're creating a clone of an existing snapshot.
2747fa9e4066Sahrens 		 */
2748e9dbad6fSeschrock 		zc->zc_value[sizeof (zc->zc_value) - 1] = '\0';
2749e9dbad6fSeschrock 		if (dataset_namecheck(zc->zc_value, NULL, NULL) != 0) {
2750ecd6cf80Smarks 			nvlist_free(nvprops);
2751fa9e4066Sahrens 			return (EINVAL);
2752e9dbad6fSeschrock 		}
2753fa9e4066Sahrens 
2754503ad85cSMatthew Ahrens 		error = dmu_objset_hold(zc->zc_value, FTAG, &clone);
2755e9dbad6fSeschrock 		if (error) {
2756ecd6cf80Smarks 			nvlist_free(nvprops);
2757fa9e4066Sahrens 			return (error);
2758e9dbad6fSeschrock 		}
2759ab04eb8eStimh 
2760ae46e4c7SMatthew Ahrens 		error = dmu_objset_clone(zc->zc_name, dmu_objset_ds(clone), 0);
2761503ad85cSMatthew Ahrens 		dmu_objset_rele(clone, FTAG);
2762da6c28aaSamw 		if (error) {
2763da6c28aaSamw 			nvlist_free(nvprops);
2764da6c28aaSamw 			return (error);
2765da6c28aaSamw 		}
2766fa9e4066Sahrens 	} else {
2767ab04eb8eStimh 		boolean_t is_insensitive = B_FALSE;
2768ab04eb8eStimh 
2769e9dbad6fSeschrock 		if (cbfunc == NULL) {
2770ecd6cf80Smarks 			nvlist_free(nvprops);
27711d452cf5Sahrens 			return (EINVAL);
2772e9dbad6fSeschrock 		}
27735c5460e9Seschrock 
2774e9dbad6fSeschrock 		if (type == DMU_OST_ZVOL) {
2775e9dbad6fSeschrock 			uint64_t volsize, volblocksize;
2776e9dbad6fSeschrock 
2777ecd6cf80Smarks 			if (nvprops == NULL ||
2778ecd6cf80Smarks 			    nvlist_lookup_uint64(nvprops,
2779e9dbad6fSeschrock 			    zfs_prop_to_name(ZFS_PROP_VOLSIZE),
2780e9dbad6fSeschrock 			    &volsize) != 0) {
2781ecd6cf80Smarks 				nvlist_free(nvprops);
2782e9dbad6fSeschrock 				return (EINVAL);
2783e9dbad6fSeschrock 			}
2784e9dbad6fSeschrock 
2785ecd6cf80Smarks 			if ((error = nvlist_lookup_uint64(nvprops,
2786e9dbad6fSeschrock 			    zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE),
2787e9dbad6fSeschrock 			    &volblocksize)) != 0 && error != ENOENT) {
2788ecd6cf80Smarks 				nvlist_free(nvprops);
2789e9dbad6fSeschrock 				return (EINVAL);
2790e9dbad6fSeschrock 			}
2791e9dbad6fSeschrock 
2792e9dbad6fSeschrock 			if (error != 0)
2793e9dbad6fSeschrock 				volblocksize = zfs_prop_default_numeric(
2794e9dbad6fSeschrock 				    ZFS_PROP_VOLBLOCKSIZE);
2795e9dbad6fSeschrock 
2796e9dbad6fSeschrock 			if ((error = zvol_check_volblocksize(
2797e9dbad6fSeschrock 			    volblocksize)) != 0 ||
2798e9dbad6fSeschrock 			    (error = zvol_check_volsize(volsize,
2799e9dbad6fSeschrock 			    volblocksize)) != 0) {
2800ecd6cf80Smarks 				nvlist_free(nvprops);
28015c5460e9Seschrock 				return (error);
2802e9dbad6fSeschrock 			}
2803e7437265Sahrens 		} else if (type == DMU_OST_ZFS) {
2804da6c28aaSamw 			int error;
2805da6c28aaSamw 
2806da6c28aaSamw 			/*
2807da6c28aaSamw 			 * We have to have normalization and
2808da6c28aaSamw 			 * case-folding flags correct when we do the
2809da6c28aaSamw 			 * file system creation, so go figure them out
2810de8267e0Stimh 			 * now.
2811da6c28aaSamw 			 */
2812de8267e0Stimh 			VERIFY(nvlist_alloc(&zct.zct_zplprops,
2813de8267e0Stimh 			    NV_UNIQUE_NAME, KM_SLEEP) == 0);
2814de8267e0Stimh 			error = zfs_fill_zplprops(zc->zc_name, nvprops,
28150a48a24eStimh 			    zct.zct_zplprops, &is_insensitive);
2816da6c28aaSamw 			if (error != 0) {
2817da6c28aaSamw 				nvlist_free(nvprops);
2818de8267e0Stimh 				nvlist_free(zct.zct_zplprops);
2819da6c28aaSamw 				return (error);
2820da6c28aaSamw 			}
2821da6c28aaSamw 		}
2822ae46e4c7SMatthew Ahrens 		error = dmu_objset_create(zc->zc_name, type,
2823ab04eb8eStimh 		    is_insensitive ? DS_FLAG_CI_DATASET : 0, cbfunc, &zct);
2824de8267e0Stimh 		nvlist_free(zct.zct_zplprops);
2825fa9e4066Sahrens 	}
2826e9dbad6fSeschrock 
2827e9dbad6fSeschrock 	/*
2828e9dbad6fSeschrock 	 * It would be nice to do this atomically.
2829e9dbad6fSeschrock 	 */
2830e9dbad6fSeschrock 	if (error == 0) {
283192241e0bSTom Erickson 		error = zfs_set_prop_nvlist(zc->zc_name, ZPROP_SRC_LOCAL,
283292241e0bSTom Erickson 		    nvprops, NULL);
283392241e0bSTom Erickson 		if (error != 0)
2834842727c2SChris Kirby 			(void) dmu_objset_destroy(zc->zc_name, B_FALSE);
2835e9dbad6fSeschrock 	}
2836ecd6cf80Smarks 	nvlist_free(nvprops);
2837fa9e4066Sahrens 	return (error);
2838fa9e4066Sahrens }
2839fa9e4066Sahrens 
28403cb34c60Sahrens /*
28413cb34c60Sahrens  * inputs:
28423cb34c60Sahrens  * zc_name	name of filesystem
28433cb34c60Sahrens  * zc_value	short name of snapshot
28443cb34c60Sahrens  * zc_cookie	recursive flag
284514843421SMatthew Ahrens  * zc_nvlist_src[_size] property list
28463cb34c60Sahrens  *
2847681d9761SEric Taylor  * outputs:
2848681d9761SEric Taylor  * zc_value	short snapname (i.e. part after the '@')
28493cb34c60Sahrens  */
2850fa9e4066Sahrens static int
28511d452cf5Sahrens zfs_ioc_snapshot(zfs_cmd_t *zc)
2852fa9e4066Sahrens {
2853bb0ade09Sahrens 	nvlist_t *nvprops = NULL;
2854bb0ade09Sahrens 	int error;
2855bb0ade09Sahrens 	boolean_t recursive = zc->zc_cookie;
2856bb0ade09Sahrens 
2857e9dbad6fSeschrock 	if (snapshot_namecheck(zc->zc_value, NULL, NULL) != 0)
28581d452cf5Sahrens 		return (EINVAL);
2859bb0ade09Sahrens 
2860bb0ade09Sahrens 	if (zc->zc_nvlist_src != NULL &&
2861bb0ade09Sahrens 	    (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
2862478ed9adSEric Taylor 	    zc->zc_iflags, &nvprops)) != 0)
2863bb0ade09Sahrens 		return (error);
2864bb0ade09Sahrens 
2865ea2f5b9eSMatthew Ahrens 	error = zfs_check_userprops(zc->zc_name, nvprops);
2866ea2f5b9eSMatthew Ahrens 	if (error)
2867ea2f5b9eSMatthew Ahrens 		goto out;
2868bb0ade09Sahrens 
286992241e0bSTom Erickson 	if (!nvlist_empty(nvprops) &&
2870ea2f5b9eSMatthew Ahrens 	    zfs_earlier_version(zc->zc_name, SPA_VERSION_SNAP_PROPS)) {
2871ea2f5b9eSMatthew Ahrens 		error = ENOTSUP;
2872ea2f5b9eSMatthew Ahrens 		goto out;
2873bb0ade09Sahrens 	}
2874ea2f5b9eSMatthew Ahrens 
2875ea2f5b9eSMatthew Ahrens 	error = dmu_objset_snapshot(zc->zc_name, zc->zc_value,
2876ea2f5b9eSMatthew Ahrens 	    nvprops, recursive);
2877ea2f5b9eSMatthew Ahrens 
2878ea2f5b9eSMatthew Ahrens out:
2879bb0ade09Sahrens 	nvlist_free(nvprops);
2880bb0ade09Sahrens 	return (error);
28811d452cf5Sahrens }
2882fa9e4066Sahrens 
2883cdf5b4caSmmusante int
2884fd136879SMatthew Ahrens zfs_unmount_snap(const char *name, void *arg)
28851d452cf5Sahrens {
28860b69c2f0Sahrens 	vfs_t *vfsp = NULL;
28871d452cf5Sahrens 
2888745cd3c5Smaybee 	if (arg) {
2889745cd3c5Smaybee 		char *snapname = arg;
2890fd136879SMatthew Ahrens 		char *fullname = kmem_asprintf("%s@%s", name, snapname);
2891fd136879SMatthew Ahrens 		vfsp = zfs_get_vfs(fullname);
2892fd136879SMatthew Ahrens 		strfree(fullname);
28930b69c2f0Sahrens 	} else if (strchr(name, '@')) {
28941d452cf5Sahrens 		vfsp = zfs_get_vfs(name);
28951d452cf5Sahrens 	}
28961d452cf5Sahrens 
28971d452cf5Sahrens 	if (vfsp) {
2898fa9e4066Sahrens 		/*
28991d452cf5Sahrens 		 * Always force the unmount for snapshots.
2900fa9e4066Sahrens 		 */
29011d452cf5Sahrens 		int flag = MS_FORCE;
29021d452cf5Sahrens 		int err;
29031d452cf5Sahrens 
29041d452cf5Sahrens 		if ((err = vn_vfswlock(vfsp->vfs_vnodecovered)) != 0) {
2905fa9e4066Sahrens 			VFS_RELE(vfsp);
29061d452cf5Sahrens 			return (err);
2907fa9e4066Sahrens 		}
29081d452cf5Sahrens 		VFS_RELE(vfsp);
29091d452cf5Sahrens 		if ((err = dounmount(vfsp, flag, kcred)) != 0)
29101d452cf5Sahrens 			return (err);
29111d452cf5Sahrens 	}
29121d452cf5Sahrens 	return (0);
29131d452cf5Sahrens }
29141d452cf5Sahrens 
29153cb34c60Sahrens /*
29163cb34c60Sahrens  * inputs:
2917842727c2SChris Kirby  * zc_name		name of filesystem
2918842727c2SChris Kirby  * zc_value		short name of snapshot
2919842727c2SChris Kirby  * zc_defer_destroy	mark for deferred destroy
29203cb34c60Sahrens  *
29213cb34c60Sahrens  * outputs:	none
29223cb34c60Sahrens  */
29231d452cf5Sahrens static int
29241d452cf5Sahrens zfs_ioc_destroy_snaps(zfs_cmd_t *zc)
29251d452cf5Sahrens {
29261d452cf5Sahrens 	int err;
29271d452cf5Sahrens 
2928e9dbad6fSeschrock 	if (snapshot_namecheck(zc->zc_value, NULL, NULL) != 0)
29291d452cf5Sahrens 		return (EINVAL);
29301d452cf5Sahrens 	err = dmu_objset_find(zc->zc_name,
2931e9dbad6fSeschrock 	    zfs_unmount_snap, zc->zc_value, DS_FIND_CHILDREN);
29321d452cf5Sahrens 	if (err)
29331d452cf5Sahrens 		return (err);
2934842727c2SChris Kirby 	return (dmu_snapshots_destroy(zc->zc_name, zc->zc_value,
2935842727c2SChris Kirby 	    zc->zc_defer_destroy));
29361d452cf5Sahrens }
29371d452cf5Sahrens 
29383cb34c60Sahrens /*
29393cb34c60Sahrens  * inputs:
29403cb34c60Sahrens  * zc_name		name of dataset to destroy
29413cb34c60Sahrens  * zc_objset_type	type of objset
2942842727c2SChris Kirby  * zc_defer_destroy	mark for deferred destroy
29433cb34c60Sahrens  *
29443cb34c60Sahrens  * outputs:		none
29453cb34c60Sahrens  */
29461d452cf5Sahrens static int
29471d452cf5Sahrens zfs_ioc_destroy(zfs_cmd_t *zc)
29481d452cf5Sahrens {
2949681d9761SEric Taylor 	int err;
29501d452cf5Sahrens 	if (strchr(zc->zc_name, '@') && zc->zc_objset_type == DMU_OST_ZFS) {
2951681d9761SEric Taylor 		err = zfs_unmount_snap(zc->zc_name, NULL);
29521d452cf5Sahrens 		if (err)
29531d452cf5Sahrens 			return (err);
2954fa9e4066Sahrens 	}
2955fa9e4066Sahrens 
2956681d9761SEric Taylor 	err = dmu_objset_destroy(zc->zc_name, zc->zc_defer_destroy);
2957681d9761SEric Taylor 	if (zc->zc_objset_type == DMU_OST_ZVOL && err == 0)
29585c987a37SChris Kirby 		(void) zvol_remove_minor(zc->zc_name);
2959681d9761SEric Taylor 	return (err);
2960fa9e4066Sahrens }
2961fa9e4066Sahrens 
29623cb34c60Sahrens /*
29633cb34c60Sahrens  * inputs:
29644ccbb6e7Sahrens  * zc_name	name of dataset to rollback (to most recent snapshot)
29653cb34c60Sahrens  *
29663cb34c60Sahrens  * outputs:	none
29673cb34c60Sahrens  */
2968fa9e4066Sahrens static int
2969fa9e4066Sahrens zfs_ioc_rollback(zfs_cmd_t *zc)
2970fa9e4066Sahrens {
2971ae46e4c7SMatthew Ahrens 	dsl_dataset_t *ds, *clone;
29724ccbb6e7Sahrens 	int error;
2973ae46e4c7SMatthew Ahrens 	zfsvfs_t *zfsvfs;
2974ae46e4c7SMatthew Ahrens 	char *clone_name;
2975ae46e4c7SMatthew Ahrens 
2976ae46e4c7SMatthew Ahrens 	error = dsl_dataset_hold(zc->zc_name, FTAG, &ds);
2977ae46e4c7SMatthew Ahrens 	if (error)
2978ae46e4c7SMatthew Ahrens 		return (error);
2979ae46e4c7SMatthew Ahrens 
2980ae46e4c7SMatthew Ahrens 	/* must not be a snapshot */
2981ae46e4c7SMatthew Ahrens 	if (dsl_dataset_is_snapshot(ds)) {
2982ae46e4c7SMatthew Ahrens 		dsl_dataset_rele(ds, FTAG);
2983ae46e4c7SMatthew Ahrens 		return (EINVAL);
2984ae46e4c7SMatthew Ahrens 	}
2985ae46e4c7SMatthew Ahrens 
2986ae46e4c7SMatthew Ahrens 	/* must have a most recent snapshot */
2987ae46e4c7SMatthew Ahrens 	if (ds->ds_phys->ds_prev_snap_txg < TXG_INITIAL) {
2988ae46e4c7SMatthew Ahrens 		dsl_dataset_rele(ds, FTAG);
2989ae46e4c7SMatthew Ahrens 		return (EINVAL);
2990ae46e4c7SMatthew Ahrens 	}
29914ccbb6e7Sahrens 
29924ccbb6e7Sahrens 	/*
2993ae46e4c7SMatthew Ahrens 	 * Create clone of most recent snapshot.
29944ccbb6e7Sahrens 	 */
2995ae46e4c7SMatthew Ahrens 	clone_name = kmem_asprintf("%s/%%rollback", zc->zc_name);
2996ae46e4c7SMatthew Ahrens 	error = dmu_objset_clone(clone_name, ds->ds_prev, DS_FLAG_INCONSISTENT);
29974ccbb6e7Sahrens 	if (error)
2998ae46e4c7SMatthew Ahrens 		goto out;
29994ccbb6e7Sahrens 
3000503ad85cSMatthew Ahrens 	error = dsl_dataset_own(clone_name, B_TRUE, FTAG, &clone);
3001ae46e4c7SMatthew Ahrens 	if (error)
3002ae46e4c7SMatthew Ahrens 		goto out;
3003ae46e4c7SMatthew Ahrens 
3004ae46e4c7SMatthew Ahrens 	/*
3005ae46e4c7SMatthew Ahrens 	 * Do clone swap.
3006ae46e4c7SMatthew Ahrens 	 */
300714843421SMatthew Ahrens 	if (getzfsvfs(zc->zc_name, &zfsvfs) == 0) {
3008503ad85cSMatthew Ahrens 		error = zfs_suspend_fs(zfsvfs);
300947f263f4Sek 		if (error == 0) {
301047f263f4Sek 			int resume_err;
30114ccbb6e7Sahrens 
3012ae46e4c7SMatthew Ahrens 			if (dsl_dataset_tryown(ds, B_FALSE, FTAG)) {
3013ae46e4c7SMatthew Ahrens 				error = dsl_dataset_clone_swap(clone, ds,
3014ae46e4c7SMatthew Ahrens 				    B_TRUE);
3015ae46e4c7SMatthew Ahrens 				dsl_dataset_disown(ds, FTAG);
3016ae46e4c7SMatthew Ahrens 				ds = NULL;
3017ae46e4c7SMatthew Ahrens 			} else {
3018ae46e4c7SMatthew Ahrens 				error = EBUSY;
3019ae46e4c7SMatthew Ahrens 			}
3020503ad85cSMatthew Ahrens 			resume_err = zfs_resume_fs(zfsvfs, zc->zc_name);
302147f263f4Sek 			error = error ? error : resume_err;
302247f263f4Sek 		}
30234ccbb6e7Sahrens 		VFS_RELE(zfsvfs->z_vfs);
30244ccbb6e7Sahrens 	} else {
3025ae46e4c7SMatthew Ahrens 		if (dsl_dataset_tryown(ds, B_FALSE, FTAG)) {
3026ae46e4c7SMatthew Ahrens 			error = dsl_dataset_clone_swap(clone, ds, B_TRUE);
3027ae46e4c7SMatthew Ahrens 			dsl_dataset_disown(ds, FTAG);
3028ae46e4c7SMatthew Ahrens 			ds = NULL;
3029ae46e4c7SMatthew Ahrens 		} else {
3030ae46e4c7SMatthew Ahrens 			error = EBUSY;
3031ae46e4c7SMatthew Ahrens 		}
30324ccbb6e7Sahrens 	}
30334ccbb6e7Sahrens 
3034ae46e4c7SMatthew Ahrens 	/*
3035ae46e4c7SMatthew Ahrens 	 * Destroy clone (which also closes it).
3036ae46e4c7SMatthew Ahrens 	 */
3037ae46e4c7SMatthew Ahrens 	(void) dsl_dataset_destroy(clone, FTAG, B_FALSE);
3038ae46e4c7SMatthew Ahrens 
3039ae46e4c7SMatthew Ahrens out:
3040ae46e4c7SMatthew Ahrens 	strfree(clone_name);
3041ae46e4c7SMatthew Ahrens 	if (ds)
3042ae46e4c7SMatthew Ahrens 		dsl_dataset_rele(ds, FTAG);
30434ccbb6e7Sahrens 	return (error);
3044fa9e4066Sahrens }
3045fa9e4066Sahrens 
30463cb34c60Sahrens /*
30473cb34c60Sahrens  * inputs:
30483cb34c60Sahrens  * zc_name	old name of dataset
30493cb34c60Sahrens  * zc_value	new name of dataset
30503cb34c60Sahrens  * zc_cookie	recursive flag (only valid for snapshots)
30513cb34c60Sahrens  *
30523cb34c60Sahrens  * outputs:	none
30533cb34c60Sahrens  */
3054fa9e4066Sahrens static int
3055fa9e4066Sahrens zfs_ioc_rename(zfs_cmd_t *zc)
3056fa9e4066Sahrens {
30577f1f55eaSvb 	boolean_t recursive = zc->zc_cookie & 1;
3058cdf5b4caSmmusante 
3059e9dbad6fSeschrock 	zc->zc_value[sizeof (zc->zc_value) - 1] = '\0';
3060f18faf3fSek 	if (dataset_namecheck(zc->zc_value, NULL, NULL) != 0 ||
3061f18faf3fSek 	    strchr(zc->zc_value, '%'))
3062fa9e4066Sahrens 		return (EINVAL);
3063fa9e4066Sahrens 
3064cdf5b4caSmmusante 	/*
3065cdf5b4caSmmusante 	 * Unmount snapshot unless we're doing a recursive rename,
3066cdf5b4caSmmusante 	 * in which case the dataset code figures out which snapshots
3067cdf5b4caSmmusante 	 * to unmount.
3068cdf5b4caSmmusante 	 */
3069cdf5b4caSmmusante 	if (!recursive && strchr(zc->zc_name, '@') != NULL &&
3070fa9e4066Sahrens 	    zc->zc_objset_type == DMU_OST_ZFS) {
30711d452cf5Sahrens 		int err = zfs_unmount_snap(zc->zc_name, NULL);
30721d452cf5Sahrens 		if (err)
30731d452cf5Sahrens 			return (err);
3074fa9e4066Sahrens 	}
3075681d9761SEric Taylor 	if (zc->zc_objset_type == DMU_OST_ZVOL)
3076681d9761SEric Taylor 		(void) zvol_remove_minor(zc->zc_name);
3077cdf5b4caSmmusante 	return (dmu_objset_rename(zc->zc_name, zc->zc_value, recursive));
3078fa9e4066Sahrens }
3079fa9e4066Sahrens 
308092241e0bSTom Erickson static int
308192241e0bSTom Erickson zfs_check_settable(const char *dsname, nvpair_t *pair, cred_t *cr)
308292241e0bSTom Erickson {
308392241e0bSTom Erickson 	const char *propname = nvpair_name(pair);
308492241e0bSTom Erickson 	boolean_t issnap = (strchr(dsname, '@') != NULL);
308592241e0bSTom Erickson 	zfs_prop_t prop = zfs_name_to_prop(propname);
308692241e0bSTom Erickson 	uint64_t intval;
308792241e0bSTom Erickson 	int err;
308892241e0bSTom Erickson 
308992241e0bSTom Erickson 	if (prop == ZPROP_INVAL) {
309092241e0bSTom Erickson 		if (zfs_prop_user(propname)) {
309192241e0bSTom Erickson 			if (err = zfs_secpolicy_write_perms(dsname,
309292241e0bSTom Erickson 			    ZFS_DELEG_PERM_USERPROP, cr))
309392241e0bSTom Erickson 				return (err);
309492241e0bSTom Erickson 			return (0);
309592241e0bSTom Erickson 		}
309692241e0bSTom Erickson 
309792241e0bSTom Erickson 		if (!issnap && zfs_prop_userquota(propname)) {
309892241e0bSTom Erickson 			const char *perm = NULL;
309992241e0bSTom Erickson 			const char *uq_prefix =
310092241e0bSTom Erickson 			    zfs_userquota_prop_prefixes[ZFS_PROP_USERQUOTA];
310192241e0bSTom Erickson 			const char *gq_prefix =
310292241e0bSTom Erickson 			    zfs_userquota_prop_prefixes[ZFS_PROP_GROUPQUOTA];
310392241e0bSTom Erickson 
310492241e0bSTom Erickson 			if (strncmp(propname, uq_prefix,
310592241e0bSTom Erickson 			    strlen(uq_prefix)) == 0) {
310692241e0bSTom Erickson 				perm = ZFS_DELEG_PERM_USERQUOTA;
310792241e0bSTom Erickson 			} else if (strncmp(propname, gq_prefix,
310892241e0bSTom Erickson 			    strlen(gq_prefix)) == 0) {
310992241e0bSTom Erickson 				perm = ZFS_DELEG_PERM_GROUPQUOTA;
311092241e0bSTom Erickson 			} else {
311192241e0bSTom Erickson 				/* USERUSED and GROUPUSED are read-only */
311292241e0bSTom Erickson 				return (EINVAL);
311392241e0bSTom Erickson 			}
311492241e0bSTom Erickson 
311592241e0bSTom Erickson 			if (err = zfs_secpolicy_write_perms(dsname, perm, cr))
311692241e0bSTom Erickson 				return (err);
311792241e0bSTom Erickson 			return (0);
311892241e0bSTom Erickson 		}
311992241e0bSTom Erickson 
312092241e0bSTom Erickson 		return (EINVAL);
312192241e0bSTom Erickson 	}
312292241e0bSTom Erickson 
312392241e0bSTom Erickson 	if (issnap)
312492241e0bSTom Erickson 		return (EINVAL);
312592241e0bSTom Erickson 
312692241e0bSTom Erickson 	if (nvpair_type(pair) == DATA_TYPE_NVLIST) {
312792241e0bSTom Erickson 		/*
312892241e0bSTom Erickson 		 * dsl_prop_get_all_impl() returns properties in this
312992241e0bSTom Erickson 		 * format.
313092241e0bSTom Erickson 		 */
313192241e0bSTom Erickson 		nvlist_t *attrs;
313292241e0bSTom Erickson 		VERIFY(nvpair_value_nvlist(pair, &attrs) == 0);
313392241e0bSTom Erickson 		VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE,
313492241e0bSTom Erickson 		    &pair) == 0);
313592241e0bSTom Erickson 	}
313692241e0bSTom Erickson 
313792241e0bSTom Erickson 	/*
313892241e0bSTom Erickson 	 * Check that this value is valid for this pool version
313992241e0bSTom Erickson 	 */
314092241e0bSTom Erickson 	switch (prop) {
314192241e0bSTom Erickson 	case ZFS_PROP_COMPRESSION:
314292241e0bSTom Erickson 		/*
314392241e0bSTom Erickson 		 * If the user specified gzip compression, make sure
314492241e0bSTom Erickson 		 * the SPA supports it. We ignore any errors here since
314592241e0bSTom Erickson 		 * we'll catch them later.
314692241e0bSTom Erickson 		 */
314792241e0bSTom Erickson 		if (nvpair_type(pair) == DATA_TYPE_UINT64 &&
314892241e0bSTom Erickson 		    nvpair_value_uint64(pair, &intval) == 0) {
314992241e0bSTom Erickson 			if (intval >= ZIO_COMPRESS_GZIP_1 &&
315092241e0bSTom Erickson 			    intval <= ZIO_COMPRESS_GZIP_9 &&
315192241e0bSTom Erickson 			    zfs_earlier_version(dsname,
315292241e0bSTom Erickson 			    SPA_VERSION_GZIP_COMPRESSION)) {
315392241e0bSTom Erickson 				return (ENOTSUP);
315492241e0bSTom Erickson 			}
315592241e0bSTom Erickson 
315692241e0bSTom Erickson 			if (intval == ZIO_COMPRESS_ZLE &&
315792241e0bSTom Erickson 			    zfs_earlier_version(dsname,
315892241e0bSTom Erickson 			    SPA_VERSION_ZLE_COMPRESSION))
315992241e0bSTom Erickson 				return (ENOTSUP);
316092241e0bSTom Erickson 
316192241e0bSTom Erickson 			/*
316292241e0bSTom Erickson 			 * If this is a bootable dataset then
316392241e0bSTom Erickson 			 * verify that the compression algorithm
316492241e0bSTom Erickson 			 * is supported for booting. We must return
316592241e0bSTom Erickson 			 * something other than ENOTSUP since it
316692241e0bSTom Erickson 			 * implies a downrev pool version.
316792241e0bSTom Erickson 			 */
316892241e0bSTom Erickson 			if (zfs_is_bootfs(dsname) &&
316992241e0bSTom Erickson 			    !BOOTFS_COMPRESS_VALID(intval)) {
317092241e0bSTom Erickson 				return (ERANGE);
317192241e0bSTom Erickson 			}
317292241e0bSTom Erickson 		}
317392241e0bSTom Erickson 		break;
317492241e0bSTom Erickson 
317592241e0bSTom Erickson 	case ZFS_PROP_COPIES:
317692241e0bSTom Erickson 		if (zfs_earlier_version(dsname, SPA_VERSION_DITTO_BLOCKS))
317792241e0bSTom Erickson 			return (ENOTSUP);
317892241e0bSTom Erickson 		break;
317992241e0bSTom Erickson 
318092241e0bSTom Erickson 	case ZFS_PROP_DEDUP:
318192241e0bSTom Erickson 		if (zfs_earlier_version(dsname, SPA_VERSION_DEDUP))
318292241e0bSTom Erickson 			return (ENOTSUP);
318392241e0bSTom Erickson 		break;
318492241e0bSTom Erickson 
318592241e0bSTom Erickson 	case ZFS_PROP_SHARESMB:
318692241e0bSTom Erickson 		if (zpl_earlier_version(dsname, ZPL_VERSION_FUID))
318792241e0bSTom Erickson 			return (ENOTSUP);
318892241e0bSTom Erickson 		break;
318992241e0bSTom Erickson 
319092241e0bSTom Erickson 	case ZFS_PROP_ACLINHERIT:
319192241e0bSTom Erickson 		if (nvpair_type(pair) == DATA_TYPE_UINT64 &&
319292241e0bSTom Erickson 		    nvpair_value_uint64(pair, &intval) == 0) {
319392241e0bSTom Erickson 			if (intval == ZFS_ACL_PASSTHROUGH_X &&
319492241e0bSTom Erickson 			    zfs_earlier_version(dsname,
319592241e0bSTom Erickson 			    SPA_VERSION_PASSTHROUGH_X))
319692241e0bSTom Erickson 				return (ENOTSUP);
319792241e0bSTom Erickson 		}
319892241e0bSTom Erickson 		break;
319992241e0bSTom Erickson 	}
320092241e0bSTom Erickson 
320192241e0bSTom Erickson 	return (zfs_secpolicy_setprop(dsname, prop, pair, CRED()));
320292241e0bSTom Erickson }
320392241e0bSTom Erickson 
320492241e0bSTom Erickson /*
320592241e0bSTom Erickson  * Removes properties from the given props list that fail permission checks
320692241e0bSTom Erickson  * needed to clear them and to restore them in case of a receive error. For each
320792241e0bSTom Erickson  * property, make sure we have both set and inherit permissions.
320892241e0bSTom Erickson  *
320992241e0bSTom Erickson  * Returns the first error encountered if any permission checks fail. If the
321092241e0bSTom Erickson  * caller provides a non-NULL errlist, it also gives the complete list of names
321192241e0bSTom Erickson  * of all the properties that failed a permission check along with the
321292241e0bSTom Erickson  * corresponding error numbers. The caller is responsible for freeing the
321392241e0bSTom Erickson  * returned errlist.
321492241e0bSTom Erickson  *
321592241e0bSTom Erickson  * If every property checks out successfully, zero is returned and the list
321692241e0bSTom Erickson  * pointed at by errlist is NULL.
321792241e0bSTom Erickson  */
321892241e0bSTom Erickson static int
321992241e0bSTom Erickson zfs_check_clearable(char *dataset, nvlist_t *props, nvlist_t **errlist)
3220745cd3c5Smaybee {
3221745cd3c5Smaybee 	zfs_cmd_t *zc;
322292241e0bSTom Erickson 	nvpair_t *pair, *next_pair;
322392241e0bSTom Erickson 	nvlist_t *errors;
322492241e0bSTom Erickson 	int err, rv = 0;
3225745cd3c5Smaybee 
3226745cd3c5Smaybee 	if (props == NULL)
322792241e0bSTom Erickson 		return (0);
322892241e0bSTom Erickson 
322992241e0bSTom Erickson 	VERIFY(nvlist_alloc(&errors, NV_UNIQUE_NAME, KM_SLEEP) == 0);
323092241e0bSTom Erickson 
3231745cd3c5Smaybee 	zc = kmem_alloc(sizeof (zfs_cmd_t), KM_SLEEP);
3232745cd3c5Smaybee 	(void) strcpy(zc->zc_name, dataset);
323392241e0bSTom Erickson 	pair = nvlist_next_nvpair(props, NULL);
323492241e0bSTom Erickson 	while (pair != NULL) {
323592241e0bSTom Erickson 		next_pair = nvlist_next_nvpair(props, pair);
323692241e0bSTom Erickson 
323792241e0bSTom Erickson 		(void) strcpy(zc->zc_value, nvpair_name(pair));
323892241e0bSTom Erickson 		if ((err = zfs_check_settable(dataset, pair, CRED())) != 0 ||
323992241e0bSTom Erickson 		    (err = zfs_secpolicy_inherit(zc, CRED())) != 0) {
324092241e0bSTom Erickson 			VERIFY(nvlist_remove_nvpair(props, pair) == 0);
324192241e0bSTom Erickson 			VERIFY(nvlist_add_int32(errors,
324292241e0bSTom Erickson 			    zc->zc_value, err) == 0);
324392241e0bSTom Erickson 		}
324492241e0bSTom Erickson 		pair = next_pair;
3245745cd3c5Smaybee 	}
3246745cd3c5Smaybee 	kmem_free(zc, sizeof (zfs_cmd_t));
324792241e0bSTom Erickson 
324892241e0bSTom Erickson 	if ((pair = nvlist_next_nvpair(errors, NULL)) == NULL) {
324992241e0bSTom Erickson 		nvlist_free(errors);
325092241e0bSTom Erickson 		errors = NULL;
325192241e0bSTom Erickson 	} else {
325292241e0bSTom Erickson 		VERIFY(nvpair_value_int32(pair, &rv) == 0);
325392241e0bSTom Erickson 	}
325492241e0bSTom Erickson 
325592241e0bSTom Erickson 	if (errlist == NULL)
325692241e0bSTom Erickson 		nvlist_free(errors);
325792241e0bSTom Erickson 	else
325892241e0bSTom Erickson 		*errlist = errors;
325992241e0bSTom Erickson 
326092241e0bSTom Erickson 	return (rv);
326192241e0bSTom Erickson }
326292241e0bSTom Erickson 
326392241e0bSTom Erickson static boolean_t
326492241e0bSTom Erickson propval_equals(nvpair_t *p1, nvpair_t *p2)
326592241e0bSTom Erickson {
326692241e0bSTom Erickson 	if (nvpair_type(p1) == DATA_TYPE_NVLIST) {
326792241e0bSTom Erickson 		/* dsl_prop_get_all_impl() format */
326892241e0bSTom Erickson 		nvlist_t *attrs;
326992241e0bSTom Erickson 		VERIFY(nvpair_value_nvlist(p1, &attrs) == 0);
327092241e0bSTom Erickson 		VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE,
327192241e0bSTom Erickson 		    &p1) == 0);
327292241e0bSTom Erickson 	}
327392241e0bSTom Erickson 
327492241e0bSTom Erickson 	if (nvpair_type(p2) == DATA_TYPE_NVLIST) {
327592241e0bSTom Erickson 		nvlist_t *attrs;
327692241e0bSTom Erickson 		VERIFY(nvpair_value_nvlist(p2, &attrs) == 0);
327792241e0bSTom Erickson 		VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE,
327892241e0bSTom Erickson 		    &p2) == 0);
327992241e0bSTom Erickson 	}
328092241e0bSTom Erickson 
328192241e0bSTom Erickson 	if (nvpair_type(p1) != nvpair_type(p2))
328292241e0bSTom Erickson 		return (B_FALSE);
328392241e0bSTom Erickson 
328492241e0bSTom Erickson 	if (nvpair_type(p1) == DATA_TYPE_STRING) {
328592241e0bSTom Erickson 		char *valstr1, *valstr2;
328692241e0bSTom Erickson 
328792241e0bSTom Erickson 		VERIFY(nvpair_value_string(p1, (char **)&valstr1) == 0);
328892241e0bSTom Erickson 		VERIFY(nvpair_value_string(p2, (char **)&valstr2) == 0);
328992241e0bSTom Erickson 		return (strcmp(valstr1, valstr2) == 0);
329092241e0bSTom Erickson 	} else {
329192241e0bSTom Erickson 		uint64_t intval1, intval2;
329292241e0bSTom Erickson 
329392241e0bSTom Erickson 		VERIFY(nvpair_value_uint64(p1, &intval1) == 0);
329492241e0bSTom Erickson 		VERIFY(nvpair_value_uint64(p2, &intval2) == 0);
329592241e0bSTom Erickson 		return (intval1 == intval2);
329692241e0bSTom Erickson 	}
3297745cd3c5Smaybee }
3298745cd3c5Smaybee 
329992241e0bSTom Erickson /*
330092241e0bSTom Erickson  * Remove properties from props if they are not going to change (as determined
330192241e0bSTom Erickson  * by comparison with origprops). Remove them from origprops as well, since we
330292241e0bSTom Erickson  * do not need to clear or restore properties that won't change.
330392241e0bSTom Erickson  */
330492241e0bSTom Erickson static void
330592241e0bSTom Erickson props_reduce(nvlist_t *props, nvlist_t *origprops)
330692241e0bSTom Erickson {
330792241e0bSTom Erickson 	nvpair_t *pair, *next_pair;
330892241e0bSTom Erickson 
330992241e0bSTom Erickson 	if (origprops == NULL)
331092241e0bSTom Erickson 		return; /* all props need to be received */
331192241e0bSTom Erickson 
331292241e0bSTom Erickson 	pair = nvlist_next_nvpair(props, NULL);
331392241e0bSTom Erickson 	while (pair != NULL) {
331492241e0bSTom Erickson 		const char *propname = nvpair_name(pair);
331592241e0bSTom Erickson 		nvpair_t *match;
331692241e0bSTom Erickson 
331792241e0bSTom Erickson 		next_pair = nvlist_next_nvpair(props, pair);
331892241e0bSTom Erickson 
331992241e0bSTom Erickson 		if ((nvlist_lookup_nvpair(origprops, propname,
332092241e0bSTom Erickson 		    &match) != 0) || !propval_equals(pair, match))
332192241e0bSTom Erickson 			goto next; /* need to set received value */
332292241e0bSTom Erickson 
332392241e0bSTom Erickson 		/* don't clear the existing received value */
332492241e0bSTom Erickson 		(void) nvlist_remove_nvpair(origprops, match);
332592241e0bSTom Erickson 		/* don't bother receiving the property */
332692241e0bSTom Erickson 		(void) nvlist_remove_nvpair(props, pair);
332792241e0bSTom Erickson next:
332892241e0bSTom Erickson 		pair = next_pair;
332992241e0bSTom Erickson 	}
333092241e0bSTom Erickson }
333192241e0bSTom Erickson 
333292241e0bSTom Erickson #ifdef	DEBUG
333392241e0bSTom Erickson static boolean_t zfs_ioc_recv_inject_err;
333492241e0bSTom Erickson #endif
333592241e0bSTom Erickson 
33363cb34c60Sahrens /*
33373cb34c60Sahrens  * inputs:
33383cb34c60Sahrens  * zc_name		name of containing filesystem
33393cb34c60Sahrens  * zc_nvlist_src{_size}	nvlist of properties to apply
33403cb34c60Sahrens  * zc_value		name of snapshot to create
33413cb34c60Sahrens  * zc_string		name of clone origin (if DRR_FLAG_CLONE)
33423cb34c60Sahrens  * zc_cookie		file descriptor to recv from
33433cb34c60Sahrens  * zc_begin_record	the BEGIN record of the stream (not byteswapped)
33443cb34c60Sahrens  * zc_guid		force flag
33453cb34c60Sahrens  *
33463cb34c60Sahrens  * outputs:
33473cb34c60Sahrens  * zc_cookie		number of bytes read
334892241e0bSTom Erickson  * zc_nvlist_dst{_size} error for each unapplied received property
334992241e0bSTom Erickson  * zc_obj		zprop_errflags_t
33503cb34c60Sahrens  */
3351fa9e4066Sahrens static int
33523cb34c60Sahrens zfs_ioc_recv(zfs_cmd_t *zc)
3353fa9e4066Sahrens {
3354fa9e4066Sahrens 	file_t *fp;
3355f18faf3fSek 	objset_t *os;
33563cb34c60Sahrens 	dmu_recv_cookie_t drc;
3357f18faf3fSek 	boolean_t force = (boolean_t)zc->zc_guid;
335892241e0bSTom Erickson 	int fd;
335992241e0bSTom Erickson 	int error = 0;
336092241e0bSTom Erickson 	int props_error = 0;
336192241e0bSTom Erickson 	nvlist_t *errors;
33623cb34c60Sahrens 	offset_t off;
336392241e0bSTom Erickson 	nvlist_t *props = NULL; /* sent properties */
336492241e0bSTom Erickson 	nvlist_t *origprops = NULL; /* existing properties */
33653cb34c60Sahrens 	objset_t *origin = NULL;
33663cb34c60Sahrens 	char *tosnap;
33673cb34c60Sahrens 	char tofs[ZFS_MAXNAMELEN];
336892241e0bSTom Erickson 	boolean_t first_recvd_props = B_FALSE;
3369fa9e4066Sahrens 
33703ccfa83cSahrens 	if (dataset_namecheck(zc->zc_value, NULL, NULL) != 0 ||
3371f18faf3fSek 	    strchr(zc->zc_value, '@') == NULL ||
3372f18faf3fSek 	    strchr(zc->zc_value, '%'))
33733ccfa83cSahrens 		return (EINVAL);
33743ccfa83cSahrens 
33753cb34c60Sahrens 	(void) strcpy(tofs, zc->zc_value);
33763cb34c60Sahrens 	tosnap = strchr(tofs, '@');
337792241e0bSTom Erickson 	*tosnap++ = '\0';
33783cb34c60Sahrens 
33793cb34c60Sahrens 	if (zc->zc_nvlist_src != NULL &&
33803cb34c60Sahrens 	    (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
3381478ed9adSEric Taylor 	    zc->zc_iflags, &props)) != 0)
33823cb34c60Sahrens 		return (error);
33833cb34c60Sahrens 
3384fa9e4066Sahrens 	fd = zc->zc_cookie;
3385fa9e4066Sahrens 	fp = getf(fd);
33863cb34c60Sahrens 	if (fp == NULL) {
33873cb34c60Sahrens 		nvlist_free(props);
3388fa9e4066Sahrens 		return (EBADF);
33893cb34c60Sahrens 	}
3390f18faf3fSek 
339192241e0bSTom Erickson 	VERIFY(nvlist_alloc(&errors, NV_UNIQUE_NAME, KM_SLEEP) == 0);
339292241e0bSTom Erickson 
3393503ad85cSMatthew Ahrens 	if (props && dmu_objset_hold(tofs, FTAG, &os) == 0) {
339492241e0bSTom Erickson 		if ((spa_version(os->os_spa) >= SPA_VERSION_RECVD_PROPS) &&
339592241e0bSTom Erickson 		    !dsl_prop_get_hasrecvd(os)) {
339692241e0bSTom Erickson 			first_recvd_props = B_TRUE;
339792241e0bSTom Erickson 		}
339892241e0bSTom Erickson 
3399745cd3c5Smaybee 		/*
340092241e0bSTom Erickson 		 * If new received properties are supplied, they are to
340192241e0bSTom Erickson 		 * completely replace the existing received properties, so stash
340292241e0bSTom Erickson 		 * away the existing ones.
3403745cd3c5Smaybee 		 */
340492241e0bSTom Erickson 		if (dsl_prop_get_received(os, &origprops) == 0) {
340592241e0bSTom Erickson 			nvlist_t *errlist = NULL;
340692241e0bSTom Erickson 			/*
340792241e0bSTom Erickson 			 * Don't bother writing a property if its value won't
340892241e0bSTom Erickson 			 * change (and avoid the unnecessary security checks).
340992241e0bSTom Erickson 			 *
341092241e0bSTom Erickson 			 * The first receive after SPA_VERSION_RECVD_PROPS is a
341192241e0bSTom Erickson 			 * special case where we blow away all local properties
341292241e0bSTom Erickson 			 * regardless.
341392241e0bSTom Erickson 			 */
341492241e0bSTom Erickson 			if (!first_recvd_props)
341592241e0bSTom Erickson 				props_reduce(props, origprops);
341692241e0bSTom Erickson 			if (zfs_check_clearable(tofs, origprops,
341792241e0bSTom Erickson 			    &errlist) != 0)
341892241e0bSTom Erickson 				(void) nvlist_merge(errors, errlist, 0);
341992241e0bSTom Erickson 			nvlist_free(errlist);
342092241e0bSTom Erickson 		}
3421745cd3c5Smaybee 
3422503ad85cSMatthew Ahrens 		dmu_objset_rele(os, FTAG);
3423f18faf3fSek 	}
3424f18faf3fSek 
34253cb34c60Sahrens 	if (zc->zc_string[0]) {
3426503ad85cSMatthew Ahrens 		error = dmu_objset_hold(zc->zc_string, FTAG, &origin);
3427745cd3c5Smaybee 		if (error)
3428745cd3c5Smaybee 			goto out;
34293cb34c60Sahrens 	}
34303cb34c60Sahrens 
34319e69d7d0SLori Alt 	error = dmu_recv_begin(tofs, tosnap, zc->zc_top_ds,
34329e69d7d0SLori Alt 	    &zc->zc_begin_record, force, origin, &drc);
34333cb34c60Sahrens 	if (origin)
3434503ad85cSMatthew Ahrens 		dmu_objset_rele(origin, FTAG);
3435745cd3c5Smaybee 	if (error)
3436745cd3c5Smaybee 		goto out;
3437f18faf3fSek 
3438f18faf3fSek 	/*
343992241e0bSTom Erickson 	 * Set properties before we receive the stream so that they are applied
344092241e0bSTom Erickson 	 * to the new data. Note that we must call dmu_recv_stream() if
344192241e0bSTom Erickson 	 * dmu_recv_begin() succeeds.
3442f18faf3fSek 	 */
34433cb34c60Sahrens 	if (props) {
344492241e0bSTom Erickson 		nvlist_t *errlist;
344592241e0bSTom Erickson 
344692241e0bSTom Erickson 		if (dmu_objset_from_ds(drc.drc_logical_ds, &os) == 0) {
344792241e0bSTom Erickson 			if (drc.drc_newfs) {
344892241e0bSTom Erickson 				if (spa_version(os->os_spa) >=
344992241e0bSTom Erickson 				    SPA_VERSION_RECVD_PROPS)
345092241e0bSTom Erickson 					first_recvd_props = B_TRUE;
345192241e0bSTom Erickson 			} else if (origprops != NULL) {
345292241e0bSTom Erickson 				if (clear_received_props(os, tofs, origprops,
345392241e0bSTom Erickson 				    first_recvd_props ? NULL : props) != 0)
345492241e0bSTom Erickson 					zc->zc_obj |= ZPROP_ERR_NOCLEAR;
345592241e0bSTom Erickson 			} else {
345692241e0bSTom Erickson 				zc->zc_obj |= ZPROP_ERR_NOCLEAR;
345792241e0bSTom Erickson 			}
345892241e0bSTom Erickson 			dsl_prop_set_hasrecvd(os);
345992241e0bSTom Erickson 		} else if (!drc.drc_newfs) {
346092241e0bSTom Erickson 			zc->zc_obj |= ZPROP_ERR_NOCLEAR;
346192241e0bSTom Erickson 		}
346292241e0bSTom Erickson 
346392241e0bSTom Erickson 		(void) zfs_set_prop_nvlist(tofs, ZPROP_SRC_RECEIVED,
346492241e0bSTom Erickson 		    props, &errlist);
346592241e0bSTom Erickson 		(void) nvlist_merge(errors, errlist, 0);
346692241e0bSTom Erickson 		nvlist_free(errlist);
346792241e0bSTom Erickson 	}
346892241e0bSTom Erickson 
346992241e0bSTom Erickson 	if (fit_error_list(zc, &errors) != 0 || put_nvlist(zc, errors) != 0) {
3470745cd3c5Smaybee 		/*
347192241e0bSTom Erickson 		 * Caller made zc->zc_nvlist_dst less than the minimum expected
347292241e0bSTom Erickson 		 * size or supplied an invalid address.
3473745cd3c5Smaybee 		 */
347492241e0bSTom Erickson 		props_error = EINVAL;
34753cb34c60Sahrens 	}
34763cb34c60Sahrens 
34773cb34c60Sahrens 	off = fp->f_offset;
34783cb34c60Sahrens 	error = dmu_recv_stream(&drc, fp->f_vnode, &off);
3479a2eea2e1Sahrens 
3480f4b94bdeSMatthew Ahrens 	if (error == 0) {
3481f4b94bdeSMatthew Ahrens 		zfsvfs_t *zfsvfs = NULL;
3482745cd3c5Smaybee 
3483f4b94bdeSMatthew Ahrens 		if (getzfsvfs(tofs, &zfsvfs) == 0) {
3484f4b94bdeSMatthew Ahrens 			/* online recv */
3485f4b94bdeSMatthew Ahrens 			int end_err;
3486745cd3c5Smaybee 
3487503ad85cSMatthew Ahrens 			error = zfs_suspend_fs(zfsvfs);
3488f4b94bdeSMatthew Ahrens 			/*
3489f4b94bdeSMatthew Ahrens 			 * If the suspend fails, then the recv_end will
3490f4b94bdeSMatthew Ahrens 			 * likely also fail, and clean up after itself.
3491f4b94bdeSMatthew Ahrens 			 */
3492f4b94bdeSMatthew Ahrens 			end_err = dmu_recv_end(&drc);
34935c703fceSGeorge Wilson 			if (error == 0)
34945c703fceSGeorge Wilson 				error = zfs_resume_fs(zfsvfs, tofs);
3495f4b94bdeSMatthew Ahrens 			error = error ? error : end_err;
3496f4b94bdeSMatthew Ahrens 			VFS_RELE(zfsvfs->z_vfs);
3497745cd3c5Smaybee 		} else {
3498f4b94bdeSMatthew Ahrens 			error = dmu_recv_end(&drc);
34993cb34c60Sahrens 		}
350047f263f4Sek 	}
35013cb34c60Sahrens 
35023cb34c60Sahrens 	zc->zc_cookie = off - fp->f_offset;
35033cb34c60Sahrens 	if (VOP_SEEK(fp->f_vnode, fp->f_offset, &off, NULL) == 0)
35043cb34c60Sahrens 		fp->f_offset = off;
3505a2eea2e1Sahrens 
350692241e0bSTom Erickson #ifdef	DEBUG
350792241e0bSTom Erickson 	if (zfs_ioc_recv_inject_err) {
350892241e0bSTom Erickson 		zfs_ioc_recv_inject_err = B_FALSE;
350992241e0bSTom Erickson 		error = 1;
351092241e0bSTom Erickson 	}
351192241e0bSTom Erickson #endif
3512745cd3c5Smaybee 	/*
3513745cd3c5Smaybee 	 * On error, restore the original props.
3514745cd3c5Smaybee 	 */
3515745cd3c5Smaybee 	if (error && props) {
351692241e0bSTom Erickson 		if (dmu_objset_hold(tofs, FTAG, &os) == 0) {
351792241e0bSTom Erickson 			if (clear_received_props(os, tofs, props, NULL) != 0) {
351892241e0bSTom Erickson 				/*
351992241e0bSTom Erickson 				 * We failed to clear the received properties.
352092241e0bSTom Erickson 				 * Since we may have left a $recvd value on the
352192241e0bSTom Erickson 				 * system, we can't clear the $hasrecvd flag.
352292241e0bSTom Erickson 				 */
352392241e0bSTom Erickson 				zc->zc_obj |= ZPROP_ERR_NORESTORE;
352492241e0bSTom Erickson 			} else if (first_recvd_props) {
352592241e0bSTom Erickson 				dsl_prop_unset_hasrecvd(os);
352692241e0bSTom Erickson 			}
352792241e0bSTom Erickson 			dmu_objset_rele(os, FTAG);
352892241e0bSTom Erickson 		} else if (!drc.drc_newfs) {
352992241e0bSTom Erickson 			/* We failed to clear the received properties. */
353092241e0bSTom Erickson 			zc->zc_obj |= ZPROP_ERR_NORESTORE;
353192241e0bSTom Erickson 		}
353292241e0bSTom Erickson 
353392241e0bSTom Erickson 		if (origprops == NULL && !drc.drc_newfs) {
353492241e0bSTom Erickson 			/* We failed to stash the original properties. */
353592241e0bSTom Erickson 			zc->zc_obj |= ZPROP_ERR_NORESTORE;
353692241e0bSTom Erickson 		}
353792241e0bSTom Erickson 
353892241e0bSTom Erickson 		/*
353992241e0bSTom Erickson 		 * dsl_props_set() will not convert RECEIVED to LOCAL on or
354092241e0bSTom Erickson 		 * after SPA_VERSION_RECVD_PROPS, so we need to specify LOCAL
354192241e0bSTom Erickson 		 * explictly if we're restoring local properties cleared in the
354292241e0bSTom Erickson 		 * first new-style receive.
354392241e0bSTom Erickson 		 */
354492241e0bSTom Erickson 		if (origprops != NULL &&
354592241e0bSTom Erickson 		    zfs_set_prop_nvlist(tofs, (first_recvd_props ?
354692241e0bSTom Erickson 		    ZPROP_SRC_LOCAL : ZPROP_SRC_RECEIVED),
354792241e0bSTom Erickson 		    origprops, NULL) != 0) {
354892241e0bSTom Erickson 			/*
354992241e0bSTom Erickson 			 * We stashed the original properties but failed to
355092241e0bSTom Erickson 			 * restore them.
355192241e0bSTom Erickson 			 */
355292241e0bSTom Erickson 			zc->zc_obj |= ZPROP_ERR_NORESTORE;
355392241e0bSTom Erickson 		}
3554745cd3c5Smaybee 	}
3555745cd3c5Smaybee out:
3556745cd3c5Smaybee 	nvlist_free(props);
3557745cd3c5Smaybee 	nvlist_free(origprops);
355892241e0bSTom Erickson 	nvlist_free(errors);
3559fa9e4066Sahrens 	releasef(fd);
356092241e0bSTom Erickson 
356192241e0bSTom Erickson 	if (error == 0)
356292241e0bSTom Erickson 		error = props_error;
356392241e0bSTom Erickson 
3564fa9e4066Sahrens 	return (error);
3565fa9e4066Sahrens }
3566fa9e4066Sahrens 
35673cb34c60Sahrens /*
35683cb34c60Sahrens  * inputs:
35693cb34c60Sahrens  * zc_name	name of snapshot to send
35703cb34c60Sahrens  * zc_value	short name of incremental fromsnap (may be empty)
35713cb34c60Sahrens  * zc_cookie	file descriptor to send stream to
35723cb34c60Sahrens  * zc_obj	fromorigin flag (mutually exclusive with zc_value)
35733cb34c60Sahrens  *
35743cb34c60Sahrens  * outputs: none
35753cb34c60Sahrens  */
3576fa9e4066Sahrens static int
35773cb34c60Sahrens zfs_ioc_send(zfs_cmd_t *zc)
3578fa9e4066Sahrens {
3579fa9e4066Sahrens 	objset_t *fromsnap = NULL;
3580fa9e4066Sahrens 	objset_t *tosnap;
3581fa9e4066Sahrens 	file_t *fp;
3582fa9e4066Sahrens 	int error;
35833cb34c60Sahrens 	offset_t off;
3584fa9e4066Sahrens 
3585503ad85cSMatthew Ahrens 	error = dmu_objset_hold(zc->zc_name, FTAG, &tosnap);
3586fa9e4066Sahrens 	if (error)
3587fa9e4066Sahrens 		return (error);
3588fa9e4066Sahrens 
3589e9dbad6fSeschrock 	if (zc->zc_value[0] != '\0') {
35906a0f0066SEric Taylor 		char *buf;
3591a2eea2e1Sahrens 		char *cp;
3592a2eea2e1Sahrens 
35936a0f0066SEric Taylor 		buf = kmem_alloc(MAXPATHLEN, KM_SLEEP);
35946a0f0066SEric Taylor 		(void) strncpy(buf, zc->zc_name, MAXPATHLEN);
3595a2eea2e1Sahrens 		cp = strchr(buf, '@');
3596a2eea2e1Sahrens 		if (cp)
3597a2eea2e1Sahrens 			*(cp+1) = 0;
35986a0f0066SEric Taylor 		(void) strncat(buf, zc->zc_value, MAXPATHLEN);
3599503ad85cSMatthew Ahrens 		error = dmu_objset_hold(buf, FTAG, &fromsnap);
36006a0f0066SEric Taylor 		kmem_free(buf, MAXPATHLEN);
3601fa9e4066Sahrens 		if (error) {
3602503ad85cSMatthew Ahrens 			dmu_objset_rele(tosnap, FTAG);
3603fa9e4066Sahrens 			return (error);
3604fa9e4066Sahrens 		}
3605fa9e4066Sahrens 	}
3606fa9e4066Sahrens 
3607fa9e4066Sahrens 	fp = getf(zc->zc_cookie);
3608fa9e4066Sahrens 	if (fp == NULL) {
3609503ad85cSMatthew Ahrens 		dmu_objset_rele(tosnap, FTAG);
3610fa9e4066Sahrens 		if (fromsnap)
3611503ad85cSMatthew Ahrens 			dmu_objset_rele(fromsnap, FTAG);
3612fa9e4066Sahrens 		return (EBADF);
3613fa9e4066Sahrens 	}
3614fa9e4066Sahrens 
36153cb34c60Sahrens 	off = fp->f_offset;
36163cb34c60Sahrens 	error = dmu_sendbackup(tosnap, fromsnap, zc->zc_obj, fp->f_vnode, &off);
3617fa9e4066Sahrens 
36183cb34c60Sahrens 	if (VOP_SEEK(fp->f_vnode, fp->f_offset, &off, NULL) == 0)
36193cb34c60Sahrens 		fp->f_offset = off;
3620fa9e4066Sahrens 	releasef(zc->zc_cookie);
3621fa9e4066Sahrens 	if (fromsnap)
3622503ad85cSMatthew Ahrens 		dmu_objset_rele(fromsnap, FTAG);
3623503ad85cSMatthew Ahrens 	dmu_objset_rele(tosnap, FTAG);
3624fa9e4066Sahrens 	return (error);
3625fa9e4066Sahrens }
3626fa9e4066Sahrens 
3627ea8dc4b6Seschrock static int
3628ea8dc4b6Seschrock zfs_ioc_inject_fault(zfs_cmd_t *zc)
3629ea8dc4b6Seschrock {
3630ea8dc4b6Seschrock 	int id, error;
3631ea8dc4b6Seschrock 
3632ea8dc4b6Seschrock 	error = zio_inject_fault(zc->zc_name, (int)zc->zc_guid, &id,
3633ea8dc4b6Seschrock 	    &zc->zc_inject_record);
3634ea8dc4b6Seschrock 
3635ea8dc4b6Seschrock 	if (error == 0)
3636ea8dc4b6Seschrock 		zc->zc_guid = (uint64_t)id;
3637ea8dc4b6Seschrock 
3638ea8dc4b6Seschrock 	return (error);
3639ea8dc4b6Seschrock }
3640ea8dc4b6Seschrock 
3641ea8dc4b6Seschrock static int
3642ea8dc4b6Seschrock zfs_ioc_clear_fault(zfs_cmd_t *zc)
3643ea8dc4b6Seschrock {
3644ea8dc4b6Seschrock 	return (zio_clear_fault((int)zc->zc_guid));
3645ea8dc4b6Seschrock }
3646ea8dc4b6Seschrock 
3647ea8dc4b6Seschrock static int
3648ea8dc4b6Seschrock zfs_ioc_inject_list_next(zfs_cmd_t *zc)
3649ea8dc4b6Seschrock {
3650ea8dc4b6Seschrock 	int id = (int)zc->zc_guid;
3651ea8dc4b6Seschrock 	int error;
3652ea8dc4b6Seschrock 
3653ea8dc4b6Seschrock 	error = zio_inject_list_next(&id, zc->zc_name, sizeof (zc->zc_name),
3654ea8dc4b6Seschrock 	    &zc->zc_inject_record);
3655ea8dc4b6Seschrock 
3656ea8dc4b6Seschrock 	zc->zc_guid = id;
3657ea8dc4b6Seschrock 
3658ea8dc4b6Seschrock 	return (error);
3659ea8dc4b6Seschrock }
3660ea8dc4b6Seschrock 
3661ea8dc4b6Seschrock static int
3662ea8dc4b6Seschrock zfs_ioc_error_log(zfs_cmd_t *zc)
3663ea8dc4b6Seschrock {
3664ea8dc4b6Seschrock 	spa_t *spa;
3665ea8dc4b6Seschrock 	int error;
3666e9dbad6fSeschrock 	size_t count = (size_t)zc->zc_nvlist_dst_size;
3667ea8dc4b6Seschrock 
3668ea8dc4b6Seschrock 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
3669ea8dc4b6Seschrock 		return (error);
3670ea8dc4b6Seschrock 
3671e9dbad6fSeschrock 	error = spa_get_errlog(spa, (void *)(uintptr_t)zc->zc_nvlist_dst,
3672ea8dc4b6Seschrock 	    &count);
3673ea8dc4b6Seschrock 	if (error == 0)
3674e9dbad6fSeschrock 		zc->zc_nvlist_dst_size = count;
3675ea8dc4b6Seschrock 	else
3676e9dbad6fSeschrock 		zc->zc_nvlist_dst_size = spa_get_errlog_size(spa);
3677ea8dc4b6Seschrock 
3678ea8dc4b6Seschrock 	spa_close(spa, FTAG);
3679ea8dc4b6Seschrock 
3680ea8dc4b6Seschrock 	return (error);
3681ea8dc4b6Seschrock }
3682ea8dc4b6Seschrock 
3683ea8dc4b6Seschrock static int
3684ea8dc4b6Seschrock zfs_ioc_clear(zfs_cmd_t *zc)
3685ea8dc4b6Seschrock {
3686ea8dc4b6Seschrock 	spa_t *spa;
3687ea8dc4b6Seschrock 	vdev_t *vd;
3688bb8b5132Sek 	int error;
3689ea8dc4b6Seschrock 
3690b87f3af3Sperrin 	/*
3691b87f3af3Sperrin 	 * On zpool clear we also fix up missing slogs
3692b87f3af3Sperrin 	 */
3693b87f3af3Sperrin 	mutex_enter(&spa_namespace_lock);
3694b87f3af3Sperrin 	spa = spa_lookup(zc->zc_name);
3695b87f3af3Sperrin 	if (spa == NULL) {
3696b87f3af3Sperrin 		mutex_exit(&spa_namespace_lock);
3697b87f3af3Sperrin 		return (EIO);
3698b87f3af3Sperrin 	}
3699b24ab676SJeff Bonwick 	if (spa_get_log_state(spa) == SPA_LOG_MISSING) {
3700b87f3af3Sperrin 		/* we need to let spa_open/spa_load clear the chains */
3701b24ab676SJeff Bonwick 		spa_set_log_state(spa, SPA_LOG_CLEAR);
3702b87f3af3Sperrin 	}
3703468c413aSTim Haley 	spa->spa_last_open_failed = 0;
3704b87f3af3Sperrin 	mutex_exit(&spa_namespace_lock);
3705b87f3af3Sperrin 
3706c8ee1847SVictor Latushkin 	if (zc->zc_cookie & ZPOOL_NO_REWIND) {
3707468c413aSTim Haley 		error = spa_open(zc->zc_name, &spa, FTAG);
3708468c413aSTim Haley 	} else {
3709468c413aSTim Haley 		nvlist_t *policy;
3710468c413aSTim Haley 		nvlist_t *config = NULL;
3711468c413aSTim Haley 
3712468c413aSTim Haley 		if (zc->zc_nvlist_src == NULL)
3713468c413aSTim Haley 			return (EINVAL);
3714468c413aSTim Haley 
3715468c413aSTim Haley 		if ((error = get_nvlist(zc->zc_nvlist_src,
3716468c413aSTim Haley 		    zc->zc_nvlist_src_size, zc->zc_iflags, &policy)) == 0) {
3717468c413aSTim Haley 			error = spa_open_rewind(zc->zc_name, &spa, FTAG,
3718468c413aSTim Haley 			    policy, &config);
3719468c413aSTim Haley 			if (config != NULL) {
3720468c413aSTim Haley 				(void) put_nvlist(zc, config);
3721468c413aSTim Haley 				nvlist_free(config);
3722468c413aSTim Haley 			}
3723468c413aSTim Haley 			nvlist_free(policy);
3724468c413aSTim Haley 		}
3725468c413aSTim Haley 	}
3726468c413aSTim Haley 
3727468c413aSTim Haley 	if (error)
3728ea8dc4b6Seschrock 		return (error);
3729ea8dc4b6Seschrock 
37308f18d1faSGeorge Wilson 	spa_vdev_state_enter(spa, SCL_NONE);
3731ea8dc4b6Seschrock 
3732e9dbad6fSeschrock 	if (zc->zc_guid == 0) {
3733ea8dc4b6Seschrock 		vd = NULL;
3734c5904d13Seschrock 	} else {
3735c5904d13Seschrock 		vd = spa_lookup_by_guid(spa, zc->zc_guid, B_TRUE);
3736fa94a07fSbrendan 		if (vd == NULL) {
3737e14bb325SJeff Bonwick 			(void) spa_vdev_state_exit(spa, NULL, ENODEV);
3738fa94a07fSbrendan 			spa_close(spa, FTAG);
3739fa94a07fSbrendan 			return (ENODEV);
3740fa94a07fSbrendan 		}
3741ea8dc4b6Seschrock 	}
3742ea8dc4b6Seschrock 
3743e14bb325SJeff Bonwick 	vdev_clear(spa, vd);
3744e14bb325SJeff Bonwick 
3745e14bb325SJeff Bonwick 	(void) spa_vdev_state_exit(spa, NULL, 0);
3746ea8dc4b6Seschrock 
3747e14bb325SJeff Bonwick 	/*
3748e14bb325SJeff Bonwick 	 * Resume any suspended I/Os.
3749e14bb325SJeff Bonwick 	 */
375054d692b7SGeorge Wilson 	if (zio_resume(spa) != 0)
375154d692b7SGeorge Wilson 		error = EIO;
3752ea8dc4b6Seschrock 
3753ea8dc4b6Seschrock 	spa_close(spa, FTAG);
3754ea8dc4b6Seschrock 
375554d692b7SGeorge Wilson 	return (error);
3756ea8dc4b6Seschrock }
3757ea8dc4b6Seschrock 
37583cb34c60Sahrens /*
37593cb34c60Sahrens  * inputs:
37603cb34c60Sahrens  * zc_name	name of filesystem
37613cb34c60Sahrens  * zc_value	name of origin snapshot
37623cb34c60Sahrens  *
3763681d9761SEric Taylor  * outputs:
3764681d9761SEric Taylor  * zc_string	name of conflicting snapshot, if there is one
37653cb34c60Sahrens  */
376699653d4eSeschrock static int
376799653d4eSeschrock zfs_ioc_promote(zfs_cmd_t *zc)
376899653d4eSeschrock {
37690b69c2f0Sahrens 	char *cp;
37700b69c2f0Sahrens 
37710b69c2f0Sahrens 	/*
37720b69c2f0Sahrens 	 * We don't need to unmount *all* the origin fs's snapshots, but
37730b69c2f0Sahrens 	 * it's easier.
37740b69c2f0Sahrens 	 */
3775e9dbad6fSeschrock 	cp = strchr(zc->zc_value, '@');
37760b69c2f0Sahrens 	if (cp)
37770b69c2f0Sahrens 		*cp = '\0';
3778e9dbad6fSeschrock 	(void) dmu_objset_find(zc->zc_value,
37790b69c2f0Sahrens 	    zfs_unmount_snap, NULL, DS_FIND_SNAPSHOTS);
3780681d9761SEric Taylor 	return (dsl_dataset_promote(zc->zc_name, zc->zc_string));
378199653d4eSeschrock }
378299653d4eSeschrock 
378314843421SMatthew Ahrens /*
378414843421SMatthew Ahrens  * Retrieve a single {user|group}{used|quota}@... property.
378514843421SMatthew Ahrens  *
378614843421SMatthew Ahrens  * inputs:
378714843421SMatthew Ahrens  * zc_name	name of filesystem
378814843421SMatthew Ahrens  * zc_objset_type zfs_userquota_prop_t
378914843421SMatthew Ahrens  * zc_value	domain name (eg. "S-1-234-567-89")
379014843421SMatthew Ahrens  * zc_guid	RID/UID/GID
379114843421SMatthew Ahrens  *
379214843421SMatthew Ahrens  * outputs:
379314843421SMatthew Ahrens  * zc_cookie	property value
379414843421SMatthew Ahrens  */
379514843421SMatthew Ahrens static int
379614843421SMatthew Ahrens zfs_ioc_userspace_one(zfs_cmd_t *zc)
379714843421SMatthew Ahrens {
379814843421SMatthew Ahrens 	zfsvfs_t *zfsvfs;
379914843421SMatthew Ahrens 	int error;
380014843421SMatthew Ahrens 
380114843421SMatthew Ahrens 	if (zc->zc_objset_type >= ZFS_NUM_USERQUOTA_PROPS)
380214843421SMatthew Ahrens 		return (EINVAL);
380314843421SMatthew Ahrens 
3804503ad85cSMatthew Ahrens 	error = zfsvfs_hold(zc->zc_name, FTAG, &zfsvfs);
380514843421SMatthew Ahrens 	if (error)
380614843421SMatthew Ahrens 		return (error);
380714843421SMatthew Ahrens 
380814843421SMatthew Ahrens 	error = zfs_userspace_one(zfsvfs,
380914843421SMatthew Ahrens 	    zc->zc_objset_type, zc->zc_value, zc->zc_guid, &zc->zc_cookie);
381014843421SMatthew Ahrens 	zfsvfs_rele(zfsvfs, FTAG);
381114843421SMatthew Ahrens 
381214843421SMatthew Ahrens 	return (error);
381314843421SMatthew Ahrens }
381414843421SMatthew Ahrens 
381514843421SMatthew Ahrens /*
381614843421SMatthew Ahrens  * inputs:
381714843421SMatthew Ahrens  * zc_name		name of filesystem
381814843421SMatthew Ahrens  * zc_cookie		zap cursor
381914843421SMatthew Ahrens  * zc_objset_type	zfs_userquota_prop_t
382014843421SMatthew Ahrens  * zc_nvlist_dst[_size] buffer to fill (not really an nvlist)
382114843421SMatthew Ahrens  *
382214843421SMatthew Ahrens  * outputs:
382314843421SMatthew Ahrens  * zc_nvlist_dst[_size]	data buffer (array of zfs_useracct_t)
382414843421SMatthew Ahrens  * zc_cookie	zap cursor
382514843421SMatthew Ahrens  */
382614843421SMatthew Ahrens static int
382714843421SMatthew Ahrens zfs_ioc_userspace_many(zfs_cmd_t *zc)
382814843421SMatthew Ahrens {
382914843421SMatthew Ahrens 	zfsvfs_t *zfsvfs;
3830eeb85002STim Haley 	int bufsize = zc->zc_nvlist_dst_size;
383114843421SMatthew Ahrens 
3832eeb85002STim Haley 	if (bufsize <= 0)
3833eeb85002STim Haley 		return (ENOMEM);
3834eeb85002STim Haley 
3835eeb85002STim Haley 	int error = zfsvfs_hold(zc->zc_name, FTAG, &zfsvfs);
383614843421SMatthew Ahrens 	if (error)
383714843421SMatthew Ahrens 		return (error);
383814843421SMatthew Ahrens 
383914843421SMatthew Ahrens 	void *buf = kmem_alloc(bufsize, KM_SLEEP);
384014843421SMatthew Ahrens 
384114843421SMatthew Ahrens 	error = zfs_userspace_many(zfsvfs, zc->zc_objset_type, &zc->zc_cookie,
384214843421SMatthew Ahrens 	    buf, &zc->zc_nvlist_dst_size);
384314843421SMatthew Ahrens 
384414843421SMatthew Ahrens 	if (error == 0) {
384514843421SMatthew Ahrens 		error = xcopyout(buf,
384614843421SMatthew Ahrens 		    (void *)(uintptr_t)zc->zc_nvlist_dst,
384714843421SMatthew Ahrens 		    zc->zc_nvlist_dst_size);
384814843421SMatthew Ahrens 	}
384914843421SMatthew Ahrens 	kmem_free(buf, bufsize);
385014843421SMatthew Ahrens 	zfsvfs_rele(zfsvfs, FTAG);
385114843421SMatthew Ahrens 
385214843421SMatthew Ahrens 	return (error);
385314843421SMatthew Ahrens }
385414843421SMatthew Ahrens 
385514843421SMatthew Ahrens /*
385614843421SMatthew Ahrens  * inputs:
385714843421SMatthew Ahrens  * zc_name		name of filesystem
385814843421SMatthew Ahrens  *
385914843421SMatthew Ahrens  * outputs:
386014843421SMatthew Ahrens  * none
386114843421SMatthew Ahrens  */
386214843421SMatthew Ahrens static int
386314843421SMatthew Ahrens zfs_ioc_userspace_upgrade(zfs_cmd_t *zc)
386414843421SMatthew Ahrens {
386514843421SMatthew Ahrens 	objset_t *os;
38661195e687SMark J Musante 	int error = 0;
386714843421SMatthew Ahrens 	zfsvfs_t *zfsvfs;
386814843421SMatthew Ahrens 
386914843421SMatthew Ahrens 	if (getzfsvfs(zc->zc_name, &zfsvfs) == 0) {
3870503ad85cSMatthew Ahrens 		if (!dmu_objset_userused_enabled(zfsvfs->z_os)) {
387114843421SMatthew Ahrens 			/*
387214843421SMatthew Ahrens 			 * If userused is not enabled, it may be because the
387314843421SMatthew Ahrens 			 * objset needs to be closed & reopened (to grow the
387414843421SMatthew Ahrens 			 * objset_phys_t).  Suspend/resume the fs will do that.
387514843421SMatthew Ahrens 			 */
3876503ad85cSMatthew Ahrens 			error = zfs_suspend_fs(zfsvfs);
3877503ad85cSMatthew Ahrens 			if (error == 0)
3878503ad85cSMatthew Ahrens 				error = zfs_resume_fs(zfsvfs, zc->zc_name);
387914843421SMatthew Ahrens 		}
388014843421SMatthew Ahrens 		if (error == 0)
388114843421SMatthew Ahrens 			error = dmu_objset_userspace_upgrade(zfsvfs->z_os);
388214843421SMatthew Ahrens 		VFS_RELE(zfsvfs->z_vfs);
388314843421SMatthew Ahrens 	} else {
3884503ad85cSMatthew Ahrens 		/* XXX kind of reading contents without owning */
3885503ad85cSMatthew Ahrens 		error = dmu_objset_hold(zc->zc_name, FTAG, &os);
388614843421SMatthew Ahrens 		if (error)
388714843421SMatthew Ahrens 			return (error);
388814843421SMatthew Ahrens 
388914843421SMatthew Ahrens 		error = dmu_objset_userspace_upgrade(os);
3890503ad85cSMatthew Ahrens 		dmu_objset_rele(os, FTAG);
389114843421SMatthew Ahrens 	}
389214843421SMatthew Ahrens 
389314843421SMatthew Ahrens 	return (error);
389414843421SMatthew Ahrens }
389514843421SMatthew Ahrens 
3896ecd6cf80Smarks /*
3897ecd6cf80Smarks  * We don't want to have a hard dependency
3898ecd6cf80Smarks  * against some special symbols in sharefs
3899da6c28aaSamw  * nfs, and smbsrv.  Determine them if needed when
3900ecd6cf80Smarks  * the first file system is shared.
3901da6c28aaSamw  * Neither sharefs, nfs or smbsrv are unloadable modules.
3902ecd6cf80Smarks  */
3903da6c28aaSamw int (*znfsexport_fs)(void *arg);
3904ecd6cf80Smarks int (*zshare_fs)(enum sharefs_sys_op, share_t *, uint32_t);
3905da6c28aaSamw int (*zsmbexport_fs)(void *arg, boolean_t add_share);
3906da6c28aaSamw 
3907da6c28aaSamw int zfs_nfsshare_inited;
3908da6c28aaSamw int zfs_smbshare_inited;
3909ecd6cf80Smarks 
3910ecd6cf80Smarks ddi_modhandle_t nfs_mod;
3911ecd6cf80Smarks ddi_modhandle_t sharefs_mod;
3912da6c28aaSamw ddi_modhandle_t smbsrv_mod;
3913ecd6cf80Smarks kmutex_t zfs_share_lock;
3914ecd6cf80Smarks 
3915da6c28aaSamw static int
3916da6c28aaSamw zfs_init_sharefs()
3917da6c28aaSamw {
3918da6c28aaSamw 	int error;
3919da6c28aaSamw 
3920da6c28aaSamw 	ASSERT(MUTEX_HELD(&zfs_share_lock));
3921da6c28aaSamw 	/* Both NFS and SMB shares also require sharetab support. */
3922da6c28aaSamw 	if (sharefs_mod == NULL && ((sharefs_mod =
3923da6c28aaSamw 	    ddi_modopen("fs/sharefs",
3924da6c28aaSamw 	    KRTLD_MODE_FIRST, &error)) == NULL)) {
3925da6c28aaSamw 		return (ENOSYS);
3926da6c28aaSamw 	}
3927da6c28aaSamw 	if (zshare_fs == NULL && ((zshare_fs =
3928da6c28aaSamw 	    (int (*)(enum sharefs_sys_op, share_t *, uint32_t))
3929da6c28aaSamw 	    ddi_modsym(sharefs_mod, "sharefs_impl", &error)) == NULL)) {
3930da6c28aaSamw 		return (ENOSYS);
3931da6c28aaSamw 	}
3932da6c28aaSamw 	return (0);
3933da6c28aaSamw }
3934da6c28aaSamw 
3935ecd6cf80Smarks static int
3936ecd6cf80Smarks zfs_ioc_share(zfs_cmd_t *zc)
3937ecd6cf80Smarks {
3938ecd6cf80Smarks 	int error;
3939ecd6cf80Smarks 	int opcode;
3940ecd6cf80Smarks 
3941da6c28aaSamw 	switch (zc->zc_share.z_sharetype) {
3942da6c28aaSamw 	case ZFS_SHARE_NFS:
3943da6c28aaSamw 	case ZFS_UNSHARE_NFS:
3944da6c28aaSamw 		if (zfs_nfsshare_inited == 0) {
3945da6c28aaSamw 			mutex_enter(&zfs_share_lock);
3946da6c28aaSamw 			if (nfs_mod == NULL && ((nfs_mod = ddi_modopen("fs/nfs",
3947da6c28aaSamw 			    KRTLD_MODE_FIRST, &error)) == NULL)) {
3948da6c28aaSamw 				mutex_exit(&zfs_share_lock);
3949da6c28aaSamw 				return (ENOSYS);
3950da6c28aaSamw 			}
3951da6c28aaSamw 			if (znfsexport_fs == NULL &&
3952da6c28aaSamw 			    ((znfsexport_fs = (int (*)(void *))
3953da6c28aaSamw 			    ddi_modsym(nfs_mod,
3954da6c28aaSamw 			    "nfs_export", &error)) == NULL)) {
3955da6c28aaSamw 				mutex_exit(&zfs_share_lock);
3956da6c28aaSamw 				return (ENOSYS);
3957da6c28aaSamw 			}
3958da6c28aaSamw 			error = zfs_init_sharefs();
3959da6c28aaSamw 			if (error) {
3960da6c28aaSamw 				mutex_exit(&zfs_share_lock);
3961da6c28aaSamw 				return (ENOSYS);
3962da6c28aaSamw 			}
3963da6c28aaSamw 			zfs_nfsshare_inited = 1;
3964ecd6cf80Smarks 			mutex_exit(&zfs_share_lock);
3965ecd6cf80Smarks 		}
3966da6c28aaSamw 		break;
3967da6c28aaSamw 	case ZFS_SHARE_SMB:
3968da6c28aaSamw 	case ZFS_UNSHARE_SMB:
3969da6c28aaSamw 		if (zfs_smbshare_inited == 0) {
3970da6c28aaSamw 			mutex_enter(&zfs_share_lock);
3971da6c28aaSamw 			if (smbsrv_mod == NULL && ((smbsrv_mod =
3972da6c28aaSamw 			    ddi_modopen("drv/smbsrv",
3973da6c28aaSamw 			    KRTLD_MODE_FIRST, &error)) == NULL)) {
3974da6c28aaSamw 				mutex_exit(&zfs_share_lock);
3975da6c28aaSamw 				return (ENOSYS);
3976da6c28aaSamw 			}
3977da6c28aaSamw 			if (zsmbexport_fs == NULL && ((zsmbexport_fs =
3978da6c28aaSamw 			    (int (*)(void *, boolean_t))ddi_modsym(smbsrv_mod,
3979faa1795aSjb 			    "smb_server_share", &error)) == NULL)) {
3980da6c28aaSamw 				mutex_exit(&zfs_share_lock);
3981da6c28aaSamw 				return (ENOSYS);
3982da6c28aaSamw 			}
3983da6c28aaSamw 			error = zfs_init_sharefs();
3984da6c28aaSamw 			if (error) {
3985da6c28aaSamw 				mutex_exit(&zfs_share_lock);
3986da6c28aaSamw 				return (ENOSYS);
3987da6c28aaSamw 			}
3988da6c28aaSamw 			zfs_smbshare_inited = 1;
3989ecd6cf80Smarks 			mutex_exit(&zfs_share_lock);
3990ecd6cf80Smarks 		}
3991da6c28aaSamw 		break;
3992da6c28aaSamw 	default:
3993da6c28aaSamw 		return (EINVAL);
3994da6c28aaSamw 	}
3995ecd6cf80Smarks 
3996da6c28aaSamw 	switch (zc->zc_share.z_sharetype) {
3997da6c28aaSamw 	case ZFS_SHARE_NFS:
3998da6c28aaSamw 	case ZFS_UNSHARE_NFS:
3999da6c28aaSamw 		if (error =
4000da6c28aaSamw 		    znfsexport_fs((void *)
4001da6c28aaSamw 		    (uintptr_t)zc->zc_share.z_exportdata))
4002da6c28aaSamw 			return (error);
4003da6c28aaSamw 		break;
4004da6c28aaSamw 	case ZFS_SHARE_SMB:
4005da6c28aaSamw 	case ZFS_UNSHARE_SMB:
4006da6c28aaSamw 		if (error = zsmbexport_fs((void *)
4007da6c28aaSamw 		    (uintptr_t)zc->zc_share.z_exportdata,
4008da6c28aaSamw 		    zc->zc_share.z_sharetype == ZFS_SHARE_SMB ?
4009743a77edSAlan Wright 		    B_TRUE: B_FALSE)) {
4010da6c28aaSamw 			return (error);
4011ecd6cf80Smarks 		}
4012da6c28aaSamw 		break;
4013ecd6cf80Smarks 	}
4014ecd6cf80Smarks 
4015da6c28aaSamw 	opcode = (zc->zc_share.z_sharetype == ZFS_SHARE_NFS ||
4016da6c28aaSamw 	    zc->zc_share.z_sharetype == ZFS_SHARE_SMB) ?
4017ecd6cf80Smarks 	    SHAREFS_ADD : SHAREFS_REMOVE;
4018ecd6cf80Smarks 
4019da6c28aaSamw 	/*
4020da6c28aaSamw 	 * Add or remove share from sharetab
4021da6c28aaSamw 	 */
4022ecd6cf80Smarks 	error = zshare_fs(opcode,
4023ecd6cf80Smarks 	    (void *)(uintptr_t)zc->zc_share.z_sharedata,
4024ecd6cf80Smarks 	    zc->zc_share.z_sharemax);
4025ecd6cf80Smarks 
4026ecd6cf80Smarks 	return (error);
4027ecd6cf80Smarks 
4028ecd6cf80Smarks }
4029ecd6cf80Smarks 
4030743a77edSAlan Wright ace_t full_access[] = {
4031743a77edSAlan Wright 	{(uid_t)-1, ACE_ALL_PERMS, ACE_EVERYONE, 0}
4032743a77edSAlan Wright };
4033743a77edSAlan Wright 
4034743a77edSAlan Wright /*
4035743a77edSAlan Wright  * Remove all ACL files in shares dir
4036743a77edSAlan Wright  */
4037743a77edSAlan Wright static int
4038743a77edSAlan Wright zfs_smb_acl_purge(znode_t *dzp)
4039743a77edSAlan Wright {
4040743a77edSAlan Wright 	zap_cursor_t	zc;
4041743a77edSAlan Wright 	zap_attribute_t	zap;
4042743a77edSAlan Wright 	zfsvfs_t *zfsvfs = dzp->z_zfsvfs;
4043743a77edSAlan Wright 	int error;
4044743a77edSAlan Wright 
4045743a77edSAlan Wright 	for (zap_cursor_init(&zc, zfsvfs->z_os, dzp->z_id);
4046743a77edSAlan Wright 	    (error = zap_cursor_retrieve(&zc, &zap)) == 0;
4047743a77edSAlan Wright 	    zap_cursor_advance(&zc)) {
4048743a77edSAlan Wright 		if ((error = VOP_REMOVE(ZTOV(dzp), zap.za_name, kcred,
4049743a77edSAlan Wright 		    NULL, 0)) != 0)
4050743a77edSAlan Wright 			break;
4051743a77edSAlan Wright 	}
4052743a77edSAlan Wright 	zap_cursor_fini(&zc);
4053743a77edSAlan Wright 	return (error);
4054743a77edSAlan Wright }
4055743a77edSAlan Wright 
4056743a77edSAlan Wright static int
4057743a77edSAlan Wright zfs_ioc_smb_acl(zfs_cmd_t *zc)
4058743a77edSAlan Wright {
4059743a77edSAlan Wright 	vnode_t *vp;
4060743a77edSAlan Wright 	znode_t *dzp;
4061743a77edSAlan Wright 	vnode_t *resourcevp = NULL;
4062743a77edSAlan Wright 	znode_t *sharedir;
4063743a77edSAlan Wright 	zfsvfs_t *zfsvfs;
4064743a77edSAlan Wright 	nvlist_t *nvlist;
4065743a77edSAlan Wright 	char *src, *target;
4066743a77edSAlan Wright 	vattr_t vattr;
4067743a77edSAlan Wright 	vsecattr_t vsec;
4068743a77edSAlan Wright 	int error = 0;
4069743a77edSAlan Wright 
4070743a77edSAlan Wright 	if ((error = lookupname(zc->zc_value, UIO_SYSSPACE,
4071743a77edSAlan Wright 	    NO_FOLLOW, NULL, &vp)) != 0)
4072743a77edSAlan Wright 		return (error);
4073743a77edSAlan Wright 
4074743a77edSAlan Wright 	/* Now make sure mntpnt and dataset are ZFS */
4075743a77edSAlan Wright 
4076743a77edSAlan Wright 	if (vp->v_vfsp->vfs_fstype != zfsfstype ||
4077743a77edSAlan Wright 	    (strcmp((char *)refstr_value(vp->v_vfsp->vfs_resource),
4078743a77edSAlan Wright 	    zc->zc_name) != 0)) {
4079743a77edSAlan Wright 		VN_RELE(vp);
4080743a77edSAlan Wright 		return (EINVAL);
4081743a77edSAlan Wright 	}
4082743a77edSAlan Wright 
4083743a77edSAlan Wright 	dzp = VTOZ(vp);
4084743a77edSAlan Wright 	zfsvfs = dzp->z_zfsvfs;
4085743a77edSAlan Wright 	ZFS_ENTER(zfsvfs);
4086743a77edSAlan Wright 
40879e1320c0SMark Shellenbaum 	/*
40889e1320c0SMark Shellenbaum 	 * Create share dir if its missing.
40899e1320c0SMark Shellenbaum 	 */
40909e1320c0SMark Shellenbaum 	mutex_enter(&zfsvfs->z_lock);
40919e1320c0SMark Shellenbaum 	if (zfsvfs->z_shares_dir == 0) {
40929e1320c0SMark Shellenbaum 		dmu_tx_t *tx;
40939e1320c0SMark Shellenbaum 
40949e1320c0SMark Shellenbaum 		tx = dmu_tx_create(zfsvfs->z_os);
40959e1320c0SMark Shellenbaum 		dmu_tx_hold_zap(tx, MASTER_NODE_OBJ, TRUE,
40969e1320c0SMark Shellenbaum 		    ZFS_SHARES_DIR);
40979e1320c0SMark Shellenbaum 		dmu_tx_hold_zap(tx, DMU_NEW_OBJECT, FALSE, NULL);
40989e1320c0SMark Shellenbaum 		error = dmu_tx_assign(tx, TXG_WAIT);
40999e1320c0SMark Shellenbaum 		if (error) {
41009e1320c0SMark Shellenbaum 			dmu_tx_abort(tx);
41019e1320c0SMark Shellenbaum 		} else {
41029e1320c0SMark Shellenbaum 			error = zfs_create_share_dir(zfsvfs, tx);
41039e1320c0SMark Shellenbaum 			dmu_tx_commit(tx);
41049e1320c0SMark Shellenbaum 		}
41059e1320c0SMark Shellenbaum 		if (error) {
41069e1320c0SMark Shellenbaum 			mutex_exit(&zfsvfs->z_lock);
41079e1320c0SMark Shellenbaum 			VN_RELE(vp);
41089e1320c0SMark Shellenbaum 			ZFS_EXIT(zfsvfs);
41099e1320c0SMark Shellenbaum 			return (error);
41109e1320c0SMark Shellenbaum 		}
41119e1320c0SMark Shellenbaum 	}
41129e1320c0SMark Shellenbaum 	mutex_exit(&zfsvfs->z_lock);
41139e1320c0SMark Shellenbaum 
41149e1320c0SMark Shellenbaum 	ASSERT(zfsvfs->z_shares_dir);
4115743a77edSAlan Wright 	if ((error = zfs_zget(zfsvfs, zfsvfs->z_shares_dir, &sharedir)) != 0) {
41169e1320c0SMark Shellenbaum 		VN_RELE(vp);
4117743a77edSAlan Wright 		ZFS_EXIT(zfsvfs);
4118743a77edSAlan Wright 		return (error);
4119743a77edSAlan Wright 	}
4120743a77edSAlan Wright 
4121743a77edSAlan Wright 	switch (zc->zc_cookie) {
4122743a77edSAlan Wright 	case ZFS_SMB_ACL_ADD:
4123743a77edSAlan Wright 		vattr.va_mask = AT_MODE|AT_UID|AT_GID|AT_TYPE;
4124743a77edSAlan Wright 		vattr.va_type = VREG;
4125743a77edSAlan Wright 		vattr.va_mode = S_IFREG|0777;
4126743a77edSAlan Wright 		vattr.va_uid = 0;
4127743a77edSAlan Wright 		vattr.va_gid = 0;
4128743a77edSAlan Wright 
4129743a77edSAlan Wright 		vsec.vsa_mask = VSA_ACE;
4130743a77edSAlan Wright 		vsec.vsa_aclentp = &full_access;
4131743a77edSAlan Wright 		vsec.vsa_aclentsz = sizeof (full_access);
4132743a77edSAlan Wright 		vsec.vsa_aclcnt = 1;
4133743a77edSAlan Wright 
4134743a77edSAlan Wright 		error = VOP_CREATE(ZTOV(sharedir), zc->zc_string,
4135743a77edSAlan Wright 		    &vattr, EXCL, 0, &resourcevp, kcred, 0, NULL, &vsec);
4136743a77edSAlan Wright 		if (resourcevp)
4137743a77edSAlan Wright 			VN_RELE(resourcevp);
4138743a77edSAlan Wright 		break;
4139743a77edSAlan Wright 
4140743a77edSAlan Wright 	case ZFS_SMB_ACL_REMOVE:
4141743a77edSAlan Wright 		error = VOP_REMOVE(ZTOV(sharedir), zc->zc_string, kcred,
4142743a77edSAlan Wright 		    NULL, 0);
4143743a77edSAlan Wright 		break;
4144743a77edSAlan Wright 
4145743a77edSAlan Wright 	case ZFS_SMB_ACL_RENAME:
4146743a77edSAlan Wright 		if ((error = get_nvlist(zc->zc_nvlist_src,
4147478ed9adSEric Taylor 		    zc->zc_nvlist_src_size, zc->zc_iflags, &nvlist)) != 0) {
4148743a77edSAlan Wright 			VN_RELE(vp);
4149743a77edSAlan Wright 			ZFS_EXIT(zfsvfs);
4150743a77edSAlan Wright 			return (error);
4151743a77edSAlan Wright 		}
4152743a77edSAlan Wright 		if (nvlist_lookup_string(nvlist, ZFS_SMB_ACL_SRC, &src) ||
4153743a77edSAlan Wright 		    nvlist_lookup_string(nvlist, ZFS_SMB_ACL_TARGET,
4154743a77edSAlan Wright 		    &target)) {
4155743a77edSAlan Wright 			VN_RELE(vp);
415689459e17SMark Shellenbaum 			VN_RELE(ZTOV(sharedir));
4157743a77edSAlan Wright 			ZFS_EXIT(zfsvfs);
41581195e687SMark J Musante 			nvlist_free(nvlist);
4159743a77edSAlan Wright 			return (error);
4160743a77edSAlan Wright 		}
4161743a77edSAlan Wright 		error = VOP_RENAME(ZTOV(sharedir), src, ZTOV(sharedir), target,
4162743a77edSAlan Wright 		    kcred, NULL, 0);
4163743a77edSAlan Wright 		nvlist_free(nvlist);
4164743a77edSAlan Wright 		break;
4165743a77edSAlan Wright 
4166743a77edSAlan Wright 	case ZFS_SMB_ACL_PURGE:
4167743a77edSAlan Wright 		error = zfs_smb_acl_purge(sharedir);
4168743a77edSAlan Wright 		break;
4169743a77edSAlan Wright 
4170743a77edSAlan Wright 	default:
4171743a77edSAlan Wright 		error = EINVAL;
4172743a77edSAlan Wright 		break;
4173743a77edSAlan Wright 	}
4174743a77edSAlan Wright 
4175743a77edSAlan Wright 	VN_RELE(vp);
4176743a77edSAlan Wright 	VN_RELE(ZTOV(sharedir));
4177743a77edSAlan Wright 
4178743a77edSAlan Wright 	ZFS_EXIT(zfsvfs);
4179743a77edSAlan Wright 
4180743a77edSAlan Wright 	return (error);
4181743a77edSAlan Wright }
4182743a77edSAlan Wright 
4183842727c2SChris Kirby /*
4184842727c2SChris Kirby  * inputs:
4185842727c2SChris Kirby  * zc_name	name of filesystem
4186842727c2SChris Kirby  * zc_value	short name of snap
4187842727c2SChris Kirby  * zc_string	user-supplied tag for this reference
4188842727c2SChris Kirby  * zc_cookie	recursive flag
4189ca45db41SChris Kirby  * zc_temphold	set if hold is temporary
4190842727c2SChris Kirby  *
4191842727c2SChris Kirby  * outputs:		none
4192842727c2SChris Kirby  */
4193842727c2SChris Kirby static int
4194842727c2SChris Kirby zfs_ioc_hold(zfs_cmd_t *zc)
4195842727c2SChris Kirby {
4196842727c2SChris Kirby 	boolean_t recursive = zc->zc_cookie;
4197842727c2SChris Kirby 
4198842727c2SChris Kirby 	if (snapshot_namecheck(zc->zc_value, NULL, NULL) != 0)
4199842727c2SChris Kirby 		return (EINVAL);
4200842727c2SChris Kirby 
4201842727c2SChris Kirby 	return (dsl_dataset_user_hold(zc->zc_name, zc->zc_value,
4202ca45db41SChris Kirby 	    zc->zc_string, recursive, zc->zc_temphold));
4203842727c2SChris Kirby }
4204842727c2SChris Kirby 
4205842727c2SChris Kirby /*
4206842727c2SChris Kirby  * inputs:
4207842727c2SChris Kirby  * zc_name	name of dataset from which we're releasing a user reference
4208842727c2SChris Kirby  * zc_value	short name of snap
4209842727c2SChris Kirby  * zc_string	user-supplied tag for this reference
4210842727c2SChris Kirby  * zc_cookie	recursive flag
4211842727c2SChris Kirby  *
4212842727c2SChris Kirby  * outputs:		none
4213842727c2SChris Kirby  */
4214842727c2SChris Kirby static int
4215842727c2SChris Kirby zfs_ioc_release(zfs_cmd_t *zc)
4216842727c2SChris Kirby {
4217842727c2SChris Kirby 	boolean_t recursive = zc->zc_cookie;
4218842727c2SChris Kirby 
4219842727c2SChris Kirby 	if (snapshot_namecheck(zc->zc_value, NULL, NULL) != 0)
4220842727c2SChris Kirby 		return (EINVAL);
4221842727c2SChris Kirby 
4222842727c2SChris Kirby 	return (dsl_dataset_user_release(zc->zc_name, zc->zc_value,
4223842727c2SChris Kirby 	    zc->zc_string, recursive));
4224842727c2SChris Kirby }
4225842727c2SChris Kirby 
4226842727c2SChris Kirby /*
4227842727c2SChris Kirby  * inputs:
4228842727c2SChris Kirby  * zc_name		name of filesystem
4229842727c2SChris Kirby  *
4230842727c2SChris Kirby  * outputs:
4231842727c2SChris Kirby  * zc_nvlist_src{_size}	nvlist of snapshot holds
4232842727c2SChris Kirby  */
4233842727c2SChris Kirby static int
4234842727c2SChris Kirby zfs_ioc_get_holds(zfs_cmd_t *zc)
4235842727c2SChris Kirby {
4236842727c2SChris Kirby 	nvlist_t *nvp;
4237842727c2SChris Kirby 	int error;
4238842727c2SChris Kirby 
4239842727c2SChris Kirby 	if ((error = dsl_dataset_get_holds(zc->zc_name, &nvp)) == 0) {
4240842727c2SChris Kirby 		error = put_nvlist(zc, nvp);
4241842727c2SChris Kirby 		nvlist_free(nvp);
4242842727c2SChris Kirby 	}
4243842727c2SChris Kirby 
4244842727c2SChris Kirby 	return (error);
4245842727c2SChris Kirby }
4246842727c2SChris Kirby 
4247ecd6cf80Smarks /*
42482a6b87f0Sek  * pool create, destroy, and export don't log the history as part of
42492a6b87f0Sek  * zfsdev_ioctl, but rather zfs_ioc_pool_create, and zfs_ioc_pool_export
42502a6b87f0Sek  * do the logging of those commands.
4251ecd6cf80Smarks  */
4252fa9e4066Sahrens static zfs_ioc_vec_t zfs_ioc_vec[] = {
425354d692b7SGeorge Wilson 	{ zfs_ioc_pool_create, zfs_secpolicy_config, POOL_NAME, B_FALSE,
425454d692b7SGeorge Wilson 	    B_FALSE },
425554d692b7SGeorge Wilson 	{ zfs_ioc_pool_destroy,	zfs_secpolicy_config, POOL_NAME, B_FALSE,
425654d692b7SGeorge Wilson 	    B_FALSE },
425754d692b7SGeorge Wilson 	{ zfs_ioc_pool_import, zfs_secpolicy_config, POOL_NAME, B_TRUE,
425854d692b7SGeorge Wilson 	    B_FALSE },
425954d692b7SGeorge Wilson 	{ zfs_ioc_pool_export, zfs_secpolicy_config, POOL_NAME, B_FALSE,
426054d692b7SGeorge Wilson 	    B_FALSE },
426154d692b7SGeorge Wilson 	{ zfs_ioc_pool_configs,	zfs_secpolicy_none, NO_NAME, B_FALSE,
426254d692b7SGeorge Wilson 	    B_FALSE },
426354d692b7SGeorge Wilson 	{ zfs_ioc_pool_stats, zfs_secpolicy_read, POOL_NAME, B_FALSE,
426454d692b7SGeorge Wilson 	    B_FALSE },
426554d692b7SGeorge Wilson 	{ zfs_ioc_pool_tryimport, zfs_secpolicy_config, NO_NAME, B_FALSE,
426654d692b7SGeorge Wilson 	    B_FALSE },
4267*3f9d6ad7SLin Ling 	{ zfs_ioc_pool_scan, zfs_secpolicy_config, POOL_NAME, B_TRUE,
426854d692b7SGeorge Wilson 	    B_TRUE },
426954d692b7SGeorge Wilson 	{ zfs_ioc_pool_freeze, zfs_secpolicy_config, NO_NAME, B_FALSE,
427054d692b7SGeorge Wilson 	    B_FALSE },
427154d692b7SGeorge Wilson 	{ zfs_ioc_pool_upgrade,	zfs_secpolicy_config, POOL_NAME, B_TRUE,
427254d692b7SGeorge Wilson 	    B_TRUE },
427354d692b7SGeorge Wilson 	{ zfs_ioc_pool_get_history, zfs_secpolicy_config, POOL_NAME, B_FALSE,
427454d692b7SGeorge Wilson 	    B_FALSE },
427554d692b7SGeorge Wilson 	{ zfs_ioc_vdev_add, zfs_secpolicy_config, POOL_NAME, B_TRUE,
427654d692b7SGeorge Wilson 	    B_TRUE },
427754d692b7SGeorge Wilson 	{ zfs_ioc_vdev_remove, zfs_secpolicy_config, POOL_NAME, B_TRUE,
427854d692b7SGeorge Wilson 	    B_TRUE },
427954d692b7SGeorge Wilson 	{ zfs_ioc_vdev_set_state, zfs_secpolicy_config,	POOL_NAME, B_TRUE,
428054d692b7SGeorge Wilson 	    B_FALSE },
428154d692b7SGeorge Wilson 	{ zfs_ioc_vdev_attach, zfs_secpolicy_config, POOL_NAME, B_TRUE,
428254d692b7SGeorge Wilson 	    B_TRUE },
428354d692b7SGeorge Wilson 	{ zfs_ioc_vdev_detach, zfs_secpolicy_config, POOL_NAME, B_TRUE,
428454d692b7SGeorge Wilson 	    B_TRUE },
428554d692b7SGeorge Wilson 	{ zfs_ioc_vdev_setpath,	zfs_secpolicy_config, POOL_NAME, B_FALSE,
428654d692b7SGeorge Wilson 	    B_TRUE },
42876809eb4eSEric Schrock 	{ zfs_ioc_vdev_setfru,	zfs_secpolicy_config, POOL_NAME, B_FALSE,
42886809eb4eSEric Schrock 	    B_TRUE },
428954d692b7SGeorge Wilson 	{ zfs_ioc_objset_stats,	zfs_secpolicy_read, DATASET_NAME, B_FALSE,
4290cf2859fcSSanjeev Bagewadi 	    B_TRUE },
429154d692b7SGeorge Wilson 	{ zfs_ioc_objset_zplprops, zfs_secpolicy_read, DATASET_NAME, B_FALSE,
429254d692b7SGeorge Wilson 	    B_FALSE },
429354d692b7SGeorge Wilson 	{ zfs_ioc_dataset_list_next, zfs_secpolicy_read, DATASET_NAME, B_FALSE,
4294cf2859fcSSanjeev Bagewadi 	    B_TRUE },
429554d692b7SGeorge Wilson 	{ zfs_ioc_snapshot_list_next, zfs_secpolicy_read, DATASET_NAME, B_FALSE,
4296cf2859fcSSanjeev Bagewadi 	    B_TRUE },
429754d692b7SGeorge Wilson 	{ zfs_ioc_set_prop, zfs_secpolicy_none, DATASET_NAME, B_TRUE, B_TRUE },
429854d692b7SGeorge Wilson 	{ zfs_ioc_create, zfs_secpolicy_create, DATASET_NAME, B_TRUE, B_TRUE },
429954d692b7SGeorge Wilson 	{ zfs_ioc_destroy, zfs_secpolicy_destroy, DATASET_NAME, B_TRUE,
430054d692b7SGeorge Wilson 	    B_TRUE},
430154d692b7SGeorge Wilson 	{ zfs_ioc_rollback, zfs_secpolicy_rollback, DATASET_NAME, B_TRUE,
430254d692b7SGeorge Wilson 	    B_TRUE },
430354d692b7SGeorge Wilson 	{ zfs_ioc_rename, zfs_secpolicy_rename,	DATASET_NAME, B_TRUE, B_TRUE },
430454d692b7SGeorge Wilson 	{ zfs_ioc_recv, zfs_secpolicy_receive, DATASET_NAME, B_TRUE, B_TRUE },
430554d692b7SGeorge Wilson 	{ zfs_ioc_send, zfs_secpolicy_send, DATASET_NAME, B_TRUE, B_FALSE },
430654d692b7SGeorge Wilson 	{ zfs_ioc_inject_fault,	zfs_secpolicy_inject, NO_NAME, B_FALSE,
430754d692b7SGeorge Wilson 	    B_FALSE },
430854d692b7SGeorge Wilson 	{ zfs_ioc_clear_fault, zfs_secpolicy_inject, NO_NAME, B_FALSE,
430954d692b7SGeorge Wilson 	    B_FALSE },
431054d692b7SGeorge Wilson 	{ zfs_ioc_inject_list_next, zfs_secpolicy_inject, NO_NAME, B_FALSE,
431154d692b7SGeorge Wilson 	    B_FALSE },
431254d692b7SGeorge Wilson 	{ zfs_ioc_error_log, zfs_secpolicy_inject, POOL_NAME, B_FALSE,
431354d692b7SGeorge Wilson 	    B_FALSE },
431454d692b7SGeorge Wilson 	{ zfs_ioc_clear, zfs_secpolicy_config, POOL_NAME, B_TRUE, B_FALSE },
431554d692b7SGeorge Wilson 	{ zfs_ioc_promote, zfs_secpolicy_promote, DATASET_NAME, B_TRUE,
431654d692b7SGeorge Wilson 	    B_TRUE },
4317cbf6f6aaSWilliam Gorrell 	{ zfs_ioc_destroy_snaps, zfs_secpolicy_destroy_snaps, DATASET_NAME,
4318cbf6f6aaSWilliam Gorrell 	    B_TRUE, B_TRUE },
431954d692b7SGeorge Wilson 	{ zfs_ioc_snapshot, zfs_secpolicy_snapshot, DATASET_NAME, B_TRUE,
432054d692b7SGeorge Wilson 	    B_TRUE },
432154d692b7SGeorge Wilson 	{ zfs_ioc_dsobj_to_dsname, zfs_secpolicy_config, POOL_NAME, B_FALSE,
432254d692b7SGeorge Wilson 	    B_FALSE },
43236e8a0f56SGeorge Wilson 	{ zfs_ioc_obj_to_path, zfs_secpolicy_config, DATASET_NAME, B_FALSE,
43246e8a0f56SGeorge Wilson 	    B_TRUE },
432554d692b7SGeorge Wilson 	{ zfs_ioc_pool_set_props, zfs_secpolicy_config,	POOL_NAME, B_TRUE,
432654d692b7SGeorge Wilson 	    B_TRUE },
432754d692b7SGeorge Wilson 	{ zfs_ioc_pool_get_props, zfs_secpolicy_read, POOL_NAME, B_FALSE,
432854d692b7SGeorge Wilson 	    B_FALSE },
432954d692b7SGeorge Wilson 	{ zfs_ioc_set_fsacl, zfs_secpolicy_fsacl, DATASET_NAME, B_TRUE,
433054d692b7SGeorge Wilson 	    B_TRUE },
433154d692b7SGeorge Wilson 	{ zfs_ioc_get_fsacl, zfs_secpolicy_read, DATASET_NAME, B_FALSE,
433254d692b7SGeorge Wilson 	    B_FALSE },
433354d692b7SGeorge Wilson 	{ zfs_ioc_share, zfs_secpolicy_share, DATASET_NAME, B_FALSE, B_FALSE },
433454d692b7SGeorge Wilson 	{ zfs_ioc_inherit_prop, zfs_secpolicy_inherit, DATASET_NAME, B_TRUE,
433554d692b7SGeorge Wilson 	    B_TRUE },
433654d692b7SGeorge Wilson 	{ zfs_ioc_smb_acl, zfs_secpolicy_smb_acl, DATASET_NAME, B_FALSE,
433714843421SMatthew Ahrens 	    B_FALSE },
433814843421SMatthew Ahrens 	{ zfs_ioc_userspace_one, zfs_secpolicy_userspace_one,
433914843421SMatthew Ahrens 	    DATASET_NAME, B_FALSE, B_FALSE },
434014843421SMatthew Ahrens 	{ zfs_ioc_userspace_many, zfs_secpolicy_userspace_many,
434114843421SMatthew Ahrens 	    DATASET_NAME, B_FALSE, B_FALSE },
434214843421SMatthew Ahrens 	{ zfs_ioc_userspace_upgrade, zfs_secpolicy_userspace_upgrade,
434314843421SMatthew Ahrens 	    DATASET_NAME, B_FALSE, B_TRUE },
4344842727c2SChris Kirby 	{ zfs_ioc_hold, zfs_secpolicy_hold, DATASET_NAME, B_TRUE, B_TRUE },
4345842727c2SChris Kirby 	{ zfs_ioc_release, zfs_secpolicy_release, DATASET_NAME, B_TRUE,
4346842727c2SChris Kirby 	    B_TRUE },
4347842727c2SChris Kirby 	{ zfs_ioc_get_holds, zfs_secpolicy_read, DATASET_NAME, B_FALSE,
434892241e0bSTom Erickson 	    B_TRUE },
434992241e0bSTom Erickson 	{ zfs_ioc_objset_recvd_props, zfs_secpolicy_read, DATASET_NAME, B_FALSE,
43501195e687SMark J Musante 	    B_FALSE },
43511195e687SMark J Musante 	{ zfs_ioc_vdev_split, zfs_secpolicy_config, POOL_NAME, B_TRUE,
43521195e687SMark J Musante 	    B_TRUE }
4353fa9e4066Sahrens };
4354fa9e4066Sahrens 
435554d692b7SGeorge Wilson int
435654d692b7SGeorge Wilson pool_status_check(const char *name, zfs_ioc_namecheck_t type)
435754d692b7SGeorge Wilson {
435854d692b7SGeorge Wilson 	spa_t *spa;
435954d692b7SGeorge Wilson 	int error;
436054d692b7SGeorge Wilson 
436154d692b7SGeorge Wilson 	ASSERT(type == POOL_NAME || type == DATASET_NAME);
436254d692b7SGeorge Wilson 
436314843421SMatthew Ahrens 	error = spa_open(name, &spa, FTAG);
436454d692b7SGeorge Wilson 	if (error == 0) {
436554d692b7SGeorge Wilson 		if (spa_suspended(spa))
436654d692b7SGeorge Wilson 			error = EAGAIN;
436754d692b7SGeorge Wilson 		spa_close(spa, FTAG);
436854d692b7SGeorge Wilson 	}
436954d692b7SGeorge Wilson 	return (error);
437054d692b7SGeorge Wilson }
437154d692b7SGeorge Wilson 
4372fa9e4066Sahrens static int
4373fa9e4066Sahrens zfsdev_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *cr, int *rvalp)
4374fa9e4066Sahrens {
4375fa9e4066Sahrens 	zfs_cmd_t *zc;
4376fa9e4066Sahrens 	uint_t vec;
43771d452cf5Sahrens 	int error, rc;
4378fa9e4066Sahrens 
4379fa9e4066Sahrens 	if (getminor(dev) != 0)
4380fa9e4066Sahrens 		return (zvol_ioctl(dev, cmd, arg, flag, cr, rvalp));
4381fa9e4066Sahrens 
4382fa9e4066Sahrens 	vec = cmd - ZFS_IOC;
438391ebeef5Sahrens 	ASSERT3U(getmajor(dev), ==, ddi_driver_major(zfs_dip));
4384fa9e4066Sahrens 
4385fa9e4066Sahrens 	if (vec >= sizeof (zfs_ioc_vec) / sizeof (zfs_ioc_vec[0]))
4386fa9e4066Sahrens 		return (EINVAL);
4387fa9e4066Sahrens 
4388fa9e4066Sahrens 	zc = kmem_zalloc(sizeof (zfs_cmd_t), KM_SLEEP);
4389fa9e4066Sahrens 
4390478ed9adSEric Taylor 	error = ddi_copyin((void *)arg, zc, sizeof (zfs_cmd_t), flag);
43916e27f868SSam Falkner 	if (error != 0)
43926e27f868SSam Falkner 		error = EFAULT;
4393fa9e4066Sahrens 
4394681d9761SEric Taylor 	if ((error == 0) && !(flag & FKIOCTL))
4395ecd6cf80Smarks 		error = zfs_ioc_vec[vec].zvec_secpolicy(zc, cr);
4396fa9e4066Sahrens 
4397fa9e4066Sahrens 	/*
4398fa9e4066Sahrens 	 * Ensure that all pool/dataset names are valid before we pass down to
4399fa9e4066Sahrens 	 * the lower layers.
4400fa9e4066Sahrens 	 */
4401fa9e4066Sahrens 	if (error == 0) {
4402fa9e4066Sahrens 		zc->zc_name[sizeof (zc->zc_name) - 1] = '\0';
4403478ed9adSEric Taylor 		zc->zc_iflags = flag & FKIOCTL;
4404fa9e4066Sahrens 		switch (zfs_ioc_vec[vec].zvec_namecheck) {
4405e7437265Sahrens 		case POOL_NAME:
4406fa9e4066Sahrens 			if (pool_namecheck(zc->zc_name, NULL, NULL) != 0)
4407fa9e4066Sahrens 				error = EINVAL;
440854d692b7SGeorge Wilson 			if (zfs_ioc_vec[vec].zvec_pool_check)
440954d692b7SGeorge Wilson 				error = pool_status_check(zc->zc_name,
441054d692b7SGeorge Wilson 				    zfs_ioc_vec[vec].zvec_namecheck);
4411fa9e4066Sahrens 			break;
4412fa9e4066Sahrens 
4413e7437265Sahrens 		case DATASET_NAME:
4414fa9e4066Sahrens 			if (dataset_namecheck(zc->zc_name, NULL, NULL) != 0)
4415fa9e4066Sahrens 				error = EINVAL;
441654d692b7SGeorge Wilson 			if (zfs_ioc_vec[vec].zvec_pool_check)
441754d692b7SGeorge Wilson 				error = pool_status_check(zc->zc_name,
441854d692b7SGeorge Wilson 				    zfs_ioc_vec[vec].zvec_namecheck);
4419fa9e4066Sahrens 			break;
44205ad82045Snd 
4421e7437265Sahrens 		case NO_NAME:
44225ad82045Snd 			break;
4423fa9e4066Sahrens 		}
4424fa9e4066Sahrens 	}
4425fa9e4066Sahrens 
4426fa9e4066Sahrens 	if (error == 0)
4427fa9e4066Sahrens 		error = zfs_ioc_vec[vec].zvec_func(zc);
4428fa9e4066Sahrens 
4429478ed9adSEric Taylor 	rc = ddi_copyout(zc, (void *)arg, sizeof (zfs_cmd_t), flag);
4430ecd6cf80Smarks 	if (error == 0) {
44316e27f868SSam Falkner 		if (rc != 0)
44326e27f868SSam Falkner 			error = EFAULT;
443314843421SMatthew Ahrens 		if (zfs_ioc_vec[vec].zvec_his_log)
4434ecd6cf80Smarks 			zfs_log_history(zc);
4435ecd6cf80Smarks 	}
4436fa9e4066Sahrens 
4437fa9e4066Sahrens 	kmem_free(zc, sizeof (zfs_cmd_t));
4438fa9e4066Sahrens 	return (error);
4439fa9e4066Sahrens }
4440fa9e4066Sahrens 
4441fa9e4066Sahrens static int
4442fa9e4066Sahrens zfs_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
4443fa9e4066Sahrens {
4444fa9e4066Sahrens 	if (cmd != DDI_ATTACH)
4445fa9e4066Sahrens 		return (DDI_FAILURE);
4446fa9e4066Sahrens 
4447fa9e4066Sahrens 	if (ddi_create_minor_node(dip, "zfs", S_IFCHR, 0,
4448fa9e4066Sahrens 	    DDI_PSEUDO, 0) == DDI_FAILURE)
4449fa9e4066Sahrens 		return (DDI_FAILURE);
4450fa9e4066Sahrens 
4451fa9e4066Sahrens 	zfs_dip = dip;
4452fa9e4066Sahrens 
4453fa9e4066Sahrens 	ddi_report_dev(dip);
4454fa9e4066Sahrens 
4455fa9e4066Sahrens 	return (DDI_SUCCESS);
4456fa9e4066Sahrens }
4457fa9e4066Sahrens 
4458fa9e4066Sahrens static int
4459fa9e4066Sahrens zfs_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
4460fa9e4066Sahrens {
4461fa9e4066Sahrens 	if (spa_busy() || zfs_busy() || zvol_busy())
4462fa9e4066Sahrens 		return (DDI_FAILURE);
4463fa9e4066Sahrens 
4464fa9e4066Sahrens 	if (cmd != DDI_DETACH)
4465fa9e4066Sahrens 		return (DDI_FAILURE);
4466fa9e4066Sahrens 
4467fa9e4066Sahrens 	zfs_dip = NULL;
4468fa9e4066Sahrens 
4469fa9e4066Sahrens 	ddi_prop_remove_all(dip);
4470fa9e4066Sahrens 	ddi_remove_minor_node(dip, NULL);
4471fa9e4066Sahrens 
4472fa9e4066Sahrens 	return (DDI_SUCCESS);
4473fa9e4066Sahrens }
4474fa9e4066Sahrens 
4475fa9e4066Sahrens /*ARGSUSED*/
4476fa9e4066Sahrens static int
4477fa9e4066Sahrens zfs_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
4478fa9e4066Sahrens {
4479fa9e4066Sahrens 	switch (infocmd) {
4480fa9e4066Sahrens 	case DDI_INFO_DEVT2DEVINFO:
4481fa9e4066Sahrens 		*result = zfs_dip;
4482fa9e4066Sahrens 		return (DDI_SUCCESS);
4483fa9e4066Sahrens 
4484fa9e4066Sahrens 	case DDI_INFO_DEVT2INSTANCE:
4485a0965f35Sbonwick 		*result = (void *)0;
4486fa9e4066Sahrens 		return (DDI_SUCCESS);
4487fa9e4066Sahrens 	}
4488fa9e4066Sahrens 
4489fa9e4066Sahrens 	return (DDI_FAILURE);
4490fa9e4066Sahrens }
4491fa9e4066Sahrens 
4492fa9e4066Sahrens /*
4493fa9e4066Sahrens  * OK, so this is a little weird.
4494fa9e4066Sahrens  *
4495fa9e4066Sahrens  * /dev/zfs is the control node, i.e. minor 0.
4496fa9e4066Sahrens  * /dev/zvol/[r]dsk/pool/dataset are the zvols, minor > 0.
4497fa9e4066Sahrens  *
4498fa9e4066Sahrens  * /dev/zfs has basically nothing to do except serve up ioctls,
4499fa9e4066Sahrens  * so most of the standard driver entry points are in zvol.c.
4500fa9e4066Sahrens  */
4501fa9e4066Sahrens static struct cb_ops zfs_cb_ops = {
4502fa9e4066Sahrens 	zvol_open,	/* open */
4503fa9e4066Sahrens 	zvol_close,	/* close */
4504fa9e4066Sahrens 	zvol_strategy,	/* strategy */
4505fa9e4066Sahrens 	nodev,		/* print */
4506e7cbe64fSgw 	zvol_dump,	/* dump */
4507fa9e4066Sahrens 	zvol_read,	/* read */
4508fa9e4066Sahrens 	zvol_write,	/* write */
4509fa9e4066Sahrens 	zfsdev_ioctl,	/* ioctl */
4510fa9e4066Sahrens 	nodev,		/* devmap */
4511fa9e4066Sahrens 	nodev,		/* mmap */
4512fa9e4066Sahrens 	nodev,		/* segmap */
4513fa9e4066Sahrens 	nochpoll,	/* poll */
4514fa9e4066Sahrens 	ddi_prop_op,	/* prop_op */
4515fa9e4066Sahrens 	NULL,		/* streamtab */
4516fa9e4066Sahrens 	D_NEW | D_MP | D_64BIT,		/* Driver compatibility flag */
4517fa9e4066Sahrens 	CB_REV,		/* version */
4518feb08c6bSbillm 	nodev,		/* async read */
4519feb08c6bSbillm 	nodev,		/* async write */
4520fa9e4066Sahrens };
4521fa9e4066Sahrens 
4522fa9e4066Sahrens static struct dev_ops zfs_dev_ops = {
4523fa9e4066Sahrens 	DEVO_REV,	/* version */
4524fa9e4066Sahrens 	0,		/* refcnt */
4525fa9e4066Sahrens 	zfs_info,	/* info */
4526fa9e4066Sahrens 	nulldev,	/* identify */
4527fa9e4066Sahrens 	nulldev,	/* probe */
4528fa9e4066Sahrens 	zfs_attach,	/* attach */
4529fa9e4066Sahrens 	zfs_detach,	/* detach */
4530fa9e4066Sahrens 	nodev,		/* reset */
4531fa9e4066Sahrens 	&zfs_cb_ops,	/* driver operations */
453219397407SSherry Moore 	NULL,		/* no bus operations */
453319397407SSherry Moore 	NULL,		/* power */
453419397407SSherry Moore 	ddi_quiesce_not_needed,	/* quiesce */
4535fa9e4066Sahrens };
4536fa9e4066Sahrens 
4537fa9e4066Sahrens static struct modldrv zfs_modldrv = {
453819397407SSherry Moore 	&mod_driverops,
453919397407SSherry Moore 	"ZFS storage pool",
454019397407SSherry Moore 	&zfs_dev_ops
4541fa9e4066Sahrens };
4542fa9e4066Sahrens 
4543fa9e4066Sahrens static struct modlinkage modlinkage = {
4544fa9e4066Sahrens 	MODREV_1,
4545fa9e4066Sahrens 	(void *)&zfs_modlfs,
4546fa9e4066Sahrens 	(void *)&zfs_modldrv,
4547fa9e4066Sahrens 	NULL
4548fa9e4066Sahrens };
4549fa9e4066Sahrens 
4550ec533521Sfr 
4551ec533521Sfr uint_t zfs_fsyncer_key;
4552f18faf3fSek extern uint_t rrw_tsd_key;
4553ec533521Sfr 
4554fa9e4066Sahrens int
4555fa9e4066Sahrens _init(void)
4556fa9e4066Sahrens {
4557fa9e4066Sahrens 	int error;
4558fa9e4066Sahrens 
4559a0965f35Sbonwick 	spa_init(FREAD | FWRITE);
4560a0965f35Sbonwick 	zfs_init();
4561a0965f35Sbonwick 	zvol_init();
4562a0965f35Sbonwick 
4563a0965f35Sbonwick 	if ((error = mod_install(&modlinkage)) != 0) {
4564a0965f35Sbonwick 		zvol_fini();
4565a0965f35Sbonwick 		zfs_fini();
4566a0965f35Sbonwick 		spa_fini();
4567fa9e4066Sahrens 		return (error);
4568a0965f35Sbonwick 	}
4569fa9e4066Sahrens 
4570ec533521Sfr 	tsd_create(&zfs_fsyncer_key, NULL);
4571f18faf3fSek 	tsd_create(&rrw_tsd_key, NULL);
4572ec533521Sfr 
4573fa9e4066Sahrens 	error = ldi_ident_from_mod(&modlinkage, &zfs_li);
4574fa9e4066Sahrens 	ASSERT(error == 0);
4575ecd6cf80Smarks 	mutex_init(&zfs_share_lock, NULL, MUTEX_DEFAULT, NULL);
4576fa9e4066Sahrens 
4577fa9e4066Sahrens 	return (0);
4578fa9e4066Sahrens }
4579fa9e4066Sahrens 
4580fa9e4066Sahrens int
4581fa9e4066Sahrens _fini(void)
4582fa9e4066Sahrens {
4583fa9e4066Sahrens 	int error;
4584fa9e4066Sahrens 
4585ea8dc4b6Seschrock 	if (spa_busy() || zfs_busy() || zvol_busy() || zio_injection_enabled)
4586fa9e4066Sahrens 		return (EBUSY);
4587fa9e4066Sahrens 
4588fa9e4066Sahrens 	if ((error = mod_remove(&modlinkage)) != 0)
4589fa9e4066Sahrens 		return (error);
4590fa9e4066Sahrens 
4591fa9e4066Sahrens 	zvol_fini();
4592fa9e4066Sahrens 	zfs_fini();
4593fa9e4066Sahrens 	spa_fini();
4594da6c28aaSamw 	if (zfs_nfsshare_inited)
4595ecd6cf80Smarks 		(void) ddi_modclose(nfs_mod);
4596da6c28aaSamw 	if (zfs_smbshare_inited)
4597da6c28aaSamw 		(void) ddi_modclose(smbsrv_mod);
4598da6c28aaSamw 	if (zfs_nfsshare_inited || zfs_smbshare_inited)
4599ecd6cf80Smarks 		(void) ddi_modclose(sharefs_mod);
4600fa9e4066Sahrens 
4601ec533521Sfr 	tsd_destroy(&zfs_fsyncer_key);
4602fa9e4066Sahrens 	ldi_ident_release(zfs_li);
4603fa9e4066Sahrens 	zfs_li = NULL;
4604ecd6cf80Smarks 	mutex_destroy(&zfs_share_lock);
4605fa9e4066Sahrens 
4606fa9e4066Sahrens 	return (error);
4607fa9e4066Sahrens }
4608fa9e4066Sahrens 
4609fa9e4066Sahrens int
4610fa9e4066Sahrens _info(struct modinfo *modinfop)
4611fa9e4066Sahrens {
4612fa9e4066Sahrens 	return (mod_info(&modlinkage, modinfop));
4613fa9e4066Sahrens }
4614