xref: /illumos-gate/usr/src/uts/common/fs/zfs/zfs_ioctl.c (revision c99e4bdc)
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 /*
223f9d6ad7SLin 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>
63*c99e4bdcSChris Kirby #include <sys/zfs_onexit.h>
64a2eea2e1Sahrens #include <sys/zvol.h>
653f9d6ad7SLin Ling #include <sys/dsl_scan.h>
66ecd6cf80Smarks #include <sharefs/share.h>
67f18faf3fSek #include <sys/dmu_objset.h>
68fa9e4066Sahrens 
69fa9e4066Sahrens #include "zfs_namecheck.h"
70e9dbad6fSeschrock #include "zfs_prop.h"
71ecd6cf80Smarks #include "zfs_deleg.h"
720a586ceaSMark Shellenbaum #include "zfs_comutil.h"
73fa9e4066Sahrens 
74fa9e4066Sahrens extern struct modlfs zfs_modlfs;
75fa9e4066Sahrens 
76fa9e4066Sahrens extern void zfs_init(void);
77fa9e4066Sahrens extern void zfs_fini(void);
78fa9e4066Sahrens 
79fa9e4066Sahrens ldi_ident_t zfs_li = NULL;
80fa9e4066Sahrens dev_info_t *zfs_dip;
81fa9e4066Sahrens 
82fa9e4066Sahrens typedef int zfs_ioc_func_t(zfs_cmd_t *);
83ecd6cf80Smarks typedef int zfs_secpolicy_func_t(zfs_cmd_t *, cred_t *);
84fa9e4066Sahrens 
8554d692b7SGeorge Wilson typedef enum {
8654d692b7SGeorge Wilson 	NO_NAME,
8754d692b7SGeorge Wilson 	POOL_NAME,
8854d692b7SGeorge Wilson 	DATASET_NAME
8954d692b7SGeorge Wilson } zfs_ioc_namecheck_t;
9054d692b7SGeorge Wilson 
91fa9e4066Sahrens typedef struct zfs_ioc_vec {
92fa9e4066Sahrens 	zfs_ioc_func_t		*zvec_func;
93fa9e4066Sahrens 	zfs_secpolicy_func_t	*zvec_secpolicy;
9454d692b7SGeorge Wilson 	zfs_ioc_namecheck_t	zvec_namecheck;
95ecd6cf80Smarks 	boolean_t		zvec_his_log;
9654d692b7SGeorge Wilson 	boolean_t		zvec_pool_check;
97fa9e4066Sahrens } zfs_ioc_vec_t;
98fa9e4066Sahrens 
9914843421SMatthew Ahrens /* This array is indexed by zfs_userquota_prop_t */
10014843421SMatthew Ahrens static const char *userquota_perms[] = {
10114843421SMatthew Ahrens 	ZFS_DELEG_PERM_USERUSED,
10214843421SMatthew Ahrens 	ZFS_DELEG_PERM_USERQUOTA,
10314843421SMatthew Ahrens 	ZFS_DELEG_PERM_GROUPUSED,
10414843421SMatthew Ahrens 	ZFS_DELEG_PERM_GROUPQUOTA,
10514843421SMatthew Ahrens };
10614843421SMatthew Ahrens 
10714843421SMatthew Ahrens static int zfs_ioc_userspace_upgrade(zfs_cmd_t *zc);
10892241e0bSTom Erickson static int zfs_check_settable(const char *name, nvpair_t *property,
10992241e0bSTom Erickson     cred_t *cr);
11092241e0bSTom Erickson static int zfs_check_clearable(char *dataset, nvlist_t *props,
11192241e0bSTom Erickson     nvlist_t **errors);
1120a48a24eStimh static int zfs_fill_zplprops_root(uint64_t, nvlist_t *, nvlist_t *,
1130a48a24eStimh     boolean_t *);
11492241e0bSTom Erickson int zfs_set_prop_nvlist(const char *, zprop_source_t, nvlist_t *, nvlist_t **);
1150a48a24eStimh 
116fa9e4066Sahrens /* _NOTE(PRINTFLIKE(4)) - this is printf-like, but lint is too whiney */
117fa9e4066Sahrens void
118fa9e4066Sahrens __dprintf(const char *file, const char *func, int line, const char *fmt, ...)
119fa9e4066Sahrens {
120fa9e4066Sahrens 	const char *newfile;
1213f9d6ad7SLin Ling 	char buf[512];
122fa9e4066Sahrens 	va_list adx;
123fa9e4066Sahrens 
124fa9e4066Sahrens 	/*
125fa9e4066Sahrens 	 * Get rid of annoying "../common/" prefix to filename.
126fa9e4066Sahrens 	 */
127fa9e4066Sahrens 	newfile = strrchr(file, '/');
128fa9e4066Sahrens 	if (newfile != NULL) {
129fa9e4066Sahrens 		newfile = newfile + 1; /* Get rid of leading / */
130fa9e4066Sahrens 	} else {
131fa9e4066Sahrens 		newfile = file;
132fa9e4066Sahrens 	}
133fa9e4066Sahrens 
134fa9e4066Sahrens 	va_start(adx, fmt);
135fa9e4066Sahrens 	(void) vsnprintf(buf, sizeof (buf), fmt, adx);
136fa9e4066Sahrens 	va_end(adx);
137fa9e4066Sahrens 
138fa9e4066Sahrens 	/*
139fa9e4066Sahrens 	 * To get this data, use the zfs-dprintf probe as so:
140fa9e4066Sahrens 	 * dtrace -q -n 'zfs-dprintf \
141fa9e4066Sahrens 	 *	/stringof(arg0) == "dbuf.c"/ \
142fa9e4066Sahrens 	 *	{printf("%s: %s", stringof(arg1), stringof(arg3))}'
143fa9e4066Sahrens 	 * arg0 = file name
144fa9e4066Sahrens 	 * arg1 = function name
145fa9e4066Sahrens 	 * arg2 = line number
146fa9e4066Sahrens 	 * arg3 = message
147fa9e4066Sahrens 	 */
148fa9e4066Sahrens 	DTRACE_PROBE4(zfs__dprintf,
149fa9e4066Sahrens 	    char *, newfile, char *, func, int, line, char *, buf);
150fa9e4066Sahrens }
151fa9e4066Sahrens 
152ecd6cf80Smarks static void
153228975ccSek history_str_free(char *buf)
154228975ccSek {
155228975ccSek 	kmem_free(buf, HIS_MAX_RECORD_LEN);
156228975ccSek }
157228975ccSek 
158228975ccSek static char *
159228975ccSek history_str_get(zfs_cmd_t *zc)
160ecd6cf80Smarks {
16140feaa91Sahrens 	char *buf;
162ecd6cf80Smarks 
163ecd6cf80Smarks 	if (zc->zc_history == NULL)
164228975ccSek 		return (NULL);
165e7437265Sahrens 
166ecd6cf80Smarks 	buf = kmem_alloc(HIS_MAX_RECORD_LEN, KM_SLEEP);
167ecd6cf80Smarks 	if (copyinstr((void *)(uintptr_t)zc->zc_history,
168ecd6cf80Smarks 	    buf, HIS_MAX_RECORD_LEN, NULL) != 0) {
169228975ccSek 		history_str_free(buf);
170228975ccSek 		return (NULL);
171ecd6cf80Smarks 	}
172ecd6cf80Smarks 
173ecd6cf80Smarks 	buf[HIS_MAX_RECORD_LEN -1] = '\0';
174ecd6cf80Smarks 
175228975ccSek 	return (buf);
176228975ccSek }
177ecd6cf80Smarks 
17815e6edf1Sgw /*
17915e6edf1Sgw  * Check to see if the named dataset is currently defined as bootable
18015e6edf1Sgw  */
18115e6edf1Sgw static boolean_t
18215e6edf1Sgw zfs_is_bootfs(const char *name)
18315e6edf1Sgw {
184503ad85cSMatthew Ahrens 	objset_t *os;
18515e6edf1Sgw 
186503ad85cSMatthew Ahrens 	if (dmu_objset_hold(name, FTAG, &os) == 0) {
187503ad85cSMatthew Ahrens 		boolean_t ret;
188b24ab676SJeff Bonwick 		ret = (dmu_objset_id(os) == spa_bootfs(dmu_objset_spa(os)));
189503ad85cSMatthew Ahrens 		dmu_objset_rele(os, FTAG);
190503ad85cSMatthew Ahrens 		return (ret);
19115e6edf1Sgw 	}
192503ad85cSMatthew Ahrens 	return (B_FALSE);
19315e6edf1Sgw }
19415e6edf1Sgw 
195c2a93d44Stimh /*
1960a48a24eStimh  * zfs_earlier_version
197c2a93d44Stimh  *
198c2a93d44Stimh  *	Return non-zero if the spa version is less than requested version.
199c2a93d44Stimh  */
200da6c28aaSamw static int
2010a48a24eStimh zfs_earlier_version(const char *name, int version)
202da6c28aaSamw {
203da6c28aaSamw 	spa_t *spa;
204da6c28aaSamw 
205da6c28aaSamw 	if (spa_open(name, &spa, FTAG) == 0) {
206da6c28aaSamw 		if (spa_version(spa) < version) {
207da6c28aaSamw 			spa_close(spa, FTAG);
208da6c28aaSamw 			return (1);
209da6c28aaSamw 		}
210da6c28aaSamw 		spa_close(spa, FTAG);
211da6c28aaSamw 	}
212da6c28aaSamw 	return (0);
213da6c28aaSamw }
214da6c28aaSamw 
2159e6eda55Smarks /*
216745cd3c5Smaybee  * zpl_earlier_version
2179e6eda55Smarks  *
218745cd3c5Smaybee  * Return TRUE if the ZPL version is less than requested version.
2199e6eda55Smarks  */
220745cd3c5Smaybee static boolean_t
221745cd3c5Smaybee zpl_earlier_version(const char *name, int version)
2229e6eda55Smarks {
2239e6eda55Smarks 	objset_t *os;
224745cd3c5Smaybee 	boolean_t rc = B_TRUE;
2259e6eda55Smarks 
226503ad85cSMatthew Ahrens 	if (dmu_objset_hold(name, FTAG, &os) == 0) {
227745cd3c5Smaybee 		uint64_t zplversion;
2289e6eda55Smarks 
229503ad85cSMatthew Ahrens 		if (dmu_objset_type(os) != DMU_OST_ZFS) {
230503ad85cSMatthew Ahrens 			dmu_objset_rele(os, FTAG);
231503ad85cSMatthew Ahrens 			return (B_TRUE);
232503ad85cSMatthew Ahrens 		}
233503ad85cSMatthew Ahrens 		/* XXX reading from non-owned objset */
234745cd3c5Smaybee 		if (zfs_get_zplprop(os, ZFS_PROP_VERSION, &zplversion) == 0)
235745cd3c5Smaybee 			rc = zplversion < version;
236503ad85cSMatthew Ahrens 		dmu_objset_rele(os, FTAG);
2379e6eda55Smarks 	}
2389e6eda55Smarks 	return (rc);
2399e6eda55Smarks }
2409e6eda55Smarks 
241228975ccSek static void
242228975ccSek zfs_log_history(zfs_cmd_t *zc)
243228975ccSek {
244228975ccSek 	spa_t *spa;
245228975ccSek 	char *buf;
246ecd6cf80Smarks 
247228975ccSek 	if ((buf = history_str_get(zc)) == NULL)
248228975ccSek 		return;
249228975ccSek 
250228975ccSek 	if (spa_open(zc->zc_name, &spa, FTAG) == 0) {
251228975ccSek 		if (spa_version(spa) >= SPA_VERSION_ZPOOL_HISTORY)
252228975ccSek 			(void) spa_history_log(spa, buf, LOG_CMD_NORMAL);
253228975ccSek 		spa_close(spa, FTAG);
254228975ccSek 	}
255228975ccSek 	history_str_free(buf);
256ecd6cf80Smarks }
257ecd6cf80Smarks 
258fa9e4066Sahrens /*
259fa9e4066Sahrens  * Policy for top-level read operations (list pools).  Requires no privileges,
260fa9e4066Sahrens  * and can be used in the local zone, as there is no associated dataset.
261fa9e4066Sahrens  */
262fa9e4066Sahrens /* ARGSUSED */
263fa9e4066Sahrens static int
264ecd6cf80Smarks zfs_secpolicy_none(zfs_cmd_t *zc, cred_t *cr)
265fa9e4066Sahrens {
266fa9e4066Sahrens 	return (0);
267fa9e4066Sahrens }
268fa9e4066Sahrens 
269fa9e4066Sahrens /*
270fa9e4066Sahrens  * Policy for dataset read operations (list children, get statistics).  Requires
271fa9e4066Sahrens  * no privileges, but must be visible in the local zone.
272fa9e4066Sahrens  */
273fa9e4066Sahrens /* ARGSUSED */
274fa9e4066Sahrens static int
275ecd6cf80Smarks zfs_secpolicy_read(zfs_cmd_t *zc, cred_t *cr)
276fa9e4066Sahrens {
277fa9e4066Sahrens 	if (INGLOBALZONE(curproc) ||
278ecd6cf80Smarks 	    zone_dataset_visible(zc->zc_name, NULL))
279fa9e4066Sahrens 		return (0);
280fa9e4066Sahrens 
281fa9e4066Sahrens 	return (ENOENT);
282fa9e4066Sahrens }
283fa9e4066Sahrens 
284fa9e4066Sahrens static int
285fa9e4066Sahrens zfs_dozonecheck(const char *dataset, cred_t *cr)
286fa9e4066Sahrens {
287fa9e4066Sahrens 	uint64_t zoned;
288fa9e4066Sahrens 	int writable = 1;
289fa9e4066Sahrens 
290fa9e4066Sahrens 	/*
291fa9e4066Sahrens 	 * The dataset must be visible by this zone -- check this first
292fa9e4066Sahrens 	 * so they don't see EPERM on something they shouldn't know about.
293fa9e4066Sahrens 	 */
294fa9e4066Sahrens 	if (!INGLOBALZONE(curproc) &&
295fa9e4066Sahrens 	    !zone_dataset_visible(dataset, &writable))
296fa9e4066Sahrens 		return (ENOENT);
297fa9e4066Sahrens 
298fa9e4066Sahrens 	if (dsl_prop_get_integer(dataset, "zoned", &zoned, NULL))
299fa9e4066Sahrens 		return (ENOENT);
300fa9e4066Sahrens 
301fa9e4066Sahrens 	if (INGLOBALZONE(curproc)) {
302fa9e4066Sahrens 		/*
303fa9e4066Sahrens 		 * If the fs is zoned, only root can access it from the
304fa9e4066Sahrens 		 * global zone.
305fa9e4066Sahrens 		 */
306fa9e4066Sahrens 		if (secpolicy_zfs(cr) && zoned)
307fa9e4066Sahrens 			return (EPERM);
308fa9e4066Sahrens 	} else {
309fa9e4066Sahrens 		/*
310fa9e4066Sahrens 		 * If we are in a local zone, the 'zoned' property must be set.
311fa9e4066Sahrens 		 */
312fa9e4066Sahrens 		if (!zoned)
313fa9e4066Sahrens 			return (EPERM);
314fa9e4066Sahrens 
315fa9e4066Sahrens 		/* must be writable by this zone */
316fa9e4066Sahrens 		if (!writable)
317fa9e4066Sahrens 			return (EPERM);
318fa9e4066Sahrens 	}
319fa9e4066Sahrens 	return (0);
320fa9e4066Sahrens }
321fa9e4066Sahrens 
322fa9e4066Sahrens int
323ecd6cf80Smarks zfs_secpolicy_write_perms(const char *name, const char *perm, cred_t *cr)
324fa9e4066Sahrens {
325fa9e4066Sahrens 	int error;
326fa9e4066Sahrens 
327ecd6cf80Smarks 	error = zfs_dozonecheck(name, cr);
328ecd6cf80Smarks 	if (error == 0) {
329ecd6cf80Smarks 		error = secpolicy_zfs(cr);
330db870a07Sahrens 		if (error)
331ecd6cf80Smarks 			error = dsl_deleg_access(name, perm, cr);
332ecd6cf80Smarks 	}
333ecd6cf80Smarks 	return (error);
334ecd6cf80Smarks }
335ecd6cf80Smarks 
3364201a95eSRic Aleshire /*
3374201a95eSRic Aleshire  * Policy for setting the security label property.
3384201a95eSRic Aleshire  *
3394201a95eSRic Aleshire  * Returns 0 for success, non-zero for access and other errors.
3404201a95eSRic Aleshire  */
3414201a95eSRic Aleshire static int
34292241e0bSTom Erickson zfs_set_slabel_policy(const char *name, char *strval, cred_t *cr)
3434201a95eSRic Aleshire {
3444201a95eSRic Aleshire 	char		ds_hexsl[MAXNAMELEN];
3454201a95eSRic Aleshire 	bslabel_t	ds_sl, new_sl;
3464201a95eSRic Aleshire 	boolean_t	new_default = FALSE;
3474201a95eSRic Aleshire 	uint64_t	zoned;
3484201a95eSRic Aleshire 	int		needed_priv = -1;
3494201a95eSRic Aleshire 	int		error;
3504201a95eSRic Aleshire 
3514201a95eSRic Aleshire 	/* First get the existing dataset label. */
3524201a95eSRic Aleshire 	error = dsl_prop_get(name, zfs_prop_to_name(ZFS_PROP_MLSLABEL),
3534201a95eSRic Aleshire 	    1, sizeof (ds_hexsl), &ds_hexsl, NULL);
3544201a95eSRic Aleshire 	if (error)
3554201a95eSRic Aleshire 		return (EPERM);
3564201a95eSRic Aleshire 
3574201a95eSRic Aleshire 	if (strcasecmp(strval, ZFS_MLSLABEL_DEFAULT) == 0)
3584201a95eSRic Aleshire 		new_default = TRUE;
3594201a95eSRic Aleshire 
3604201a95eSRic Aleshire 	/* The label must be translatable */
3614201a95eSRic Aleshire 	if (!new_default && (hexstr_to_label(strval, &new_sl) != 0))
3624201a95eSRic Aleshire 		return (EINVAL);
3634201a95eSRic Aleshire 
3644201a95eSRic Aleshire 	/*
3654201a95eSRic Aleshire 	 * In a non-global zone, disallow attempts to set a label that
3664201a95eSRic Aleshire 	 * doesn't match that of the zone; otherwise no other checks
3674201a95eSRic Aleshire 	 * are needed.
3684201a95eSRic Aleshire 	 */
3694201a95eSRic Aleshire 	if (!INGLOBALZONE(curproc)) {
3704201a95eSRic Aleshire 		if (new_default || !blequal(&new_sl, CR_SL(CRED())))
3714201a95eSRic Aleshire 			return (EPERM);
3724201a95eSRic Aleshire 		return (0);
3734201a95eSRic Aleshire 	}
3744201a95eSRic Aleshire 
3754201a95eSRic Aleshire 	/*
3764201a95eSRic Aleshire 	 * For global-zone datasets (i.e., those whose zoned property is
3774201a95eSRic Aleshire 	 * "off", verify that the specified new label is valid for the
3784201a95eSRic Aleshire 	 * global zone.
3794201a95eSRic Aleshire 	 */
3804201a95eSRic Aleshire 	if (dsl_prop_get_integer(name,
3814201a95eSRic Aleshire 	    zfs_prop_to_name(ZFS_PROP_ZONED), &zoned, NULL))
3824201a95eSRic Aleshire 		return (EPERM);
3834201a95eSRic Aleshire 	if (!zoned) {
3844201a95eSRic Aleshire 		if (zfs_check_global_label(name, strval) != 0)
3854201a95eSRic Aleshire 			return (EPERM);
3864201a95eSRic Aleshire 	}
3874201a95eSRic Aleshire 
3884201a95eSRic Aleshire 	/*
3894201a95eSRic Aleshire 	 * If the existing dataset label is nondefault, check if the
3904201a95eSRic Aleshire 	 * dataset is mounted (label cannot be changed while mounted).
3914201a95eSRic Aleshire 	 * Get the zfsvfs; if there isn't one, then the dataset isn't
3924201a95eSRic Aleshire 	 * mounted (or isn't a dataset, doesn't exist, ...).
3934201a95eSRic Aleshire 	 */
3944201a95eSRic Aleshire 	if (strcasecmp(ds_hexsl, ZFS_MLSLABEL_DEFAULT) != 0) {
39592241e0bSTom Erickson 		objset_t *os;
39692241e0bSTom Erickson 		static char *setsl_tag = "setsl_tag";
39792241e0bSTom Erickson 
3984201a95eSRic Aleshire 		/*
3994201a95eSRic Aleshire 		 * Try to own the dataset; abort if there is any error,
4004201a95eSRic Aleshire 		 * (e.g., already mounted, in use, or other error).
4014201a95eSRic Aleshire 		 */
4024201a95eSRic Aleshire 		error = dmu_objset_own(name, DMU_OST_ZFS, B_TRUE,
40392241e0bSTom Erickson 		    setsl_tag, &os);
4044201a95eSRic Aleshire 		if (error)
4054201a95eSRic Aleshire 			return (EPERM);
4064201a95eSRic Aleshire 
40792241e0bSTom Erickson 		dmu_objset_disown(os, setsl_tag);
40892241e0bSTom Erickson 
4094201a95eSRic Aleshire 		if (new_default) {
4104201a95eSRic Aleshire 			needed_priv = PRIV_FILE_DOWNGRADE_SL;
4114201a95eSRic Aleshire 			goto out_check;
4124201a95eSRic Aleshire 		}
4134201a95eSRic Aleshire 
4144201a95eSRic Aleshire 		if (hexstr_to_label(strval, &new_sl) != 0)
4154201a95eSRic Aleshire 			return (EPERM);
4164201a95eSRic Aleshire 
4174201a95eSRic Aleshire 		if (blstrictdom(&ds_sl, &new_sl))
4184201a95eSRic Aleshire 			needed_priv = PRIV_FILE_DOWNGRADE_SL;
4194201a95eSRic Aleshire 		else if (blstrictdom(&new_sl, &ds_sl))
4204201a95eSRic Aleshire 			needed_priv = PRIV_FILE_UPGRADE_SL;
4214201a95eSRic Aleshire 	} else {
4224201a95eSRic Aleshire 		/* dataset currently has a default label */
4234201a95eSRic Aleshire 		if (!new_default)
4244201a95eSRic Aleshire 			needed_priv = PRIV_FILE_UPGRADE_SL;
4254201a95eSRic Aleshire 	}
4264201a95eSRic Aleshire 
4274201a95eSRic Aleshire out_check:
4284201a95eSRic Aleshire 	if (needed_priv != -1)
4294201a95eSRic Aleshire 		return (PRIV_POLICY(cr, needed_priv, B_FALSE, EPERM, NULL));
4304201a95eSRic Aleshire 	return (0);
4314201a95eSRic Aleshire }
4324201a95eSRic Aleshire 
433ecd6cf80Smarks static int
43492241e0bSTom Erickson zfs_secpolicy_setprop(const char *dsname, zfs_prop_t prop, nvpair_t *propval,
43592241e0bSTom Erickson     cred_t *cr)
436ecd6cf80Smarks {
43792241e0bSTom Erickson 	char *strval;
43892241e0bSTom Erickson 
439ecd6cf80Smarks 	/*
440ecd6cf80Smarks 	 * Check permissions for special properties.
441ecd6cf80Smarks 	 */
442ecd6cf80Smarks 	switch (prop) {
443ecd6cf80Smarks 	case ZFS_PROP_ZONED:
444ecd6cf80Smarks 		/*
445ecd6cf80Smarks 		 * Disallow setting of 'zoned' from within a local zone.
446ecd6cf80Smarks 		 */
447ecd6cf80Smarks 		if (!INGLOBALZONE(curproc))
448ecd6cf80Smarks 			return (EPERM);
449ecd6cf80Smarks 		break;
450ecd6cf80Smarks 
451ecd6cf80Smarks 	case ZFS_PROP_QUOTA:
452ecd6cf80Smarks 		if (!INGLOBALZONE(curproc)) {
453ecd6cf80Smarks 			uint64_t zoned;
454ecd6cf80Smarks 			char setpoint[MAXNAMELEN];
455ecd6cf80Smarks 			/*
456ecd6cf80Smarks 			 * Unprivileged users are allowed to modify the
457ecd6cf80Smarks 			 * quota on things *under* (ie. contained by)
458ecd6cf80Smarks 			 * the thing they own.
459ecd6cf80Smarks 			 */
46092241e0bSTom Erickson 			if (dsl_prop_get_integer(dsname, "zoned", &zoned,
461ecd6cf80Smarks 			    setpoint))
462ecd6cf80Smarks 				return (EPERM);
46392241e0bSTom Erickson 			if (!zoned || strlen(dsname) <= strlen(setpoint))
464ecd6cf80Smarks 				return (EPERM);
465ecd6cf80Smarks 		}
466db870a07Sahrens 		break;
4674201a95eSRic Aleshire 
4684201a95eSRic Aleshire 	case ZFS_PROP_MLSLABEL:
4694201a95eSRic Aleshire 		if (!is_system_labeled())
4704201a95eSRic Aleshire 			return (EPERM);
47192241e0bSTom Erickson 
47292241e0bSTom Erickson 		if (nvpair_value_string(propval, &strval) == 0) {
47392241e0bSTom Erickson 			int err;
47492241e0bSTom Erickson 
47592241e0bSTom Erickson 			err = zfs_set_slabel_policy(dsname, strval, CRED());
47692241e0bSTom Erickson 			if (err != 0)
47792241e0bSTom Erickson 				return (err);
47892241e0bSTom Erickson 		}
4794201a95eSRic Aleshire 		break;
480ecd6cf80Smarks 	}
481ecd6cf80Smarks 
48292241e0bSTom Erickson 	return (zfs_secpolicy_write_perms(dsname, zfs_prop_to_name(prop), cr));
483ecd6cf80Smarks }
484ecd6cf80Smarks 
485ecd6cf80Smarks int
486ecd6cf80Smarks zfs_secpolicy_fsacl(zfs_cmd_t *zc, cred_t *cr)
487ecd6cf80Smarks {
488ecd6cf80Smarks 	int error;
489ecd6cf80Smarks 
490ecd6cf80Smarks 	error = zfs_dozonecheck(zc->zc_name, cr);
491ecd6cf80Smarks 	if (error)
492fa9e4066Sahrens 		return (error);
493fa9e4066Sahrens 
494ecd6cf80Smarks 	/*
495ecd6cf80Smarks 	 * permission to set permissions will be evaluated later in
496ecd6cf80Smarks 	 * dsl_deleg_can_allow()
497ecd6cf80Smarks 	 */
498ecd6cf80Smarks 	return (0);
499ecd6cf80Smarks }
500ecd6cf80Smarks 
501ecd6cf80Smarks int
502ecd6cf80Smarks zfs_secpolicy_rollback(zfs_cmd_t *zc, cred_t *cr)
503ecd6cf80Smarks {
504681d9761SEric Taylor 	return (zfs_secpolicy_write_perms(zc->zc_name,
505681d9761SEric Taylor 	    ZFS_DELEG_PERM_ROLLBACK, cr));
506ecd6cf80Smarks }
507ecd6cf80Smarks 
508ecd6cf80Smarks int
509ecd6cf80Smarks zfs_secpolicy_send(zfs_cmd_t *zc, cred_t *cr)
510ecd6cf80Smarks {
511ecd6cf80Smarks 	return (zfs_secpolicy_write_perms(zc->zc_name,
512ecd6cf80Smarks 	    ZFS_DELEG_PERM_SEND, cr));
513ecd6cf80Smarks }
514ecd6cf80Smarks 
515743a77edSAlan Wright static int
516743a77edSAlan Wright zfs_secpolicy_deleg_share(zfs_cmd_t *zc, cred_t *cr)
517743a77edSAlan Wright {
518743a77edSAlan Wright 	vnode_t *vp;
519743a77edSAlan Wright 	int error;
520743a77edSAlan Wright 
521743a77edSAlan Wright 	if ((error = lookupname(zc->zc_value, UIO_SYSSPACE,
522743a77edSAlan Wright 	    NO_FOLLOW, NULL, &vp)) != 0)
523743a77edSAlan Wright 		return (error);
524743a77edSAlan Wright 
525743a77edSAlan Wright 	/* Now make sure mntpnt and dataset are ZFS */
526743a77edSAlan Wright 
527743a77edSAlan Wright 	if (vp->v_vfsp->vfs_fstype != zfsfstype ||
528743a77edSAlan Wright 	    (strcmp((char *)refstr_value(vp->v_vfsp->vfs_resource),
529743a77edSAlan Wright 	    zc->zc_name) != 0)) {
530743a77edSAlan Wright 		VN_RELE(vp);
531743a77edSAlan Wright 		return (EPERM);
532743a77edSAlan Wright 	}
533743a77edSAlan Wright 
534743a77edSAlan Wright 	VN_RELE(vp);
535743a77edSAlan Wright 	return (dsl_deleg_access(zc->zc_name,
536743a77edSAlan Wright 	    ZFS_DELEG_PERM_SHARE, cr));
537743a77edSAlan Wright }
538743a77edSAlan Wright 
539ecd6cf80Smarks int
540ecd6cf80Smarks zfs_secpolicy_share(zfs_cmd_t *zc, cred_t *cr)
541ecd6cf80Smarks {
542ecd6cf80Smarks 	if (!INGLOBALZONE(curproc))
543ecd6cf80Smarks 		return (EPERM);
544ecd6cf80Smarks 
5453cb34c60Sahrens 	if (secpolicy_nfs(cr) == 0) {
546ecd6cf80Smarks 		return (0);
547ecd6cf80Smarks 	} else {
548743a77edSAlan Wright 		return (zfs_secpolicy_deleg_share(zc, cr));
549743a77edSAlan Wright 	}
550743a77edSAlan Wright }
551ecd6cf80Smarks 
552743a77edSAlan Wright int
553743a77edSAlan Wright zfs_secpolicy_smb_acl(zfs_cmd_t *zc, cred_t *cr)
554743a77edSAlan Wright {
555743a77edSAlan Wright 	if (!INGLOBALZONE(curproc))
556743a77edSAlan Wright 		return (EPERM);
557ecd6cf80Smarks 
558743a77edSAlan Wright 	if (secpolicy_smb(cr) == 0) {
559743a77edSAlan Wright 		return (0);
560743a77edSAlan Wright 	} else {
561743a77edSAlan Wright 		return (zfs_secpolicy_deleg_share(zc, cr));
562ecd6cf80Smarks 	}
563fa9e4066Sahrens }
564fa9e4066Sahrens 
565fa9e4066Sahrens static int
566ecd6cf80Smarks zfs_get_parent(const char *datasetname, char *parent, int parentsize)
567fa9e4066Sahrens {
568fa9e4066Sahrens 	char *cp;
569fa9e4066Sahrens 
570fa9e4066Sahrens 	/*
571fa9e4066Sahrens 	 * Remove the @bla or /bla from the end of the name to get the parent.
572fa9e4066Sahrens 	 */
573ecd6cf80Smarks 	(void) strncpy(parent, datasetname, parentsize);
574ecd6cf80Smarks 	cp = strrchr(parent, '@');
575fa9e4066Sahrens 	if (cp != NULL) {
576fa9e4066Sahrens 		cp[0] = '\0';
577fa9e4066Sahrens 	} else {
578ecd6cf80Smarks 		cp = strrchr(parent, '/');
579fa9e4066Sahrens 		if (cp == NULL)
580fa9e4066Sahrens 			return (ENOENT);
581fa9e4066Sahrens 		cp[0] = '\0';
582ecd6cf80Smarks 	}
583ecd6cf80Smarks 
584ecd6cf80Smarks 	return (0);
585ecd6cf80Smarks }
586ecd6cf80Smarks 
587ecd6cf80Smarks int
588ecd6cf80Smarks zfs_secpolicy_destroy_perms(const char *name, cred_t *cr)
589ecd6cf80Smarks {
590ecd6cf80Smarks 	int error;
591ecd6cf80Smarks 
592ecd6cf80Smarks 	if ((error = zfs_secpolicy_write_perms(name,
593ecd6cf80Smarks 	    ZFS_DELEG_PERM_MOUNT, cr)) != 0)
594ecd6cf80Smarks 		return (error);
595ecd6cf80Smarks 
596ecd6cf80Smarks 	return (zfs_secpolicy_write_perms(name, ZFS_DELEG_PERM_DESTROY, cr));
597ecd6cf80Smarks }
598ecd6cf80Smarks 
599ecd6cf80Smarks static int
600ecd6cf80Smarks zfs_secpolicy_destroy(zfs_cmd_t *zc, cred_t *cr)
601ecd6cf80Smarks {
602ecd6cf80Smarks 	return (zfs_secpolicy_destroy_perms(zc->zc_name, cr));
603ecd6cf80Smarks }
604ecd6cf80Smarks 
605cbf6f6aaSWilliam Gorrell /*
606cbf6f6aaSWilliam Gorrell  * Destroying snapshots with delegated permissions requires
607cbf6f6aaSWilliam Gorrell  * descendent mount and destroy permissions.
608cbf6f6aaSWilliam Gorrell  * Reassemble the full filesystem@snap name so dsl_deleg_access()
609cbf6f6aaSWilliam Gorrell  * can do the correct permission check.
610cbf6f6aaSWilliam Gorrell  *
611cbf6f6aaSWilliam Gorrell  * Since this routine is used when doing a recursive destroy of snapshots
612cbf6f6aaSWilliam Gorrell  * and destroying snapshots requires descendent permissions, a successfull
613cbf6f6aaSWilliam Gorrell  * check of the top level snapshot applies to snapshots of all descendent
614cbf6f6aaSWilliam Gorrell  * datasets as well.
615cbf6f6aaSWilliam Gorrell  */
616cbf6f6aaSWilliam Gorrell static int
617cbf6f6aaSWilliam Gorrell zfs_secpolicy_destroy_snaps(zfs_cmd_t *zc, cred_t *cr)
618cbf6f6aaSWilliam Gorrell {
619cbf6f6aaSWilliam Gorrell 	int error;
620cbf6f6aaSWilliam Gorrell 	char *dsname;
621cbf6f6aaSWilliam Gorrell 
622cbf6f6aaSWilliam Gorrell 	dsname = kmem_asprintf("%s@%s", zc->zc_name, zc->zc_value);
623cbf6f6aaSWilliam Gorrell 
624cbf6f6aaSWilliam Gorrell 	error = zfs_secpolicy_destroy_perms(dsname, cr);
625cbf6f6aaSWilliam Gorrell 
626cbf6f6aaSWilliam Gorrell 	strfree(dsname);
627cbf6f6aaSWilliam Gorrell 	return (error);
628cbf6f6aaSWilliam Gorrell }
629cbf6f6aaSWilliam Gorrell 
630ecd6cf80Smarks int
631ecd6cf80Smarks zfs_secpolicy_rename_perms(const char *from, const char *to, cred_t *cr)
632ecd6cf80Smarks {
63392241e0bSTom Erickson 	char	parentname[MAXNAMELEN];
634ecd6cf80Smarks 	int	error;
635ecd6cf80Smarks 
636ecd6cf80Smarks 	if ((error = zfs_secpolicy_write_perms(from,
637ecd6cf80Smarks 	    ZFS_DELEG_PERM_RENAME, cr)) != 0)
638ecd6cf80Smarks 		return (error);
639ecd6cf80Smarks 
640ecd6cf80Smarks 	if ((error = zfs_secpolicy_write_perms(from,
641ecd6cf80Smarks 	    ZFS_DELEG_PERM_MOUNT, cr)) != 0)
642ecd6cf80Smarks 		return (error);
643ecd6cf80Smarks 
644ecd6cf80Smarks 	if ((error = zfs_get_parent(to, parentname,
645ecd6cf80Smarks 	    sizeof (parentname))) != 0)
646ecd6cf80Smarks 		return (error);
647ecd6cf80Smarks 
648ecd6cf80Smarks 	if ((error = zfs_secpolicy_write_perms(parentname,
649ecd6cf80Smarks 	    ZFS_DELEG_PERM_CREATE, cr)) != 0)
650ecd6cf80Smarks 		return (error);
651ecd6cf80Smarks 
652ecd6cf80Smarks 	if ((error = zfs_secpolicy_write_perms(parentname,
653ecd6cf80Smarks 	    ZFS_DELEG_PERM_MOUNT, cr)) != 0)
654ecd6cf80Smarks 		return (error);
655ecd6cf80Smarks 
656ecd6cf80Smarks 	return (error);
657ecd6cf80Smarks }
658ecd6cf80Smarks 
659ecd6cf80Smarks static int
660ecd6cf80Smarks zfs_secpolicy_rename(zfs_cmd_t *zc, cred_t *cr)
661ecd6cf80Smarks {
662ecd6cf80Smarks 	return (zfs_secpolicy_rename_perms(zc->zc_name, zc->zc_value, cr));
663ecd6cf80Smarks }
664ecd6cf80Smarks 
665ecd6cf80Smarks static int
666ecd6cf80Smarks zfs_secpolicy_promote(zfs_cmd_t *zc, cred_t *cr)
667ecd6cf80Smarks {
66892241e0bSTom Erickson 	char	parentname[MAXNAMELEN];
669ecd6cf80Smarks 	objset_t *clone;
670ecd6cf80Smarks 	int error;
671ecd6cf80Smarks 
672ecd6cf80Smarks 	error = zfs_secpolicy_write_perms(zc->zc_name,
673ecd6cf80Smarks 	    ZFS_DELEG_PERM_PROMOTE, cr);
674ecd6cf80Smarks 	if (error)
675ecd6cf80Smarks 		return (error);
676ecd6cf80Smarks 
677503ad85cSMatthew Ahrens 	error = dmu_objset_hold(zc->zc_name, FTAG, &clone);
678ecd6cf80Smarks 
679ecd6cf80Smarks 	if (error == 0) {
680ecd6cf80Smarks 		dsl_dataset_t *pclone = NULL;
681ecd6cf80Smarks 		dsl_dir_t *dd;
682503ad85cSMatthew Ahrens 		dd = clone->os_dsl_dataset->ds_dir;
683ecd6cf80Smarks 
684ecd6cf80Smarks 		rw_enter(&dd->dd_pool->dp_config_rwlock, RW_READER);
685745cd3c5Smaybee 		error = dsl_dataset_hold_obj(dd->dd_pool,
686745cd3c5Smaybee 		    dd->dd_phys->dd_origin_obj, FTAG, &pclone);
687ecd6cf80Smarks 		rw_exit(&dd->dd_pool->dp_config_rwlock);
688ecd6cf80Smarks 		if (error) {
689503ad85cSMatthew Ahrens 			dmu_objset_rele(clone, FTAG);
690ecd6cf80Smarks 			return (error);
691ecd6cf80Smarks 		}
692ecd6cf80Smarks 
693ecd6cf80Smarks 		error = zfs_secpolicy_write_perms(zc->zc_name,
694ecd6cf80Smarks 		    ZFS_DELEG_PERM_MOUNT, cr);
695ecd6cf80Smarks 
696ecd6cf80Smarks 		dsl_dataset_name(pclone, parentname);
697503ad85cSMatthew Ahrens 		dmu_objset_rele(clone, FTAG);
698745cd3c5Smaybee 		dsl_dataset_rele(pclone, FTAG);
699ecd6cf80Smarks 		if (error == 0)
700ecd6cf80Smarks 			error = zfs_secpolicy_write_perms(parentname,
701ecd6cf80Smarks 			    ZFS_DELEG_PERM_PROMOTE, cr);
702ecd6cf80Smarks 	}
703ecd6cf80Smarks 	return (error);
704ecd6cf80Smarks }
705ecd6cf80Smarks 
706ecd6cf80Smarks static int
707ecd6cf80Smarks zfs_secpolicy_receive(zfs_cmd_t *zc, cred_t *cr)
708ecd6cf80Smarks {
709ecd6cf80Smarks 	int error;
710ecd6cf80Smarks 
711ecd6cf80Smarks 	if ((error = zfs_secpolicy_write_perms(zc->zc_name,
712ecd6cf80Smarks 	    ZFS_DELEG_PERM_RECEIVE, cr)) != 0)
713ecd6cf80Smarks 		return (error);
714ecd6cf80Smarks 
715ecd6cf80Smarks 	if ((error = zfs_secpolicy_write_perms(zc->zc_name,
716ecd6cf80Smarks 	    ZFS_DELEG_PERM_MOUNT, cr)) != 0)
717ecd6cf80Smarks 		return (error);
718ecd6cf80Smarks 
719ecd6cf80Smarks 	return (zfs_secpolicy_write_perms(zc->zc_name,
720ecd6cf80Smarks 	    ZFS_DELEG_PERM_CREATE, cr));
721ecd6cf80Smarks }
722ecd6cf80Smarks 
723ecd6cf80Smarks int
724ecd6cf80Smarks zfs_secpolicy_snapshot_perms(const char *name, cred_t *cr)
725ecd6cf80Smarks {
726681d9761SEric Taylor 	return (zfs_secpolicy_write_perms(name,
727681d9761SEric Taylor 	    ZFS_DELEG_PERM_SNAPSHOT, cr));
728ecd6cf80Smarks }
729ecd6cf80Smarks 
730ecd6cf80Smarks static int
731ecd6cf80Smarks zfs_secpolicy_snapshot(zfs_cmd_t *zc, cred_t *cr)
732ecd6cf80Smarks {
733ecd6cf80Smarks 
734ecd6cf80Smarks 	return (zfs_secpolicy_snapshot_perms(zc->zc_name, cr));
735ecd6cf80Smarks }
736ecd6cf80Smarks 
737ecd6cf80Smarks static int
738ecd6cf80Smarks zfs_secpolicy_create(zfs_cmd_t *zc, cred_t *cr)
739ecd6cf80Smarks {
74092241e0bSTom Erickson 	char	parentname[MAXNAMELEN];
74192241e0bSTom Erickson 	int	error;
742ecd6cf80Smarks 
743ecd6cf80Smarks 	if ((error = zfs_get_parent(zc->zc_name, parentname,
744ecd6cf80Smarks 	    sizeof (parentname))) != 0)
745ecd6cf80Smarks 		return (error);
746fa9e4066Sahrens 
747ecd6cf80Smarks 	if (zc->zc_value[0] != '\0') {
748ecd6cf80Smarks 		if ((error = zfs_secpolicy_write_perms(zc->zc_value,
749ecd6cf80Smarks 		    ZFS_DELEG_PERM_CLONE, cr)) != 0)
750ecd6cf80Smarks 			return (error);
751fa9e4066Sahrens 	}
752fa9e4066Sahrens 
753ecd6cf80Smarks 	if ((error = zfs_secpolicy_write_perms(parentname,
754ecd6cf80Smarks 	    ZFS_DELEG_PERM_CREATE, cr)) != 0)
755ecd6cf80Smarks 		return (error);
756ecd6cf80Smarks 
757ecd6cf80Smarks 	error = zfs_secpolicy_write_perms(parentname,
758ecd6cf80Smarks 	    ZFS_DELEG_PERM_MOUNT, cr);
759ecd6cf80Smarks 
760ecd6cf80Smarks 	return (error);
761ecd6cf80Smarks }
762ecd6cf80Smarks 
763ecd6cf80Smarks static int
764ecd6cf80Smarks zfs_secpolicy_umount(zfs_cmd_t *zc, cred_t *cr)
765ecd6cf80Smarks {
766ecd6cf80Smarks 	int error;
767ecd6cf80Smarks 
768ecd6cf80Smarks 	error = secpolicy_fs_unmount(cr, NULL);
769ecd6cf80Smarks 	if (error) {
770ecd6cf80Smarks 		error = dsl_deleg_access(zc->zc_name, ZFS_DELEG_PERM_MOUNT, cr);
771ecd6cf80Smarks 	}
772ecd6cf80Smarks 	return (error);
773fa9e4066Sahrens }
774fa9e4066Sahrens 
775fa9e4066Sahrens /*
776fa9e4066Sahrens  * Policy for pool operations - create/destroy pools, add vdevs, etc.  Requires
777fa9e4066Sahrens  * SYS_CONFIG privilege, which is not available in a local zone.
778fa9e4066Sahrens  */
779fa9e4066Sahrens /* ARGSUSED */
780fa9e4066Sahrens static int
781ecd6cf80Smarks zfs_secpolicy_config(zfs_cmd_t *zc, cred_t *cr)
782fa9e4066Sahrens {
783fa9e4066Sahrens 	if (secpolicy_sys_config(cr, B_FALSE) != 0)
784fa9e4066Sahrens 		return (EPERM);
785fa9e4066Sahrens 
786fa9e4066Sahrens 	return (0);
787fa9e4066Sahrens }
788fa9e4066Sahrens 
789ea8dc4b6Seschrock /*
790ea8dc4b6Seschrock  * Policy for fault injection.  Requires all privileges.
791ea8dc4b6Seschrock  */
792ea8dc4b6Seschrock /* ARGSUSED */
793ea8dc4b6Seschrock static int
794ecd6cf80Smarks zfs_secpolicy_inject(zfs_cmd_t *zc, cred_t *cr)
795ea8dc4b6Seschrock {
796ea8dc4b6Seschrock 	return (secpolicy_zinject(cr));
797ea8dc4b6Seschrock }
798ea8dc4b6Seschrock 
799e45ce728Sahrens static int
800e45ce728Sahrens zfs_secpolicy_inherit(zfs_cmd_t *zc, cred_t *cr)
801e45ce728Sahrens {
802e45ce728Sahrens 	zfs_prop_t prop = zfs_name_to_prop(zc->zc_value);
803e45ce728Sahrens 
804990b4856Slling 	if (prop == ZPROP_INVAL) {
805e45ce728Sahrens 		if (!zfs_prop_user(zc->zc_value))
806e45ce728Sahrens 			return (EINVAL);
807e45ce728Sahrens 		return (zfs_secpolicy_write_perms(zc->zc_name,
808e45ce728Sahrens 		    ZFS_DELEG_PERM_USERPROP, cr));
809e45ce728Sahrens 	} else {
81092241e0bSTom Erickson 		return (zfs_secpolicy_setprop(zc->zc_name, prop,
81192241e0bSTom Erickson 		    NULL, cr));
812e45ce728Sahrens 	}
813e45ce728Sahrens }
814e45ce728Sahrens 
81514843421SMatthew Ahrens static int
81614843421SMatthew Ahrens zfs_secpolicy_userspace_one(zfs_cmd_t *zc, cred_t *cr)
81714843421SMatthew Ahrens {
81814843421SMatthew Ahrens 	int err = zfs_secpolicy_read(zc, cr);
81914843421SMatthew Ahrens 	if (err)
82014843421SMatthew Ahrens 		return (err);
82114843421SMatthew Ahrens 
82214843421SMatthew Ahrens 	if (zc->zc_objset_type >= ZFS_NUM_USERQUOTA_PROPS)
82314843421SMatthew Ahrens 		return (EINVAL);
82414843421SMatthew Ahrens 
82514843421SMatthew Ahrens 	if (zc->zc_value[0] == 0) {
82614843421SMatthew Ahrens 		/*
82714843421SMatthew Ahrens 		 * They are asking about a posix uid/gid.  If it's
82814843421SMatthew Ahrens 		 * themself, allow it.
82914843421SMatthew Ahrens 		 */
83014843421SMatthew Ahrens 		if (zc->zc_objset_type == ZFS_PROP_USERUSED ||
83114843421SMatthew Ahrens 		    zc->zc_objset_type == ZFS_PROP_USERQUOTA) {
83214843421SMatthew Ahrens 			if (zc->zc_guid == crgetuid(cr))
83314843421SMatthew Ahrens 				return (0);
83414843421SMatthew Ahrens 		} else {
83514843421SMatthew Ahrens 			if (groupmember(zc->zc_guid, cr))
83614843421SMatthew Ahrens 				return (0);
83714843421SMatthew Ahrens 		}
83814843421SMatthew Ahrens 	}
83914843421SMatthew Ahrens 
84014843421SMatthew Ahrens 	return (zfs_secpolicy_write_perms(zc->zc_name,
84114843421SMatthew Ahrens 	    userquota_perms[zc->zc_objset_type], cr));
84214843421SMatthew Ahrens }
84314843421SMatthew Ahrens 
84414843421SMatthew Ahrens static int
84514843421SMatthew Ahrens zfs_secpolicy_userspace_many(zfs_cmd_t *zc, cred_t *cr)
84614843421SMatthew Ahrens {
84714843421SMatthew Ahrens 	int err = zfs_secpolicy_read(zc, cr);
84814843421SMatthew Ahrens 	if (err)
84914843421SMatthew Ahrens 		return (err);
85014843421SMatthew Ahrens 
85114843421SMatthew Ahrens 	if (zc->zc_objset_type >= ZFS_NUM_USERQUOTA_PROPS)
85214843421SMatthew Ahrens 		return (EINVAL);
85314843421SMatthew Ahrens 
85414843421SMatthew Ahrens 	return (zfs_secpolicy_write_perms(zc->zc_name,
85514843421SMatthew Ahrens 	    userquota_perms[zc->zc_objset_type], cr));
85614843421SMatthew Ahrens }
85714843421SMatthew Ahrens 
85814843421SMatthew Ahrens static int
85914843421SMatthew Ahrens zfs_secpolicy_userspace_upgrade(zfs_cmd_t *zc, cred_t *cr)
86014843421SMatthew Ahrens {
86192241e0bSTom Erickson 	return (zfs_secpolicy_setprop(zc->zc_name, ZFS_PROP_VERSION,
86292241e0bSTom Erickson 	    NULL, cr));
86314843421SMatthew Ahrens }
86414843421SMatthew Ahrens 
865842727c2SChris Kirby static int
866842727c2SChris Kirby zfs_secpolicy_hold(zfs_cmd_t *zc, cred_t *cr)
867842727c2SChris Kirby {
868842727c2SChris Kirby 	return (zfs_secpolicy_write_perms(zc->zc_name,
869842727c2SChris Kirby 	    ZFS_DELEG_PERM_HOLD, cr));
870842727c2SChris Kirby }
871842727c2SChris Kirby 
872842727c2SChris Kirby static int
873842727c2SChris Kirby zfs_secpolicy_release(zfs_cmd_t *zc, cred_t *cr)
874842727c2SChris Kirby {
875842727c2SChris Kirby 	return (zfs_secpolicy_write_perms(zc->zc_name,
876842727c2SChris Kirby 	    ZFS_DELEG_PERM_RELEASE, cr));
877842727c2SChris Kirby }
878842727c2SChris Kirby 
879fa9e4066Sahrens /*
880fa9e4066Sahrens  * Returns the nvlist as specified by the user in the zfs_cmd_t.
881fa9e4066Sahrens  */
882fa9e4066Sahrens static int
883478ed9adSEric Taylor get_nvlist(uint64_t nvl, uint64_t size, int iflag, nvlist_t **nvp)
884fa9e4066Sahrens {
885fa9e4066Sahrens 	char *packed;
886fa9e4066Sahrens 	int error;
887990b4856Slling 	nvlist_t *list = NULL;
888fa9e4066Sahrens 
889fa9e4066Sahrens 	/*
890e9dbad6fSeschrock 	 * Read in and unpack the user-supplied nvlist.
891fa9e4066Sahrens 	 */
892990b4856Slling 	if (size == 0)
893fa9e4066Sahrens 		return (EINVAL);
894fa9e4066Sahrens 
895fa9e4066Sahrens 	packed = kmem_alloc(size, KM_SLEEP);
896fa9e4066Sahrens 
897478ed9adSEric Taylor 	if ((error = ddi_copyin((void *)(uintptr_t)nvl, packed, size,
898478ed9adSEric Taylor 	    iflag)) != 0) {
899fa9e4066Sahrens 		kmem_free(packed, size);
900fa9e4066Sahrens 		return (error);
901fa9e4066Sahrens 	}
902fa9e4066Sahrens 
903990b4856Slling 	if ((error = nvlist_unpack(packed, size, &list, 0)) != 0) {
904fa9e4066Sahrens 		kmem_free(packed, size);
905fa9e4066Sahrens 		return (error);
906fa9e4066Sahrens 	}
907fa9e4066Sahrens 
908fa9e4066Sahrens 	kmem_free(packed, size);
909fa9e4066Sahrens 
910990b4856Slling 	*nvp = list;
911fa9e4066Sahrens 	return (0);
912fa9e4066Sahrens }
913fa9e4066Sahrens 
91492241e0bSTom Erickson static int
91592241e0bSTom Erickson fit_error_list(zfs_cmd_t *zc, nvlist_t **errors)
91692241e0bSTom Erickson {
91792241e0bSTom Erickson 	size_t size;
91892241e0bSTom Erickson 
91992241e0bSTom Erickson 	VERIFY(nvlist_size(*errors, &size, NV_ENCODE_NATIVE) == 0);
92092241e0bSTom Erickson 
92192241e0bSTom Erickson 	if (size > zc->zc_nvlist_dst_size) {
92292241e0bSTom Erickson 		nvpair_t *more_errors;
92392241e0bSTom Erickson 		int n = 0;
92492241e0bSTom Erickson 
92592241e0bSTom Erickson 		if (zc->zc_nvlist_dst_size < 1024)
92692241e0bSTom Erickson 			return (ENOMEM);
92792241e0bSTom Erickson 
92892241e0bSTom Erickson 		VERIFY(nvlist_add_int32(*errors, ZPROP_N_MORE_ERRORS, 0) == 0);
92992241e0bSTom Erickson 		more_errors = nvlist_prev_nvpair(*errors, NULL);
93092241e0bSTom Erickson 
93192241e0bSTom Erickson 		do {
93292241e0bSTom Erickson 			nvpair_t *pair = nvlist_prev_nvpair(*errors,
93392241e0bSTom Erickson 			    more_errors);
93492241e0bSTom Erickson 			VERIFY(nvlist_remove_nvpair(*errors, pair) == 0);
93592241e0bSTom Erickson 			n++;
93692241e0bSTom Erickson 			VERIFY(nvlist_size(*errors, &size,
93792241e0bSTom Erickson 			    NV_ENCODE_NATIVE) == 0);
93892241e0bSTom Erickson 		} while (size > zc->zc_nvlist_dst_size);
93992241e0bSTom Erickson 
94092241e0bSTom Erickson 		VERIFY(nvlist_remove_nvpair(*errors, more_errors) == 0);
94192241e0bSTom Erickson 		VERIFY(nvlist_add_int32(*errors, ZPROP_N_MORE_ERRORS, n) == 0);
94292241e0bSTom Erickson 		ASSERT(nvlist_size(*errors, &size, NV_ENCODE_NATIVE) == 0);
94392241e0bSTom Erickson 		ASSERT(size <= zc->zc_nvlist_dst_size);
94492241e0bSTom Erickson 	}
94592241e0bSTom Erickson 
94692241e0bSTom Erickson 	return (0);
94792241e0bSTom Erickson }
94892241e0bSTom Erickson 
949e9dbad6fSeschrock static int
950e9dbad6fSeschrock put_nvlist(zfs_cmd_t *zc, nvlist_t *nvl)
951e9dbad6fSeschrock {
952e9dbad6fSeschrock 	char *packed = NULL;
9536e27f868SSam Falkner 	int error = 0;
954e9dbad6fSeschrock 	size_t size;
955e9dbad6fSeschrock 
956e9dbad6fSeschrock 	VERIFY(nvlist_size(nvl, &size, NV_ENCODE_NATIVE) == 0);
957e9dbad6fSeschrock 
958e9dbad6fSeschrock 	if (size > zc->zc_nvlist_dst_size) {
959e9dbad6fSeschrock 		error = ENOMEM;
960e9dbad6fSeschrock 	} else {
961da165920Smarks 		packed = kmem_alloc(size, KM_SLEEP);
962e9dbad6fSeschrock 		VERIFY(nvlist_pack(nvl, &packed, &size, NV_ENCODE_NATIVE,
963e9dbad6fSeschrock 		    KM_SLEEP) == 0);
9646e27f868SSam Falkner 		if (ddi_copyout(packed, (void *)(uintptr_t)zc->zc_nvlist_dst,
9656e27f868SSam Falkner 		    size, zc->zc_iflags) != 0)
9666e27f868SSam Falkner 			error = EFAULT;
967e9dbad6fSeschrock 		kmem_free(packed, size);
968e9dbad6fSeschrock 	}
969e9dbad6fSeschrock 
970e9dbad6fSeschrock 	zc->zc_nvlist_dst_size = size;
971e9dbad6fSeschrock 	return (error);
972e9dbad6fSeschrock }
973e9dbad6fSeschrock 
97414843421SMatthew Ahrens static int
975af4c679fSSean McEnroe getzfsvfs(const char *dsname, zfsvfs_t **zfvp)
97614843421SMatthew Ahrens {
97714843421SMatthew Ahrens 	objset_t *os;
97814843421SMatthew Ahrens 	int error;
97914843421SMatthew Ahrens 
980503ad85cSMatthew Ahrens 	error = dmu_objset_hold(dsname, FTAG, &os);
98114843421SMatthew Ahrens 	if (error)
98214843421SMatthew Ahrens 		return (error);
983503ad85cSMatthew Ahrens 	if (dmu_objset_type(os) != DMU_OST_ZFS) {
984503ad85cSMatthew Ahrens 		dmu_objset_rele(os, FTAG);
985503ad85cSMatthew Ahrens 		return (EINVAL);
986503ad85cSMatthew Ahrens 	}
98714843421SMatthew Ahrens 
988503ad85cSMatthew Ahrens 	mutex_enter(&os->os_user_ptr_lock);
989af4c679fSSean McEnroe 	*zfvp = dmu_objset_get_user(os);
990af4c679fSSean McEnroe 	if (*zfvp) {
991af4c679fSSean McEnroe 		VFS_HOLD((*zfvp)->z_vfs);
99214843421SMatthew Ahrens 	} else {
99314843421SMatthew Ahrens 		error = ESRCH;
99414843421SMatthew Ahrens 	}
995503ad85cSMatthew Ahrens 	mutex_exit(&os->os_user_ptr_lock);
996503ad85cSMatthew Ahrens 	dmu_objset_rele(os, FTAG);
99714843421SMatthew Ahrens 	return (error);
99814843421SMatthew Ahrens }
99914843421SMatthew Ahrens 
100014843421SMatthew Ahrens /*
100114843421SMatthew Ahrens  * Find a zfsvfs_t for a mounted filesystem, or create our own, in which
100214843421SMatthew Ahrens  * case its z_vfs will be NULL, and it will be opened as the owner.
100314843421SMatthew Ahrens  */
100414843421SMatthew Ahrens static int
1005af4c679fSSean McEnroe zfsvfs_hold(const char *name, void *tag, zfsvfs_t **zfvp)
100614843421SMatthew Ahrens {
100714843421SMatthew Ahrens 	int error = 0;
100814843421SMatthew Ahrens 
1009af4c679fSSean McEnroe 	if (getzfsvfs(name, zfvp) != 0)
1010af4c679fSSean McEnroe 		error = zfsvfs_create(name, zfvp);
101114843421SMatthew Ahrens 	if (error == 0) {
1012af4c679fSSean McEnroe 		rrw_enter(&(*zfvp)->z_teardown_lock, RW_READER, tag);
1013af4c679fSSean McEnroe 		if ((*zfvp)->z_unmounted) {
101414843421SMatthew Ahrens 			/*
101514843421SMatthew Ahrens 			 * XXX we could probably try again, since the unmounting
101614843421SMatthew Ahrens 			 * thread should be just about to disassociate the
101714843421SMatthew Ahrens 			 * objset from the zfsvfs.
101814843421SMatthew Ahrens 			 */
1019af4c679fSSean McEnroe 			rrw_exit(&(*zfvp)->z_teardown_lock, tag);
102014843421SMatthew Ahrens 			return (EBUSY);
102114843421SMatthew Ahrens 		}
102214843421SMatthew Ahrens 	}
102314843421SMatthew Ahrens 	return (error);
102414843421SMatthew Ahrens }
102514843421SMatthew Ahrens 
102614843421SMatthew Ahrens static void
102714843421SMatthew Ahrens zfsvfs_rele(zfsvfs_t *zfsvfs, void *tag)
102814843421SMatthew Ahrens {
102914843421SMatthew Ahrens 	rrw_exit(&zfsvfs->z_teardown_lock, tag);
103014843421SMatthew Ahrens 
103114843421SMatthew Ahrens 	if (zfsvfs->z_vfs) {
103214843421SMatthew Ahrens 		VFS_RELE(zfsvfs->z_vfs);
103314843421SMatthew Ahrens 	} else {
1034503ad85cSMatthew Ahrens 		dmu_objset_disown(zfsvfs->z_os, zfsvfs);
103514843421SMatthew Ahrens 		zfsvfs_free(zfsvfs);
103614843421SMatthew Ahrens 	}
103714843421SMatthew Ahrens }
103814843421SMatthew Ahrens 
1039fa9e4066Sahrens static int
1040fa9e4066Sahrens zfs_ioc_pool_create(zfs_cmd_t *zc)
1041fa9e4066Sahrens {
1042fa9e4066Sahrens 	int error;
1043990b4856Slling 	nvlist_t *config, *props = NULL;
10440a48a24eStimh 	nvlist_t *rootprops = NULL;
10450a48a24eStimh 	nvlist_t *zplprops = NULL;
1046228975ccSek 	char *buf;
1047fa9e4066Sahrens 
1048990b4856Slling 	if (error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size,
1049478ed9adSEric Taylor 	    zc->zc_iflags, &config))
1050fa9e4066Sahrens 		return (error);
10512a6b87f0Sek 
1052990b4856Slling 	if (zc->zc_nvlist_src_size != 0 && (error =
1053478ed9adSEric Taylor 	    get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
1054478ed9adSEric Taylor 	    zc->zc_iflags, &props))) {
1055990b4856Slling 		nvlist_free(config);
1056990b4856Slling 		return (error);
1057990b4856Slling 	}
1058990b4856Slling 
10590a48a24eStimh 	if (props) {
10600a48a24eStimh 		nvlist_t *nvl = NULL;
10610a48a24eStimh 		uint64_t version = SPA_VERSION;
10620a48a24eStimh 
10630a48a24eStimh 		(void) nvlist_lookup_uint64(props,
10640a48a24eStimh 		    zpool_prop_to_name(ZPOOL_PROP_VERSION), &version);
10650a48a24eStimh 		if (version < SPA_VERSION_INITIAL || version > SPA_VERSION) {
10660a48a24eStimh 			error = EINVAL;
10670a48a24eStimh 			goto pool_props_bad;
10680a48a24eStimh 		}
10690a48a24eStimh 		(void) nvlist_lookup_nvlist(props, ZPOOL_ROOTFS_PROPS, &nvl);
10700a48a24eStimh 		if (nvl) {
10710a48a24eStimh 			error = nvlist_dup(nvl, &rootprops, KM_SLEEP);
10720a48a24eStimh 			if (error != 0) {
10730a48a24eStimh 				nvlist_free(config);
10740a48a24eStimh 				nvlist_free(props);
10750a48a24eStimh 				return (error);
10760a48a24eStimh 			}
10770a48a24eStimh 			(void) nvlist_remove_all(props, ZPOOL_ROOTFS_PROPS);
10780a48a24eStimh 		}
10790a48a24eStimh 		VERIFY(nvlist_alloc(&zplprops, NV_UNIQUE_NAME, KM_SLEEP) == 0);
10800a48a24eStimh 		error = zfs_fill_zplprops_root(version, rootprops,
10810a48a24eStimh 		    zplprops, NULL);
10820a48a24eStimh 		if (error)
10830a48a24eStimh 			goto pool_props_bad;
10840a48a24eStimh 	}
10850a48a24eStimh 
10862a6b87f0Sek 	buf = history_str_get(zc);
1087fa9e4066Sahrens 
10880a48a24eStimh 	error = spa_create(zc->zc_name, config, props, buf, zplprops);
10890a48a24eStimh 
10900a48a24eStimh 	/*
10910a48a24eStimh 	 * Set the remaining root properties
10920a48a24eStimh 	 */
109392241e0bSTom Erickson 	if (!error && (error = zfs_set_prop_nvlist(zc->zc_name,
109492241e0bSTom Erickson 	    ZPROP_SRC_LOCAL, rootprops, NULL)) != 0)
10950a48a24eStimh 		(void) spa_destroy(zc->zc_name);
1096fa9e4066Sahrens 
10972a6b87f0Sek 	if (buf != NULL)
10982a6b87f0Sek 		history_str_free(buf);
1099990b4856Slling 
11000a48a24eStimh pool_props_bad:
11010a48a24eStimh 	nvlist_free(rootprops);
11020a48a24eStimh 	nvlist_free(zplprops);
1103fa9e4066Sahrens 	nvlist_free(config);
11040a48a24eStimh 	nvlist_free(props);
1105990b4856Slling 
1106fa9e4066Sahrens 	return (error);
1107fa9e4066Sahrens }
1108fa9e4066Sahrens 
1109fa9e4066Sahrens static int
1110fa9e4066Sahrens zfs_ioc_pool_destroy(zfs_cmd_t *zc)
1111fa9e4066Sahrens {
1112ecd6cf80Smarks 	int error;
1113ecd6cf80Smarks 	zfs_log_history(zc);
1114ecd6cf80Smarks 	error = spa_destroy(zc->zc_name);
1115681d9761SEric Taylor 	if (error == 0)
1116681d9761SEric Taylor 		zvol_remove_minors(zc->zc_name);
1117ecd6cf80Smarks 	return (error);
1118fa9e4066Sahrens }
1119fa9e4066Sahrens 
1120fa9e4066Sahrens static int
1121fa9e4066Sahrens zfs_ioc_pool_import(zfs_cmd_t *zc)
1122fa9e4066Sahrens {
1123990b4856Slling 	nvlist_t *config, *props = NULL;
1124fa9e4066Sahrens 	uint64_t guid;
1125468c413aSTim Haley 	int error;
1126fa9e4066Sahrens 
1127990b4856Slling 	if ((error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size,
1128478ed9adSEric Taylor 	    zc->zc_iflags, &config)) != 0)
1129990b4856Slling 		return (error);
1130990b4856Slling 
1131990b4856Slling 	if (zc->zc_nvlist_src_size != 0 && (error =
1132478ed9adSEric Taylor 	    get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
1133478ed9adSEric Taylor 	    zc->zc_iflags, &props))) {
1134990b4856Slling 		nvlist_free(config);
1135fa9e4066Sahrens 		return (error);
1136990b4856Slling 	}
1137fa9e4066Sahrens 
1138fa9e4066Sahrens 	if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID, &guid) != 0 ||
1139ea8dc4b6Seschrock 	    guid != zc->zc_guid)
1140fa9e4066Sahrens 		error = EINVAL;
1141c5904d13Seschrock 	else if (zc->zc_cookie)
1142468c413aSTim Haley 		error = spa_import_verbatim(zc->zc_name, config, props);
1143fa9e4066Sahrens 	else
1144990b4856Slling 		error = spa_import(zc->zc_name, config, props);
1145fa9e4066Sahrens 
1146468c413aSTim Haley 	if (zc->zc_nvlist_dst != 0)
1147468c413aSTim Haley 		(void) put_nvlist(zc, config);
1148468c413aSTim Haley 
1149fa9e4066Sahrens 	nvlist_free(config);
1150fa9e4066Sahrens 
1151990b4856Slling 	if (props)
1152990b4856Slling 		nvlist_free(props);
1153990b4856Slling 
1154fa9e4066Sahrens 	return (error);
1155fa9e4066Sahrens }
1156fa9e4066Sahrens 
1157fa9e4066Sahrens static int
1158fa9e4066Sahrens zfs_ioc_pool_export(zfs_cmd_t *zc)
1159fa9e4066Sahrens {
1160ecd6cf80Smarks 	int error;
116189a89ebfSlling 	boolean_t force = (boolean_t)zc->zc_cookie;
1162394ab0cbSGeorge Wilson 	boolean_t hardforce = (boolean_t)zc->zc_guid;
116389a89ebfSlling 
1164ecd6cf80Smarks 	zfs_log_history(zc);
1165394ab0cbSGeorge Wilson 	error = spa_export(zc->zc_name, NULL, force, hardforce);
1166681d9761SEric Taylor 	if (error == 0)
1167681d9761SEric Taylor 		zvol_remove_minors(zc->zc_name);
1168ecd6cf80Smarks 	return (error);
1169fa9e4066Sahrens }
1170fa9e4066Sahrens 
1171fa9e4066Sahrens static int
1172fa9e4066Sahrens zfs_ioc_pool_configs(zfs_cmd_t *zc)
1173fa9e4066Sahrens {
1174fa9e4066Sahrens 	nvlist_t *configs;
1175fa9e4066Sahrens 	int error;
1176fa9e4066Sahrens 
1177fa9e4066Sahrens 	if ((configs = spa_all_configs(&zc->zc_cookie)) == NULL)
1178fa9e4066Sahrens 		return (EEXIST);
1179fa9e4066Sahrens 
1180e9dbad6fSeschrock 	error = put_nvlist(zc, configs);
1181fa9e4066Sahrens 
1182fa9e4066Sahrens 	nvlist_free(configs);
1183fa9e4066Sahrens 
1184fa9e4066Sahrens 	return (error);
1185fa9e4066Sahrens }
1186fa9e4066Sahrens 
1187fa9e4066Sahrens static int
1188fa9e4066Sahrens zfs_ioc_pool_stats(zfs_cmd_t *zc)
1189fa9e4066Sahrens {
1190fa9e4066Sahrens 	nvlist_t *config;
1191fa9e4066Sahrens 	int error;
1192ea8dc4b6Seschrock 	int ret = 0;
1193fa9e4066Sahrens 
1194e9dbad6fSeschrock 	error = spa_get_stats(zc->zc_name, &config, zc->zc_value,
1195e9dbad6fSeschrock 	    sizeof (zc->zc_value));
1196fa9e4066Sahrens 
1197fa9e4066Sahrens 	if (config != NULL) {
1198e9dbad6fSeschrock 		ret = put_nvlist(zc, config);
1199fa9e4066Sahrens 		nvlist_free(config);
1200ea8dc4b6Seschrock 
1201ea8dc4b6Seschrock 		/*
1202ea8dc4b6Seschrock 		 * The config may be present even if 'error' is non-zero.
1203ea8dc4b6Seschrock 		 * In this case we return success, and preserve the real errno
1204ea8dc4b6Seschrock 		 * in 'zc_cookie'.
1205ea8dc4b6Seschrock 		 */
1206ea8dc4b6Seschrock 		zc->zc_cookie = error;
1207fa9e4066Sahrens 	} else {
1208ea8dc4b6Seschrock 		ret = error;
1209fa9e4066Sahrens 	}
1210fa9e4066Sahrens 
1211ea8dc4b6Seschrock 	return (ret);
1212fa9e4066Sahrens }
1213fa9e4066Sahrens 
1214fa9e4066Sahrens /*
1215fa9e4066Sahrens  * Try to import the given pool, returning pool stats as appropriate so that
1216fa9e4066Sahrens  * user land knows which devices are available and overall pool health.
1217fa9e4066Sahrens  */
1218fa9e4066Sahrens static int
1219fa9e4066Sahrens zfs_ioc_pool_tryimport(zfs_cmd_t *zc)
1220fa9e4066Sahrens {
1221fa9e4066Sahrens 	nvlist_t *tryconfig, *config;
1222fa9e4066Sahrens 	int error;
1223fa9e4066Sahrens 
1224990b4856Slling 	if ((error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size,
1225478ed9adSEric Taylor 	    zc->zc_iflags, &tryconfig)) != 0)
1226fa9e4066Sahrens 		return (error);
1227fa9e4066Sahrens 
1228fa9e4066Sahrens 	config = spa_tryimport(tryconfig);
1229fa9e4066Sahrens 
1230fa9e4066Sahrens 	nvlist_free(tryconfig);
1231fa9e4066Sahrens 
1232fa9e4066Sahrens 	if (config == NULL)
1233fa9e4066Sahrens 		return (EINVAL);
1234fa9e4066Sahrens 
1235e9dbad6fSeschrock 	error = put_nvlist(zc, config);
1236fa9e4066Sahrens 	nvlist_free(config);
1237fa9e4066Sahrens 
1238fa9e4066Sahrens 	return (error);
1239fa9e4066Sahrens }
1240fa9e4066Sahrens 
12413f9d6ad7SLin Ling /*
12423f9d6ad7SLin Ling  * inputs:
12433f9d6ad7SLin Ling  * zc_name              name of the pool
12443f9d6ad7SLin Ling  * zc_cookie            scan func (pool_scan_func_t)
12453f9d6ad7SLin Ling  */
1246fa9e4066Sahrens static int
12473f9d6ad7SLin Ling zfs_ioc_pool_scan(zfs_cmd_t *zc)
1248fa9e4066Sahrens {
1249fa9e4066Sahrens 	spa_t *spa;
1250fa9e4066Sahrens 	int error;
1251fa9e4066Sahrens 
125206eeb2adSek 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
125306eeb2adSek 		return (error);
125406eeb2adSek 
12553f9d6ad7SLin Ling 	if (zc->zc_cookie == POOL_SCAN_NONE)
12563f9d6ad7SLin Ling 		error = spa_scan_stop(spa);
12573f9d6ad7SLin Ling 	else
12583f9d6ad7SLin Ling 		error = spa_scan(spa, zc->zc_cookie);
125906eeb2adSek 
126006eeb2adSek 	spa_close(spa, FTAG);
126106eeb2adSek 
1262fa9e4066Sahrens 	return (error);
1263fa9e4066Sahrens }
1264fa9e4066Sahrens 
1265fa9e4066Sahrens static int
1266fa9e4066Sahrens zfs_ioc_pool_freeze(zfs_cmd_t *zc)
1267fa9e4066Sahrens {
1268fa9e4066Sahrens 	spa_t *spa;
1269fa9e4066Sahrens 	int error;
1270fa9e4066Sahrens 
1271fa9e4066Sahrens 	error = spa_open(zc->zc_name, &spa, FTAG);
1272fa9e4066Sahrens 	if (error == 0) {
1273fa9e4066Sahrens 		spa_freeze(spa);
1274fa9e4066Sahrens 		spa_close(spa, FTAG);
1275fa9e4066Sahrens 	}
1276fa9e4066Sahrens 	return (error);
1277fa9e4066Sahrens }
1278fa9e4066Sahrens 
1279eaca9bbdSeschrock static int
1280eaca9bbdSeschrock zfs_ioc_pool_upgrade(zfs_cmd_t *zc)
1281eaca9bbdSeschrock {
1282eaca9bbdSeschrock 	spa_t *spa;
1283eaca9bbdSeschrock 	int error;
1284eaca9bbdSeschrock 
128506eeb2adSek 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
128606eeb2adSek 		return (error);
128706eeb2adSek 
1288558d2d50Slling 	if (zc->zc_cookie < spa_version(spa) || zc->zc_cookie > SPA_VERSION) {
1289558d2d50Slling 		spa_close(spa, FTAG);
1290558d2d50Slling 		return (EINVAL);
1291558d2d50Slling 	}
1292558d2d50Slling 
1293990b4856Slling 	spa_upgrade(spa, zc->zc_cookie);
129406eeb2adSek 	spa_close(spa, FTAG);
129506eeb2adSek 
129606eeb2adSek 	return (error);
129706eeb2adSek }
129806eeb2adSek 
129906eeb2adSek static int
130006eeb2adSek zfs_ioc_pool_get_history(zfs_cmd_t *zc)
130106eeb2adSek {
130206eeb2adSek 	spa_t *spa;
130306eeb2adSek 	char *hist_buf;
130406eeb2adSek 	uint64_t size;
130506eeb2adSek 	int error;
130606eeb2adSek 
130706eeb2adSek 	if ((size = zc->zc_history_len) == 0)
130806eeb2adSek 		return (EINVAL);
130906eeb2adSek 
131006eeb2adSek 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
131106eeb2adSek 		return (error);
131206eeb2adSek 
1313e7437265Sahrens 	if (spa_version(spa) < SPA_VERSION_ZPOOL_HISTORY) {
1314d7306b64Sek 		spa_close(spa, FTAG);
1315d7306b64Sek 		return (ENOTSUP);
1316d7306b64Sek 	}
1317d7306b64Sek 
131806eeb2adSek 	hist_buf = kmem_alloc(size, KM_SLEEP);
131906eeb2adSek 	if ((error = spa_history_get(spa, &zc->zc_history_offset,
132006eeb2adSek 	    &zc->zc_history_len, hist_buf)) == 0) {
1321478ed9adSEric Taylor 		error = ddi_copyout(hist_buf,
1322478ed9adSEric Taylor 		    (void *)(uintptr_t)zc->zc_history,
1323478ed9adSEric Taylor 		    zc->zc_history_len, zc->zc_iflags);
132406eeb2adSek 	}
132506eeb2adSek 
132606eeb2adSek 	spa_close(spa, FTAG);
132706eeb2adSek 	kmem_free(hist_buf, size);
132806eeb2adSek 	return (error);
132906eeb2adSek }
133006eeb2adSek 
133155434c77Sek static int
133255434c77Sek zfs_ioc_dsobj_to_dsname(zfs_cmd_t *zc)
133355434c77Sek {
133455434c77Sek 	int error;
133555434c77Sek 
1336b1b8ab34Slling 	if (error = dsl_dsobj_to_dsname(zc->zc_name, zc->zc_obj, zc->zc_value))
133755434c77Sek 		return (error);
133855434c77Sek 
133955434c77Sek 	return (0);
134055434c77Sek }
134155434c77Sek 
1342503ad85cSMatthew Ahrens /*
1343503ad85cSMatthew Ahrens  * inputs:
1344503ad85cSMatthew Ahrens  * zc_name		name of filesystem
1345503ad85cSMatthew Ahrens  * zc_obj		object to find
1346503ad85cSMatthew Ahrens  *
1347503ad85cSMatthew Ahrens  * outputs:
1348503ad85cSMatthew Ahrens  * zc_value		name of object
1349503ad85cSMatthew Ahrens  */
135055434c77Sek static int
135155434c77Sek zfs_ioc_obj_to_path(zfs_cmd_t *zc)
135255434c77Sek {
1353503ad85cSMatthew Ahrens 	objset_t *os;
135455434c77Sek 	int error;
135555434c77Sek 
1356503ad85cSMatthew Ahrens 	/* XXX reading from objset not owned */
1357503ad85cSMatthew Ahrens 	if ((error = dmu_objset_hold(zc->zc_name, FTAG, &os)) != 0)
135855434c77Sek 		return (error);
1359503ad85cSMatthew Ahrens 	if (dmu_objset_type(os) != DMU_OST_ZFS) {
1360503ad85cSMatthew Ahrens 		dmu_objset_rele(os, FTAG);
1361503ad85cSMatthew Ahrens 		return (EINVAL);
1362503ad85cSMatthew Ahrens 	}
1363503ad85cSMatthew Ahrens 	error = zfs_obj_to_path(os, zc->zc_obj, zc->zc_value,
136455434c77Sek 	    sizeof (zc->zc_value));
1365503ad85cSMatthew Ahrens 	dmu_objset_rele(os, FTAG);
136655434c77Sek 
136755434c77Sek 	return (error);
136855434c77Sek }
136955434c77Sek 
1370fa9e4066Sahrens static int
1371fa9e4066Sahrens zfs_ioc_vdev_add(zfs_cmd_t *zc)
1372fa9e4066Sahrens {
1373fa9e4066Sahrens 	spa_t *spa;
1374fa9e4066Sahrens 	int error;
1375e7cbe64fSgw 	nvlist_t *config, **l2cache, **spares;
1376e7cbe64fSgw 	uint_t nl2cache = 0, nspares = 0;
1377fa9e4066Sahrens 
1378fa9e4066Sahrens 	error = spa_open(zc->zc_name, &spa, FTAG);
1379fa9e4066Sahrens 	if (error != 0)
1380fa9e4066Sahrens 		return (error);
1381fa9e4066Sahrens 
1382fa94a07fSbrendan 	error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size,
1383478ed9adSEric Taylor 	    zc->zc_iflags, &config);
1384fa94a07fSbrendan 	(void) nvlist_lookup_nvlist_array(config, ZPOOL_CONFIG_L2CACHE,
1385fa94a07fSbrendan 	    &l2cache, &nl2cache);
1386fa94a07fSbrendan 
1387e7cbe64fSgw 	(void) nvlist_lookup_nvlist_array(config, ZPOOL_CONFIG_SPARES,
1388e7cbe64fSgw 	    &spares, &nspares);
1389e7cbe64fSgw 
1390b1b8ab34Slling 	/*
1391b1b8ab34Slling 	 * A root pool with concatenated devices is not supported.
1392e7cbe64fSgw 	 * Thus, can not add a device to a root pool.
1393e7cbe64fSgw 	 *
1394e7cbe64fSgw 	 * Intent log device can not be added to a rootpool because
1395e7cbe64fSgw 	 * during mountroot, zil is replayed, a seperated log device
1396e7cbe64fSgw 	 * can not be accessed during the mountroot time.
1397e7cbe64fSgw 	 *
1398e7cbe64fSgw 	 * l2cache and spare devices are ok to be added to a rootpool.
1399b1b8ab34Slling 	 */
1400b24ab676SJeff Bonwick 	if (spa_bootfs(spa) != 0 && nl2cache == 0 && nspares == 0) {
14011195e687SMark J Musante 		nvlist_free(config);
1402b1b8ab34Slling 		spa_close(spa, FTAG);
1403b1b8ab34Slling 		return (EDOM);
1404b1b8ab34Slling 	}
1405b1b8ab34Slling 
1406fa94a07fSbrendan 	if (error == 0) {
1407fa9e4066Sahrens 		error = spa_vdev_add(spa, config);
1408fa9e4066Sahrens 		nvlist_free(config);
1409fa9e4066Sahrens 	}
1410fa9e4066Sahrens 	spa_close(spa, FTAG);
1411fa9e4066Sahrens 	return (error);
1412fa9e4066Sahrens }
1413fa9e4066Sahrens 
14143f9d6ad7SLin Ling /*
14153f9d6ad7SLin Ling  * inputs:
14163f9d6ad7SLin Ling  * zc_name		name of the pool
14173f9d6ad7SLin Ling  * zc_nvlist_conf	nvlist of devices to remove
14183f9d6ad7SLin Ling  * zc_cookie		to stop the remove?
14193f9d6ad7SLin Ling  */
1420fa9e4066Sahrens static int
1421fa9e4066Sahrens zfs_ioc_vdev_remove(zfs_cmd_t *zc)
1422fa9e4066Sahrens {
142399653d4eSeschrock 	spa_t *spa;
142499653d4eSeschrock 	int error;
142599653d4eSeschrock 
142699653d4eSeschrock 	error = spa_open(zc->zc_name, &spa, FTAG);
142799653d4eSeschrock 	if (error != 0)
142899653d4eSeschrock 		return (error);
142999653d4eSeschrock 	error = spa_vdev_remove(spa, zc->zc_guid, B_FALSE);
143099653d4eSeschrock 	spa_close(spa, FTAG);
143199653d4eSeschrock 	return (error);
1432fa9e4066Sahrens }
1433fa9e4066Sahrens 
1434fa9e4066Sahrens static int
14353d7072f8Seschrock zfs_ioc_vdev_set_state(zfs_cmd_t *zc)
1436fa9e4066Sahrens {
1437fa9e4066Sahrens 	spa_t *spa;
1438fa9e4066Sahrens 	int error;
14393d7072f8Seschrock 	vdev_state_t newstate = VDEV_STATE_UNKNOWN;
1440fa9e4066Sahrens 
144106eeb2adSek 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
1442fa9e4066Sahrens 		return (error);
14433d7072f8Seschrock 	switch (zc->zc_cookie) {
14443d7072f8Seschrock 	case VDEV_STATE_ONLINE:
14453d7072f8Seschrock 		error = vdev_online(spa, zc->zc_guid, zc->zc_obj, &newstate);
14463d7072f8Seschrock 		break;
1447fa9e4066Sahrens 
14483d7072f8Seschrock 	case VDEV_STATE_OFFLINE:
14493d7072f8Seschrock 		error = vdev_offline(spa, zc->zc_guid, zc->zc_obj);
14503d7072f8Seschrock 		break;
1451fa9e4066Sahrens 
14523d7072f8Seschrock 	case VDEV_STATE_FAULTED:
1453069f55e2SEric Schrock 		if (zc->zc_obj != VDEV_AUX_ERR_EXCEEDED &&
1454069f55e2SEric Schrock 		    zc->zc_obj != VDEV_AUX_EXTERNAL)
1455069f55e2SEric Schrock 			zc->zc_obj = VDEV_AUX_ERR_EXCEEDED;
1456069f55e2SEric Schrock 
1457069f55e2SEric Schrock 		error = vdev_fault(spa, zc->zc_guid, zc->zc_obj);
14583d7072f8Seschrock 		break;
14593d7072f8Seschrock 
14603d7072f8Seschrock 	case VDEV_STATE_DEGRADED:
1461069f55e2SEric Schrock 		if (zc->zc_obj != VDEV_AUX_ERR_EXCEEDED &&
1462069f55e2SEric Schrock 		    zc->zc_obj != VDEV_AUX_EXTERNAL)
1463069f55e2SEric Schrock 			zc->zc_obj = VDEV_AUX_ERR_EXCEEDED;
1464069f55e2SEric Schrock 
1465069f55e2SEric Schrock 		error = vdev_degrade(spa, zc->zc_guid, zc->zc_obj);
14663d7072f8Seschrock 		break;
14673d7072f8Seschrock 
14683d7072f8Seschrock 	default:
14693d7072f8Seschrock 		error = EINVAL;
14703d7072f8Seschrock 	}
14713d7072f8Seschrock 	zc->zc_cookie = newstate;
1472fa9e4066Sahrens 	spa_close(spa, FTAG);
1473fa9e4066Sahrens 	return (error);
1474fa9e4066Sahrens }
1475fa9e4066Sahrens 
1476fa9e4066Sahrens static int
1477fa9e4066Sahrens zfs_ioc_vdev_attach(zfs_cmd_t *zc)
1478fa9e4066Sahrens {
1479fa9e4066Sahrens 	spa_t *spa;
1480fa9e4066Sahrens 	int replacing = zc->zc_cookie;
1481fa9e4066Sahrens 	nvlist_t *config;
1482fa9e4066Sahrens 	int error;
1483fa9e4066Sahrens 
148406eeb2adSek 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
1485fa9e4066Sahrens 		return (error);
1486fa9e4066Sahrens 
1487990b4856Slling 	if ((error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size,
1488478ed9adSEric Taylor 	    zc->zc_iflags, &config)) == 0) {
1489ea8dc4b6Seschrock 		error = spa_vdev_attach(spa, zc->zc_guid, config, replacing);
1490fa9e4066Sahrens 		nvlist_free(config);
1491fa9e4066Sahrens 	}
1492fa9e4066Sahrens 
1493fa9e4066Sahrens 	spa_close(spa, FTAG);
1494fa9e4066Sahrens 	return (error);
1495fa9e4066Sahrens }
1496fa9e4066Sahrens 
1497fa9e4066Sahrens static int
1498fa9e4066Sahrens zfs_ioc_vdev_detach(zfs_cmd_t *zc)
1499fa9e4066Sahrens {
1500fa9e4066Sahrens 	spa_t *spa;
1501fa9e4066Sahrens 	int error;
1502fa9e4066Sahrens 
150306eeb2adSek 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
1504fa9e4066Sahrens 		return (error);
1505fa9e4066Sahrens 
15068ad4d6ddSJeff Bonwick 	error = spa_vdev_detach(spa, zc->zc_guid, 0, B_FALSE);
1507fa9e4066Sahrens 
1508fa9e4066Sahrens 	spa_close(spa, FTAG);
1509fa9e4066Sahrens 	return (error);
1510fa9e4066Sahrens }
1511fa9e4066Sahrens 
15121195e687SMark J Musante static int
15131195e687SMark J Musante zfs_ioc_vdev_split(zfs_cmd_t *zc)
15141195e687SMark J Musante {
15151195e687SMark J Musante 	spa_t *spa;
15161195e687SMark J Musante 	nvlist_t *config, *props = NULL;
15171195e687SMark J Musante 	int error;
15181195e687SMark J Musante 	boolean_t exp = !!(zc->zc_cookie & ZPOOL_EXPORT_AFTER_SPLIT);
15191195e687SMark J Musante 
15201195e687SMark J Musante 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
15211195e687SMark J Musante 		return (error);
15221195e687SMark J Musante 
15231195e687SMark J Musante 	if (error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size,
15241195e687SMark J Musante 	    zc->zc_iflags, &config)) {
15251195e687SMark J Musante 		spa_close(spa, FTAG);
15261195e687SMark J Musante 		return (error);
15271195e687SMark J Musante 	}
15281195e687SMark J Musante 
15291195e687SMark J Musante 	if (zc->zc_nvlist_src_size != 0 && (error =
15301195e687SMark J Musante 	    get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
15311195e687SMark J Musante 	    zc->zc_iflags, &props))) {
15321195e687SMark J Musante 		spa_close(spa, FTAG);
15331195e687SMark J Musante 		nvlist_free(config);
15341195e687SMark J Musante 		return (error);
15351195e687SMark J Musante 	}
15361195e687SMark J Musante 
15371195e687SMark J Musante 	error = spa_vdev_split_mirror(spa, zc->zc_string, config, props, exp);
15381195e687SMark J Musante 
15391195e687SMark J Musante 	spa_close(spa, FTAG);
15401195e687SMark J Musante 
15411195e687SMark J Musante 	nvlist_free(config);
15421195e687SMark J Musante 	nvlist_free(props);
15431195e687SMark J Musante 
15441195e687SMark J Musante 	return (error);
15451195e687SMark J Musante }
15461195e687SMark J Musante 
1547c67d9675Seschrock static int
1548c67d9675Seschrock zfs_ioc_vdev_setpath(zfs_cmd_t *zc)
1549c67d9675Seschrock {
1550c67d9675Seschrock 	spa_t *spa;
1551e9dbad6fSeschrock 	char *path = zc->zc_value;
1552ea8dc4b6Seschrock 	uint64_t guid = zc->zc_guid;
1553c67d9675Seschrock 	int error;
1554c67d9675Seschrock 
1555c67d9675Seschrock 	error = spa_open(zc->zc_name, &spa, FTAG);
1556c67d9675Seschrock 	if (error != 0)
1557c67d9675Seschrock 		return (error);
1558c67d9675Seschrock 
1559c67d9675Seschrock 	error = spa_vdev_setpath(spa, guid, path);
1560c67d9675Seschrock 	spa_close(spa, FTAG);
1561c67d9675Seschrock 	return (error);
1562c67d9675Seschrock }
1563c67d9675Seschrock 
15646809eb4eSEric Schrock static int
15656809eb4eSEric Schrock zfs_ioc_vdev_setfru(zfs_cmd_t *zc)
15666809eb4eSEric Schrock {
15676809eb4eSEric Schrock 	spa_t *spa;
15686809eb4eSEric Schrock 	char *fru = zc->zc_value;
15696809eb4eSEric Schrock 	uint64_t guid = zc->zc_guid;
15706809eb4eSEric Schrock 	int error;
15716809eb4eSEric Schrock 
15726809eb4eSEric Schrock 	error = spa_open(zc->zc_name, &spa, FTAG);
15736809eb4eSEric Schrock 	if (error != 0)
15746809eb4eSEric Schrock 		return (error);
15756809eb4eSEric Schrock 
15766809eb4eSEric Schrock 	error = spa_vdev_setfru(spa, guid, fru);
15776809eb4eSEric Schrock 	spa_close(spa, FTAG);
15786809eb4eSEric Schrock 	return (error);
15796809eb4eSEric Schrock }
15806809eb4eSEric Schrock 
15813cb34c60Sahrens /*
15823cb34c60Sahrens  * inputs:
15833cb34c60Sahrens  * zc_name		name of filesystem
15843cb34c60Sahrens  * zc_nvlist_dst_size	size of buffer for property nvlist
15853cb34c60Sahrens  *
15863cb34c60Sahrens  * outputs:
15873cb34c60Sahrens  * zc_objset_stats	stats
15883cb34c60Sahrens  * zc_nvlist_dst	property nvlist
15893cb34c60Sahrens  * zc_nvlist_dst_size	size of property nvlist
15903cb34c60Sahrens  */
1591fa9e4066Sahrens static int
1592fa9e4066Sahrens zfs_ioc_objset_stats(zfs_cmd_t *zc)
1593fa9e4066Sahrens {
1594fa9e4066Sahrens 	objset_t *os = NULL;
1595fa9e4066Sahrens 	int error;
15967f7322feSeschrock 	nvlist_t *nv;
1597fa9e4066Sahrens 
1598503ad85cSMatthew Ahrens 	if (error = dmu_objset_hold(zc->zc_name, FTAG, &os))
1599fa9e4066Sahrens 		return (error);
1600fa9e4066Sahrens 
1601a2eea2e1Sahrens 	dmu_objset_fast_stat(os, &zc->zc_objset_stats);
1602fa9e4066Sahrens 
16035ad82045Snd 	if (zc->zc_nvlist_dst != 0 &&
160492241e0bSTom Erickson 	    (error = dsl_prop_get_all(os, &nv)) == 0) {
1605a2eea2e1Sahrens 		dmu_objset_stats(os, nv);
1606432f72fdSahrens 		/*
1607bd00f61bSrm 		 * NB: zvol_get_stats() will read the objset contents,
1608432f72fdSahrens 		 * which we aren't supposed to do with a
1609745cd3c5Smaybee 		 * DS_MODE_USER hold, because it could be
1610432f72fdSahrens 		 * inconsistent.  So this is a bit of a workaround...
1611503ad85cSMatthew Ahrens 		 * XXX reading with out owning
1612432f72fdSahrens 		 */
1613e7437265Sahrens 		if (!zc->zc_objset_stats.dds_inconsistent) {
1614e7437265Sahrens 			if (dmu_objset_type(os) == DMU_OST_ZVOL)
1615e7437265Sahrens 				VERIFY(zvol_get_stats(os, nv) == 0);
1616e7437265Sahrens 		}
1617e9dbad6fSeschrock 		error = put_nvlist(zc, nv);
16187f7322feSeschrock 		nvlist_free(nv);
16197f7322feSeschrock 	}
1620fa9e4066Sahrens 
1621503ad85cSMatthew Ahrens 	dmu_objset_rele(os, FTAG);
1622fa9e4066Sahrens 	return (error);
1623fa9e4066Sahrens }
1624fa9e4066Sahrens 
162592241e0bSTom Erickson /*
162692241e0bSTom Erickson  * inputs:
162792241e0bSTom Erickson  * zc_name		name of filesystem
162892241e0bSTom Erickson  * zc_nvlist_dst_size	size of buffer for property nvlist
162992241e0bSTom Erickson  *
163092241e0bSTom Erickson  * outputs:
163192241e0bSTom Erickson  * zc_nvlist_dst	received property nvlist
163292241e0bSTom Erickson  * zc_nvlist_dst_size	size of received property nvlist
163392241e0bSTom Erickson  *
163492241e0bSTom Erickson  * Gets received properties (distinct from local properties on or after
163592241e0bSTom Erickson  * SPA_VERSION_RECVD_PROPS) for callers who want to differentiate received from
163692241e0bSTom Erickson  * local property values.
163792241e0bSTom Erickson  */
163892241e0bSTom Erickson static int
163992241e0bSTom Erickson zfs_ioc_objset_recvd_props(zfs_cmd_t *zc)
164092241e0bSTom Erickson {
164192241e0bSTom Erickson 	objset_t *os = NULL;
164292241e0bSTom Erickson 	int error;
164392241e0bSTom Erickson 	nvlist_t *nv;
164492241e0bSTom Erickson 
164592241e0bSTom Erickson 	if (error = dmu_objset_hold(zc->zc_name, FTAG, &os))
164692241e0bSTom Erickson 		return (error);
164792241e0bSTom Erickson 
164892241e0bSTom Erickson 	/*
164992241e0bSTom Erickson 	 * Without this check, we would return local property values if the
165092241e0bSTom Erickson 	 * caller has not already received properties on or after
165192241e0bSTom Erickson 	 * SPA_VERSION_RECVD_PROPS.
165292241e0bSTom Erickson 	 */
165392241e0bSTom Erickson 	if (!dsl_prop_get_hasrecvd(os)) {
165492241e0bSTom Erickson 		dmu_objset_rele(os, FTAG);
165592241e0bSTom Erickson 		return (ENOTSUP);
165692241e0bSTom Erickson 	}
165792241e0bSTom Erickson 
165892241e0bSTom Erickson 	if (zc->zc_nvlist_dst != 0 &&
165992241e0bSTom Erickson 	    (error = dsl_prop_get_received(os, &nv)) == 0) {
166092241e0bSTom Erickson 		error = put_nvlist(zc, nv);
166192241e0bSTom Erickson 		nvlist_free(nv);
166292241e0bSTom Erickson 	}
166392241e0bSTom Erickson 
166492241e0bSTom Erickson 	dmu_objset_rele(os, FTAG);
166592241e0bSTom Erickson 	return (error);
166692241e0bSTom Erickson }
166792241e0bSTom Erickson 
1668de8267e0Stimh static int
1669de8267e0Stimh nvl_add_zplprop(objset_t *os, nvlist_t *props, zfs_prop_t prop)
1670de8267e0Stimh {
1671de8267e0Stimh 	uint64_t value;
1672de8267e0Stimh 	int error;
1673de8267e0Stimh 
1674de8267e0Stimh 	/*
1675de8267e0Stimh 	 * zfs_get_zplprop() will either find a value or give us
1676de8267e0Stimh 	 * the default value (if there is one).
1677de8267e0Stimh 	 */
1678de8267e0Stimh 	if ((error = zfs_get_zplprop(os, prop, &value)) != 0)
1679de8267e0Stimh 		return (error);
1680de8267e0Stimh 	VERIFY(nvlist_add_uint64(props, zfs_prop_to_name(prop), value) == 0);
1681de8267e0Stimh 	return (0);
1682de8267e0Stimh }
1683de8267e0Stimh 
16843cb34c60Sahrens /*
16853cb34c60Sahrens  * inputs:
16863cb34c60Sahrens  * zc_name		name of filesystem
1687de8267e0Stimh  * zc_nvlist_dst_size	size of buffer for zpl property nvlist
16883cb34c60Sahrens  *
16893cb34c60Sahrens  * outputs:
1690de8267e0Stimh  * zc_nvlist_dst	zpl property nvlist
1691de8267e0Stimh  * zc_nvlist_dst_size	size of zpl property nvlist
16923cb34c60Sahrens  */
1693bd00f61bSrm static int
1694de8267e0Stimh zfs_ioc_objset_zplprops(zfs_cmd_t *zc)
1695bd00f61bSrm {
1696de8267e0Stimh 	objset_t *os;
1697de8267e0Stimh 	int err;
1698bd00f61bSrm 
1699503ad85cSMatthew Ahrens 	/* XXX reading without owning */
1700503ad85cSMatthew Ahrens 	if (err = dmu_objset_hold(zc->zc_name, FTAG, &os))
1701de8267e0Stimh 		return (err);
1702bd00f61bSrm 
1703bd00f61bSrm 	dmu_objset_fast_stat(os, &zc->zc_objset_stats);
1704bd00f61bSrm 
1705bd00f61bSrm 	/*
1706de8267e0Stimh 	 * NB: nvl_add_zplprop() will read the objset contents,
1707745cd3c5Smaybee 	 * which we aren't supposed to do with a DS_MODE_USER
1708745cd3c5Smaybee 	 * hold, because it could be inconsistent.
1709bd00f61bSrm 	 */
1710de8267e0Stimh 	if (zc->zc_nvlist_dst != NULL &&
1711de8267e0Stimh 	    !zc->zc_objset_stats.dds_inconsistent &&
1712de8267e0Stimh 	    dmu_objset_type(os) == DMU_OST_ZFS) {
1713de8267e0Stimh 		nvlist_t *nv;
1714de8267e0Stimh 
1715de8267e0Stimh 		VERIFY(nvlist_alloc(&nv, NV_UNIQUE_NAME, KM_SLEEP) == 0);
1716de8267e0Stimh 		if ((err = nvl_add_zplprop(os, nv, ZFS_PROP_VERSION)) == 0 &&
1717de8267e0Stimh 		    (err = nvl_add_zplprop(os, nv, ZFS_PROP_NORMALIZE)) == 0 &&
1718de8267e0Stimh 		    (err = nvl_add_zplprop(os, nv, ZFS_PROP_UTF8ONLY)) == 0 &&
1719de8267e0Stimh 		    (err = nvl_add_zplprop(os, nv, ZFS_PROP_CASE)) == 0)
1720de8267e0Stimh 			err = put_nvlist(zc, nv);
1721de8267e0Stimh 		nvlist_free(nv);
1722de8267e0Stimh 	} else {
1723de8267e0Stimh 		err = ENOENT;
1724de8267e0Stimh 	}
1725503ad85cSMatthew Ahrens 	dmu_objset_rele(os, FTAG);
1726de8267e0Stimh 	return (err);
1727bd00f61bSrm }
1728bd00f61bSrm 
172914843421SMatthew Ahrens static boolean_t
173014843421SMatthew Ahrens dataset_name_hidden(const char *name)
173114843421SMatthew Ahrens {
173214843421SMatthew Ahrens 	/*
173314843421SMatthew Ahrens 	 * Skip over datasets that are not visible in this zone,
173414843421SMatthew Ahrens 	 * internal datasets (which have a $ in their name), and
173514843421SMatthew Ahrens 	 * temporary datasets (which have a % in their name).
173614843421SMatthew Ahrens 	 */
173714843421SMatthew Ahrens 	if (strchr(name, '$') != NULL)
173814843421SMatthew Ahrens 		return (B_TRUE);
173914843421SMatthew Ahrens 	if (strchr(name, '%') != NULL)
174014843421SMatthew Ahrens 		return (B_TRUE);
174114843421SMatthew Ahrens 	if (!INGLOBALZONE(curproc) && !zone_dataset_visible(name, NULL))
174214843421SMatthew Ahrens 		return (B_TRUE);
174314843421SMatthew Ahrens 	return (B_FALSE);
174414843421SMatthew Ahrens }
174514843421SMatthew Ahrens 
1746de8267e0Stimh /*
1747de8267e0Stimh  * inputs:
1748de8267e0Stimh  * zc_name		name of filesystem
1749de8267e0Stimh  * zc_cookie		zap cursor
1750de8267e0Stimh  * zc_nvlist_dst_size	size of buffer for property nvlist
1751de8267e0Stimh  *
1752de8267e0Stimh  * outputs:
1753de8267e0Stimh  * zc_name		name of next filesystem
175414843421SMatthew Ahrens  * zc_cookie		zap cursor
1755de8267e0Stimh  * zc_objset_stats	stats
1756de8267e0Stimh  * zc_nvlist_dst	property nvlist
1757de8267e0Stimh  * zc_nvlist_dst_size	size of property nvlist
1758de8267e0Stimh  */
1759fa9e4066Sahrens static int
1760fa9e4066Sahrens zfs_ioc_dataset_list_next(zfs_cmd_t *zc)
1761fa9e4066Sahrens {
176287e5029aSahrens 	objset_t *os;
1763fa9e4066Sahrens 	int error;
1764fa9e4066Sahrens 	char *p;
1765620252bcSChris Kirby 	size_t orig_len = strlen(zc->zc_name);
1766fa9e4066Sahrens 
1767620252bcSChris Kirby top:
1768503ad85cSMatthew Ahrens 	if (error = dmu_objset_hold(zc->zc_name, FTAG, &os)) {
176987e5029aSahrens 		if (error == ENOENT)
177087e5029aSahrens 			error = ESRCH;
177187e5029aSahrens 		return (error);
1772fa9e4066Sahrens 	}
1773fa9e4066Sahrens 
1774fa9e4066Sahrens 	p = strrchr(zc->zc_name, '/');
1775fa9e4066Sahrens 	if (p == NULL || p[1] != '\0')
1776fa9e4066Sahrens 		(void) strlcat(zc->zc_name, "/", sizeof (zc->zc_name));
1777fa9e4066Sahrens 	p = zc->zc_name + strlen(zc->zc_name);
1778fa9e4066Sahrens 
17795c0b6a79SRich Morris 	/*
17805c0b6a79SRich Morris 	 * Pre-fetch the datasets.  dmu_objset_prefetch() always returns 0
17815c0b6a79SRich Morris 	 * but is not declared void because its called by dmu_objset_find().
17825c0b6a79SRich Morris 	 */
17837f73c863SRich Morris 	if (zc->zc_cookie == 0) {
17847f73c863SRich Morris 		uint64_t cookie = 0;
17857f73c863SRich Morris 		int len = sizeof (zc->zc_name) - (p - zc->zc_name);
17867f73c863SRich Morris 
17877f73c863SRich Morris 		while (dmu_dir_list_next(os, len, p, NULL, &cookie) == 0)
17885c0b6a79SRich Morris 			(void) dmu_objset_prefetch(p, NULL);
17897f73c863SRich Morris 	}
17907f73c863SRich Morris 
1791fa9e4066Sahrens 	do {
179287e5029aSahrens 		error = dmu_dir_list_next(os,
179387e5029aSahrens 		    sizeof (zc->zc_name) - (p - zc->zc_name), p,
179487e5029aSahrens 		    NULL, &zc->zc_cookie);
1795fa9e4066Sahrens 		if (error == ENOENT)
1796fa9e4066Sahrens 			error = ESRCH;
1797681d9761SEric Taylor 	} while (error == 0 && dataset_name_hidden(zc->zc_name) &&
1798681d9761SEric Taylor 	    !(zc->zc_iflags & FKIOCTL));
1799503ad85cSMatthew Ahrens 	dmu_objset_rele(os, FTAG);
1800fa9e4066Sahrens 
1801681d9761SEric Taylor 	/*
1802681d9761SEric Taylor 	 * If it's an internal dataset (ie. with a '$' in its name),
1803681d9761SEric Taylor 	 * don't try to get stats for it, otherwise we'll return ENOENT.
1804681d9761SEric Taylor 	 */
1805620252bcSChris Kirby 	if (error == 0 && strchr(zc->zc_name, '$') == NULL) {
180687e5029aSahrens 		error = zfs_ioc_objset_stats(zc); /* fill in the stats */
1807620252bcSChris Kirby 		if (error == ENOENT) {
1808620252bcSChris Kirby 			/* We lost a race with destroy, get the next one. */
1809620252bcSChris Kirby 			zc->zc_name[orig_len] = '\0';
1810620252bcSChris Kirby 			goto top;
1811620252bcSChris Kirby 		}
1812620252bcSChris Kirby 	}
1813fa9e4066Sahrens 	return (error);
1814fa9e4066Sahrens }
1815fa9e4066Sahrens 
18163cb34c60Sahrens /*
18173cb34c60Sahrens  * inputs:
18183cb34c60Sahrens  * zc_name		name of filesystem
18193cb34c60Sahrens  * zc_cookie		zap cursor
18203cb34c60Sahrens  * zc_nvlist_dst_size	size of buffer for property nvlist
18213cb34c60Sahrens  *
18223cb34c60Sahrens  * outputs:
18233cb34c60Sahrens  * zc_name		name of next snapshot
18243cb34c60Sahrens  * zc_objset_stats	stats
18253cb34c60Sahrens  * zc_nvlist_dst	property nvlist
18263cb34c60Sahrens  * zc_nvlist_dst_size	size of property nvlist
18273cb34c60Sahrens  */
1828fa9e4066Sahrens static int
1829fa9e4066Sahrens zfs_ioc_snapshot_list_next(zfs_cmd_t *zc)
1830fa9e4066Sahrens {
183187e5029aSahrens 	objset_t *os;
1832fa9e4066Sahrens 	int error;
1833fa9e4066Sahrens 
1834620252bcSChris Kirby top:
18357cbf8b43SRich Morris 	if (zc->zc_cookie == 0)
18367cbf8b43SRich Morris 		(void) dmu_objset_find(zc->zc_name, dmu_objset_prefetch,
18377cbf8b43SRich Morris 		    NULL, DS_FIND_SNAPSHOTS);
18387cbf8b43SRich Morris 
1839503ad85cSMatthew Ahrens 	error = dmu_objset_hold(zc->zc_name, FTAG, &os);
1840745cd3c5Smaybee 	if (error)
1841745cd3c5Smaybee 		return (error == ENOENT ? ESRCH : error);
1842fa9e4066Sahrens 
1843b81d61a6Slling 	/*
1844b81d61a6Slling 	 * A dataset name of maximum length cannot have any snapshots,
1845b81d61a6Slling 	 * so exit immediately.
1846b81d61a6Slling 	 */
1847b81d61a6Slling 	if (strlcat(zc->zc_name, "@", sizeof (zc->zc_name)) >= MAXNAMELEN) {
1848503ad85cSMatthew Ahrens 		dmu_objset_rele(os, FTAG);
1849b81d61a6Slling 		return (ESRCH);
1850fa9e4066Sahrens 	}
1851fa9e4066Sahrens 
185287e5029aSahrens 	error = dmu_snapshot_list_next(os,
185387e5029aSahrens 	    sizeof (zc->zc_name) - strlen(zc->zc_name),
1854b38f0970Sck 	    zc->zc_name + strlen(zc->zc_name), NULL, &zc->zc_cookie, NULL);
1855503ad85cSMatthew Ahrens 	dmu_objset_rele(os, FTAG);
1856620252bcSChris Kirby 	if (error == 0) {
185787e5029aSahrens 		error = zfs_ioc_objset_stats(zc); /* fill in the stats */
1858620252bcSChris Kirby 		if (error == ENOENT)  {
1859620252bcSChris Kirby 			/* We lost a race with destroy, get the next one. */
1860620252bcSChris Kirby 			*strchr(zc->zc_name, '@') = '\0';
1861620252bcSChris Kirby 			goto top;
1862620252bcSChris Kirby 		}
1863620252bcSChris Kirby 	} else if (error == ENOENT) {
1864745cd3c5Smaybee 		error = ESRCH;
1865620252bcSChris Kirby 	}
1866fa9e4066Sahrens 
18673cb34c60Sahrens 	/* if we failed, undo the @ that we tacked on to zc_name */
1868745cd3c5Smaybee 	if (error)
18693cb34c60Sahrens 		*strchr(zc->zc_name, '@') = '\0';
1870fa9e4066Sahrens 	return (error);
1871fa9e4066Sahrens }
1872fa9e4066Sahrens 
187392241e0bSTom Erickson static int
187492241e0bSTom Erickson zfs_prop_set_userquota(const char *dsname, nvpair_t *pair)
1875fa9e4066Sahrens {
187692241e0bSTom Erickson 	const char *propname = nvpair_name(pair);
187792241e0bSTom Erickson 	uint64_t *valary;
187892241e0bSTom Erickson 	unsigned int vallen;
187992241e0bSTom Erickson 	const char *domain;
1880eeb85002STim Haley 	char *dash;
188192241e0bSTom Erickson 	zfs_userquota_prop_t type;
188292241e0bSTom Erickson 	uint64_t rid;
188392241e0bSTom Erickson 	uint64_t quota;
188492241e0bSTom Erickson 	zfsvfs_t *zfsvfs;
188592241e0bSTom Erickson 	int err;
188692241e0bSTom Erickson 
188792241e0bSTom Erickson 	if (nvpair_type(pair) == DATA_TYPE_NVLIST) {
188892241e0bSTom Erickson 		nvlist_t *attrs;
188992241e0bSTom Erickson 		VERIFY(nvpair_value_nvlist(pair, &attrs) == 0);
1890eeb85002STim Haley 		if (nvlist_lookup_nvpair(attrs, ZPROP_VALUE,
1891eeb85002STim Haley 		    &pair) != 0)
1892eeb85002STim Haley 			return (EINVAL);
189392241e0bSTom Erickson 	}
1894e9dbad6fSeschrock 
1895ecd6cf80Smarks 	/*
1896eeb85002STim Haley 	 * A correctly constructed propname is encoded as
189792241e0bSTom Erickson 	 * userquota@<rid>-<domain>.
1898ecd6cf80Smarks 	 */
1899eeb85002STim Haley 	if ((dash = strchr(propname, '-')) == NULL ||
1900eeb85002STim Haley 	    nvpair_value_uint64_array(pair, &valary, &vallen) != 0 ||
1901eeb85002STim Haley 	    vallen != 3)
1902eeb85002STim Haley 		return (EINVAL);
1903eeb85002STim Haley 
1904eeb85002STim Haley 	domain = dash + 1;
1905eeb85002STim Haley 	type = valary[0];
1906eeb85002STim Haley 	rid = valary[1];
1907eeb85002STim Haley 	quota = valary[2];
1908e9dbad6fSeschrock 
190992241e0bSTom Erickson 	err = zfsvfs_hold(dsname, FTAG, &zfsvfs);
191092241e0bSTom Erickson 	if (err == 0) {
191192241e0bSTom Erickson 		err = zfs_set_userquota(zfsvfs, type, domain, rid, quota);
191292241e0bSTom Erickson 		zfsvfs_rele(zfsvfs, FTAG);
191392241e0bSTom Erickson 	}
1914e9dbad6fSeschrock 
191592241e0bSTom Erickson 	return (err);
191692241e0bSTom Erickson }
191714843421SMatthew Ahrens 
191892241e0bSTom Erickson /*
191992241e0bSTom Erickson  * If the named property is one that has a special function to set its value,
192092241e0bSTom Erickson  * return 0 on success and a positive error code on failure; otherwise if it is
192192241e0bSTom Erickson  * not one of the special properties handled by this function, return -1.
192292241e0bSTom Erickson  *
1923eeb85002STim Haley  * XXX: It would be better for callers of the property interface if we handled
192492241e0bSTom Erickson  * these special cases in dsl_prop.c (in the dsl layer).
192592241e0bSTom Erickson  */
192692241e0bSTom Erickson static int
192792241e0bSTom Erickson zfs_prop_set_special(const char *dsname, zprop_source_t source,
192892241e0bSTom Erickson     nvpair_t *pair)
192992241e0bSTom Erickson {
193092241e0bSTom Erickson 	const char *propname = nvpair_name(pair);
193192241e0bSTom Erickson 	zfs_prop_t prop = zfs_name_to_prop(propname);
193292241e0bSTom Erickson 	uint64_t intval;
193392241e0bSTom Erickson 	int err;
1934fa9e4066Sahrens 
193592241e0bSTom Erickson 	if (prop == ZPROP_INVAL) {
193692241e0bSTom Erickson 		if (zfs_prop_userquota(propname))
193792241e0bSTom Erickson 			return (zfs_prop_set_userquota(dsname, pair));
193892241e0bSTom Erickson 		return (-1);
193992241e0bSTom Erickson 	}
194014843421SMatthew Ahrens 
194192241e0bSTom Erickson 	if (nvpair_type(pair) == DATA_TYPE_NVLIST) {
194292241e0bSTom Erickson 		nvlist_t *attrs;
194392241e0bSTom Erickson 		VERIFY(nvpair_value_nvlist(pair, &attrs) == 0);
194492241e0bSTom Erickson 		VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE,
194592241e0bSTom Erickson 		    &pair) == 0);
194692241e0bSTom Erickson 	}
1947db870a07Sahrens 
194892241e0bSTom Erickson 	if (zfs_prop_get_type(prop) == PROP_TYPE_STRING)
194992241e0bSTom Erickson 		return (-1);
1950b24ab676SJeff Bonwick 
195192241e0bSTom Erickson 	VERIFY(0 == nvpair_value_uint64(pair, &intval));
195240feaa91Sahrens 
195392241e0bSTom Erickson 	switch (prop) {
195492241e0bSTom Erickson 	case ZFS_PROP_QUOTA:
195592241e0bSTom Erickson 		err = dsl_dir_set_quota(dsname, source, intval);
195692241e0bSTom Erickson 		break;
195792241e0bSTom Erickson 	case ZFS_PROP_REFQUOTA:
195892241e0bSTom Erickson 		err = dsl_dataset_set_quota(dsname, source, intval);
195992241e0bSTom Erickson 		break;
196092241e0bSTom Erickson 	case ZFS_PROP_RESERVATION:
196192241e0bSTom Erickson 		err = dsl_dir_set_reservation(dsname, source, intval);
196292241e0bSTom Erickson 		break;
196392241e0bSTom Erickson 	case ZFS_PROP_REFRESERVATION:
196492241e0bSTom Erickson 		err = dsl_dataset_set_reservation(dsname, source, intval);
196592241e0bSTom Erickson 		break;
196692241e0bSTom Erickson 	case ZFS_PROP_VOLSIZE:
196792241e0bSTom Erickson 		err = zvol_set_volsize(dsname, ddi_driver_major(zfs_dip),
196892241e0bSTom Erickson 		    intval);
196992241e0bSTom Erickson 		break;
197092241e0bSTom Erickson 	case ZFS_PROP_VERSION:
197192241e0bSTom Erickson 	{
197292241e0bSTom Erickson 		zfsvfs_t *zfsvfs;
19739e6eda55Smarks 
197492241e0bSTom Erickson 		if ((err = zfsvfs_hold(dsname, FTAG, &zfsvfs)) != 0)
1975b24ab676SJeff Bonwick 			break;
1976b24ab676SJeff Bonwick 
197792241e0bSTom Erickson 		err = zfs_set_version(zfsvfs, intval);
197892241e0bSTom Erickson 		zfsvfs_rele(zfsvfs, FTAG);
1979d0f3f37eSMark Shellenbaum 
198092241e0bSTom Erickson 		if (err == 0 && intval >= ZPL_VERSION_USERSPACE) {
1981b16da2e2SGeorge Wilson 			zfs_cmd_t *zc;
1982b16da2e2SGeorge Wilson 
1983b16da2e2SGeorge Wilson 			zc = kmem_zalloc(sizeof (zfs_cmd_t), KM_SLEEP);
1984b16da2e2SGeorge Wilson 			(void) strcpy(zc->zc_name, dsname);
1985b16da2e2SGeorge Wilson 			(void) zfs_ioc_userspace_upgrade(zc);
1986b16da2e2SGeorge Wilson 			kmem_free(zc, sizeof (zfs_cmd_t));
198740feaa91Sahrens 		}
198892241e0bSTom Erickson 		break;
1989ecd6cf80Smarks 	}
1990ecd6cf80Smarks 
199192241e0bSTom Erickson 	default:
199292241e0bSTom Erickson 		err = -1;
199392241e0bSTom Erickson 	}
1994e9dbad6fSeschrock 
199592241e0bSTom Erickson 	return (err);
199692241e0bSTom Erickson }
1997a9799022Sck 
199892241e0bSTom Erickson /*
199992241e0bSTom Erickson  * This function is best effort. If it fails to set any of the given properties,
200092241e0bSTom Erickson  * it continues to set as many as it can and returns the first error
200192241e0bSTom Erickson  * encountered. If the caller provides a non-NULL errlist, it also gives the
200292241e0bSTom Erickson  * complete list of names of all the properties it failed to set along with the
200392241e0bSTom Erickson  * corresponding error numbers. The caller is responsible for freeing the
200492241e0bSTom Erickson  * returned errlist.
200592241e0bSTom Erickson  *
200692241e0bSTom Erickson  * If every property is set successfully, zero is returned and the list pointed
200792241e0bSTom Erickson  * at by errlist is NULL.
200892241e0bSTom Erickson  */
200992241e0bSTom Erickson int
201092241e0bSTom Erickson zfs_set_prop_nvlist(const char *dsname, zprop_source_t source, nvlist_t *nvl,
201192241e0bSTom Erickson     nvlist_t **errlist)
201292241e0bSTom Erickson {
201392241e0bSTom Erickson 	nvpair_t *pair;
201492241e0bSTom Erickson 	nvpair_t *propval;
201502e383d1STom Erickson 	int rv = 0;
201692241e0bSTom Erickson 	uint64_t intval;
201792241e0bSTom Erickson 	char *strval;
201892241e0bSTom Erickson 	nvlist_t *genericnvl;
201992241e0bSTom Erickson 	nvlist_t *errors;
202092241e0bSTom Erickson 	nvlist_t *retrynvl;
2021e9dbad6fSeschrock 
202292241e0bSTom Erickson 	VERIFY(nvlist_alloc(&genericnvl, NV_UNIQUE_NAME, KM_SLEEP) == 0);
202392241e0bSTom Erickson 	VERIFY(nvlist_alloc(&errors, NV_UNIQUE_NAME, KM_SLEEP) == 0);
202492241e0bSTom Erickson 	VERIFY(nvlist_alloc(&retrynvl, NV_UNIQUE_NAME, KM_SLEEP) == 0);
2025a9799022Sck 
202692241e0bSTom Erickson retry:
202792241e0bSTom Erickson 	pair = NULL;
202892241e0bSTom Erickson 	while ((pair = nvlist_next_nvpair(nvl, pair)) != NULL) {
202992241e0bSTom Erickson 		const char *propname = nvpair_name(pair);
203092241e0bSTom Erickson 		zfs_prop_t prop = zfs_name_to_prop(propname);
2031cfa69fd2STom Erickson 		int err = 0;
2032e9dbad6fSeschrock 
203392241e0bSTom Erickson 		/* decode the property value */
203492241e0bSTom Erickson 		propval = pair;
203592241e0bSTom Erickson 		if (nvpair_type(pair) == DATA_TYPE_NVLIST) {
203692241e0bSTom Erickson 			nvlist_t *attrs;
203792241e0bSTom Erickson 			VERIFY(nvpair_value_nvlist(pair, &attrs) == 0);
2038eeb85002STim Haley 			if (nvlist_lookup_nvpair(attrs, ZPROP_VALUE,
2039eeb85002STim Haley 			    &propval) != 0)
2040eeb85002STim Haley 				err = EINVAL;
204114843421SMatthew Ahrens 		}
2042e9dbad6fSeschrock 
204392241e0bSTom Erickson 		/* Validate value type */
2044eeb85002STim Haley 		if (err == 0 && prop == ZPROP_INVAL) {
204592241e0bSTom Erickson 			if (zfs_prop_user(propname)) {
204692241e0bSTom Erickson 				if (nvpair_type(propval) != DATA_TYPE_STRING)
204792241e0bSTom Erickson 					err = EINVAL;
204892241e0bSTom Erickson 			} else if (zfs_prop_userquota(propname)) {
204992241e0bSTom Erickson 				if (nvpair_type(propval) !=
205092241e0bSTom Erickson 				    DATA_TYPE_UINT64_ARRAY)
205192241e0bSTom Erickson 					err = EINVAL;
20524201a95eSRic Aleshire 			}
2053eeb85002STim Haley 		} else if (err == 0) {
205492241e0bSTom Erickson 			if (nvpair_type(propval) == DATA_TYPE_STRING) {
205592241e0bSTom Erickson 				if (zfs_prop_get_type(prop) != PROP_TYPE_STRING)
205692241e0bSTom Erickson 					err = EINVAL;
205792241e0bSTom Erickson 			} else if (nvpair_type(propval) == DATA_TYPE_UINT64) {
2058a2eea2e1Sahrens 				const char *unused;
2059a2eea2e1Sahrens 
206092241e0bSTom Erickson 				VERIFY(nvpair_value_uint64(propval,
206192241e0bSTom Erickson 				    &intval) == 0);
2062e9dbad6fSeschrock 
2063e9dbad6fSeschrock 				switch (zfs_prop_get_type(prop)) {
206491ebeef5Sahrens 				case PROP_TYPE_NUMBER:
2065e9dbad6fSeschrock 					break;
206691ebeef5Sahrens 				case PROP_TYPE_STRING:
206792241e0bSTom Erickson 					err = EINVAL;
206892241e0bSTom Erickson 					break;
206991ebeef5Sahrens 				case PROP_TYPE_INDEX:
2070acd76fe5Seschrock 					if (zfs_prop_index_to_string(prop,
207192241e0bSTom Erickson 					    intval, &unused) != 0)
207292241e0bSTom Erickson 						err = EINVAL;
2073e9dbad6fSeschrock 					break;
2074e9dbad6fSeschrock 				default:
2075e7437265Sahrens 					cmn_err(CE_PANIC,
2076e7437265Sahrens 					    "unknown property type");
2077e9dbad6fSeschrock 				}
2078e9dbad6fSeschrock 			} else {
207992241e0bSTom Erickson 				err = EINVAL;
2080e9dbad6fSeschrock 			}
2081e9dbad6fSeschrock 		}
208292241e0bSTom Erickson 
208392241e0bSTom Erickson 		/* Validate permissions */
208492241e0bSTom Erickson 		if (err == 0)
208592241e0bSTom Erickson 			err = zfs_check_settable(dsname, pair, CRED());
208692241e0bSTom Erickson 
208792241e0bSTom Erickson 		if (err == 0) {
208892241e0bSTom Erickson 			err = zfs_prop_set_special(dsname, source, pair);
208992241e0bSTom Erickson 			if (err == -1) {
209092241e0bSTom Erickson 				/*
209192241e0bSTom Erickson 				 * For better performance we build up a list of
209292241e0bSTom Erickson 				 * properties to set in a single transaction.
209392241e0bSTom Erickson 				 */
209492241e0bSTom Erickson 				err = nvlist_add_nvpair(genericnvl, pair);
209592241e0bSTom Erickson 			} else if (err != 0 && nvl != retrynvl) {
209692241e0bSTom Erickson 				/*
209792241e0bSTom Erickson 				 * This may be a spurious error caused by
209892241e0bSTom Erickson 				 * receiving quota and reservation out of order.
209992241e0bSTom Erickson 				 * Try again in a second pass.
210092241e0bSTom Erickson 				 */
210192241e0bSTom Erickson 				err = nvlist_add_nvpair(retrynvl, pair);
210292241e0bSTom Erickson 			}
210392241e0bSTom Erickson 		}
210492241e0bSTom Erickson 
210592241e0bSTom Erickson 		if (err != 0)
210692241e0bSTom Erickson 			VERIFY(nvlist_add_int32(errors, propname, err) == 0);
2107e9dbad6fSeschrock 	}
2108e9dbad6fSeschrock 
210992241e0bSTom Erickson 	if (nvl != retrynvl && !nvlist_empty(retrynvl)) {
211092241e0bSTom Erickson 		nvl = retrynvl;
211192241e0bSTom Erickson 		goto retry;
211292241e0bSTom Erickson 	}
211392241e0bSTom Erickson 
211492241e0bSTom Erickson 	if (!nvlist_empty(genericnvl) &&
211592241e0bSTom Erickson 	    dsl_props_set(dsname, source, genericnvl) != 0) {
211692241e0bSTom Erickson 		/*
211792241e0bSTom Erickson 		 * If this fails, we still want to set as many properties as we
211892241e0bSTom Erickson 		 * can, so try setting them individually.
211992241e0bSTom Erickson 		 */
212092241e0bSTom Erickson 		pair = NULL;
212192241e0bSTom Erickson 		while ((pair = nvlist_next_nvpair(genericnvl, pair)) != NULL) {
212292241e0bSTom Erickson 			const char *propname = nvpair_name(pair);
2123cfa69fd2STom Erickson 			int err = 0;
212492241e0bSTom Erickson 
212592241e0bSTom Erickson 			propval = pair;
212692241e0bSTom Erickson 			if (nvpair_type(pair) == DATA_TYPE_NVLIST) {
212792241e0bSTom Erickson 				nvlist_t *attrs;
212892241e0bSTom Erickson 				VERIFY(nvpair_value_nvlist(pair, &attrs) == 0);
212992241e0bSTom Erickson 				VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE,
213092241e0bSTom Erickson 				    &propval) == 0);
213192241e0bSTom Erickson 			}
213292241e0bSTom Erickson 
213392241e0bSTom Erickson 			if (nvpair_type(propval) == DATA_TYPE_STRING) {
213492241e0bSTom Erickson 				VERIFY(nvpair_value_string(propval,
213592241e0bSTom Erickson 				    &strval) == 0);
213692241e0bSTom Erickson 				err = dsl_prop_set(dsname, propname, source, 1,
213792241e0bSTom Erickson 				    strlen(strval) + 1, strval);
213892241e0bSTom Erickson 			} else {
213992241e0bSTom Erickson 				VERIFY(nvpair_value_uint64(propval,
214092241e0bSTom Erickson 				    &intval) == 0);
214192241e0bSTom Erickson 				err = dsl_prop_set(dsname, propname, source, 8,
214292241e0bSTom Erickson 				    1, &intval);
214392241e0bSTom Erickson 			}
214492241e0bSTom Erickson 
214592241e0bSTom Erickson 			if (err != 0) {
214692241e0bSTom Erickson 				VERIFY(nvlist_add_int32(errors, propname,
214792241e0bSTom Erickson 				    err) == 0);
214892241e0bSTom Erickson 			}
214992241e0bSTom Erickson 		}
21505c0b6a79SRich Morris 	}
21515c0b6a79SRich Morris 	nvlist_free(genericnvl);
215292241e0bSTom Erickson 	nvlist_free(retrynvl);
215392241e0bSTom Erickson 
215492241e0bSTom Erickson 	if ((pair = nvlist_next_nvpair(errors, NULL)) == NULL) {
215592241e0bSTom Erickson 		nvlist_free(errors);
215692241e0bSTom Erickson 		errors = NULL;
215792241e0bSTom Erickson 	} else {
215892241e0bSTom Erickson 		VERIFY(nvpair_value_int32(pair, &rv) == 0);
215992241e0bSTom Erickson 	}
216092241e0bSTom Erickson 
216192241e0bSTom Erickson 	if (errlist == NULL)
216292241e0bSTom Erickson 		nvlist_free(errors);
216392241e0bSTom Erickson 	else
216492241e0bSTom Erickson 		*errlist = errors;
216592241e0bSTom Erickson 
216692241e0bSTom Erickson 	return (rv);
2167fa9e4066Sahrens }
2168fa9e4066Sahrens 
2169ea2f5b9eSMatthew Ahrens /*
2170ea2f5b9eSMatthew Ahrens  * Check that all the properties are valid user properties.
2171ea2f5b9eSMatthew Ahrens  */
2172ea2f5b9eSMatthew Ahrens static int
2173ea2f5b9eSMatthew Ahrens zfs_check_userprops(char *fsname, nvlist_t *nvl)
2174ea2f5b9eSMatthew Ahrens {
217592241e0bSTom Erickson 	nvpair_t *pair = NULL;
2176ea2f5b9eSMatthew Ahrens 	int error = 0;
2177ea2f5b9eSMatthew Ahrens 
217892241e0bSTom Erickson 	while ((pair = nvlist_next_nvpair(nvl, pair)) != NULL) {
217992241e0bSTom Erickson 		const char *propname = nvpair_name(pair);
2180ea2f5b9eSMatthew Ahrens 		char *valstr;
2181ea2f5b9eSMatthew Ahrens 
2182ea2f5b9eSMatthew Ahrens 		if (!zfs_prop_user(propname) ||
218392241e0bSTom Erickson 		    nvpair_type(pair) != DATA_TYPE_STRING)
2184ea2f5b9eSMatthew Ahrens 			return (EINVAL);
2185ea2f5b9eSMatthew Ahrens 
2186ea2f5b9eSMatthew Ahrens 		if (error = zfs_secpolicy_write_perms(fsname,
2187ea2f5b9eSMatthew Ahrens 		    ZFS_DELEG_PERM_USERPROP, CRED()))
2188ea2f5b9eSMatthew Ahrens 			return (error);
2189ea2f5b9eSMatthew Ahrens 
2190ea2f5b9eSMatthew Ahrens 		if (strlen(propname) >= ZAP_MAXNAMELEN)
2191ea2f5b9eSMatthew Ahrens 			return (ENAMETOOLONG);
2192ea2f5b9eSMatthew Ahrens 
219392241e0bSTom Erickson 		VERIFY(nvpair_value_string(pair, &valstr) == 0);
2194ea2f5b9eSMatthew Ahrens 		if (strlen(valstr) >= ZAP_MAXVALUELEN)
2195ea2f5b9eSMatthew Ahrens 			return (E2BIG);
2196ea2f5b9eSMatthew Ahrens 	}
2197ea2f5b9eSMatthew Ahrens 	return (0);
2198ea2f5b9eSMatthew Ahrens }
2199ea2f5b9eSMatthew Ahrens 
220092241e0bSTom Erickson static void
220192241e0bSTom Erickson props_skip(nvlist_t *props, nvlist_t *skipped, nvlist_t **newprops)
220292241e0bSTom Erickson {
220392241e0bSTom Erickson 	nvpair_t *pair;
220492241e0bSTom Erickson 
220592241e0bSTom Erickson 	VERIFY(nvlist_alloc(newprops, NV_UNIQUE_NAME, KM_SLEEP) == 0);
220692241e0bSTom Erickson 
220792241e0bSTom Erickson 	pair = NULL;
220892241e0bSTom Erickson 	while ((pair = nvlist_next_nvpair(props, pair)) != NULL) {
220992241e0bSTom Erickson 		if (nvlist_exists(skipped, nvpair_name(pair)))
221092241e0bSTom Erickson 			continue;
221192241e0bSTom Erickson 
221292241e0bSTom Erickson 		VERIFY(nvlist_add_nvpair(*newprops, pair) == 0);
221392241e0bSTom Erickson 	}
221492241e0bSTom Erickson }
221592241e0bSTom Erickson 
221692241e0bSTom Erickson static int
221792241e0bSTom Erickson clear_received_props(objset_t *os, const char *fs, nvlist_t *props,
221892241e0bSTom Erickson     nvlist_t *skipped)
221992241e0bSTom Erickson {
222092241e0bSTom Erickson 	int err = 0;
222192241e0bSTom Erickson 	nvlist_t *cleared_props = NULL;
222292241e0bSTom Erickson 	props_skip(props, skipped, &cleared_props);
222392241e0bSTom Erickson 	if (!nvlist_empty(cleared_props)) {
222492241e0bSTom Erickson 		/*
222592241e0bSTom Erickson 		 * Acts on local properties until the dataset has received
222692241e0bSTom Erickson 		 * properties at least once on or after SPA_VERSION_RECVD_PROPS.
222792241e0bSTom Erickson 		 */
222892241e0bSTom Erickson 		zprop_source_t flags = (ZPROP_SRC_NONE |
222992241e0bSTom Erickson 		    (dsl_prop_get_hasrecvd(os) ? ZPROP_SRC_RECEIVED : 0));
223092241e0bSTom Erickson 		err = zfs_set_prop_nvlist(fs, flags, cleared_props, NULL);
223192241e0bSTom Erickson 	}
223292241e0bSTom Erickson 	nvlist_free(cleared_props);
223392241e0bSTom Erickson 	return (err);
223492241e0bSTom Erickson }
223592241e0bSTom Erickson 
22363cb34c60Sahrens /*
22373cb34c60Sahrens  * inputs:
22383cb34c60Sahrens  * zc_name		name of filesystem
22395c0b6a79SRich Morris  * zc_value		name of property to set
22403cb34c60Sahrens  * zc_nvlist_src{_size}	nvlist of properties to apply
224192241e0bSTom Erickson  * zc_cookie		received properties flag
22423cb34c60Sahrens  *
224392241e0bSTom Erickson  * outputs:
224492241e0bSTom Erickson  * zc_nvlist_dst{_size} error for each unapplied received property
22453cb34c60Sahrens  */
2246fa9e4066Sahrens static int
2247e9dbad6fSeschrock zfs_ioc_set_prop(zfs_cmd_t *zc)
2248fa9e4066Sahrens {
2249e9dbad6fSeschrock 	nvlist_t *nvl;
225092241e0bSTom Erickson 	boolean_t received = zc->zc_cookie;
225192241e0bSTom Erickson 	zprop_source_t source = (received ? ZPROP_SRC_RECEIVED :
225292241e0bSTom Erickson 	    ZPROP_SRC_LOCAL);
225392241e0bSTom Erickson 	nvlist_t *errors = NULL;
2254e9dbad6fSeschrock 	int error;
2255e9dbad6fSeschrock 
2256990b4856Slling 	if ((error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
2257478ed9adSEric Taylor 	    zc->zc_iflags, &nvl)) != 0)
2258e9dbad6fSeschrock 		return (error);
2259e9dbad6fSeschrock 
226092241e0bSTom Erickson 	if (received) {
2261bb0ade09Sahrens 		nvlist_t *origprops;
2262bb0ade09Sahrens 		objset_t *os;
2263bb0ade09Sahrens 
2264503ad85cSMatthew Ahrens 		if (dmu_objset_hold(zc->zc_name, FTAG, &os) == 0) {
226592241e0bSTom Erickson 			if (dsl_prop_get_received(os, &origprops) == 0) {
226692241e0bSTom Erickson 				(void) clear_received_props(os,
226792241e0bSTom Erickson 				    zc->zc_name, origprops, nvl);
2268bb0ade09Sahrens 				nvlist_free(origprops);
2269bb0ade09Sahrens 			}
227092241e0bSTom Erickson 
227192241e0bSTom Erickson 			dsl_prop_set_hasrecvd(os);
2272503ad85cSMatthew Ahrens 			dmu_objset_rele(os, FTAG);
2273bb0ade09Sahrens 		}
2274bb0ade09Sahrens 	}
2275bb0ade09Sahrens 
227692241e0bSTom Erickson 	error = zfs_set_prop_nvlist(zc->zc_name, source, nvl, &errors);
227792241e0bSTom Erickson 
227892241e0bSTom Erickson 	if (zc->zc_nvlist_dst != NULL && errors != NULL) {
227992241e0bSTom Erickson 		(void) put_nvlist(zc, errors);
228092241e0bSTom Erickson 	}
2281ecd6cf80Smarks 
228292241e0bSTom Erickson 	nvlist_free(errors);
2283e9dbad6fSeschrock 	nvlist_free(nvl);
2284e9dbad6fSeschrock 	return (error);
2285fa9e4066Sahrens }
2286fa9e4066Sahrens 
22873cb34c60Sahrens /*
22883cb34c60Sahrens  * inputs:
22893cb34c60Sahrens  * zc_name		name of filesystem
22903cb34c60Sahrens  * zc_value		name of property to inherit
229192241e0bSTom Erickson  * zc_cookie		revert to received value if TRUE
22923cb34c60Sahrens  *
22933cb34c60Sahrens  * outputs:		none
22943cb34c60Sahrens  */
2295e45ce728Sahrens static int
2296e45ce728Sahrens zfs_ioc_inherit_prop(zfs_cmd_t *zc)
2297e45ce728Sahrens {
229892241e0bSTom Erickson 	const char *propname = zc->zc_value;
229992241e0bSTom Erickson 	zfs_prop_t prop = zfs_name_to_prop(propname);
230092241e0bSTom Erickson 	boolean_t received = zc->zc_cookie;
230192241e0bSTom Erickson 	zprop_source_t source = (received
230292241e0bSTom Erickson 	    ? ZPROP_SRC_NONE		/* revert to received value, if any */
230392241e0bSTom Erickson 	    : ZPROP_SRC_INHERITED);	/* explicitly inherit */
230492241e0bSTom Erickson 
230592241e0bSTom Erickson 	if (received) {
230692241e0bSTom Erickson 		nvlist_t *dummy;
230792241e0bSTom Erickson 		nvpair_t *pair;
230892241e0bSTom Erickson 		zprop_type_t type;
230992241e0bSTom Erickson 		int err;
231092241e0bSTom Erickson 
231192241e0bSTom Erickson 		/*
231292241e0bSTom Erickson 		 * zfs_prop_set_special() expects properties in the form of an
231392241e0bSTom Erickson 		 * nvpair with type info.
231492241e0bSTom Erickson 		 */
231592241e0bSTom Erickson 		if (prop == ZPROP_INVAL) {
231692241e0bSTom Erickson 			if (!zfs_prop_user(propname))
231792241e0bSTom Erickson 				return (EINVAL);
231892241e0bSTom Erickson 
231992241e0bSTom Erickson 			type = PROP_TYPE_STRING;
2320a79992aaSTom Erickson 		} else if (prop == ZFS_PROP_VOLSIZE ||
2321a79992aaSTom Erickson 		    prop == ZFS_PROP_VERSION) {
2322a79992aaSTom Erickson 			return (EINVAL);
232392241e0bSTom Erickson 		} else {
232492241e0bSTom Erickson 			type = zfs_prop_get_type(prop);
232592241e0bSTom Erickson 		}
232692241e0bSTom Erickson 
232792241e0bSTom Erickson 		VERIFY(nvlist_alloc(&dummy, NV_UNIQUE_NAME, KM_SLEEP) == 0);
232892241e0bSTom Erickson 
232992241e0bSTom Erickson 		switch (type) {
233092241e0bSTom Erickson 		case PROP_TYPE_STRING:
233192241e0bSTom Erickson 			VERIFY(0 == nvlist_add_string(dummy, propname, ""));
233292241e0bSTom Erickson 			break;
233392241e0bSTom Erickson 		case PROP_TYPE_NUMBER:
233492241e0bSTom Erickson 		case PROP_TYPE_INDEX:
233592241e0bSTom Erickson 			VERIFY(0 == nvlist_add_uint64(dummy, propname, 0));
233692241e0bSTom Erickson 			break;
233792241e0bSTom Erickson 		default:
233892241e0bSTom Erickson 			nvlist_free(dummy);
233992241e0bSTom Erickson 			return (EINVAL);
234092241e0bSTom Erickson 		}
234192241e0bSTom Erickson 
234292241e0bSTom Erickson 		pair = nvlist_next_nvpair(dummy, NULL);
234392241e0bSTom Erickson 		err = zfs_prop_set_special(zc->zc_name, source, pair);
234492241e0bSTom Erickson 		nvlist_free(dummy);
234592241e0bSTom Erickson 		if (err != -1)
234692241e0bSTom Erickson 			return (err); /* special property already handled */
234792241e0bSTom Erickson 	} else {
234892241e0bSTom Erickson 		/*
234992241e0bSTom Erickson 		 * Only check this in the non-received case. We want to allow
235092241e0bSTom Erickson 		 * 'inherit -S' to revert non-inheritable properties like quota
235192241e0bSTom Erickson 		 * and reservation to the received or default values even though
235292241e0bSTom Erickson 		 * they are not considered inheritable.
235392241e0bSTom Erickson 		 */
235492241e0bSTom Erickson 		if (prop != ZPROP_INVAL && !zfs_prop_inheritable(prop))
235592241e0bSTom Erickson 			return (EINVAL);
235692241e0bSTom Erickson 	}
235792241e0bSTom Erickson 
2358e45ce728Sahrens 	/* the property name has been validated by zfs_secpolicy_inherit() */
235992241e0bSTom Erickson 	return (dsl_prop_set(zc->zc_name, zc->zc_value, source, 0, 0, NULL));
2360e45ce728Sahrens }
2361e45ce728Sahrens 
2362b1b8ab34Slling static int
236311a41203Slling zfs_ioc_pool_set_props(zfs_cmd_t *zc)
2364b1b8ab34Slling {
2365990b4856Slling 	nvlist_t *props;
2366b1b8ab34Slling 	spa_t *spa;
2367990b4856Slling 	int error;
236892241e0bSTom Erickson 	nvpair_t *pair;
2369b1b8ab34Slling 
237092241e0bSTom Erickson 	if (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
237192241e0bSTom Erickson 	    zc->zc_iflags, &props))
2372b1b8ab34Slling 		return (error);
2373b1b8ab34Slling 
2374379c004dSEric Schrock 	/*
2375379c004dSEric Schrock 	 * If the only property is the configfile, then just do a spa_lookup()
2376379c004dSEric Schrock 	 * to handle the faulted case.
2377379c004dSEric Schrock 	 */
237892241e0bSTom Erickson 	pair = nvlist_next_nvpair(props, NULL);
237992241e0bSTom Erickson 	if (pair != NULL && strcmp(nvpair_name(pair),
2380379c004dSEric Schrock 	    zpool_prop_to_name(ZPOOL_PROP_CACHEFILE)) == 0 &&
238192241e0bSTom Erickson 	    nvlist_next_nvpair(props, pair) == NULL) {
2382379c004dSEric Schrock 		mutex_enter(&spa_namespace_lock);
2383379c004dSEric Schrock 		if ((spa = spa_lookup(zc->zc_name)) != NULL) {
2384379c004dSEric Schrock 			spa_configfile_set(spa, props, B_FALSE);
2385379c004dSEric Schrock 			spa_config_sync(spa, B_FALSE, B_TRUE);
2386379c004dSEric Schrock 		}
2387379c004dSEric Schrock 		mutex_exit(&spa_namespace_lock);
2388b693757aSEric Schrock 		if (spa != NULL) {
2389b693757aSEric Schrock 			nvlist_free(props);
2390379c004dSEric Schrock 			return (0);
2391b693757aSEric Schrock 		}
2392379c004dSEric Schrock 	}
2393379c004dSEric Schrock 
2394b1b8ab34Slling 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) {
2395990b4856Slling 		nvlist_free(props);
2396b1b8ab34Slling 		return (error);
2397b1b8ab34Slling 	}
2398b1b8ab34Slling 
2399990b4856Slling 	error = spa_prop_set(spa, props);
2400b1b8ab34Slling 
2401990b4856Slling 	nvlist_free(props);
2402b1b8ab34Slling 	spa_close(spa, FTAG);
2403b1b8ab34Slling 
2404b1b8ab34Slling 	return (error);
2405b1b8ab34Slling }
2406b1b8ab34Slling 
2407b1b8ab34Slling static int
240811a41203Slling zfs_ioc_pool_get_props(zfs_cmd_t *zc)
2409b1b8ab34Slling {
2410b1b8ab34Slling 	spa_t *spa;
2411b1b8ab34Slling 	int error;
2412b1b8ab34Slling 	nvlist_t *nvp = NULL;
2413b1b8ab34Slling 
2414379c004dSEric Schrock 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) {
2415379c004dSEric Schrock 		/*
2416379c004dSEric Schrock 		 * If the pool is faulted, there may be properties we can still
2417379c004dSEric Schrock 		 * get (such as altroot and cachefile), so attempt to get them
2418379c004dSEric Schrock 		 * anyway.
2419379c004dSEric Schrock 		 */
2420379c004dSEric Schrock 		mutex_enter(&spa_namespace_lock);
2421379c004dSEric Schrock 		if ((spa = spa_lookup(zc->zc_name)) != NULL)
2422379c004dSEric Schrock 			error = spa_prop_get(spa, &nvp);
2423379c004dSEric Schrock 		mutex_exit(&spa_namespace_lock);
2424379c004dSEric Schrock 	} else {
2425379c004dSEric Schrock 		error = spa_prop_get(spa, &nvp);
2426379c004dSEric Schrock 		spa_close(spa, FTAG);
2427379c004dSEric Schrock 	}
2428b1b8ab34Slling 
2429b1b8ab34Slling 	if (error == 0 && zc->zc_nvlist_dst != NULL)
2430b1b8ab34Slling 		error = put_nvlist(zc, nvp);
2431b1b8ab34Slling 	else
2432b1b8ab34Slling 		error = EFAULT;
2433b1b8ab34Slling 
2434379c004dSEric Schrock 	nvlist_free(nvp);
2435b1b8ab34Slling 	return (error);
2436b1b8ab34Slling }
2437b1b8ab34Slling 
24383cb34c60Sahrens /*
24393cb34c60Sahrens  * inputs:
24403cb34c60Sahrens  * zc_name		name of filesystem
24413cb34c60Sahrens  * zc_nvlist_src{_size}	nvlist of delegated permissions
24423cb34c60Sahrens  * zc_perm_action	allow/unallow flag
24433cb34c60Sahrens  *
24443cb34c60Sahrens  * outputs:		none
24453cb34c60Sahrens  */
2446ecd6cf80Smarks static int
2447ecd6cf80Smarks zfs_ioc_set_fsacl(zfs_cmd_t *zc)
2448ecd6cf80Smarks {
2449ecd6cf80Smarks 	int error;
2450ecd6cf80Smarks 	nvlist_t *fsaclnv = NULL;
2451ecd6cf80Smarks 
2452990b4856Slling 	if ((error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
2453478ed9adSEric Taylor 	    zc->zc_iflags, &fsaclnv)) != 0)
2454ecd6cf80Smarks 		return (error);
2455ecd6cf80Smarks 
2456ecd6cf80Smarks 	/*
2457ecd6cf80Smarks 	 * Verify nvlist is constructed correctly
2458ecd6cf80Smarks 	 */
2459ecd6cf80Smarks 	if ((error = zfs_deleg_verify_nvlist(fsaclnv)) != 0) {
2460ecd6cf80Smarks 		nvlist_free(fsaclnv);
2461ecd6cf80Smarks 		return (EINVAL);
2462ecd6cf80Smarks 	}
2463ecd6cf80Smarks 
2464ecd6cf80Smarks 	/*
2465ecd6cf80Smarks 	 * If we don't have PRIV_SYS_MOUNT, then validate
2466ecd6cf80Smarks 	 * that user is allowed to hand out each permission in
2467ecd6cf80Smarks 	 * the nvlist(s)
2468ecd6cf80Smarks 	 */
2469ecd6cf80Smarks 
247091ebeef5Sahrens 	error = secpolicy_zfs(CRED());
2471ecd6cf80Smarks 	if (error) {
247291ebeef5Sahrens 		if (zc->zc_perm_action == B_FALSE) {
247391ebeef5Sahrens 			error = dsl_deleg_can_allow(zc->zc_name,
247491ebeef5Sahrens 			    fsaclnv, CRED());
247591ebeef5Sahrens 		} else {
247691ebeef5Sahrens 			error = dsl_deleg_can_unallow(zc->zc_name,
247791ebeef5Sahrens 			    fsaclnv, CRED());
247891ebeef5Sahrens 		}
2479ecd6cf80Smarks 	}
2480ecd6cf80Smarks 
2481ecd6cf80Smarks 	if (error == 0)
2482ecd6cf80Smarks 		error = dsl_deleg_set(zc->zc_name, fsaclnv, zc->zc_perm_action);
2483ecd6cf80Smarks 
2484ecd6cf80Smarks 	nvlist_free(fsaclnv);
2485ecd6cf80Smarks 	return (error);
2486ecd6cf80Smarks }
2487ecd6cf80Smarks 
24883cb34c60Sahrens /*
24893cb34c60Sahrens  * inputs:
24903cb34c60Sahrens  * zc_name		name of filesystem
24913cb34c60Sahrens  *
24923cb34c60Sahrens  * outputs:
24933cb34c60Sahrens  * zc_nvlist_src{_size}	nvlist of delegated permissions
24943cb34c60Sahrens  */
2495ecd6cf80Smarks static int
2496ecd6cf80Smarks zfs_ioc_get_fsacl(zfs_cmd_t *zc)
2497ecd6cf80Smarks {
2498ecd6cf80Smarks 	nvlist_t *nvp;
2499ecd6cf80Smarks 	int error;
2500ecd6cf80Smarks 
2501ecd6cf80Smarks 	if ((error = dsl_deleg_get(zc->zc_name, &nvp)) == 0) {
2502ecd6cf80Smarks 		error = put_nvlist(zc, nvp);
2503ecd6cf80Smarks 		nvlist_free(nvp);
2504ecd6cf80Smarks 	}
2505ecd6cf80Smarks 
2506ecd6cf80Smarks 	return (error);
2507ecd6cf80Smarks }
2508ecd6cf80Smarks 
2509fa9e4066Sahrens /*
2510fa9e4066Sahrens  * Search the vfs list for a specified resource.  Returns a pointer to it
2511fa9e4066Sahrens  * or NULL if no suitable entry is found. The caller of this routine
2512fa9e4066Sahrens  * is responsible for releasing the returned vfs pointer.
2513fa9e4066Sahrens  */
2514fa9e4066Sahrens static vfs_t *
2515fa9e4066Sahrens zfs_get_vfs(const char *resource)
2516fa9e4066Sahrens {
2517fa9e4066Sahrens 	struct vfs *vfsp;
2518fa9e4066Sahrens 	struct vfs *vfs_found = NULL;
2519fa9e4066Sahrens 
2520fa9e4066Sahrens 	vfs_list_read_lock();
2521fa9e4066Sahrens 	vfsp = rootvfs;
2522fa9e4066Sahrens 	do {
2523fa9e4066Sahrens 		if (strcmp(refstr_value(vfsp->vfs_resource), resource) == 0) {
2524fa9e4066Sahrens 			VFS_HOLD(vfsp);
2525fa9e4066Sahrens 			vfs_found = vfsp;
2526fa9e4066Sahrens 			break;
2527fa9e4066Sahrens 		}
2528fa9e4066Sahrens 		vfsp = vfsp->vfs_next;
2529fa9e4066Sahrens 	} while (vfsp != rootvfs);
2530fa9e4066Sahrens 	vfs_list_unlock();
2531fa9e4066Sahrens 	return (vfs_found);
2532fa9e4066Sahrens }
2533fa9e4066Sahrens 
2534ecd6cf80Smarks /* ARGSUSED */
2535fa9e4066Sahrens static void
2536ecd6cf80Smarks zfs_create_cb(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx)
2537fa9e4066Sahrens {
2538da6c28aaSamw 	zfs_creat_t *zct = arg;
2539da6c28aaSamw 
2540de8267e0Stimh 	zfs_create_fs(os, cr, zct->zct_zplprops, tx);
2541da6c28aaSamw }
2542da6c28aaSamw 
2543de8267e0Stimh #define	ZFS_PROP_UNDEFINED	((uint64_t)-1)
2544da6c28aaSamw 
2545da6c28aaSamw /*
2546de8267e0Stimh  * inputs:
25470a48a24eStimh  * createprops		list of properties requested by creator
25480a48a24eStimh  * default_zplver	zpl version to use if unspecified in createprops
25490a48a24eStimh  * fuids_ok		fuids allowed in this version of the spa?
25500a48a24eStimh  * os			parent objset pointer (NULL if root fs)
2551de8267e0Stimh  *
2552de8267e0Stimh  * outputs:
2553de8267e0Stimh  * zplprops	values for the zplprops we attach to the master node object
25540a48a24eStimh  * is_ci	true if requested file system will be purely case-insensitive
2555da6c28aaSamw  *
2556de8267e0Stimh  * Determine the settings for utf8only, normalization and
2557de8267e0Stimh  * casesensitivity.  Specific values may have been requested by the
2558de8267e0Stimh  * creator and/or we can inherit values from the parent dataset.  If
2559de8267e0Stimh  * the file system is of too early a vintage, a creator can not
2560de8267e0Stimh  * request settings for these properties, even if the requested
2561de8267e0Stimh  * setting is the default value.  We don't actually want to create dsl
2562de8267e0Stimh  * properties for these, so remove them from the source nvlist after
2563de8267e0Stimh  * processing.
2564da6c28aaSamw  */
2565da6c28aaSamw static int
256614843421SMatthew Ahrens zfs_fill_zplprops_impl(objset_t *os, uint64_t zplver,
25670a586ceaSMark Shellenbaum     boolean_t fuids_ok, boolean_t sa_ok, nvlist_t *createprops,
25680a586ceaSMark Shellenbaum     nvlist_t *zplprops, boolean_t *is_ci)
2569da6c28aaSamw {
2570de8267e0Stimh 	uint64_t sense = ZFS_PROP_UNDEFINED;
2571de8267e0Stimh 	uint64_t norm = ZFS_PROP_UNDEFINED;
2572de8267e0Stimh 	uint64_t u8 = ZFS_PROP_UNDEFINED;
2573da6c28aaSamw 
2574de8267e0Stimh 	ASSERT(zplprops != NULL);
2575da6c28aaSamw 
2576de8267e0Stimh 	/*
2577de8267e0Stimh 	 * Pull out creator prop choices, if any.
2578de8267e0Stimh 	 */
2579de8267e0Stimh 	if (createprops) {
25800a48a24eStimh 		(void) nvlist_lookup_uint64(createprops,
25810a48a24eStimh 		    zfs_prop_to_name(ZFS_PROP_VERSION), &zplver);
2582de8267e0Stimh 		(void) nvlist_lookup_uint64(createprops,
2583de8267e0Stimh 		    zfs_prop_to_name(ZFS_PROP_NORMALIZE), &norm);
2584de8267e0Stimh 		(void) nvlist_remove_all(createprops,
2585de8267e0Stimh 		    zfs_prop_to_name(ZFS_PROP_NORMALIZE));
2586de8267e0Stimh 		(void) nvlist_lookup_uint64(createprops,
2587de8267e0Stimh 		    zfs_prop_to_name(ZFS_PROP_UTF8ONLY), &u8);
2588de8267e0Stimh 		(void) nvlist_remove_all(createprops,
2589de8267e0Stimh 		    zfs_prop_to_name(ZFS_PROP_UTF8ONLY));
2590de8267e0Stimh 		(void) nvlist_lookup_uint64(createprops,
2591de8267e0Stimh 		    zfs_prop_to_name(ZFS_PROP_CASE), &sense);
2592de8267e0Stimh 		(void) nvlist_remove_all(createprops,
2593de8267e0Stimh 		    zfs_prop_to_name(ZFS_PROP_CASE));
2594de8267e0Stimh 	}
2595da6c28aaSamw 
2596c2a93d44Stimh 	/*
25970a48a24eStimh 	 * If the zpl version requested is whacky or the file system
25980a48a24eStimh 	 * or pool is version is too "young" to support normalization
25990a48a24eStimh 	 * and the creator tried to set a value for one of the props,
26000a48a24eStimh 	 * error out.
2601c2a93d44Stimh 	 */
26020a48a24eStimh 	if ((zplver < ZPL_VERSION_INITIAL || zplver > ZPL_VERSION) ||
26030a48a24eStimh 	    (zplver >= ZPL_VERSION_FUID && !fuids_ok) ||
26040a586ceaSMark Shellenbaum 	    (zplver >= ZPL_VERSION_SA && !sa_ok) ||
26050a48a24eStimh 	    (zplver < ZPL_VERSION_NORMALIZATION &&
2606de8267e0Stimh 	    (norm != ZFS_PROP_UNDEFINED || u8 != ZFS_PROP_UNDEFINED ||
26070a48a24eStimh 	    sense != ZFS_PROP_UNDEFINED)))
2608de8267e0Stimh 		return (ENOTSUP);
2609c2a93d44Stimh 
2610de8267e0Stimh 	/*
2611de8267e0Stimh 	 * Put the version in the zplprops
2612de8267e0Stimh 	 */
2613de8267e0Stimh 	VERIFY(nvlist_add_uint64(zplprops,
2614de8267e0Stimh 	    zfs_prop_to_name(ZFS_PROP_VERSION), zplver) == 0);
2615da6c28aaSamw 
2616de8267e0Stimh 	if (norm == ZFS_PROP_UNDEFINED)
2617de8267e0Stimh 		VERIFY(zfs_get_zplprop(os, ZFS_PROP_NORMALIZE, &norm) == 0);
2618de8267e0Stimh 	VERIFY(nvlist_add_uint64(zplprops,
2619de8267e0Stimh 	    zfs_prop_to_name(ZFS_PROP_NORMALIZE), norm) == 0);
2620da6c28aaSamw 
2621c2a93d44Stimh 	/*
2622de8267e0Stimh 	 * If we're normalizing, names must always be valid UTF-8 strings.
2623c2a93d44Stimh 	 */
2624de8267e0Stimh 	if (norm)
2625de8267e0Stimh 		u8 = 1;
2626de8267e0Stimh 	if (u8 == ZFS_PROP_UNDEFINED)
2627de8267e0Stimh 		VERIFY(zfs_get_zplprop(os, ZFS_PROP_UTF8ONLY, &u8) == 0);
2628de8267e0Stimh 	VERIFY(nvlist_add_uint64(zplprops,
2629de8267e0Stimh 	    zfs_prop_to_name(ZFS_PROP_UTF8ONLY), u8) == 0);
2630de8267e0Stimh 
2631de8267e0Stimh 	if (sense == ZFS_PROP_UNDEFINED)
2632de8267e0Stimh 		VERIFY(zfs_get_zplprop(os, ZFS_PROP_CASE, &sense) == 0);
2633de8267e0Stimh 	VERIFY(nvlist_add_uint64(zplprops,
2634de8267e0Stimh 	    zfs_prop_to_name(ZFS_PROP_CASE), sense) == 0);
2635c2a93d44Stimh 
2636ab04eb8eStimh 	if (is_ci)
2637ab04eb8eStimh 		*is_ci = (sense == ZFS_CASE_INSENSITIVE);
2638ab04eb8eStimh 
2639da6c28aaSamw 	return (0);
2640fa9e4066Sahrens }
2641fa9e4066Sahrens 
26420a48a24eStimh static int
26430a48a24eStimh zfs_fill_zplprops(const char *dataset, nvlist_t *createprops,
26440a48a24eStimh     nvlist_t *zplprops, boolean_t *is_ci)
26450a48a24eStimh {
26460a586ceaSMark Shellenbaum 	boolean_t fuids_ok, sa_ok;
26470a48a24eStimh 	uint64_t zplver = ZPL_VERSION;
26480a48a24eStimh 	objset_t *os = NULL;
26490a48a24eStimh 	char parentname[MAXNAMELEN];
26500a48a24eStimh 	char *cp;
26510a586ceaSMark Shellenbaum 	spa_t *spa;
26520a586ceaSMark Shellenbaum 	uint64_t spa_vers;
26530a48a24eStimh 	int error;
26540a48a24eStimh 
26550a48a24eStimh 	(void) strlcpy(parentname, dataset, sizeof (parentname));
26560a48a24eStimh 	cp = strrchr(parentname, '/');
26570a48a24eStimh 	ASSERT(cp != NULL);
26580a48a24eStimh 	cp[0] = '\0';
26590a48a24eStimh 
26600a586ceaSMark Shellenbaum 	if ((error = spa_open(dataset, &spa, FTAG)) != 0)
26610a586ceaSMark Shellenbaum 		return (error);
26620a586ceaSMark Shellenbaum 
26630a586ceaSMark Shellenbaum 	spa_vers = spa_version(spa);
26640a586ceaSMark Shellenbaum 	spa_close(spa, FTAG);
26650a586ceaSMark Shellenbaum 
26660a586ceaSMark Shellenbaum 	zplver = zfs_zpl_version_map(spa_vers);
26670a586ceaSMark Shellenbaum 	fuids_ok = (zplver >= ZPL_VERSION_FUID);
26680a586ceaSMark Shellenbaum 	sa_ok = (zplver >= ZPL_VERSION_SA);
26690a48a24eStimh 
26700a48a24eStimh 	/*
26710a48a24eStimh 	 * Open parent object set so we can inherit zplprop values.
26720a48a24eStimh 	 */
2673503ad85cSMatthew Ahrens 	if ((error = dmu_objset_hold(parentname, FTAG, &os)) != 0)
26740a48a24eStimh 		return (error);
26750a48a24eStimh 
26760a586ceaSMark Shellenbaum 	error = zfs_fill_zplprops_impl(os, zplver, fuids_ok, sa_ok, createprops,
26770a48a24eStimh 	    zplprops, is_ci);
2678503ad85cSMatthew Ahrens 	dmu_objset_rele(os, FTAG);
26790a48a24eStimh 	return (error);
26800a48a24eStimh }
26810a48a24eStimh 
26820a48a24eStimh static int
26830a48a24eStimh zfs_fill_zplprops_root(uint64_t spa_vers, nvlist_t *createprops,
26840a48a24eStimh     nvlist_t *zplprops, boolean_t *is_ci)
26850a48a24eStimh {
26860a586ceaSMark Shellenbaum 	boolean_t fuids_ok;
26870a586ceaSMark Shellenbaum 	boolean_t sa_ok;
26880a48a24eStimh 	uint64_t zplver = ZPL_VERSION;
26890a48a24eStimh 	int error;
26900a48a24eStimh 
26910a586ceaSMark Shellenbaum 	zplver = zfs_zpl_version_map(spa_vers);
26920a586ceaSMark Shellenbaum 	fuids_ok = (zplver >= ZPL_VERSION_FUID);
26930a586ceaSMark Shellenbaum 	sa_ok = (zplver >= ZPL_VERSION_SA);
26940a48a24eStimh 
26950a586ceaSMark Shellenbaum 	error = zfs_fill_zplprops_impl(NULL, zplver, fuids_ok, sa_ok,
26960a586ceaSMark Shellenbaum 	    createprops, zplprops, is_ci);
26970a48a24eStimh 	return (error);
26980a48a24eStimh }
26990a48a24eStimh 
27003cb34c60Sahrens /*
27013cb34c60Sahrens  * inputs:
27023cb34c60Sahrens  * zc_objset_type	type of objset to create (fs vs zvol)
27033cb34c60Sahrens  * zc_name		name of new objset
27043cb34c60Sahrens  * zc_value		name of snapshot to clone from (may be empty)
27053cb34c60Sahrens  * zc_nvlist_src{_size}	nvlist of properties to apply
27063cb34c60Sahrens  *
2707de8267e0Stimh  * outputs: none
27083cb34c60Sahrens  */
2709fa9e4066Sahrens static int
2710fa9e4066Sahrens zfs_ioc_create(zfs_cmd_t *zc)
2711fa9e4066Sahrens {
2712fa9e4066Sahrens 	objset_t *clone;
2713fa9e4066Sahrens 	int error = 0;
2714da6c28aaSamw 	zfs_creat_t zct;
2715ecd6cf80Smarks 	nvlist_t *nvprops = NULL;
2716ecd6cf80Smarks 	void (*cbfunc)(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx);
2717fa9e4066Sahrens 	dmu_objset_type_t type = zc->zc_objset_type;
2718fa9e4066Sahrens 
2719fa9e4066Sahrens 	switch (type) {
2720fa9e4066Sahrens 
2721fa9e4066Sahrens 	case DMU_OST_ZFS:
2722fa9e4066Sahrens 		cbfunc = zfs_create_cb;
2723fa9e4066Sahrens 		break;
2724fa9e4066Sahrens 
2725fa9e4066Sahrens 	case DMU_OST_ZVOL:
2726fa9e4066Sahrens 		cbfunc = zvol_create_cb;
2727fa9e4066Sahrens 		break;
2728fa9e4066Sahrens 
2729fa9e4066Sahrens 	default:
27301d452cf5Sahrens 		cbfunc = NULL;
2731e7cbe64fSgw 		break;
2732fa9e4066Sahrens 	}
2733f18faf3fSek 	if (strchr(zc->zc_name, '@') ||
2734f18faf3fSek 	    strchr(zc->zc_name, '%'))
27351d452cf5Sahrens 		return (EINVAL);
2736fa9e4066Sahrens 
2737e9dbad6fSeschrock 	if (zc->zc_nvlist_src != NULL &&
2738990b4856Slling 	    (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
2739478ed9adSEric Taylor 	    zc->zc_iflags, &nvprops)) != 0)
2740e9dbad6fSeschrock 		return (error);
2741e9dbad6fSeschrock 
2742de8267e0Stimh 	zct.zct_zplprops = NULL;
2743da6c28aaSamw 	zct.zct_props = nvprops;
2744da6c28aaSamw 
2745e9dbad6fSeschrock 	if (zc->zc_value[0] != '\0') {
2746fa9e4066Sahrens 		/*
2747fa9e4066Sahrens 		 * We're creating a clone of an existing snapshot.
2748fa9e4066Sahrens 		 */
2749e9dbad6fSeschrock 		zc->zc_value[sizeof (zc->zc_value) - 1] = '\0';
2750e9dbad6fSeschrock 		if (dataset_namecheck(zc->zc_value, NULL, NULL) != 0) {
2751ecd6cf80Smarks 			nvlist_free(nvprops);
2752fa9e4066Sahrens 			return (EINVAL);
2753e9dbad6fSeschrock 		}
2754fa9e4066Sahrens 
2755503ad85cSMatthew Ahrens 		error = dmu_objset_hold(zc->zc_value, FTAG, &clone);
2756e9dbad6fSeschrock 		if (error) {
2757ecd6cf80Smarks 			nvlist_free(nvprops);
2758fa9e4066Sahrens 			return (error);
2759e9dbad6fSeschrock 		}
2760ab04eb8eStimh 
2761ae46e4c7SMatthew Ahrens 		error = dmu_objset_clone(zc->zc_name, dmu_objset_ds(clone), 0);
2762503ad85cSMatthew Ahrens 		dmu_objset_rele(clone, FTAG);
2763da6c28aaSamw 		if (error) {
2764da6c28aaSamw 			nvlist_free(nvprops);
2765da6c28aaSamw 			return (error);
2766da6c28aaSamw 		}
2767fa9e4066Sahrens 	} else {
2768ab04eb8eStimh 		boolean_t is_insensitive = B_FALSE;
2769ab04eb8eStimh 
2770e9dbad6fSeschrock 		if (cbfunc == NULL) {
2771ecd6cf80Smarks 			nvlist_free(nvprops);
27721d452cf5Sahrens 			return (EINVAL);
2773e9dbad6fSeschrock 		}
27745c5460e9Seschrock 
2775e9dbad6fSeschrock 		if (type == DMU_OST_ZVOL) {
2776e9dbad6fSeschrock 			uint64_t volsize, volblocksize;
2777e9dbad6fSeschrock 
2778ecd6cf80Smarks 			if (nvprops == NULL ||
2779ecd6cf80Smarks 			    nvlist_lookup_uint64(nvprops,
2780e9dbad6fSeschrock 			    zfs_prop_to_name(ZFS_PROP_VOLSIZE),
2781e9dbad6fSeschrock 			    &volsize) != 0) {
2782ecd6cf80Smarks 				nvlist_free(nvprops);
2783e9dbad6fSeschrock 				return (EINVAL);
2784e9dbad6fSeschrock 			}
2785e9dbad6fSeschrock 
2786ecd6cf80Smarks 			if ((error = nvlist_lookup_uint64(nvprops,
2787e9dbad6fSeschrock 			    zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE),
2788e9dbad6fSeschrock 			    &volblocksize)) != 0 && error != ENOENT) {
2789ecd6cf80Smarks 				nvlist_free(nvprops);
2790e9dbad6fSeschrock 				return (EINVAL);
2791e9dbad6fSeschrock 			}
2792e9dbad6fSeschrock 
2793e9dbad6fSeschrock 			if (error != 0)
2794e9dbad6fSeschrock 				volblocksize = zfs_prop_default_numeric(
2795e9dbad6fSeschrock 				    ZFS_PROP_VOLBLOCKSIZE);
2796e9dbad6fSeschrock 
2797e9dbad6fSeschrock 			if ((error = zvol_check_volblocksize(
2798e9dbad6fSeschrock 			    volblocksize)) != 0 ||
2799e9dbad6fSeschrock 			    (error = zvol_check_volsize(volsize,
2800e9dbad6fSeschrock 			    volblocksize)) != 0) {
2801ecd6cf80Smarks 				nvlist_free(nvprops);
28025c5460e9Seschrock 				return (error);
2803e9dbad6fSeschrock 			}
2804e7437265Sahrens 		} else if (type == DMU_OST_ZFS) {
2805da6c28aaSamw 			int error;
2806da6c28aaSamw 
2807da6c28aaSamw 			/*
2808da6c28aaSamw 			 * We have to have normalization and
2809da6c28aaSamw 			 * case-folding flags correct when we do the
2810da6c28aaSamw 			 * file system creation, so go figure them out
2811de8267e0Stimh 			 * now.
2812da6c28aaSamw 			 */
2813de8267e0Stimh 			VERIFY(nvlist_alloc(&zct.zct_zplprops,
2814de8267e0Stimh 			    NV_UNIQUE_NAME, KM_SLEEP) == 0);
2815de8267e0Stimh 			error = zfs_fill_zplprops(zc->zc_name, nvprops,
28160a48a24eStimh 			    zct.zct_zplprops, &is_insensitive);
2817da6c28aaSamw 			if (error != 0) {
2818da6c28aaSamw 				nvlist_free(nvprops);
2819de8267e0Stimh 				nvlist_free(zct.zct_zplprops);
2820da6c28aaSamw 				return (error);
2821da6c28aaSamw 			}
2822da6c28aaSamw 		}
2823ae46e4c7SMatthew Ahrens 		error = dmu_objset_create(zc->zc_name, type,
2824ab04eb8eStimh 		    is_insensitive ? DS_FLAG_CI_DATASET : 0, cbfunc, &zct);
2825de8267e0Stimh 		nvlist_free(zct.zct_zplprops);
2826fa9e4066Sahrens 	}
2827e9dbad6fSeschrock 
2828e9dbad6fSeschrock 	/*
2829e9dbad6fSeschrock 	 * It would be nice to do this atomically.
2830e9dbad6fSeschrock 	 */
2831e9dbad6fSeschrock 	if (error == 0) {
283292241e0bSTom Erickson 		error = zfs_set_prop_nvlist(zc->zc_name, ZPROP_SRC_LOCAL,
283392241e0bSTom Erickson 		    nvprops, NULL);
283492241e0bSTom Erickson 		if (error != 0)
2835842727c2SChris Kirby 			(void) dmu_objset_destroy(zc->zc_name, B_FALSE);
2836e9dbad6fSeschrock 	}
2837ecd6cf80Smarks 	nvlist_free(nvprops);
2838fa9e4066Sahrens 	return (error);
2839fa9e4066Sahrens }
2840fa9e4066Sahrens 
28413cb34c60Sahrens /*
28423cb34c60Sahrens  * inputs:
28433cb34c60Sahrens  * zc_name	name of filesystem
28443cb34c60Sahrens  * zc_value	short name of snapshot
28453cb34c60Sahrens  * zc_cookie	recursive flag
284614843421SMatthew Ahrens  * zc_nvlist_src[_size] property list
28473cb34c60Sahrens  *
2848681d9761SEric Taylor  * outputs:
2849681d9761SEric Taylor  * zc_value	short snapname (i.e. part after the '@')
28503cb34c60Sahrens  */
2851fa9e4066Sahrens static int
28521d452cf5Sahrens zfs_ioc_snapshot(zfs_cmd_t *zc)
2853fa9e4066Sahrens {
2854bb0ade09Sahrens 	nvlist_t *nvprops = NULL;
2855bb0ade09Sahrens 	int error;
2856bb0ade09Sahrens 	boolean_t recursive = zc->zc_cookie;
2857bb0ade09Sahrens 
2858e9dbad6fSeschrock 	if (snapshot_namecheck(zc->zc_value, NULL, NULL) != 0)
28591d452cf5Sahrens 		return (EINVAL);
2860bb0ade09Sahrens 
2861bb0ade09Sahrens 	if (zc->zc_nvlist_src != NULL &&
2862bb0ade09Sahrens 	    (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
2863478ed9adSEric Taylor 	    zc->zc_iflags, &nvprops)) != 0)
2864bb0ade09Sahrens 		return (error);
2865bb0ade09Sahrens 
2866ea2f5b9eSMatthew Ahrens 	error = zfs_check_userprops(zc->zc_name, nvprops);
2867ea2f5b9eSMatthew Ahrens 	if (error)
2868ea2f5b9eSMatthew Ahrens 		goto out;
2869bb0ade09Sahrens 
287092241e0bSTom Erickson 	if (!nvlist_empty(nvprops) &&
2871ea2f5b9eSMatthew Ahrens 	    zfs_earlier_version(zc->zc_name, SPA_VERSION_SNAP_PROPS)) {
2872ea2f5b9eSMatthew Ahrens 		error = ENOTSUP;
2873ea2f5b9eSMatthew Ahrens 		goto out;
2874bb0ade09Sahrens 	}
2875ea2f5b9eSMatthew Ahrens 
2876ea2f5b9eSMatthew Ahrens 	error = dmu_objset_snapshot(zc->zc_name, zc->zc_value,
2877ea2f5b9eSMatthew Ahrens 	    nvprops, recursive);
2878ea2f5b9eSMatthew Ahrens 
2879ea2f5b9eSMatthew Ahrens out:
2880bb0ade09Sahrens 	nvlist_free(nvprops);
2881bb0ade09Sahrens 	return (error);
28821d452cf5Sahrens }
2883fa9e4066Sahrens 
2884cdf5b4caSmmusante int
2885fd136879SMatthew Ahrens zfs_unmount_snap(const char *name, void *arg)
28861d452cf5Sahrens {
28870b69c2f0Sahrens 	vfs_t *vfsp = NULL;
28881d452cf5Sahrens 
2889745cd3c5Smaybee 	if (arg) {
2890745cd3c5Smaybee 		char *snapname = arg;
2891fd136879SMatthew Ahrens 		char *fullname = kmem_asprintf("%s@%s", name, snapname);
2892fd136879SMatthew Ahrens 		vfsp = zfs_get_vfs(fullname);
2893fd136879SMatthew Ahrens 		strfree(fullname);
28940b69c2f0Sahrens 	} else if (strchr(name, '@')) {
28951d452cf5Sahrens 		vfsp = zfs_get_vfs(name);
28961d452cf5Sahrens 	}
28971d452cf5Sahrens 
28981d452cf5Sahrens 	if (vfsp) {
2899fa9e4066Sahrens 		/*
29001d452cf5Sahrens 		 * Always force the unmount for snapshots.
2901fa9e4066Sahrens 		 */
29021d452cf5Sahrens 		int flag = MS_FORCE;
29031d452cf5Sahrens 		int err;
29041d452cf5Sahrens 
29051d452cf5Sahrens 		if ((err = vn_vfswlock(vfsp->vfs_vnodecovered)) != 0) {
2906fa9e4066Sahrens 			VFS_RELE(vfsp);
29071d452cf5Sahrens 			return (err);
2908fa9e4066Sahrens 		}
29091d452cf5Sahrens 		VFS_RELE(vfsp);
29101d452cf5Sahrens 		if ((err = dounmount(vfsp, flag, kcred)) != 0)
29111d452cf5Sahrens 			return (err);
29121d452cf5Sahrens 	}
29131d452cf5Sahrens 	return (0);
29141d452cf5Sahrens }
29151d452cf5Sahrens 
29163cb34c60Sahrens /*
29173cb34c60Sahrens  * inputs:
2918842727c2SChris Kirby  * zc_name		name of filesystem
2919842727c2SChris Kirby  * zc_value		short name of snapshot
2920842727c2SChris Kirby  * zc_defer_destroy	mark for deferred destroy
29213cb34c60Sahrens  *
29223cb34c60Sahrens  * outputs:	none
29233cb34c60Sahrens  */
29241d452cf5Sahrens static int
29251d452cf5Sahrens zfs_ioc_destroy_snaps(zfs_cmd_t *zc)
29261d452cf5Sahrens {
29271d452cf5Sahrens 	int err;
29281d452cf5Sahrens 
2929e9dbad6fSeschrock 	if (snapshot_namecheck(zc->zc_value, NULL, NULL) != 0)
29301d452cf5Sahrens 		return (EINVAL);
29311d452cf5Sahrens 	err = dmu_objset_find(zc->zc_name,
2932e9dbad6fSeschrock 	    zfs_unmount_snap, zc->zc_value, DS_FIND_CHILDREN);
29331d452cf5Sahrens 	if (err)
29341d452cf5Sahrens 		return (err);
2935842727c2SChris Kirby 	return (dmu_snapshots_destroy(zc->zc_name, zc->zc_value,
2936842727c2SChris Kirby 	    zc->zc_defer_destroy));
29371d452cf5Sahrens }
29381d452cf5Sahrens 
29393cb34c60Sahrens /*
29403cb34c60Sahrens  * inputs:
29413cb34c60Sahrens  * zc_name		name of dataset to destroy
29423cb34c60Sahrens  * zc_objset_type	type of objset
2943842727c2SChris Kirby  * zc_defer_destroy	mark for deferred destroy
29443cb34c60Sahrens  *
29453cb34c60Sahrens  * outputs:		none
29463cb34c60Sahrens  */
29471d452cf5Sahrens static int
29481d452cf5Sahrens zfs_ioc_destroy(zfs_cmd_t *zc)
29491d452cf5Sahrens {
2950681d9761SEric Taylor 	int err;
29511d452cf5Sahrens 	if (strchr(zc->zc_name, '@') && zc->zc_objset_type == DMU_OST_ZFS) {
2952681d9761SEric Taylor 		err = zfs_unmount_snap(zc->zc_name, NULL);
29531d452cf5Sahrens 		if (err)
29541d452cf5Sahrens 			return (err);
2955fa9e4066Sahrens 	}
2956fa9e4066Sahrens 
2957681d9761SEric Taylor 	err = dmu_objset_destroy(zc->zc_name, zc->zc_defer_destroy);
2958681d9761SEric Taylor 	if (zc->zc_objset_type == DMU_OST_ZVOL && err == 0)
29595c987a37SChris Kirby 		(void) zvol_remove_minor(zc->zc_name);
2960681d9761SEric Taylor 	return (err);
2961fa9e4066Sahrens }
2962fa9e4066Sahrens 
29633cb34c60Sahrens /*
29643cb34c60Sahrens  * inputs:
29654ccbb6e7Sahrens  * zc_name	name of dataset to rollback (to most recent snapshot)
29663cb34c60Sahrens  *
29673cb34c60Sahrens  * outputs:	none
29683cb34c60Sahrens  */
2969fa9e4066Sahrens static int
2970fa9e4066Sahrens zfs_ioc_rollback(zfs_cmd_t *zc)
2971fa9e4066Sahrens {
2972ae46e4c7SMatthew Ahrens 	dsl_dataset_t *ds, *clone;
29734ccbb6e7Sahrens 	int error;
2974ae46e4c7SMatthew Ahrens 	zfsvfs_t *zfsvfs;
2975ae46e4c7SMatthew Ahrens 	char *clone_name;
2976ae46e4c7SMatthew Ahrens 
2977ae46e4c7SMatthew Ahrens 	error = dsl_dataset_hold(zc->zc_name, FTAG, &ds);
2978ae46e4c7SMatthew Ahrens 	if (error)
2979ae46e4c7SMatthew Ahrens 		return (error);
2980ae46e4c7SMatthew Ahrens 
2981ae46e4c7SMatthew Ahrens 	/* must not be a snapshot */
2982ae46e4c7SMatthew Ahrens 	if (dsl_dataset_is_snapshot(ds)) {
2983ae46e4c7SMatthew Ahrens 		dsl_dataset_rele(ds, FTAG);
2984ae46e4c7SMatthew Ahrens 		return (EINVAL);
2985ae46e4c7SMatthew Ahrens 	}
2986ae46e4c7SMatthew Ahrens 
2987ae46e4c7SMatthew Ahrens 	/* must have a most recent snapshot */
2988ae46e4c7SMatthew Ahrens 	if (ds->ds_phys->ds_prev_snap_txg < TXG_INITIAL) {
2989ae46e4c7SMatthew Ahrens 		dsl_dataset_rele(ds, FTAG);
2990ae46e4c7SMatthew Ahrens 		return (EINVAL);
2991ae46e4c7SMatthew Ahrens 	}
29924ccbb6e7Sahrens 
29934ccbb6e7Sahrens 	/*
2994ae46e4c7SMatthew Ahrens 	 * Create clone of most recent snapshot.
29954ccbb6e7Sahrens 	 */
2996ae46e4c7SMatthew Ahrens 	clone_name = kmem_asprintf("%s/%%rollback", zc->zc_name);
2997ae46e4c7SMatthew Ahrens 	error = dmu_objset_clone(clone_name, ds->ds_prev, DS_FLAG_INCONSISTENT);
29984ccbb6e7Sahrens 	if (error)
2999ae46e4c7SMatthew Ahrens 		goto out;
30004ccbb6e7Sahrens 
3001503ad85cSMatthew Ahrens 	error = dsl_dataset_own(clone_name, B_TRUE, FTAG, &clone);
3002ae46e4c7SMatthew Ahrens 	if (error)
3003ae46e4c7SMatthew Ahrens 		goto out;
3004ae46e4c7SMatthew Ahrens 
3005ae46e4c7SMatthew Ahrens 	/*
3006ae46e4c7SMatthew Ahrens 	 * Do clone swap.
3007ae46e4c7SMatthew Ahrens 	 */
300814843421SMatthew Ahrens 	if (getzfsvfs(zc->zc_name, &zfsvfs) == 0) {
3009503ad85cSMatthew Ahrens 		error = zfs_suspend_fs(zfsvfs);
301047f263f4Sek 		if (error == 0) {
301147f263f4Sek 			int resume_err;
30124ccbb6e7Sahrens 
3013ae46e4c7SMatthew Ahrens 			if (dsl_dataset_tryown(ds, B_FALSE, FTAG)) {
3014ae46e4c7SMatthew Ahrens 				error = dsl_dataset_clone_swap(clone, ds,
3015ae46e4c7SMatthew Ahrens 				    B_TRUE);
3016ae46e4c7SMatthew Ahrens 				dsl_dataset_disown(ds, FTAG);
3017ae46e4c7SMatthew Ahrens 				ds = NULL;
3018ae46e4c7SMatthew Ahrens 			} else {
3019ae46e4c7SMatthew Ahrens 				error = EBUSY;
3020ae46e4c7SMatthew Ahrens 			}
3021503ad85cSMatthew Ahrens 			resume_err = zfs_resume_fs(zfsvfs, zc->zc_name);
302247f263f4Sek 			error = error ? error : resume_err;
302347f263f4Sek 		}
30244ccbb6e7Sahrens 		VFS_RELE(zfsvfs->z_vfs);
30254ccbb6e7Sahrens 	} else {
3026ae46e4c7SMatthew Ahrens 		if (dsl_dataset_tryown(ds, B_FALSE, FTAG)) {
3027ae46e4c7SMatthew Ahrens 			error = dsl_dataset_clone_swap(clone, ds, B_TRUE);
3028ae46e4c7SMatthew Ahrens 			dsl_dataset_disown(ds, FTAG);
3029ae46e4c7SMatthew Ahrens 			ds = NULL;
3030ae46e4c7SMatthew Ahrens 		} else {
3031ae46e4c7SMatthew Ahrens 			error = EBUSY;
3032ae46e4c7SMatthew Ahrens 		}
30334ccbb6e7Sahrens 	}
30344ccbb6e7Sahrens 
3035ae46e4c7SMatthew Ahrens 	/*
3036ae46e4c7SMatthew Ahrens 	 * Destroy clone (which also closes it).
3037ae46e4c7SMatthew Ahrens 	 */
3038ae46e4c7SMatthew Ahrens 	(void) dsl_dataset_destroy(clone, FTAG, B_FALSE);
3039ae46e4c7SMatthew Ahrens 
3040ae46e4c7SMatthew Ahrens out:
3041ae46e4c7SMatthew Ahrens 	strfree(clone_name);
3042ae46e4c7SMatthew Ahrens 	if (ds)
3043ae46e4c7SMatthew Ahrens 		dsl_dataset_rele(ds, FTAG);
30444ccbb6e7Sahrens 	return (error);
3045fa9e4066Sahrens }
3046fa9e4066Sahrens 
30473cb34c60Sahrens /*
30483cb34c60Sahrens  * inputs:
30493cb34c60Sahrens  * zc_name	old name of dataset
30503cb34c60Sahrens  * zc_value	new name of dataset
30513cb34c60Sahrens  * zc_cookie	recursive flag (only valid for snapshots)
30523cb34c60Sahrens  *
30533cb34c60Sahrens  * outputs:	none
30543cb34c60Sahrens  */
3055fa9e4066Sahrens static int
3056fa9e4066Sahrens zfs_ioc_rename(zfs_cmd_t *zc)
3057fa9e4066Sahrens {
30587f1f55eaSvb 	boolean_t recursive = zc->zc_cookie & 1;
3059cdf5b4caSmmusante 
3060e9dbad6fSeschrock 	zc->zc_value[sizeof (zc->zc_value) - 1] = '\0';
3061f18faf3fSek 	if (dataset_namecheck(zc->zc_value, NULL, NULL) != 0 ||
3062f18faf3fSek 	    strchr(zc->zc_value, '%'))
3063fa9e4066Sahrens 		return (EINVAL);
3064fa9e4066Sahrens 
3065cdf5b4caSmmusante 	/*
3066cdf5b4caSmmusante 	 * Unmount snapshot unless we're doing a recursive rename,
3067cdf5b4caSmmusante 	 * in which case the dataset code figures out which snapshots
3068cdf5b4caSmmusante 	 * to unmount.
3069cdf5b4caSmmusante 	 */
3070cdf5b4caSmmusante 	if (!recursive && strchr(zc->zc_name, '@') != NULL &&
3071fa9e4066Sahrens 	    zc->zc_objset_type == DMU_OST_ZFS) {
30721d452cf5Sahrens 		int err = zfs_unmount_snap(zc->zc_name, NULL);
30731d452cf5Sahrens 		if (err)
30741d452cf5Sahrens 			return (err);
3075fa9e4066Sahrens 	}
3076681d9761SEric Taylor 	if (zc->zc_objset_type == DMU_OST_ZVOL)
3077681d9761SEric Taylor 		(void) zvol_remove_minor(zc->zc_name);
3078cdf5b4caSmmusante 	return (dmu_objset_rename(zc->zc_name, zc->zc_value, recursive));
3079fa9e4066Sahrens }
3080fa9e4066Sahrens 
308192241e0bSTom Erickson static int
308292241e0bSTom Erickson zfs_check_settable(const char *dsname, nvpair_t *pair, cred_t *cr)
308392241e0bSTom Erickson {
308492241e0bSTom Erickson 	const char *propname = nvpair_name(pair);
308592241e0bSTom Erickson 	boolean_t issnap = (strchr(dsname, '@') != NULL);
308692241e0bSTom Erickson 	zfs_prop_t prop = zfs_name_to_prop(propname);
308792241e0bSTom Erickson 	uint64_t intval;
308892241e0bSTom Erickson 	int err;
308992241e0bSTom Erickson 
309092241e0bSTom Erickson 	if (prop == ZPROP_INVAL) {
309192241e0bSTom Erickson 		if (zfs_prop_user(propname)) {
309292241e0bSTom Erickson 			if (err = zfs_secpolicy_write_perms(dsname,
309392241e0bSTom Erickson 			    ZFS_DELEG_PERM_USERPROP, cr))
309492241e0bSTom Erickson 				return (err);
309592241e0bSTom Erickson 			return (0);
309692241e0bSTom Erickson 		}
309792241e0bSTom Erickson 
309892241e0bSTom Erickson 		if (!issnap && zfs_prop_userquota(propname)) {
309992241e0bSTom Erickson 			const char *perm = NULL;
310092241e0bSTom Erickson 			const char *uq_prefix =
310192241e0bSTom Erickson 			    zfs_userquota_prop_prefixes[ZFS_PROP_USERQUOTA];
310292241e0bSTom Erickson 			const char *gq_prefix =
310392241e0bSTom Erickson 			    zfs_userquota_prop_prefixes[ZFS_PROP_GROUPQUOTA];
310492241e0bSTom Erickson 
310592241e0bSTom Erickson 			if (strncmp(propname, uq_prefix,
310692241e0bSTom Erickson 			    strlen(uq_prefix)) == 0) {
310792241e0bSTom Erickson 				perm = ZFS_DELEG_PERM_USERQUOTA;
310892241e0bSTom Erickson 			} else if (strncmp(propname, gq_prefix,
310992241e0bSTom Erickson 			    strlen(gq_prefix)) == 0) {
311092241e0bSTom Erickson 				perm = ZFS_DELEG_PERM_GROUPQUOTA;
311192241e0bSTom Erickson 			} else {
311292241e0bSTom Erickson 				/* USERUSED and GROUPUSED are read-only */
311392241e0bSTom Erickson 				return (EINVAL);
311492241e0bSTom Erickson 			}
311592241e0bSTom Erickson 
311692241e0bSTom Erickson 			if (err = zfs_secpolicy_write_perms(dsname, perm, cr))
311792241e0bSTom Erickson 				return (err);
311892241e0bSTom Erickson 			return (0);
311992241e0bSTom Erickson 		}
312092241e0bSTom Erickson 
312192241e0bSTom Erickson 		return (EINVAL);
312292241e0bSTom Erickson 	}
312392241e0bSTom Erickson 
312492241e0bSTom Erickson 	if (issnap)
312592241e0bSTom Erickson 		return (EINVAL);
312692241e0bSTom Erickson 
312792241e0bSTom Erickson 	if (nvpair_type(pair) == DATA_TYPE_NVLIST) {
312892241e0bSTom Erickson 		/*
312992241e0bSTom Erickson 		 * dsl_prop_get_all_impl() returns properties in this
313092241e0bSTom Erickson 		 * format.
313192241e0bSTom Erickson 		 */
313292241e0bSTom Erickson 		nvlist_t *attrs;
313392241e0bSTom Erickson 		VERIFY(nvpair_value_nvlist(pair, &attrs) == 0);
313492241e0bSTom Erickson 		VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE,
313592241e0bSTom Erickson 		    &pair) == 0);
313692241e0bSTom Erickson 	}
313792241e0bSTom Erickson 
313892241e0bSTom Erickson 	/*
313992241e0bSTom Erickson 	 * Check that this value is valid for this pool version
314092241e0bSTom Erickson 	 */
314192241e0bSTom Erickson 	switch (prop) {
314292241e0bSTom Erickson 	case ZFS_PROP_COMPRESSION:
314392241e0bSTom Erickson 		/*
314492241e0bSTom Erickson 		 * If the user specified gzip compression, make sure
314592241e0bSTom Erickson 		 * the SPA supports it. We ignore any errors here since
314692241e0bSTom Erickson 		 * we'll catch them later.
314792241e0bSTom Erickson 		 */
314892241e0bSTom Erickson 		if (nvpair_type(pair) == DATA_TYPE_UINT64 &&
314992241e0bSTom Erickson 		    nvpair_value_uint64(pair, &intval) == 0) {
315092241e0bSTom Erickson 			if (intval >= ZIO_COMPRESS_GZIP_1 &&
315192241e0bSTom Erickson 			    intval <= ZIO_COMPRESS_GZIP_9 &&
315292241e0bSTom Erickson 			    zfs_earlier_version(dsname,
315392241e0bSTom Erickson 			    SPA_VERSION_GZIP_COMPRESSION)) {
315492241e0bSTom Erickson 				return (ENOTSUP);
315592241e0bSTom Erickson 			}
315692241e0bSTom Erickson 
315792241e0bSTom Erickson 			if (intval == ZIO_COMPRESS_ZLE &&
315892241e0bSTom Erickson 			    zfs_earlier_version(dsname,
315992241e0bSTom Erickson 			    SPA_VERSION_ZLE_COMPRESSION))
316092241e0bSTom Erickson 				return (ENOTSUP);
316192241e0bSTom Erickson 
316292241e0bSTom Erickson 			/*
316392241e0bSTom Erickson 			 * If this is a bootable dataset then
316492241e0bSTom Erickson 			 * verify that the compression algorithm
316592241e0bSTom Erickson 			 * is supported for booting. We must return
316692241e0bSTom Erickson 			 * something other than ENOTSUP since it
316792241e0bSTom Erickson 			 * implies a downrev pool version.
316892241e0bSTom Erickson 			 */
316992241e0bSTom Erickson 			if (zfs_is_bootfs(dsname) &&
317092241e0bSTom Erickson 			    !BOOTFS_COMPRESS_VALID(intval)) {
317192241e0bSTom Erickson 				return (ERANGE);
317292241e0bSTom Erickson 			}
317392241e0bSTom Erickson 		}
317492241e0bSTom Erickson 		break;
317592241e0bSTom Erickson 
317692241e0bSTom Erickson 	case ZFS_PROP_COPIES:
317792241e0bSTom Erickson 		if (zfs_earlier_version(dsname, SPA_VERSION_DITTO_BLOCKS))
317892241e0bSTom Erickson 			return (ENOTSUP);
317992241e0bSTom Erickson 		break;
318092241e0bSTom Erickson 
318192241e0bSTom Erickson 	case ZFS_PROP_DEDUP:
318292241e0bSTom Erickson 		if (zfs_earlier_version(dsname, SPA_VERSION_DEDUP))
318392241e0bSTom Erickson 			return (ENOTSUP);
318492241e0bSTom Erickson 		break;
318592241e0bSTom Erickson 
318692241e0bSTom Erickson 	case ZFS_PROP_SHARESMB:
318792241e0bSTom Erickson 		if (zpl_earlier_version(dsname, ZPL_VERSION_FUID))
318892241e0bSTom Erickson 			return (ENOTSUP);
318992241e0bSTom Erickson 		break;
319092241e0bSTom Erickson 
319192241e0bSTom Erickson 	case ZFS_PROP_ACLINHERIT:
319292241e0bSTom Erickson 		if (nvpair_type(pair) == DATA_TYPE_UINT64 &&
319392241e0bSTom Erickson 		    nvpair_value_uint64(pair, &intval) == 0) {
319492241e0bSTom Erickson 			if (intval == ZFS_ACL_PASSTHROUGH_X &&
319592241e0bSTom Erickson 			    zfs_earlier_version(dsname,
319692241e0bSTom Erickson 			    SPA_VERSION_PASSTHROUGH_X))
319792241e0bSTom Erickson 				return (ENOTSUP);
319892241e0bSTom Erickson 		}
319992241e0bSTom Erickson 		break;
320092241e0bSTom Erickson 	}
320192241e0bSTom Erickson 
320292241e0bSTom Erickson 	return (zfs_secpolicy_setprop(dsname, prop, pair, CRED()));
320392241e0bSTom Erickson }
320492241e0bSTom Erickson 
320592241e0bSTom Erickson /*
320692241e0bSTom Erickson  * Removes properties from the given props list that fail permission checks
320792241e0bSTom Erickson  * needed to clear them and to restore them in case of a receive error. For each
320892241e0bSTom Erickson  * property, make sure we have both set and inherit permissions.
320992241e0bSTom Erickson  *
321092241e0bSTom Erickson  * Returns the first error encountered if any permission checks fail. If the
321192241e0bSTom Erickson  * caller provides a non-NULL errlist, it also gives the complete list of names
321292241e0bSTom Erickson  * of all the properties that failed a permission check along with the
321392241e0bSTom Erickson  * corresponding error numbers. The caller is responsible for freeing the
321492241e0bSTom Erickson  * returned errlist.
321592241e0bSTom Erickson  *
321692241e0bSTom Erickson  * If every property checks out successfully, zero is returned and the list
321792241e0bSTom Erickson  * pointed at by errlist is NULL.
321892241e0bSTom Erickson  */
321992241e0bSTom Erickson static int
322092241e0bSTom Erickson zfs_check_clearable(char *dataset, nvlist_t *props, nvlist_t **errlist)
3221745cd3c5Smaybee {
3222745cd3c5Smaybee 	zfs_cmd_t *zc;
322392241e0bSTom Erickson 	nvpair_t *pair, *next_pair;
322492241e0bSTom Erickson 	nvlist_t *errors;
322592241e0bSTom Erickson 	int err, rv = 0;
3226745cd3c5Smaybee 
3227745cd3c5Smaybee 	if (props == NULL)
322892241e0bSTom Erickson 		return (0);
322992241e0bSTom Erickson 
323092241e0bSTom Erickson 	VERIFY(nvlist_alloc(&errors, NV_UNIQUE_NAME, KM_SLEEP) == 0);
323192241e0bSTom Erickson 
3232745cd3c5Smaybee 	zc = kmem_alloc(sizeof (zfs_cmd_t), KM_SLEEP);
3233745cd3c5Smaybee 	(void) strcpy(zc->zc_name, dataset);
323492241e0bSTom Erickson 	pair = nvlist_next_nvpair(props, NULL);
323592241e0bSTom Erickson 	while (pair != NULL) {
323692241e0bSTom Erickson 		next_pair = nvlist_next_nvpair(props, pair);
323792241e0bSTom Erickson 
323892241e0bSTom Erickson 		(void) strcpy(zc->zc_value, nvpair_name(pair));
323992241e0bSTom Erickson 		if ((err = zfs_check_settable(dataset, pair, CRED())) != 0 ||
324092241e0bSTom Erickson 		    (err = zfs_secpolicy_inherit(zc, CRED())) != 0) {
324192241e0bSTom Erickson 			VERIFY(nvlist_remove_nvpair(props, pair) == 0);
324292241e0bSTom Erickson 			VERIFY(nvlist_add_int32(errors,
324392241e0bSTom Erickson 			    zc->zc_value, err) == 0);
324492241e0bSTom Erickson 		}
324592241e0bSTom Erickson 		pair = next_pair;
3246745cd3c5Smaybee 	}
3247745cd3c5Smaybee 	kmem_free(zc, sizeof (zfs_cmd_t));
324892241e0bSTom Erickson 
324992241e0bSTom Erickson 	if ((pair = nvlist_next_nvpair(errors, NULL)) == NULL) {
325092241e0bSTom Erickson 		nvlist_free(errors);
325192241e0bSTom Erickson 		errors = NULL;
325292241e0bSTom Erickson 	} else {
325392241e0bSTom Erickson 		VERIFY(nvpair_value_int32(pair, &rv) == 0);
325492241e0bSTom Erickson 	}
325592241e0bSTom Erickson 
325692241e0bSTom Erickson 	if (errlist == NULL)
325792241e0bSTom Erickson 		nvlist_free(errors);
325892241e0bSTom Erickson 	else
325992241e0bSTom Erickson 		*errlist = errors;
326092241e0bSTom Erickson 
326192241e0bSTom Erickson 	return (rv);
326292241e0bSTom Erickson }
326392241e0bSTom Erickson 
326492241e0bSTom Erickson static boolean_t
326592241e0bSTom Erickson propval_equals(nvpair_t *p1, nvpair_t *p2)
326692241e0bSTom Erickson {
326792241e0bSTom Erickson 	if (nvpair_type(p1) == DATA_TYPE_NVLIST) {
326892241e0bSTom Erickson 		/* dsl_prop_get_all_impl() format */
326992241e0bSTom Erickson 		nvlist_t *attrs;
327092241e0bSTom Erickson 		VERIFY(nvpair_value_nvlist(p1, &attrs) == 0);
327192241e0bSTom Erickson 		VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE,
327292241e0bSTom Erickson 		    &p1) == 0);
327392241e0bSTom Erickson 	}
327492241e0bSTom Erickson 
327592241e0bSTom Erickson 	if (nvpair_type(p2) == DATA_TYPE_NVLIST) {
327692241e0bSTom Erickson 		nvlist_t *attrs;
327792241e0bSTom Erickson 		VERIFY(nvpair_value_nvlist(p2, &attrs) == 0);
327892241e0bSTom Erickson 		VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE,
327992241e0bSTom Erickson 		    &p2) == 0);
328092241e0bSTom Erickson 	}
328192241e0bSTom Erickson 
328292241e0bSTom Erickson 	if (nvpair_type(p1) != nvpair_type(p2))
328392241e0bSTom Erickson 		return (B_FALSE);
328492241e0bSTom Erickson 
328592241e0bSTom Erickson 	if (nvpair_type(p1) == DATA_TYPE_STRING) {
328692241e0bSTom Erickson 		char *valstr1, *valstr2;
328792241e0bSTom Erickson 
328892241e0bSTom Erickson 		VERIFY(nvpair_value_string(p1, (char **)&valstr1) == 0);
328992241e0bSTom Erickson 		VERIFY(nvpair_value_string(p2, (char **)&valstr2) == 0);
329092241e0bSTom Erickson 		return (strcmp(valstr1, valstr2) == 0);
329192241e0bSTom Erickson 	} else {
329292241e0bSTom Erickson 		uint64_t intval1, intval2;
329392241e0bSTom Erickson 
329492241e0bSTom Erickson 		VERIFY(nvpair_value_uint64(p1, &intval1) == 0);
329592241e0bSTom Erickson 		VERIFY(nvpair_value_uint64(p2, &intval2) == 0);
329692241e0bSTom Erickson 		return (intval1 == intval2);
329792241e0bSTom Erickson 	}
3298745cd3c5Smaybee }
3299745cd3c5Smaybee 
330092241e0bSTom Erickson /*
330192241e0bSTom Erickson  * Remove properties from props if they are not going to change (as determined
330292241e0bSTom Erickson  * by comparison with origprops). Remove them from origprops as well, since we
330392241e0bSTom Erickson  * do not need to clear or restore properties that won't change.
330492241e0bSTom Erickson  */
330592241e0bSTom Erickson static void
330692241e0bSTom Erickson props_reduce(nvlist_t *props, nvlist_t *origprops)
330792241e0bSTom Erickson {
330892241e0bSTom Erickson 	nvpair_t *pair, *next_pair;
330992241e0bSTom Erickson 
331092241e0bSTom Erickson 	if (origprops == NULL)
331192241e0bSTom Erickson 		return; /* all props need to be received */
331292241e0bSTom Erickson 
331392241e0bSTom Erickson 	pair = nvlist_next_nvpair(props, NULL);
331492241e0bSTom Erickson 	while (pair != NULL) {
331592241e0bSTom Erickson 		const char *propname = nvpair_name(pair);
331692241e0bSTom Erickson 		nvpair_t *match;
331792241e0bSTom Erickson 
331892241e0bSTom Erickson 		next_pair = nvlist_next_nvpair(props, pair);
331992241e0bSTom Erickson 
332092241e0bSTom Erickson 		if ((nvlist_lookup_nvpair(origprops, propname,
332192241e0bSTom Erickson 		    &match) != 0) || !propval_equals(pair, match))
332292241e0bSTom Erickson 			goto next; /* need to set received value */
332392241e0bSTom Erickson 
332492241e0bSTom Erickson 		/* don't clear the existing received value */
332592241e0bSTom Erickson 		(void) nvlist_remove_nvpair(origprops, match);
332692241e0bSTom Erickson 		/* don't bother receiving the property */
332792241e0bSTom Erickson 		(void) nvlist_remove_nvpair(props, pair);
332892241e0bSTom Erickson next:
332992241e0bSTom Erickson 		pair = next_pair;
333092241e0bSTom Erickson 	}
333192241e0bSTom Erickson }
333292241e0bSTom Erickson 
333392241e0bSTom Erickson #ifdef	DEBUG
333492241e0bSTom Erickson static boolean_t zfs_ioc_recv_inject_err;
333592241e0bSTom Erickson #endif
333692241e0bSTom Erickson 
33373cb34c60Sahrens /*
33383cb34c60Sahrens  * inputs:
33393cb34c60Sahrens  * zc_name		name of containing filesystem
33403cb34c60Sahrens  * zc_nvlist_src{_size}	nvlist of properties to apply
33413cb34c60Sahrens  * zc_value		name of snapshot to create
33423cb34c60Sahrens  * zc_string		name of clone origin (if DRR_FLAG_CLONE)
33433cb34c60Sahrens  * zc_cookie		file descriptor to recv from
33443cb34c60Sahrens  * zc_begin_record	the BEGIN record of the stream (not byteswapped)
33453cb34c60Sahrens  * zc_guid		force flag
3346*c99e4bdcSChris Kirby  * zc_cleanup_fd	cleanup-on-exit file descriptor
3347*c99e4bdcSChris Kirby  * zc_action_handle	handle for this guid/ds mapping (or zero on first call)
33483cb34c60Sahrens  *
33493cb34c60Sahrens  * outputs:
33503cb34c60Sahrens  * zc_cookie		number of bytes read
335192241e0bSTom Erickson  * zc_nvlist_dst{_size} error for each unapplied received property
335292241e0bSTom Erickson  * zc_obj		zprop_errflags_t
3353*c99e4bdcSChris Kirby  * zc_action_handle	handle for this guid/ds mapping
33543cb34c60Sahrens  */
3355fa9e4066Sahrens static int
33563cb34c60Sahrens zfs_ioc_recv(zfs_cmd_t *zc)
3357fa9e4066Sahrens {
3358fa9e4066Sahrens 	file_t *fp;
3359f18faf3fSek 	objset_t *os;
33603cb34c60Sahrens 	dmu_recv_cookie_t drc;
3361f18faf3fSek 	boolean_t force = (boolean_t)zc->zc_guid;
336292241e0bSTom Erickson 	int fd;
336392241e0bSTom Erickson 	int error = 0;
336492241e0bSTom Erickson 	int props_error = 0;
336592241e0bSTom Erickson 	nvlist_t *errors;
33663cb34c60Sahrens 	offset_t off;
336792241e0bSTom Erickson 	nvlist_t *props = NULL; /* sent properties */
336892241e0bSTom Erickson 	nvlist_t *origprops = NULL; /* existing properties */
33693cb34c60Sahrens 	objset_t *origin = NULL;
33703cb34c60Sahrens 	char *tosnap;
33713cb34c60Sahrens 	char tofs[ZFS_MAXNAMELEN];
337292241e0bSTom Erickson 	boolean_t first_recvd_props = B_FALSE;
3373fa9e4066Sahrens 
33743ccfa83cSahrens 	if (dataset_namecheck(zc->zc_value, NULL, NULL) != 0 ||
3375f18faf3fSek 	    strchr(zc->zc_value, '@') == NULL ||
3376f18faf3fSek 	    strchr(zc->zc_value, '%'))
33773ccfa83cSahrens 		return (EINVAL);
33783ccfa83cSahrens 
33793cb34c60Sahrens 	(void) strcpy(tofs, zc->zc_value);
33803cb34c60Sahrens 	tosnap = strchr(tofs, '@');
338192241e0bSTom Erickson 	*tosnap++ = '\0';
33823cb34c60Sahrens 
33833cb34c60Sahrens 	if (zc->zc_nvlist_src != NULL &&
33843cb34c60Sahrens 	    (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
3385478ed9adSEric Taylor 	    zc->zc_iflags, &props)) != 0)
33863cb34c60Sahrens 		return (error);
33873cb34c60Sahrens 
3388fa9e4066Sahrens 	fd = zc->zc_cookie;
3389fa9e4066Sahrens 	fp = getf(fd);
33903cb34c60Sahrens 	if (fp == NULL) {
33913cb34c60Sahrens 		nvlist_free(props);
3392fa9e4066Sahrens 		return (EBADF);
33933cb34c60Sahrens 	}
3394f18faf3fSek 
339592241e0bSTom Erickson 	VERIFY(nvlist_alloc(&errors, NV_UNIQUE_NAME, KM_SLEEP) == 0);
339692241e0bSTom Erickson 
3397503ad85cSMatthew Ahrens 	if (props && dmu_objset_hold(tofs, FTAG, &os) == 0) {
339892241e0bSTom Erickson 		if ((spa_version(os->os_spa) >= SPA_VERSION_RECVD_PROPS) &&
339992241e0bSTom Erickson 		    !dsl_prop_get_hasrecvd(os)) {
340092241e0bSTom Erickson 			first_recvd_props = B_TRUE;
340192241e0bSTom Erickson 		}
340292241e0bSTom Erickson 
3403745cd3c5Smaybee 		/*
340492241e0bSTom Erickson 		 * If new received properties are supplied, they are to
340592241e0bSTom Erickson 		 * completely replace the existing received properties, so stash
340692241e0bSTom Erickson 		 * away the existing ones.
3407745cd3c5Smaybee 		 */
340892241e0bSTom Erickson 		if (dsl_prop_get_received(os, &origprops) == 0) {
340992241e0bSTom Erickson 			nvlist_t *errlist = NULL;
341092241e0bSTom Erickson 			/*
341192241e0bSTom Erickson 			 * Don't bother writing a property if its value won't
341292241e0bSTom Erickson 			 * change (and avoid the unnecessary security checks).
341392241e0bSTom Erickson 			 *
341492241e0bSTom Erickson 			 * The first receive after SPA_VERSION_RECVD_PROPS is a
341592241e0bSTom Erickson 			 * special case where we blow away all local properties
341692241e0bSTom Erickson 			 * regardless.
341792241e0bSTom Erickson 			 */
341892241e0bSTom Erickson 			if (!first_recvd_props)
341992241e0bSTom Erickson 				props_reduce(props, origprops);
342092241e0bSTom Erickson 			if (zfs_check_clearable(tofs, origprops,
342192241e0bSTom Erickson 			    &errlist) != 0)
342292241e0bSTom Erickson 				(void) nvlist_merge(errors, errlist, 0);
342392241e0bSTom Erickson 			nvlist_free(errlist);
342492241e0bSTom Erickson 		}
3425745cd3c5Smaybee 
3426503ad85cSMatthew Ahrens 		dmu_objset_rele(os, FTAG);
3427f18faf3fSek 	}
3428f18faf3fSek 
34293cb34c60Sahrens 	if (zc->zc_string[0]) {
3430503ad85cSMatthew Ahrens 		error = dmu_objset_hold(zc->zc_string, FTAG, &origin);
3431745cd3c5Smaybee 		if (error)
3432745cd3c5Smaybee 			goto out;
34333cb34c60Sahrens 	}
34343cb34c60Sahrens 
34359e69d7d0SLori Alt 	error = dmu_recv_begin(tofs, tosnap, zc->zc_top_ds,
34369e69d7d0SLori Alt 	    &zc->zc_begin_record, force, origin, &drc);
34373cb34c60Sahrens 	if (origin)
3438503ad85cSMatthew Ahrens 		dmu_objset_rele(origin, FTAG);
3439745cd3c5Smaybee 	if (error)
3440745cd3c5Smaybee 		goto out;
3441f18faf3fSek 
3442f18faf3fSek 	/*
344392241e0bSTom Erickson 	 * Set properties before we receive the stream so that they are applied
344492241e0bSTom Erickson 	 * to the new data. Note that we must call dmu_recv_stream() if
344592241e0bSTom Erickson 	 * dmu_recv_begin() succeeds.
3446f18faf3fSek 	 */
34473cb34c60Sahrens 	if (props) {
344892241e0bSTom Erickson 		nvlist_t *errlist;
344992241e0bSTom Erickson 
345092241e0bSTom Erickson 		if (dmu_objset_from_ds(drc.drc_logical_ds, &os) == 0) {
345192241e0bSTom Erickson 			if (drc.drc_newfs) {
345292241e0bSTom Erickson 				if (spa_version(os->os_spa) >=
345392241e0bSTom Erickson 				    SPA_VERSION_RECVD_PROPS)
345492241e0bSTom Erickson 					first_recvd_props = B_TRUE;
345592241e0bSTom Erickson 			} else if (origprops != NULL) {
345692241e0bSTom Erickson 				if (clear_received_props(os, tofs, origprops,
345792241e0bSTom Erickson 				    first_recvd_props ? NULL : props) != 0)
345892241e0bSTom Erickson 					zc->zc_obj |= ZPROP_ERR_NOCLEAR;
345992241e0bSTom Erickson 			} else {
346092241e0bSTom Erickson 				zc->zc_obj |= ZPROP_ERR_NOCLEAR;
346192241e0bSTom Erickson 			}
346292241e0bSTom Erickson 			dsl_prop_set_hasrecvd(os);
346392241e0bSTom Erickson 		} else if (!drc.drc_newfs) {
346492241e0bSTom Erickson 			zc->zc_obj |= ZPROP_ERR_NOCLEAR;
346592241e0bSTom Erickson 		}
346692241e0bSTom Erickson 
346792241e0bSTom Erickson 		(void) zfs_set_prop_nvlist(tofs, ZPROP_SRC_RECEIVED,
346892241e0bSTom Erickson 		    props, &errlist);
346992241e0bSTom Erickson 		(void) nvlist_merge(errors, errlist, 0);
347092241e0bSTom Erickson 		nvlist_free(errlist);
347192241e0bSTom Erickson 	}
347292241e0bSTom Erickson 
347392241e0bSTom Erickson 	if (fit_error_list(zc, &errors) != 0 || put_nvlist(zc, errors) != 0) {
3474745cd3c5Smaybee 		/*
347592241e0bSTom Erickson 		 * Caller made zc->zc_nvlist_dst less than the minimum expected
347692241e0bSTom Erickson 		 * size or supplied an invalid address.
3477745cd3c5Smaybee 		 */
347892241e0bSTom Erickson 		props_error = EINVAL;
34793cb34c60Sahrens 	}
34803cb34c60Sahrens 
34813cb34c60Sahrens 	off = fp->f_offset;
3482*c99e4bdcSChris Kirby 	error = dmu_recv_stream(&drc, fp->f_vnode, &off, zc->zc_cleanup_fd,
3483*c99e4bdcSChris Kirby 	    &zc->zc_action_handle);
3484a2eea2e1Sahrens 
3485f4b94bdeSMatthew Ahrens 	if (error == 0) {
3486f4b94bdeSMatthew Ahrens 		zfsvfs_t *zfsvfs = NULL;
3487745cd3c5Smaybee 
3488f4b94bdeSMatthew Ahrens 		if (getzfsvfs(tofs, &zfsvfs) == 0) {
3489f4b94bdeSMatthew Ahrens 			/* online recv */
3490f4b94bdeSMatthew Ahrens 			int end_err;
3491745cd3c5Smaybee 
3492503ad85cSMatthew Ahrens 			error = zfs_suspend_fs(zfsvfs);
3493f4b94bdeSMatthew Ahrens 			/*
3494f4b94bdeSMatthew Ahrens 			 * If the suspend fails, then the recv_end will
3495f4b94bdeSMatthew Ahrens 			 * likely also fail, and clean up after itself.
3496f4b94bdeSMatthew Ahrens 			 */
3497f4b94bdeSMatthew Ahrens 			end_err = dmu_recv_end(&drc);
34985c703fceSGeorge Wilson 			if (error == 0)
34995c703fceSGeorge Wilson 				error = zfs_resume_fs(zfsvfs, tofs);
3500f4b94bdeSMatthew Ahrens 			error = error ? error : end_err;
3501f4b94bdeSMatthew Ahrens 			VFS_RELE(zfsvfs->z_vfs);
3502745cd3c5Smaybee 		} else {
3503f4b94bdeSMatthew Ahrens 			error = dmu_recv_end(&drc);
35043cb34c60Sahrens 		}
350547f263f4Sek 	}
35063cb34c60Sahrens 
35073cb34c60Sahrens 	zc->zc_cookie = off - fp->f_offset;
35083cb34c60Sahrens 	if (VOP_SEEK(fp->f_vnode, fp->f_offset, &off, NULL) == 0)
35093cb34c60Sahrens 		fp->f_offset = off;
3510a2eea2e1Sahrens 
351192241e0bSTom Erickson #ifdef	DEBUG
351292241e0bSTom Erickson 	if (zfs_ioc_recv_inject_err) {
351392241e0bSTom Erickson 		zfs_ioc_recv_inject_err = B_FALSE;
351492241e0bSTom Erickson 		error = 1;
351592241e0bSTom Erickson 	}
351692241e0bSTom Erickson #endif
3517745cd3c5Smaybee 	/*
3518745cd3c5Smaybee 	 * On error, restore the original props.
3519745cd3c5Smaybee 	 */
3520745cd3c5Smaybee 	if (error && props) {
352192241e0bSTom Erickson 		if (dmu_objset_hold(tofs, FTAG, &os) == 0) {
352292241e0bSTom Erickson 			if (clear_received_props(os, tofs, props, NULL) != 0) {
352392241e0bSTom Erickson 				/*
352492241e0bSTom Erickson 				 * We failed to clear the received properties.
352592241e0bSTom Erickson 				 * Since we may have left a $recvd value on the
352692241e0bSTom Erickson 				 * system, we can't clear the $hasrecvd flag.
352792241e0bSTom Erickson 				 */
352892241e0bSTom Erickson 				zc->zc_obj |= ZPROP_ERR_NORESTORE;
352992241e0bSTom Erickson 			} else if (first_recvd_props) {
353092241e0bSTom Erickson 				dsl_prop_unset_hasrecvd(os);
353192241e0bSTom Erickson 			}
353292241e0bSTom Erickson 			dmu_objset_rele(os, FTAG);
353392241e0bSTom Erickson 		} else if (!drc.drc_newfs) {
353492241e0bSTom Erickson 			/* We failed to clear the received properties. */
353592241e0bSTom Erickson 			zc->zc_obj |= ZPROP_ERR_NORESTORE;
353692241e0bSTom Erickson 		}
353792241e0bSTom Erickson 
353892241e0bSTom Erickson 		if (origprops == NULL && !drc.drc_newfs) {
353992241e0bSTom Erickson 			/* We failed to stash the original properties. */
354092241e0bSTom Erickson 			zc->zc_obj |= ZPROP_ERR_NORESTORE;
354192241e0bSTom Erickson 		}
354292241e0bSTom Erickson 
354392241e0bSTom Erickson 		/*
354492241e0bSTom Erickson 		 * dsl_props_set() will not convert RECEIVED to LOCAL on or
354592241e0bSTom Erickson 		 * after SPA_VERSION_RECVD_PROPS, so we need to specify LOCAL
354692241e0bSTom Erickson 		 * explictly if we're restoring local properties cleared in the
354792241e0bSTom Erickson 		 * first new-style receive.
354892241e0bSTom Erickson 		 */
354992241e0bSTom Erickson 		if (origprops != NULL &&
355092241e0bSTom Erickson 		    zfs_set_prop_nvlist(tofs, (first_recvd_props ?
355192241e0bSTom Erickson 		    ZPROP_SRC_LOCAL : ZPROP_SRC_RECEIVED),
355292241e0bSTom Erickson 		    origprops, NULL) != 0) {
355392241e0bSTom Erickson 			/*
355492241e0bSTom Erickson 			 * We stashed the original properties but failed to
355592241e0bSTom Erickson 			 * restore them.
355692241e0bSTom Erickson 			 */
355792241e0bSTom Erickson 			zc->zc_obj |= ZPROP_ERR_NORESTORE;
355892241e0bSTom Erickson 		}
3559745cd3c5Smaybee 	}
3560745cd3c5Smaybee out:
3561745cd3c5Smaybee 	nvlist_free(props);
3562745cd3c5Smaybee 	nvlist_free(origprops);
356392241e0bSTom Erickson 	nvlist_free(errors);
3564fa9e4066Sahrens 	releasef(fd);
356592241e0bSTom Erickson 
356692241e0bSTom Erickson 	if (error == 0)
356792241e0bSTom Erickson 		error = props_error;
356892241e0bSTom Erickson 
3569fa9e4066Sahrens 	return (error);
3570fa9e4066Sahrens }
3571fa9e4066Sahrens 
35723cb34c60Sahrens /*
35733cb34c60Sahrens  * inputs:
35743cb34c60Sahrens  * zc_name	name of snapshot to send
35753cb34c60Sahrens  * zc_value	short name of incremental fromsnap (may be empty)
35763cb34c60Sahrens  * zc_cookie	file descriptor to send stream to
35773cb34c60Sahrens  * zc_obj	fromorigin flag (mutually exclusive with zc_value)
35783cb34c60Sahrens  *
35793cb34c60Sahrens  * outputs: none
35803cb34c60Sahrens  */
3581fa9e4066Sahrens static int
35823cb34c60Sahrens zfs_ioc_send(zfs_cmd_t *zc)
3583fa9e4066Sahrens {
3584fa9e4066Sahrens 	objset_t *fromsnap = NULL;
3585fa9e4066Sahrens 	objset_t *tosnap;
3586fa9e4066Sahrens 	file_t *fp;
3587fa9e4066Sahrens 	int error;
35883cb34c60Sahrens 	offset_t off;
3589fa9e4066Sahrens 
3590503ad85cSMatthew Ahrens 	error = dmu_objset_hold(zc->zc_name, FTAG, &tosnap);
3591fa9e4066Sahrens 	if (error)
3592fa9e4066Sahrens 		return (error);
3593fa9e4066Sahrens 
3594e9dbad6fSeschrock 	if (zc->zc_value[0] != '\0') {
35956a0f0066SEric Taylor 		char *buf;
3596a2eea2e1Sahrens 		char *cp;
3597a2eea2e1Sahrens 
35986a0f0066SEric Taylor 		buf = kmem_alloc(MAXPATHLEN, KM_SLEEP);
35996a0f0066SEric Taylor 		(void) strncpy(buf, zc->zc_name, MAXPATHLEN);
3600a2eea2e1Sahrens 		cp = strchr(buf, '@');
3601a2eea2e1Sahrens 		if (cp)
3602a2eea2e1Sahrens 			*(cp+1) = 0;
36036a0f0066SEric Taylor 		(void) strncat(buf, zc->zc_value, MAXPATHLEN);
3604503ad85cSMatthew Ahrens 		error = dmu_objset_hold(buf, FTAG, &fromsnap);
36056a0f0066SEric Taylor 		kmem_free(buf, MAXPATHLEN);
3606fa9e4066Sahrens 		if (error) {
3607503ad85cSMatthew Ahrens 			dmu_objset_rele(tosnap, FTAG);
3608fa9e4066Sahrens 			return (error);
3609fa9e4066Sahrens 		}
3610fa9e4066Sahrens 	}
3611fa9e4066Sahrens 
3612fa9e4066Sahrens 	fp = getf(zc->zc_cookie);
3613fa9e4066Sahrens 	if (fp == NULL) {
3614503ad85cSMatthew Ahrens 		dmu_objset_rele(tosnap, FTAG);
3615fa9e4066Sahrens 		if (fromsnap)
3616503ad85cSMatthew Ahrens 			dmu_objset_rele(fromsnap, FTAG);
3617fa9e4066Sahrens 		return (EBADF);
3618fa9e4066Sahrens 	}
3619fa9e4066Sahrens 
36203cb34c60Sahrens 	off = fp->f_offset;
36213cb34c60Sahrens 	error = dmu_sendbackup(tosnap, fromsnap, zc->zc_obj, fp->f_vnode, &off);
3622fa9e4066Sahrens 
36233cb34c60Sahrens 	if (VOP_SEEK(fp->f_vnode, fp->f_offset, &off, NULL) == 0)
36243cb34c60Sahrens 		fp->f_offset = off;
3625fa9e4066Sahrens 	releasef(zc->zc_cookie);
3626fa9e4066Sahrens 	if (fromsnap)
3627503ad85cSMatthew Ahrens 		dmu_objset_rele(fromsnap, FTAG);
3628503ad85cSMatthew Ahrens 	dmu_objset_rele(tosnap, FTAG);
3629fa9e4066Sahrens 	return (error);
3630fa9e4066Sahrens }
3631fa9e4066Sahrens 
3632ea8dc4b6Seschrock static int
3633ea8dc4b6Seschrock zfs_ioc_inject_fault(zfs_cmd_t *zc)
3634ea8dc4b6Seschrock {
3635ea8dc4b6Seschrock 	int id, error;
3636ea8dc4b6Seschrock 
3637ea8dc4b6Seschrock 	error = zio_inject_fault(zc->zc_name, (int)zc->zc_guid, &id,
3638ea8dc4b6Seschrock 	    &zc->zc_inject_record);
3639ea8dc4b6Seschrock 
3640ea8dc4b6Seschrock 	if (error == 0)
3641ea8dc4b6Seschrock 		zc->zc_guid = (uint64_t)id;
3642ea8dc4b6Seschrock 
3643ea8dc4b6Seschrock 	return (error);
3644ea8dc4b6Seschrock }
3645ea8dc4b6Seschrock 
3646ea8dc4b6Seschrock static int
3647ea8dc4b6Seschrock zfs_ioc_clear_fault(zfs_cmd_t *zc)
3648ea8dc4b6Seschrock {
3649ea8dc4b6Seschrock 	return (zio_clear_fault((int)zc->zc_guid));
3650ea8dc4b6Seschrock }
3651ea8dc4b6Seschrock 
3652ea8dc4b6Seschrock static int
3653ea8dc4b6Seschrock zfs_ioc_inject_list_next(zfs_cmd_t *zc)
3654ea8dc4b6Seschrock {
3655ea8dc4b6Seschrock 	int id = (int)zc->zc_guid;
3656ea8dc4b6Seschrock 	int error;
3657ea8dc4b6Seschrock 
3658ea8dc4b6Seschrock 	error = zio_inject_list_next(&id, zc->zc_name, sizeof (zc->zc_name),
3659ea8dc4b6Seschrock 	    &zc->zc_inject_record);
3660ea8dc4b6Seschrock 
3661ea8dc4b6Seschrock 	zc->zc_guid = id;
3662ea8dc4b6Seschrock 
3663ea8dc4b6Seschrock 	return (error);
3664ea8dc4b6Seschrock }
3665ea8dc4b6Seschrock 
3666ea8dc4b6Seschrock static int
3667ea8dc4b6Seschrock zfs_ioc_error_log(zfs_cmd_t *zc)
3668ea8dc4b6Seschrock {
3669ea8dc4b6Seschrock 	spa_t *spa;
3670ea8dc4b6Seschrock 	int error;
3671e9dbad6fSeschrock 	size_t count = (size_t)zc->zc_nvlist_dst_size;
3672ea8dc4b6Seschrock 
3673ea8dc4b6Seschrock 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
3674ea8dc4b6Seschrock 		return (error);
3675ea8dc4b6Seschrock 
3676e9dbad6fSeschrock 	error = spa_get_errlog(spa, (void *)(uintptr_t)zc->zc_nvlist_dst,
3677ea8dc4b6Seschrock 	    &count);
3678ea8dc4b6Seschrock 	if (error == 0)
3679e9dbad6fSeschrock 		zc->zc_nvlist_dst_size = count;
3680ea8dc4b6Seschrock 	else
3681e9dbad6fSeschrock 		zc->zc_nvlist_dst_size = spa_get_errlog_size(spa);
3682ea8dc4b6Seschrock 
3683ea8dc4b6Seschrock 	spa_close(spa, FTAG);
3684ea8dc4b6Seschrock 
3685ea8dc4b6Seschrock 	return (error);
3686ea8dc4b6Seschrock }
3687ea8dc4b6Seschrock 
3688ea8dc4b6Seschrock static int
3689ea8dc4b6Seschrock zfs_ioc_clear(zfs_cmd_t *zc)
3690ea8dc4b6Seschrock {
3691ea8dc4b6Seschrock 	spa_t *spa;
3692ea8dc4b6Seschrock 	vdev_t *vd;
3693bb8b5132Sek 	int error;
3694ea8dc4b6Seschrock 
3695b87f3af3Sperrin 	/*
3696b87f3af3Sperrin 	 * On zpool clear we also fix up missing slogs
3697b87f3af3Sperrin 	 */
3698b87f3af3Sperrin 	mutex_enter(&spa_namespace_lock);
3699b87f3af3Sperrin 	spa = spa_lookup(zc->zc_name);
3700b87f3af3Sperrin 	if (spa == NULL) {
3701b87f3af3Sperrin 		mutex_exit(&spa_namespace_lock);
3702b87f3af3Sperrin 		return (EIO);
3703b87f3af3Sperrin 	}
3704b24ab676SJeff Bonwick 	if (spa_get_log_state(spa) == SPA_LOG_MISSING) {
3705b87f3af3Sperrin 		/* we need to let spa_open/spa_load clear the chains */
3706b24ab676SJeff Bonwick 		spa_set_log_state(spa, SPA_LOG_CLEAR);
3707b87f3af3Sperrin 	}
3708468c413aSTim Haley 	spa->spa_last_open_failed = 0;
3709b87f3af3Sperrin 	mutex_exit(&spa_namespace_lock);
3710b87f3af3Sperrin 
3711c8ee1847SVictor Latushkin 	if (zc->zc_cookie & ZPOOL_NO_REWIND) {
3712468c413aSTim Haley 		error = spa_open(zc->zc_name, &spa, FTAG);
3713468c413aSTim Haley 	} else {
3714468c413aSTim Haley 		nvlist_t *policy;
3715468c413aSTim Haley 		nvlist_t *config = NULL;
3716468c413aSTim Haley 
3717468c413aSTim Haley 		if (zc->zc_nvlist_src == NULL)
3718468c413aSTim Haley 			return (EINVAL);
3719468c413aSTim Haley 
3720468c413aSTim Haley 		if ((error = get_nvlist(zc->zc_nvlist_src,
3721468c413aSTim Haley 		    zc->zc_nvlist_src_size, zc->zc_iflags, &policy)) == 0) {
3722468c413aSTim Haley 			error = spa_open_rewind(zc->zc_name, &spa, FTAG,
3723468c413aSTim Haley 			    policy, &config);
3724468c413aSTim Haley 			if (config != NULL) {
3725468c413aSTim Haley 				(void) put_nvlist(zc, config);
3726468c413aSTim Haley 				nvlist_free(config);
3727468c413aSTim Haley 			}
3728468c413aSTim Haley 			nvlist_free(policy);
3729468c413aSTim Haley 		}
3730468c413aSTim Haley 	}
3731468c413aSTim Haley 
3732468c413aSTim Haley 	if (error)
3733ea8dc4b6Seschrock 		return (error);
3734ea8dc4b6Seschrock 
37358f18d1faSGeorge Wilson 	spa_vdev_state_enter(spa, SCL_NONE);
3736ea8dc4b6Seschrock 
3737e9dbad6fSeschrock 	if (zc->zc_guid == 0) {
3738ea8dc4b6Seschrock 		vd = NULL;
3739c5904d13Seschrock 	} else {
3740c5904d13Seschrock 		vd = spa_lookup_by_guid(spa, zc->zc_guid, B_TRUE);
3741fa94a07fSbrendan 		if (vd == NULL) {
3742e14bb325SJeff Bonwick 			(void) spa_vdev_state_exit(spa, NULL, ENODEV);
3743fa94a07fSbrendan 			spa_close(spa, FTAG);
3744fa94a07fSbrendan 			return (ENODEV);
3745fa94a07fSbrendan 		}
3746ea8dc4b6Seschrock 	}
3747ea8dc4b6Seschrock 
3748e14bb325SJeff Bonwick 	vdev_clear(spa, vd);
3749e14bb325SJeff Bonwick 
3750e14bb325SJeff Bonwick 	(void) spa_vdev_state_exit(spa, NULL, 0);
3751ea8dc4b6Seschrock 
3752e14bb325SJeff Bonwick 	/*
3753e14bb325SJeff Bonwick 	 * Resume any suspended I/Os.
3754e14bb325SJeff Bonwick 	 */
375554d692b7SGeorge Wilson 	if (zio_resume(spa) != 0)
375654d692b7SGeorge Wilson 		error = EIO;
3757ea8dc4b6Seschrock 
3758ea8dc4b6Seschrock 	spa_close(spa, FTAG);
3759ea8dc4b6Seschrock 
376054d692b7SGeorge Wilson 	return (error);
3761ea8dc4b6Seschrock }
3762ea8dc4b6Seschrock 
37633cb34c60Sahrens /*
37643cb34c60Sahrens  * inputs:
37653cb34c60Sahrens  * zc_name	name of filesystem
37663cb34c60Sahrens  * zc_value	name of origin snapshot
37673cb34c60Sahrens  *
3768681d9761SEric Taylor  * outputs:
3769681d9761SEric Taylor  * zc_string	name of conflicting snapshot, if there is one
37703cb34c60Sahrens  */
377199653d4eSeschrock static int
377299653d4eSeschrock zfs_ioc_promote(zfs_cmd_t *zc)
377399653d4eSeschrock {
37740b69c2f0Sahrens 	char *cp;
37750b69c2f0Sahrens 
37760b69c2f0Sahrens 	/*
37770b69c2f0Sahrens 	 * We don't need to unmount *all* the origin fs's snapshots, but
37780b69c2f0Sahrens 	 * it's easier.
37790b69c2f0Sahrens 	 */
3780e9dbad6fSeschrock 	cp = strchr(zc->zc_value, '@');
37810b69c2f0Sahrens 	if (cp)
37820b69c2f0Sahrens 		*cp = '\0';
3783e9dbad6fSeschrock 	(void) dmu_objset_find(zc->zc_value,
37840b69c2f0Sahrens 	    zfs_unmount_snap, NULL, DS_FIND_SNAPSHOTS);
3785681d9761SEric Taylor 	return (dsl_dataset_promote(zc->zc_name, zc->zc_string));
378699653d4eSeschrock }
378799653d4eSeschrock 
378814843421SMatthew Ahrens /*
378914843421SMatthew Ahrens  * Retrieve a single {user|group}{used|quota}@... property.
379014843421SMatthew Ahrens  *
379114843421SMatthew Ahrens  * inputs:
379214843421SMatthew Ahrens  * zc_name	name of filesystem
379314843421SMatthew Ahrens  * zc_objset_type zfs_userquota_prop_t
379414843421SMatthew Ahrens  * zc_value	domain name (eg. "S-1-234-567-89")
379514843421SMatthew Ahrens  * zc_guid	RID/UID/GID
379614843421SMatthew Ahrens  *
379714843421SMatthew Ahrens  * outputs:
379814843421SMatthew Ahrens  * zc_cookie	property value
379914843421SMatthew Ahrens  */
380014843421SMatthew Ahrens static int
380114843421SMatthew Ahrens zfs_ioc_userspace_one(zfs_cmd_t *zc)
380214843421SMatthew Ahrens {
380314843421SMatthew Ahrens 	zfsvfs_t *zfsvfs;
380414843421SMatthew Ahrens 	int error;
380514843421SMatthew Ahrens 
380614843421SMatthew Ahrens 	if (zc->zc_objset_type >= ZFS_NUM_USERQUOTA_PROPS)
380714843421SMatthew Ahrens 		return (EINVAL);
380814843421SMatthew Ahrens 
3809503ad85cSMatthew Ahrens 	error = zfsvfs_hold(zc->zc_name, FTAG, &zfsvfs);
381014843421SMatthew Ahrens 	if (error)
381114843421SMatthew Ahrens 		return (error);
381214843421SMatthew Ahrens 
381314843421SMatthew Ahrens 	error = zfs_userspace_one(zfsvfs,
381414843421SMatthew Ahrens 	    zc->zc_objset_type, zc->zc_value, zc->zc_guid, &zc->zc_cookie);
381514843421SMatthew Ahrens 	zfsvfs_rele(zfsvfs, FTAG);
381614843421SMatthew Ahrens 
381714843421SMatthew Ahrens 	return (error);
381814843421SMatthew Ahrens }
381914843421SMatthew Ahrens 
382014843421SMatthew Ahrens /*
382114843421SMatthew Ahrens  * inputs:
382214843421SMatthew Ahrens  * zc_name		name of filesystem
382314843421SMatthew Ahrens  * zc_cookie		zap cursor
382414843421SMatthew Ahrens  * zc_objset_type	zfs_userquota_prop_t
382514843421SMatthew Ahrens  * zc_nvlist_dst[_size] buffer to fill (not really an nvlist)
382614843421SMatthew Ahrens  *
382714843421SMatthew Ahrens  * outputs:
382814843421SMatthew Ahrens  * zc_nvlist_dst[_size]	data buffer (array of zfs_useracct_t)
382914843421SMatthew Ahrens  * zc_cookie	zap cursor
383014843421SMatthew Ahrens  */
383114843421SMatthew Ahrens static int
383214843421SMatthew Ahrens zfs_ioc_userspace_many(zfs_cmd_t *zc)
383314843421SMatthew Ahrens {
383414843421SMatthew Ahrens 	zfsvfs_t *zfsvfs;
3835eeb85002STim Haley 	int bufsize = zc->zc_nvlist_dst_size;
383614843421SMatthew Ahrens 
3837eeb85002STim Haley 	if (bufsize <= 0)
3838eeb85002STim Haley 		return (ENOMEM);
3839eeb85002STim Haley 
3840eeb85002STim Haley 	int error = zfsvfs_hold(zc->zc_name, FTAG, &zfsvfs);
384114843421SMatthew Ahrens 	if (error)
384214843421SMatthew Ahrens 		return (error);
384314843421SMatthew Ahrens 
384414843421SMatthew Ahrens 	void *buf = kmem_alloc(bufsize, KM_SLEEP);
384514843421SMatthew Ahrens 
384614843421SMatthew Ahrens 	error = zfs_userspace_many(zfsvfs, zc->zc_objset_type, &zc->zc_cookie,
384714843421SMatthew Ahrens 	    buf, &zc->zc_nvlist_dst_size);
384814843421SMatthew Ahrens 
384914843421SMatthew Ahrens 	if (error == 0) {
385014843421SMatthew Ahrens 		error = xcopyout(buf,
385114843421SMatthew Ahrens 		    (void *)(uintptr_t)zc->zc_nvlist_dst,
385214843421SMatthew Ahrens 		    zc->zc_nvlist_dst_size);
385314843421SMatthew Ahrens 	}
385414843421SMatthew Ahrens 	kmem_free(buf, bufsize);
385514843421SMatthew Ahrens 	zfsvfs_rele(zfsvfs, FTAG);
385614843421SMatthew Ahrens 
385714843421SMatthew Ahrens 	return (error);
385814843421SMatthew Ahrens }
385914843421SMatthew Ahrens 
386014843421SMatthew Ahrens /*
386114843421SMatthew Ahrens  * inputs:
386214843421SMatthew Ahrens  * zc_name		name of filesystem
386314843421SMatthew Ahrens  *
386414843421SMatthew Ahrens  * outputs:
386514843421SMatthew Ahrens  * none
386614843421SMatthew Ahrens  */
386714843421SMatthew Ahrens static int
386814843421SMatthew Ahrens zfs_ioc_userspace_upgrade(zfs_cmd_t *zc)
386914843421SMatthew Ahrens {
387014843421SMatthew Ahrens 	objset_t *os;
38711195e687SMark J Musante 	int error = 0;
387214843421SMatthew Ahrens 	zfsvfs_t *zfsvfs;
387314843421SMatthew Ahrens 
387414843421SMatthew Ahrens 	if (getzfsvfs(zc->zc_name, &zfsvfs) == 0) {
3875503ad85cSMatthew Ahrens 		if (!dmu_objset_userused_enabled(zfsvfs->z_os)) {
387614843421SMatthew Ahrens 			/*
387714843421SMatthew Ahrens 			 * If userused is not enabled, it may be because the
387814843421SMatthew Ahrens 			 * objset needs to be closed & reopened (to grow the
387914843421SMatthew Ahrens 			 * objset_phys_t).  Suspend/resume the fs will do that.
388014843421SMatthew Ahrens 			 */
3881503ad85cSMatthew Ahrens 			error = zfs_suspend_fs(zfsvfs);
3882503ad85cSMatthew Ahrens 			if (error == 0)
3883503ad85cSMatthew Ahrens 				error = zfs_resume_fs(zfsvfs, zc->zc_name);
388414843421SMatthew Ahrens 		}
388514843421SMatthew Ahrens 		if (error == 0)
388614843421SMatthew Ahrens 			error = dmu_objset_userspace_upgrade(zfsvfs->z_os);
388714843421SMatthew Ahrens 		VFS_RELE(zfsvfs->z_vfs);
388814843421SMatthew Ahrens 	} else {
3889503ad85cSMatthew Ahrens 		/* XXX kind of reading contents without owning */
3890503ad85cSMatthew Ahrens 		error = dmu_objset_hold(zc->zc_name, FTAG, &os);
389114843421SMatthew Ahrens 		if (error)
389214843421SMatthew Ahrens 			return (error);
389314843421SMatthew Ahrens 
389414843421SMatthew Ahrens 		error = dmu_objset_userspace_upgrade(os);
3895503ad85cSMatthew Ahrens 		dmu_objset_rele(os, FTAG);
389614843421SMatthew Ahrens 	}
389714843421SMatthew Ahrens 
389814843421SMatthew Ahrens 	return (error);
389914843421SMatthew Ahrens }
390014843421SMatthew Ahrens 
3901ecd6cf80Smarks /*
3902ecd6cf80Smarks  * We don't want to have a hard dependency
3903ecd6cf80Smarks  * against some special symbols in sharefs
3904da6c28aaSamw  * nfs, and smbsrv.  Determine them if needed when
3905ecd6cf80Smarks  * the first file system is shared.
3906da6c28aaSamw  * Neither sharefs, nfs or smbsrv are unloadable modules.
3907ecd6cf80Smarks  */
3908da6c28aaSamw int (*znfsexport_fs)(void *arg);
3909ecd6cf80Smarks int (*zshare_fs)(enum sharefs_sys_op, share_t *, uint32_t);
3910da6c28aaSamw int (*zsmbexport_fs)(void *arg, boolean_t add_share);
3911da6c28aaSamw 
3912da6c28aaSamw int zfs_nfsshare_inited;
3913da6c28aaSamw int zfs_smbshare_inited;
3914ecd6cf80Smarks 
3915ecd6cf80Smarks ddi_modhandle_t nfs_mod;
3916ecd6cf80Smarks ddi_modhandle_t sharefs_mod;
3917da6c28aaSamw ddi_modhandle_t smbsrv_mod;
3918ecd6cf80Smarks kmutex_t zfs_share_lock;
3919ecd6cf80Smarks 
3920da6c28aaSamw static int
3921da6c28aaSamw zfs_init_sharefs()
3922da6c28aaSamw {
3923da6c28aaSamw 	int error;
3924da6c28aaSamw 
3925da6c28aaSamw 	ASSERT(MUTEX_HELD(&zfs_share_lock));
3926da6c28aaSamw 	/* Both NFS and SMB shares also require sharetab support. */
3927da6c28aaSamw 	if (sharefs_mod == NULL && ((sharefs_mod =
3928da6c28aaSamw 	    ddi_modopen("fs/sharefs",
3929da6c28aaSamw 	    KRTLD_MODE_FIRST, &error)) == NULL)) {
3930da6c28aaSamw 		return (ENOSYS);
3931da6c28aaSamw 	}
3932da6c28aaSamw 	if (zshare_fs == NULL && ((zshare_fs =
3933da6c28aaSamw 	    (int (*)(enum sharefs_sys_op, share_t *, uint32_t))
3934da6c28aaSamw 	    ddi_modsym(sharefs_mod, "sharefs_impl", &error)) == NULL)) {
3935da6c28aaSamw 		return (ENOSYS);
3936da6c28aaSamw 	}
3937da6c28aaSamw 	return (0);
3938da6c28aaSamw }
3939da6c28aaSamw 
3940ecd6cf80Smarks static int
3941ecd6cf80Smarks zfs_ioc_share(zfs_cmd_t *zc)
3942ecd6cf80Smarks {
3943ecd6cf80Smarks 	int error;
3944ecd6cf80Smarks 	int opcode;
3945ecd6cf80Smarks 
3946da6c28aaSamw 	switch (zc->zc_share.z_sharetype) {
3947da6c28aaSamw 	case ZFS_SHARE_NFS:
3948da6c28aaSamw 	case ZFS_UNSHARE_NFS:
3949da6c28aaSamw 		if (zfs_nfsshare_inited == 0) {
3950da6c28aaSamw 			mutex_enter(&zfs_share_lock);
3951da6c28aaSamw 			if (nfs_mod == NULL && ((nfs_mod = ddi_modopen("fs/nfs",
3952da6c28aaSamw 			    KRTLD_MODE_FIRST, &error)) == NULL)) {
3953da6c28aaSamw 				mutex_exit(&zfs_share_lock);
3954da6c28aaSamw 				return (ENOSYS);
3955da6c28aaSamw 			}
3956da6c28aaSamw 			if (znfsexport_fs == NULL &&
3957da6c28aaSamw 			    ((znfsexport_fs = (int (*)(void *))
3958da6c28aaSamw 			    ddi_modsym(nfs_mod,
3959da6c28aaSamw 			    "nfs_export", &error)) == NULL)) {
3960da6c28aaSamw 				mutex_exit(&zfs_share_lock);
3961da6c28aaSamw 				return (ENOSYS);
3962da6c28aaSamw 			}
3963da6c28aaSamw 			error = zfs_init_sharefs();
3964da6c28aaSamw 			if (error) {
3965da6c28aaSamw 				mutex_exit(&zfs_share_lock);
3966da6c28aaSamw 				return (ENOSYS);
3967da6c28aaSamw 			}
3968da6c28aaSamw 			zfs_nfsshare_inited = 1;
3969ecd6cf80Smarks 			mutex_exit(&zfs_share_lock);
3970ecd6cf80Smarks 		}
3971da6c28aaSamw 		break;
3972da6c28aaSamw 	case ZFS_SHARE_SMB:
3973da6c28aaSamw 	case ZFS_UNSHARE_SMB:
3974da6c28aaSamw 		if (zfs_smbshare_inited == 0) {
3975da6c28aaSamw 			mutex_enter(&zfs_share_lock);
3976da6c28aaSamw 			if (smbsrv_mod == NULL && ((smbsrv_mod =
3977da6c28aaSamw 			    ddi_modopen("drv/smbsrv",
3978da6c28aaSamw 			    KRTLD_MODE_FIRST, &error)) == NULL)) {
3979da6c28aaSamw 				mutex_exit(&zfs_share_lock);
3980da6c28aaSamw 				return (ENOSYS);
3981da6c28aaSamw 			}
3982da6c28aaSamw 			if (zsmbexport_fs == NULL && ((zsmbexport_fs =
3983da6c28aaSamw 			    (int (*)(void *, boolean_t))ddi_modsym(smbsrv_mod,
3984faa1795aSjb 			    "smb_server_share", &error)) == NULL)) {
3985da6c28aaSamw 				mutex_exit(&zfs_share_lock);
3986da6c28aaSamw 				return (ENOSYS);
3987da6c28aaSamw 			}
3988da6c28aaSamw 			error = zfs_init_sharefs();
3989da6c28aaSamw 			if (error) {
3990da6c28aaSamw 				mutex_exit(&zfs_share_lock);
3991da6c28aaSamw 				return (ENOSYS);
3992da6c28aaSamw 			}
3993da6c28aaSamw 			zfs_smbshare_inited = 1;
3994ecd6cf80Smarks 			mutex_exit(&zfs_share_lock);
3995ecd6cf80Smarks 		}
3996da6c28aaSamw 		break;
3997da6c28aaSamw 	default:
3998da6c28aaSamw 		return (EINVAL);
3999da6c28aaSamw 	}
4000ecd6cf80Smarks 
4001da6c28aaSamw 	switch (zc->zc_share.z_sharetype) {
4002da6c28aaSamw 	case ZFS_SHARE_NFS:
4003da6c28aaSamw 	case ZFS_UNSHARE_NFS:
4004da6c28aaSamw 		if (error =
4005da6c28aaSamw 		    znfsexport_fs((void *)
4006da6c28aaSamw 		    (uintptr_t)zc->zc_share.z_exportdata))
4007da6c28aaSamw 			return (error);
4008da6c28aaSamw 		break;
4009da6c28aaSamw 	case ZFS_SHARE_SMB:
4010da6c28aaSamw 	case ZFS_UNSHARE_SMB:
4011da6c28aaSamw 		if (error = zsmbexport_fs((void *)
4012da6c28aaSamw 		    (uintptr_t)zc->zc_share.z_exportdata,
4013da6c28aaSamw 		    zc->zc_share.z_sharetype == ZFS_SHARE_SMB ?
4014743a77edSAlan Wright 		    B_TRUE: B_FALSE)) {
4015da6c28aaSamw 			return (error);
4016ecd6cf80Smarks 		}
4017da6c28aaSamw 		break;
4018ecd6cf80Smarks 	}
4019ecd6cf80Smarks 
4020da6c28aaSamw 	opcode = (zc->zc_share.z_sharetype == ZFS_SHARE_NFS ||
4021da6c28aaSamw 	    zc->zc_share.z_sharetype == ZFS_SHARE_SMB) ?
4022ecd6cf80Smarks 	    SHAREFS_ADD : SHAREFS_REMOVE;
4023ecd6cf80Smarks 
4024da6c28aaSamw 	/*
4025da6c28aaSamw 	 * Add or remove share from sharetab
4026da6c28aaSamw 	 */
4027ecd6cf80Smarks 	error = zshare_fs(opcode,
4028ecd6cf80Smarks 	    (void *)(uintptr_t)zc->zc_share.z_sharedata,
4029ecd6cf80Smarks 	    zc->zc_share.z_sharemax);
4030ecd6cf80Smarks 
4031ecd6cf80Smarks 	return (error);
4032ecd6cf80Smarks 
4033ecd6cf80Smarks }
4034ecd6cf80Smarks 
4035743a77edSAlan Wright ace_t full_access[] = {
4036743a77edSAlan Wright 	{(uid_t)-1, ACE_ALL_PERMS, ACE_EVERYONE, 0}
4037743a77edSAlan Wright };
4038743a77edSAlan Wright 
4039743a77edSAlan Wright /*
4040743a77edSAlan Wright  * Remove all ACL files in shares dir
4041743a77edSAlan Wright  */
4042743a77edSAlan Wright static int
4043743a77edSAlan Wright zfs_smb_acl_purge(znode_t *dzp)
4044743a77edSAlan Wright {
4045743a77edSAlan Wright 	zap_cursor_t	zc;
4046743a77edSAlan Wright 	zap_attribute_t	zap;
4047743a77edSAlan Wright 	zfsvfs_t *zfsvfs = dzp->z_zfsvfs;
4048743a77edSAlan Wright 	int error;
4049743a77edSAlan Wright 
4050743a77edSAlan Wright 	for (zap_cursor_init(&zc, zfsvfs->z_os, dzp->z_id);
4051743a77edSAlan Wright 	    (error = zap_cursor_retrieve(&zc, &zap)) == 0;
4052743a77edSAlan Wright 	    zap_cursor_advance(&zc)) {
4053743a77edSAlan Wright 		if ((error = VOP_REMOVE(ZTOV(dzp), zap.za_name, kcred,
4054743a77edSAlan Wright 		    NULL, 0)) != 0)
4055743a77edSAlan Wright 			break;
4056743a77edSAlan Wright 	}
4057743a77edSAlan Wright 	zap_cursor_fini(&zc);
4058743a77edSAlan Wright 	return (error);
4059743a77edSAlan Wright }
4060743a77edSAlan Wright 
4061743a77edSAlan Wright static int
4062743a77edSAlan Wright zfs_ioc_smb_acl(zfs_cmd_t *zc)
4063743a77edSAlan Wright {
4064743a77edSAlan Wright 	vnode_t *vp;
4065743a77edSAlan Wright 	znode_t *dzp;
4066743a77edSAlan Wright 	vnode_t *resourcevp = NULL;
4067743a77edSAlan Wright 	znode_t *sharedir;
4068743a77edSAlan Wright 	zfsvfs_t *zfsvfs;
4069743a77edSAlan Wright 	nvlist_t *nvlist;
4070743a77edSAlan Wright 	char *src, *target;
4071743a77edSAlan Wright 	vattr_t vattr;
4072743a77edSAlan Wright 	vsecattr_t vsec;
4073743a77edSAlan Wright 	int error = 0;
4074743a77edSAlan Wright 
4075743a77edSAlan Wright 	if ((error = lookupname(zc->zc_value, UIO_SYSSPACE,
4076743a77edSAlan Wright 	    NO_FOLLOW, NULL, &vp)) != 0)
4077743a77edSAlan Wright 		return (error);
4078743a77edSAlan Wright 
4079743a77edSAlan Wright 	/* Now make sure mntpnt and dataset are ZFS */
4080743a77edSAlan Wright 
4081743a77edSAlan Wright 	if (vp->v_vfsp->vfs_fstype != zfsfstype ||
4082743a77edSAlan Wright 	    (strcmp((char *)refstr_value(vp->v_vfsp->vfs_resource),
4083743a77edSAlan Wright 	    zc->zc_name) != 0)) {
4084743a77edSAlan Wright 		VN_RELE(vp);
4085743a77edSAlan Wright 		return (EINVAL);
4086743a77edSAlan Wright 	}
4087743a77edSAlan Wright 
4088743a77edSAlan Wright 	dzp = VTOZ(vp);
4089743a77edSAlan Wright 	zfsvfs = dzp->z_zfsvfs;
4090743a77edSAlan Wright 	ZFS_ENTER(zfsvfs);
4091743a77edSAlan Wright 
40929e1320c0SMark Shellenbaum 	/*
40939e1320c0SMark Shellenbaum 	 * Create share dir if its missing.
40949e1320c0SMark Shellenbaum 	 */
40959e1320c0SMark Shellenbaum 	mutex_enter(&zfsvfs->z_lock);
40969e1320c0SMark Shellenbaum 	if (zfsvfs->z_shares_dir == 0) {
40979e1320c0SMark Shellenbaum 		dmu_tx_t *tx;
40989e1320c0SMark Shellenbaum 
40999e1320c0SMark Shellenbaum 		tx = dmu_tx_create(zfsvfs->z_os);
41009e1320c0SMark Shellenbaum 		dmu_tx_hold_zap(tx, MASTER_NODE_OBJ, TRUE,
41019e1320c0SMark Shellenbaum 		    ZFS_SHARES_DIR);
41029e1320c0SMark Shellenbaum 		dmu_tx_hold_zap(tx, DMU_NEW_OBJECT, FALSE, NULL);
41039e1320c0SMark Shellenbaum 		error = dmu_tx_assign(tx, TXG_WAIT);
41049e1320c0SMark Shellenbaum 		if (error) {
41059e1320c0SMark Shellenbaum 			dmu_tx_abort(tx);
41069e1320c0SMark Shellenbaum 		} else {
41079e1320c0SMark Shellenbaum 			error = zfs_create_share_dir(zfsvfs, tx);
41089e1320c0SMark Shellenbaum 			dmu_tx_commit(tx);
41099e1320c0SMark Shellenbaum 		}
41109e1320c0SMark Shellenbaum 		if (error) {
41119e1320c0SMark Shellenbaum 			mutex_exit(&zfsvfs->z_lock);
41129e1320c0SMark Shellenbaum 			VN_RELE(vp);
41139e1320c0SMark Shellenbaum 			ZFS_EXIT(zfsvfs);
41149e1320c0SMark Shellenbaum 			return (error);
41159e1320c0SMark Shellenbaum 		}
41169e1320c0SMark Shellenbaum 	}
41179e1320c0SMark Shellenbaum 	mutex_exit(&zfsvfs->z_lock);
41189e1320c0SMark Shellenbaum 
41199e1320c0SMark Shellenbaum 	ASSERT(zfsvfs->z_shares_dir);
4120743a77edSAlan Wright 	if ((error = zfs_zget(zfsvfs, zfsvfs->z_shares_dir, &sharedir)) != 0) {
41219e1320c0SMark Shellenbaum 		VN_RELE(vp);
4122743a77edSAlan Wright 		ZFS_EXIT(zfsvfs);
4123743a77edSAlan Wright 		return (error);
4124743a77edSAlan Wright 	}
4125743a77edSAlan Wright 
4126743a77edSAlan Wright 	switch (zc->zc_cookie) {
4127743a77edSAlan Wright 	case ZFS_SMB_ACL_ADD:
4128743a77edSAlan Wright 		vattr.va_mask = AT_MODE|AT_UID|AT_GID|AT_TYPE;
4129743a77edSAlan Wright 		vattr.va_type = VREG;
4130743a77edSAlan Wright 		vattr.va_mode = S_IFREG|0777;
4131743a77edSAlan Wright 		vattr.va_uid = 0;
4132743a77edSAlan Wright 		vattr.va_gid = 0;
4133743a77edSAlan Wright 
4134743a77edSAlan Wright 		vsec.vsa_mask = VSA_ACE;
4135743a77edSAlan Wright 		vsec.vsa_aclentp = &full_access;
4136743a77edSAlan Wright 		vsec.vsa_aclentsz = sizeof (full_access);
4137743a77edSAlan Wright 		vsec.vsa_aclcnt = 1;
4138743a77edSAlan Wright 
4139743a77edSAlan Wright 		error = VOP_CREATE(ZTOV(sharedir), zc->zc_string,
4140743a77edSAlan Wright 		    &vattr, EXCL, 0, &resourcevp, kcred, 0, NULL, &vsec);
4141743a77edSAlan Wright 		if (resourcevp)
4142743a77edSAlan Wright 			VN_RELE(resourcevp);
4143743a77edSAlan Wright 		break;
4144743a77edSAlan Wright 
4145743a77edSAlan Wright 	case ZFS_SMB_ACL_REMOVE:
4146743a77edSAlan Wright 		error = VOP_REMOVE(ZTOV(sharedir), zc->zc_string, kcred,
4147743a77edSAlan Wright 		    NULL, 0);
4148743a77edSAlan Wright 		break;
4149743a77edSAlan Wright 
4150743a77edSAlan Wright 	case ZFS_SMB_ACL_RENAME:
4151743a77edSAlan Wright 		if ((error = get_nvlist(zc->zc_nvlist_src,
4152478ed9adSEric Taylor 		    zc->zc_nvlist_src_size, zc->zc_iflags, &nvlist)) != 0) {
4153743a77edSAlan Wright 			VN_RELE(vp);
4154743a77edSAlan Wright 			ZFS_EXIT(zfsvfs);
4155743a77edSAlan Wright 			return (error);
4156743a77edSAlan Wright 		}
4157743a77edSAlan Wright 		if (nvlist_lookup_string(nvlist, ZFS_SMB_ACL_SRC, &src) ||
4158743a77edSAlan Wright 		    nvlist_lookup_string(nvlist, ZFS_SMB_ACL_TARGET,
4159743a77edSAlan Wright 		    &target)) {
4160743a77edSAlan Wright 			VN_RELE(vp);
416189459e17SMark Shellenbaum 			VN_RELE(ZTOV(sharedir));
4162743a77edSAlan Wright 			ZFS_EXIT(zfsvfs);
41631195e687SMark J Musante 			nvlist_free(nvlist);
4164743a77edSAlan Wright 			return (error);
4165743a77edSAlan Wright 		}
4166743a77edSAlan Wright 		error = VOP_RENAME(ZTOV(sharedir), src, ZTOV(sharedir), target,
4167743a77edSAlan Wright 		    kcred, NULL, 0);
4168743a77edSAlan Wright 		nvlist_free(nvlist);
4169743a77edSAlan Wright 		break;
4170743a77edSAlan Wright 
4171743a77edSAlan Wright 	case ZFS_SMB_ACL_PURGE:
4172743a77edSAlan Wright 		error = zfs_smb_acl_purge(sharedir);
4173743a77edSAlan Wright 		break;
4174743a77edSAlan Wright 
4175743a77edSAlan Wright 	default:
4176743a77edSAlan Wright 		error = EINVAL;
4177743a77edSAlan Wright 		break;
4178743a77edSAlan Wright 	}
4179743a77edSAlan Wright 
4180743a77edSAlan Wright 	VN_RELE(vp);
4181743a77edSAlan Wright 	VN_RELE(ZTOV(sharedir));
4182743a77edSAlan Wright 
4183743a77edSAlan Wright 	ZFS_EXIT(zfsvfs);
4184743a77edSAlan Wright 
4185743a77edSAlan Wright 	return (error);
4186743a77edSAlan Wright }
4187743a77edSAlan Wright 
4188842727c2SChris Kirby /*
4189842727c2SChris Kirby  * inputs:
4190*c99e4bdcSChris Kirby  * zc_name		name of filesystem
4191*c99e4bdcSChris Kirby  * zc_value		short name of snap
4192*c99e4bdcSChris Kirby  * zc_string		user-supplied tag for this hold
4193*c99e4bdcSChris Kirby  * zc_cookie		recursive flag
4194*c99e4bdcSChris Kirby  * zc_temphold		set if hold is temporary
4195*c99e4bdcSChris Kirby  * zc_cleanup_fd	cleanup-on-exit file descriptor for calling process
4196842727c2SChris Kirby  *
4197842727c2SChris Kirby  * outputs:		none
4198842727c2SChris Kirby  */
4199842727c2SChris Kirby static int
4200842727c2SChris Kirby zfs_ioc_hold(zfs_cmd_t *zc)
4201842727c2SChris Kirby {
4202842727c2SChris Kirby 	boolean_t recursive = zc->zc_cookie;
4203842727c2SChris Kirby 
4204842727c2SChris Kirby 	if (snapshot_namecheck(zc->zc_value, NULL, NULL) != 0)
4205842727c2SChris Kirby 		return (EINVAL);
4206842727c2SChris Kirby 
4207842727c2SChris Kirby 	return (dsl_dataset_user_hold(zc->zc_name, zc->zc_value,
4208*c99e4bdcSChris Kirby 	    zc->zc_string, recursive, zc->zc_temphold, zc->zc_cleanup_fd));
4209842727c2SChris Kirby }
4210842727c2SChris Kirby 
4211842727c2SChris Kirby /*
4212842727c2SChris Kirby  * inputs:
4213*c99e4bdcSChris Kirby  * zc_name	name of dataset from which we're releasing a user hold
4214842727c2SChris Kirby  * zc_value	short name of snap
4215*c99e4bdcSChris Kirby  * zc_string	user-supplied tag for this hold
4216842727c2SChris Kirby  * zc_cookie	recursive flag
4217842727c2SChris Kirby  *
4218*c99e4bdcSChris Kirby  * outputs:	none
4219842727c2SChris Kirby  */
4220842727c2SChris Kirby static int
4221842727c2SChris Kirby zfs_ioc_release(zfs_cmd_t *zc)
4222842727c2SChris Kirby {
4223842727c2SChris Kirby 	boolean_t recursive = zc->zc_cookie;
4224842727c2SChris Kirby 
4225842727c2SChris Kirby 	if (snapshot_namecheck(zc->zc_value, NULL, NULL) != 0)
4226842727c2SChris Kirby 		return (EINVAL);
4227842727c2SChris Kirby 
4228842727c2SChris Kirby 	return (dsl_dataset_user_release(zc->zc_name, zc->zc_value,
4229842727c2SChris Kirby 	    zc->zc_string, recursive));
4230842727c2SChris Kirby }
4231842727c2SChris Kirby 
4232842727c2SChris Kirby /*
4233842727c2SChris Kirby  * inputs:
4234842727c2SChris Kirby  * zc_name		name of filesystem
4235842727c2SChris Kirby  *
4236842727c2SChris Kirby  * outputs:
4237842727c2SChris Kirby  * zc_nvlist_src{_size}	nvlist of snapshot holds
4238842727c2SChris Kirby  */
4239842727c2SChris Kirby static int
4240842727c2SChris Kirby zfs_ioc_get_holds(zfs_cmd_t *zc)
4241842727c2SChris Kirby {
4242842727c2SChris Kirby 	nvlist_t *nvp;
4243842727c2SChris Kirby 	int error;
4244842727c2SChris Kirby 
4245842727c2SChris Kirby 	if ((error = dsl_dataset_get_holds(zc->zc_name, &nvp)) == 0) {
4246842727c2SChris Kirby 		error = put_nvlist(zc, nvp);
4247842727c2SChris Kirby 		nvlist_free(nvp);
4248842727c2SChris Kirby 	}
4249842727c2SChris Kirby 
4250842727c2SChris Kirby 	return (error);
4251842727c2SChris Kirby }
4252842727c2SChris Kirby 
4253ecd6cf80Smarks /*
42542a6b87f0Sek  * pool create, destroy, and export don't log the history as part of
42552a6b87f0Sek  * zfsdev_ioctl, but rather zfs_ioc_pool_create, and zfs_ioc_pool_export
42562a6b87f0Sek  * do the logging of those commands.
4257ecd6cf80Smarks  */
4258fa9e4066Sahrens static zfs_ioc_vec_t zfs_ioc_vec[] = {
425954d692b7SGeorge Wilson 	{ zfs_ioc_pool_create, zfs_secpolicy_config, POOL_NAME, B_FALSE,
426054d692b7SGeorge Wilson 	    B_FALSE },
426154d692b7SGeorge Wilson 	{ zfs_ioc_pool_destroy,	zfs_secpolicy_config, POOL_NAME, B_FALSE,
426254d692b7SGeorge Wilson 	    B_FALSE },
426354d692b7SGeorge Wilson 	{ zfs_ioc_pool_import, zfs_secpolicy_config, POOL_NAME, B_TRUE,
426454d692b7SGeorge Wilson 	    B_FALSE },
426554d692b7SGeorge Wilson 	{ zfs_ioc_pool_export, zfs_secpolicy_config, POOL_NAME, B_FALSE,
426654d692b7SGeorge Wilson 	    B_FALSE },
426754d692b7SGeorge Wilson 	{ zfs_ioc_pool_configs,	zfs_secpolicy_none, NO_NAME, B_FALSE,
426854d692b7SGeorge Wilson 	    B_FALSE },
426954d692b7SGeorge Wilson 	{ zfs_ioc_pool_stats, zfs_secpolicy_read, POOL_NAME, B_FALSE,
427054d692b7SGeorge Wilson 	    B_FALSE },
427154d692b7SGeorge Wilson 	{ zfs_ioc_pool_tryimport, zfs_secpolicy_config, NO_NAME, B_FALSE,
427254d692b7SGeorge Wilson 	    B_FALSE },
42733f9d6ad7SLin Ling 	{ zfs_ioc_pool_scan, zfs_secpolicy_config, POOL_NAME, B_TRUE,
427454d692b7SGeorge Wilson 	    B_TRUE },
427554d692b7SGeorge Wilson 	{ zfs_ioc_pool_freeze, zfs_secpolicy_config, NO_NAME, B_FALSE,
427654d692b7SGeorge Wilson 	    B_FALSE },
427754d692b7SGeorge Wilson 	{ zfs_ioc_pool_upgrade,	zfs_secpolicy_config, POOL_NAME, B_TRUE,
427854d692b7SGeorge Wilson 	    B_TRUE },
427954d692b7SGeorge Wilson 	{ zfs_ioc_pool_get_history, zfs_secpolicy_config, POOL_NAME, B_FALSE,
428054d692b7SGeorge Wilson 	    B_FALSE },
428154d692b7SGeorge Wilson 	{ zfs_ioc_vdev_add, zfs_secpolicy_config, POOL_NAME, B_TRUE,
428254d692b7SGeorge Wilson 	    B_TRUE },
428354d692b7SGeorge Wilson 	{ zfs_ioc_vdev_remove, zfs_secpolicy_config, POOL_NAME, B_TRUE,
428454d692b7SGeorge Wilson 	    B_TRUE },
428554d692b7SGeorge Wilson 	{ zfs_ioc_vdev_set_state, zfs_secpolicy_config,	POOL_NAME, B_TRUE,
428654d692b7SGeorge Wilson 	    B_FALSE },
428754d692b7SGeorge Wilson 	{ zfs_ioc_vdev_attach, zfs_secpolicy_config, POOL_NAME, B_TRUE,
428854d692b7SGeorge Wilson 	    B_TRUE },
428954d692b7SGeorge Wilson 	{ zfs_ioc_vdev_detach, zfs_secpolicy_config, POOL_NAME, B_TRUE,
429054d692b7SGeorge Wilson 	    B_TRUE },
429154d692b7SGeorge Wilson 	{ zfs_ioc_vdev_setpath,	zfs_secpolicy_config, POOL_NAME, B_FALSE,
429254d692b7SGeorge Wilson 	    B_TRUE },
42936809eb4eSEric Schrock 	{ zfs_ioc_vdev_setfru,	zfs_secpolicy_config, POOL_NAME, B_FALSE,
42946809eb4eSEric Schrock 	    B_TRUE },
429554d692b7SGeorge Wilson 	{ zfs_ioc_objset_stats,	zfs_secpolicy_read, DATASET_NAME, B_FALSE,
4296cf2859fcSSanjeev Bagewadi 	    B_TRUE },
429754d692b7SGeorge Wilson 	{ zfs_ioc_objset_zplprops, zfs_secpolicy_read, DATASET_NAME, B_FALSE,
429854d692b7SGeorge Wilson 	    B_FALSE },
429954d692b7SGeorge Wilson 	{ zfs_ioc_dataset_list_next, zfs_secpolicy_read, DATASET_NAME, B_FALSE,
4300cf2859fcSSanjeev Bagewadi 	    B_TRUE },
430154d692b7SGeorge Wilson 	{ zfs_ioc_snapshot_list_next, zfs_secpolicy_read, DATASET_NAME, B_FALSE,
4302cf2859fcSSanjeev Bagewadi 	    B_TRUE },
430354d692b7SGeorge Wilson 	{ zfs_ioc_set_prop, zfs_secpolicy_none, DATASET_NAME, B_TRUE, B_TRUE },
430454d692b7SGeorge Wilson 	{ zfs_ioc_create, zfs_secpolicy_create, DATASET_NAME, B_TRUE, B_TRUE },
430554d692b7SGeorge Wilson 	{ zfs_ioc_destroy, zfs_secpolicy_destroy, DATASET_NAME, B_TRUE,
430654d692b7SGeorge Wilson 	    B_TRUE},
430754d692b7SGeorge Wilson 	{ zfs_ioc_rollback, zfs_secpolicy_rollback, DATASET_NAME, B_TRUE,
430854d692b7SGeorge Wilson 	    B_TRUE },
430954d692b7SGeorge Wilson 	{ zfs_ioc_rename, zfs_secpolicy_rename,	DATASET_NAME, B_TRUE, B_TRUE },
431054d692b7SGeorge Wilson 	{ zfs_ioc_recv, zfs_secpolicy_receive, DATASET_NAME, B_TRUE, B_TRUE },
431154d692b7SGeorge Wilson 	{ zfs_ioc_send, zfs_secpolicy_send, DATASET_NAME, B_TRUE, B_FALSE },
431254d692b7SGeorge Wilson 	{ zfs_ioc_inject_fault,	zfs_secpolicy_inject, NO_NAME, B_FALSE,
431354d692b7SGeorge Wilson 	    B_FALSE },
431454d692b7SGeorge Wilson 	{ zfs_ioc_clear_fault, zfs_secpolicy_inject, NO_NAME, B_FALSE,
431554d692b7SGeorge Wilson 	    B_FALSE },
431654d692b7SGeorge Wilson 	{ zfs_ioc_inject_list_next, zfs_secpolicy_inject, NO_NAME, B_FALSE,
431754d692b7SGeorge Wilson 	    B_FALSE },
431854d692b7SGeorge Wilson 	{ zfs_ioc_error_log, zfs_secpolicy_inject, POOL_NAME, B_FALSE,
431954d692b7SGeorge Wilson 	    B_FALSE },
432054d692b7SGeorge Wilson 	{ zfs_ioc_clear, zfs_secpolicy_config, POOL_NAME, B_TRUE, B_FALSE },
432154d692b7SGeorge Wilson 	{ zfs_ioc_promote, zfs_secpolicy_promote, DATASET_NAME, B_TRUE,
432254d692b7SGeorge Wilson 	    B_TRUE },
4323cbf6f6aaSWilliam Gorrell 	{ zfs_ioc_destroy_snaps, zfs_secpolicy_destroy_snaps, DATASET_NAME,
4324cbf6f6aaSWilliam Gorrell 	    B_TRUE, B_TRUE },
432554d692b7SGeorge Wilson 	{ zfs_ioc_snapshot, zfs_secpolicy_snapshot, DATASET_NAME, B_TRUE,
432654d692b7SGeorge Wilson 	    B_TRUE },
432754d692b7SGeorge Wilson 	{ zfs_ioc_dsobj_to_dsname, zfs_secpolicy_config, POOL_NAME, B_FALSE,
432854d692b7SGeorge Wilson 	    B_FALSE },
43296e8a0f56SGeorge Wilson 	{ zfs_ioc_obj_to_path, zfs_secpolicy_config, DATASET_NAME, B_FALSE,
43306e8a0f56SGeorge Wilson 	    B_TRUE },
433154d692b7SGeorge Wilson 	{ zfs_ioc_pool_set_props, zfs_secpolicy_config,	POOL_NAME, B_TRUE,
433254d692b7SGeorge Wilson 	    B_TRUE },
433354d692b7SGeorge Wilson 	{ zfs_ioc_pool_get_props, zfs_secpolicy_read, POOL_NAME, B_FALSE,
433454d692b7SGeorge Wilson 	    B_FALSE },
433554d692b7SGeorge Wilson 	{ zfs_ioc_set_fsacl, zfs_secpolicy_fsacl, DATASET_NAME, B_TRUE,
433654d692b7SGeorge Wilson 	    B_TRUE },
433754d692b7SGeorge Wilson 	{ zfs_ioc_get_fsacl, zfs_secpolicy_read, DATASET_NAME, B_FALSE,
433854d692b7SGeorge Wilson 	    B_FALSE },
433954d692b7SGeorge Wilson 	{ zfs_ioc_share, zfs_secpolicy_share, DATASET_NAME, B_FALSE, B_FALSE },
434054d692b7SGeorge Wilson 	{ zfs_ioc_inherit_prop, zfs_secpolicy_inherit, DATASET_NAME, B_TRUE,
434154d692b7SGeorge Wilson 	    B_TRUE },
434254d692b7SGeorge Wilson 	{ zfs_ioc_smb_acl, zfs_secpolicy_smb_acl, DATASET_NAME, B_FALSE,
434314843421SMatthew Ahrens 	    B_FALSE },
434414843421SMatthew Ahrens 	{ zfs_ioc_userspace_one, zfs_secpolicy_userspace_one,
434514843421SMatthew Ahrens 	    DATASET_NAME, B_FALSE, B_FALSE },
434614843421SMatthew Ahrens 	{ zfs_ioc_userspace_many, zfs_secpolicy_userspace_many,
434714843421SMatthew Ahrens 	    DATASET_NAME, B_FALSE, B_FALSE },
434814843421SMatthew Ahrens 	{ zfs_ioc_userspace_upgrade, zfs_secpolicy_userspace_upgrade,
434914843421SMatthew Ahrens 	    DATASET_NAME, B_FALSE, B_TRUE },
4350842727c2SChris Kirby 	{ zfs_ioc_hold, zfs_secpolicy_hold, DATASET_NAME, B_TRUE, B_TRUE },
4351842727c2SChris Kirby 	{ zfs_ioc_release, zfs_secpolicy_release, DATASET_NAME, B_TRUE,
4352842727c2SChris Kirby 	    B_TRUE },
4353842727c2SChris Kirby 	{ zfs_ioc_get_holds, zfs_secpolicy_read, DATASET_NAME, B_FALSE,
435492241e0bSTom Erickson 	    B_TRUE },
435592241e0bSTom Erickson 	{ zfs_ioc_objset_recvd_props, zfs_secpolicy_read, DATASET_NAME, B_FALSE,
43561195e687SMark J Musante 	    B_FALSE },
43571195e687SMark J Musante 	{ zfs_ioc_vdev_split, zfs_secpolicy_config, POOL_NAME, B_TRUE,
43581195e687SMark J Musante 	    B_TRUE }
4359fa9e4066Sahrens };
4360fa9e4066Sahrens 
436154d692b7SGeorge Wilson int
436254d692b7SGeorge Wilson pool_status_check(const char *name, zfs_ioc_namecheck_t type)
436354d692b7SGeorge Wilson {
436454d692b7SGeorge Wilson 	spa_t *spa;
436554d692b7SGeorge Wilson 	int error;
436654d692b7SGeorge Wilson 
436754d692b7SGeorge Wilson 	ASSERT(type == POOL_NAME || type == DATASET_NAME);
436854d692b7SGeorge Wilson 
436914843421SMatthew Ahrens 	error = spa_open(name, &spa, FTAG);
437054d692b7SGeorge Wilson 	if (error == 0) {
437154d692b7SGeorge Wilson 		if (spa_suspended(spa))
437254d692b7SGeorge Wilson 			error = EAGAIN;
437354d692b7SGeorge Wilson 		spa_close(spa, FTAG);
437454d692b7SGeorge Wilson 	}
437554d692b7SGeorge Wilson 	return (error);
437654d692b7SGeorge Wilson }
437754d692b7SGeorge Wilson 
4378*c99e4bdcSChris Kirby /*
4379*c99e4bdcSChris Kirby  * Find a free minor number.
4380*c99e4bdcSChris Kirby  */
4381*c99e4bdcSChris Kirby minor_t
4382*c99e4bdcSChris Kirby zfsdev_minor_alloc(void)
4383*c99e4bdcSChris Kirby {
4384*c99e4bdcSChris Kirby 	static minor_t last_minor;
4385*c99e4bdcSChris Kirby 	minor_t m;
4386*c99e4bdcSChris Kirby 
4387*c99e4bdcSChris Kirby 	ASSERT(MUTEX_HELD(&zfsdev_state_lock));
4388*c99e4bdcSChris Kirby 
4389*c99e4bdcSChris Kirby 	for (m = last_minor + 1; m != last_minor; m++) {
4390*c99e4bdcSChris Kirby 		if (m > ZFSDEV_MAX_MINOR)
4391*c99e4bdcSChris Kirby 			m = 1;
4392*c99e4bdcSChris Kirby 		if (ddi_get_soft_state(zfsdev_state, m) == NULL) {
4393*c99e4bdcSChris Kirby 			last_minor = m;
4394*c99e4bdcSChris Kirby 			return (m);
4395*c99e4bdcSChris Kirby 		}
4396*c99e4bdcSChris Kirby 	}
4397*c99e4bdcSChris Kirby 
4398*c99e4bdcSChris Kirby 	return (0);
4399*c99e4bdcSChris Kirby }
4400*c99e4bdcSChris Kirby 
4401*c99e4bdcSChris Kirby static int
4402*c99e4bdcSChris Kirby zfs_ctldev_init(dev_t *devp)
4403*c99e4bdcSChris Kirby {
4404*c99e4bdcSChris Kirby 	minor_t minor;
4405*c99e4bdcSChris Kirby 	zfs_soft_state_t *zs;
4406*c99e4bdcSChris Kirby 
4407*c99e4bdcSChris Kirby 	ASSERT(MUTEX_HELD(&zfsdev_state_lock));
4408*c99e4bdcSChris Kirby 	ASSERT(getminor(*devp) == 0);
4409*c99e4bdcSChris Kirby 
4410*c99e4bdcSChris Kirby 	minor = zfsdev_minor_alloc();
4411*c99e4bdcSChris Kirby 	if (minor == 0)
4412*c99e4bdcSChris Kirby 		return (ENXIO);
4413*c99e4bdcSChris Kirby 
4414*c99e4bdcSChris Kirby 	if (ddi_soft_state_zalloc(zfsdev_state, minor) != DDI_SUCCESS)
4415*c99e4bdcSChris Kirby 		return (EAGAIN);
4416*c99e4bdcSChris Kirby 
4417*c99e4bdcSChris Kirby 	*devp = makedevice(getemajor(*devp), minor);
4418*c99e4bdcSChris Kirby 
4419*c99e4bdcSChris Kirby 	zs = ddi_get_soft_state(zfsdev_state, minor);
4420*c99e4bdcSChris Kirby 	zs->zss_type = ZSST_CTLDEV;
4421*c99e4bdcSChris Kirby 	zfs_onexit_init((zfs_onexit_t **)&zs->zss_data);
4422*c99e4bdcSChris Kirby 
4423*c99e4bdcSChris Kirby 	return (0);
4424*c99e4bdcSChris Kirby }
4425*c99e4bdcSChris Kirby 
4426*c99e4bdcSChris Kirby static void
4427*c99e4bdcSChris Kirby zfs_ctldev_destroy(zfs_onexit_t *zo, minor_t minor)
4428*c99e4bdcSChris Kirby {
4429*c99e4bdcSChris Kirby 	ASSERT(MUTEX_HELD(&zfsdev_state_lock));
4430*c99e4bdcSChris Kirby 
4431*c99e4bdcSChris Kirby 	zfs_onexit_destroy(zo);
4432*c99e4bdcSChris Kirby 	ddi_soft_state_free(zfsdev_state, minor);
4433*c99e4bdcSChris Kirby }
4434*c99e4bdcSChris Kirby 
4435*c99e4bdcSChris Kirby void *
4436*c99e4bdcSChris Kirby zfsdev_get_soft_state(minor_t minor, enum zfs_soft_state_type which)
4437*c99e4bdcSChris Kirby {
4438*c99e4bdcSChris Kirby 	zfs_soft_state_t *zp;
4439*c99e4bdcSChris Kirby 
4440*c99e4bdcSChris Kirby 	zp = ddi_get_soft_state(zfsdev_state, minor);
4441*c99e4bdcSChris Kirby 	if (zp == NULL || zp->zss_type != which)
4442*c99e4bdcSChris Kirby 		return (NULL);
4443*c99e4bdcSChris Kirby 
4444*c99e4bdcSChris Kirby 	return (zp->zss_data);
4445*c99e4bdcSChris Kirby }
4446*c99e4bdcSChris Kirby 
4447*c99e4bdcSChris Kirby static int
4448*c99e4bdcSChris Kirby zfsdev_open(dev_t *devp, int flag, int otyp, cred_t *cr)
4449*c99e4bdcSChris Kirby {
4450*c99e4bdcSChris Kirby 	int error = 0;
4451*c99e4bdcSChris Kirby 
4452*c99e4bdcSChris Kirby 	if (getminor(*devp) != 0)
4453*c99e4bdcSChris Kirby 		return (zvol_open(devp, flag, otyp, cr));
4454*c99e4bdcSChris Kirby 
4455*c99e4bdcSChris Kirby 	/* This is the control device. Allocate a new minor if requested. */
4456*c99e4bdcSChris Kirby 	if (flag & FEXCL) {
4457*c99e4bdcSChris Kirby 		mutex_enter(&zfsdev_state_lock);
4458*c99e4bdcSChris Kirby 		error = zfs_ctldev_init(devp);
4459*c99e4bdcSChris Kirby 		mutex_exit(&zfsdev_state_lock);
4460*c99e4bdcSChris Kirby 	}
4461*c99e4bdcSChris Kirby 
4462*c99e4bdcSChris Kirby 	return (error);
4463*c99e4bdcSChris Kirby }
4464*c99e4bdcSChris Kirby 
4465*c99e4bdcSChris Kirby static int
4466*c99e4bdcSChris Kirby zfsdev_close(dev_t dev, int flag, int otyp, cred_t *cr)
4467*c99e4bdcSChris Kirby {
4468*c99e4bdcSChris Kirby 	zfs_onexit_t *zo;
4469*c99e4bdcSChris Kirby 	minor_t minor = getminor(dev);
4470*c99e4bdcSChris Kirby 
4471*c99e4bdcSChris Kirby 	if (minor == 0)
4472*c99e4bdcSChris Kirby 		return (0);
4473*c99e4bdcSChris Kirby 
4474*c99e4bdcSChris Kirby 	mutex_enter(&zfsdev_state_lock);
4475*c99e4bdcSChris Kirby 	zo = zfsdev_get_soft_state(minor, ZSST_CTLDEV);
4476*c99e4bdcSChris Kirby 	if (zo == NULL) {
4477*c99e4bdcSChris Kirby 		mutex_exit(&zfsdev_state_lock);
4478*c99e4bdcSChris Kirby 		return (zvol_close(dev, flag, otyp, cr));
4479*c99e4bdcSChris Kirby 	}
4480*c99e4bdcSChris Kirby 	zfs_ctldev_destroy(zo, minor);
4481*c99e4bdcSChris Kirby 	mutex_exit(&zfsdev_state_lock);
4482*c99e4bdcSChris Kirby 
4483*c99e4bdcSChris Kirby 	return (0);
4484*c99e4bdcSChris Kirby }
4485*c99e4bdcSChris Kirby 
4486fa9e4066Sahrens static int
4487fa9e4066Sahrens zfsdev_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *cr, int *rvalp)
4488fa9e4066Sahrens {
4489fa9e4066Sahrens 	zfs_cmd_t *zc;
4490fa9e4066Sahrens 	uint_t vec;
44911d452cf5Sahrens 	int error, rc;
4492*c99e4bdcSChris Kirby 	minor_t minor = getminor(dev);
4493fa9e4066Sahrens 
4494*c99e4bdcSChris Kirby 	if (minor != 0 &&
4495*c99e4bdcSChris Kirby 	    zfsdev_get_soft_state(minor, ZSST_CTLDEV) == NULL)
4496fa9e4066Sahrens 		return (zvol_ioctl(dev, cmd, arg, flag, cr, rvalp));
4497fa9e4066Sahrens 
4498fa9e4066Sahrens 	vec = cmd - ZFS_IOC;
449991ebeef5Sahrens 	ASSERT3U(getmajor(dev), ==, ddi_driver_major(zfs_dip));
4500fa9e4066Sahrens 
4501fa9e4066Sahrens 	if (vec >= sizeof (zfs_ioc_vec) / sizeof (zfs_ioc_vec[0]))
4502fa9e4066Sahrens 		return (EINVAL);
4503fa9e4066Sahrens 
4504fa9e4066Sahrens 	zc = kmem_zalloc(sizeof (zfs_cmd_t), KM_SLEEP);
4505fa9e4066Sahrens 
4506478ed9adSEric Taylor 	error = ddi_copyin((void *)arg, zc, sizeof (zfs_cmd_t), flag);
45076e27f868SSam Falkner 	if (error != 0)
45086e27f868SSam Falkner 		error = EFAULT;
4509fa9e4066Sahrens 
4510681d9761SEric Taylor 	if ((error == 0) && !(flag & FKIOCTL))
4511ecd6cf80Smarks 		error = zfs_ioc_vec[vec].zvec_secpolicy(zc, cr);
4512fa9e4066Sahrens 
4513fa9e4066Sahrens 	/*
4514fa9e4066Sahrens 	 * Ensure that all pool/dataset names are valid before we pass down to
4515fa9e4066Sahrens 	 * the lower layers.
4516fa9e4066Sahrens 	 */
4517fa9e4066Sahrens 	if (error == 0) {
4518fa9e4066Sahrens 		zc->zc_name[sizeof (zc->zc_name) - 1] = '\0';
4519478ed9adSEric Taylor 		zc->zc_iflags = flag & FKIOCTL;
4520fa9e4066Sahrens 		switch (zfs_ioc_vec[vec].zvec_namecheck) {
4521e7437265Sahrens 		case POOL_NAME:
4522fa9e4066Sahrens 			if (pool_namecheck(zc->zc_name, NULL, NULL) != 0)
4523fa9e4066Sahrens 				error = EINVAL;
452454d692b7SGeorge Wilson 			if (zfs_ioc_vec[vec].zvec_pool_check)
452554d692b7SGeorge Wilson 				error = pool_status_check(zc->zc_name,
452654d692b7SGeorge Wilson 				    zfs_ioc_vec[vec].zvec_namecheck);
4527fa9e4066Sahrens 			break;
4528fa9e4066Sahrens 
4529e7437265Sahrens 		case DATASET_NAME:
4530fa9e4066Sahrens 			if (dataset_namecheck(zc->zc_name, NULL, NULL) != 0)
4531fa9e4066Sahrens 				error = EINVAL;
453254d692b7SGeorge Wilson 			if (zfs_ioc_vec[vec].zvec_pool_check)
453354d692b7SGeorge Wilson 				error = pool_status_check(zc->zc_name,
453454d692b7SGeorge Wilson 				    zfs_ioc_vec[vec].zvec_namecheck);
4535fa9e4066Sahrens 			break;
45365ad82045Snd 
4537e7437265Sahrens 		case NO_NAME:
45385ad82045Snd 			break;
4539fa9e4066Sahrens 		}
4540fa9e4066Sahrens 	}
4541fa9e4066Sahrens 
4542fa9e4066Sahrens 	if (error == 0)
4543fa9e4066Sahrens 		error = zfs_ioc_vec[vec].zvec_func(zc);
4544fa9e4066Sahrens 
4545478ed9adSEric Taylor 	rc = ddi_copyout(zc, (void *)arg, sizeof (zfs_cmd_t), flag);
4546ecd6cf80Smarks 	if (error == 0) {
45476e27f868SSam Falkner 		if (rc != 0)
45486e27f868SSam Falkner 			error = EFAULT;
454914843421SMatthew Ahrens 		if (zfs_ioc_vec[vec].zvec_his_log)
4550ecd6cf80Smarks 			zfs_log_history(zc);
4551ecd6cf80Smarks 	}
4552fa9e4066Sahrens 
4553fa9e4066Sahrens 	kmem_free(zc, sizeof (zfs_cmd_t));
4554fa9e4066Sahrens 	return (error);
4555fa9e4066Sahrens }
4556fa9e4066Sahrens 
4557fa9e4066Sahrens static int
4558fa9e4066Sahrens zfs_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
4559fa9e4066Sahrens {
4560fa9e4066Sahrens 	if (cmd != DDI_ATTACH)
4561fa9e4066Sahrens 		return (DDI_FAILURE);
4562fa9e4066Sahrens 
4563fa9e4066Sahrens 	if (ddi_create_minor_node(dip, "zfs", S_IFCHR, 0,
4564fa9e4066Sahrens 	    DDI_PSEUDO, 0) == DDI_FAILURE)
4565fa9e4066Sahrens 		return (DDI_FAILURE);
4566fa9e4066Sahrens 
4567fa9e4066Sahrens 	zfs_dip = dip;
4568fa9e4066Sahrens 
4569fa9e4066Sahrens 	ddi_report_dev(dip);
4570fa9e4066Sahrens 
4571fa9e4066Sahrens 	return (DDI_SUCCESS);
4572fa9e4066Sahrens }
4573fa9e4066Sahrens 
4574fa9e4066Sahrens static int
4575fa9e4066Sahrens zfs_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
4576fa9e4066Sahrens {
4577fa9e4066Sahrens 	if (spa_busy() || zfs_busy() || zvol_busy())
4578fa9e4066Sahrens 		return (DDI_FAILURE);
4579fa9e4066Sahrens 
4580fa9e4066Sahrens 	if (cmd != DDI_DETACH)
4581fa9e4066Sahrens 		return (DDI_FAILURE);
4582fa9e4066Sahrens 
4583fa9e4066Sahrens 	zfs_dip = NULL;
4584fa9e4066Sahrens 
4585fa9e4066Sahrens 	ddi_prop_remove_all(dip);
4586fa9e4066Sahrens 	ddi_remove_minor_node(dip, NULL);
4587fa9e4066Sahrens 
4588fa9e4066Sahrens 	return (DDI_SUCCESS);
4589fa9e4066Sahrens }
4590fa9e4066Sahrens 
4591fa9e4066Sahrens /*ARGSUSED*/
4592fa9e4066Sahrens static int
4593fa9e4066Sahrens zfs_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
4594fa9e4066Sahrens {
4595fa9e4066Sahrens 	switch (infocmd) {
4596fa9e4066Sahrens 	case DDI_INFO_DEVT2DEVINFO:
4597fa9e4066Sahrens 		*result = zfs_dip;
4598fa9e4066Sahrens 		return (DDI_SUCCESS);
4599fa9e4066Sahrens 
4600fa9e4066Sahrens 	case DDI_INFO_DEVT2INSTANCE:
4601a0965f35Sbonwick 		*result = (void *)0;
4602fa9e4066Sahrens 		return (DDI_SUCCESS);
4603fa9e4066Sahrens 	}
4604fa9e4066Sahrens 
4605fa9e4066Sahrens 	return (DDI_FAILURE);
4606fa9e4066Sahrens }
4607fa9e4066Sahrens 
4608fa9e4066Sahrens /*
4609fa9e4066Sahrens  * OK, so this is a little weird.
4610fa9e4066Sahrens  *
4611fa9e4066Sahrens  * /dev/zfs is the control node, i.e. minor 0.
4612fa9e4066Sahrens  * /dev/zvol/[r]dsk/pool/dataset are the zvols, minor > 0.
4613fa9e4066Sahrens  *
4614fa9e4066Sahrens  * /dev/zfs has basically nothing to do except serve up ioctls,
4615fa9e4066Sahrens  * so most of the standard driver entry points are in zvol.c.
4616fa9e4066Sahrens  */
4617fa9e4066Sahrens static struct cb_ops zfs_cb_ops = {
4618*c99e4bdcSChris Kirby 	zfsdev_open,	/* open */
4619*c99e4bdcSChris Kirby 	zfsdev_close,	/* close */
4620fa9e4066Sahrens 	zvol_strategy,	/* strategy */
4621fa9e4066Sahrens 	nodev,		/* print */
4622e7cbe64fSgw 	zvol_dump,	/* dump */
4623fa9e4066Sahrens 	zvol_read,	/* read */
4624fa9e4066Sahrens 	zvol_write,	/* write */
4625fa9e4066Sahrens 	zfsdev_ioctl,	/* ioctl */
4626fa9e4066Sahrens 	nodev,		/* devmap */
4627fa9e4066Sahrens 	nodev,		/* mmap */
4628fa9e4066Sahrens 	nodev,		/* segmap */
4629fa9e4066Sahrens 	nochpoll,	/* poll */
4630fa9e4066Sahrens 	ddi_prop_op,	/* prop_op */
4631fa9e4066Sahrens 	NULL,		/* streamtab */
4632fa9e4066Sahrens 	D_NEW | D_MP | D_64BIT,		/* Driver compatibility flag */
4633fa9e4066Sahrens 	CB_REV,		/* version */
4634feb08c6bSbillm 	nodev,		/* async read */
4635feb08c6bSbillm 	nodev,		/* async write */
4636fa9e4066Sahrens };
4637fa9e4066Sahrens 
4638fa9e4066Sahrens static struct dev_ops zfs_dev_ops = {
4639fa9e4066Sahrens 	DEVO_REV,	/* version */
4640fa9e4066Sahrens 	0,		/* refcnt */
4641fa9e4066Sahrens 	zfs_info,	/* info */
4642fa9e4066Sahrens 	nulldev,	/* identify */
4643fa9e4066Sahrens 	nulldev,	/* probe */
4644fa9e4066Sahrens 	zfs_attach,	/* attach */
4645fa9e4066Sahrens 	zfs_detach,	/* detach */
4646fa9e4066Sahrens 	nodev,		/* reset */
4647fa9e4066Sahrens 	&zfs_cb_ops,	/* driver operations */
464819397407SSherry Moore 	NULL,		/* no bus operations */
464919397407SSherry Moore 	NULL,		/* power */
465019397407SSherry Moore 	ddi_quiesce_not_needed,	/* quiesce */
4651fa9e4066Sahrens };
4652fa9e4066Sahrens 
4653fa9e4066Sahrens static struct modldrv zfs_modldrv = {
465419397407SSherry Moore 	&mod_driverops,
465519397407SSherry Moore 	"ZFS storage pool",
465619397407SSherry Moore 	&zfs_dev_ops
4657fa9e4066Sahrens };
4658fa9e4066Sahrens 
4659fa9e4066Sahrens static struct modlinkage modlinkage = {
4660fa9e4066Sahrens 	MODREV_1,
4661fa9e4066Sahrens 	(void *)&zfs_modlfs,
4662fa9e4066Sahrens 	(void *)&zfs_modldrv,
4663fa9e4066Sahrens 	NULL
4664fa9e4066Sahrens };
4665fa9e4066Sahrens 
4666ec533521Sfr 
4667ec533521Sfr uint_t zfs_fsyncer_key;
4668f18faf3fSek extern uint_t rrw_tsd_key;
4669ec533521Sfr 
4670fa9e4066Sahrens int
4671fa9e4066Sahrens _init(void)
4672fa9e4066Sahrens {
4673fa9e4066Sahrens 	int error;
4674fa9e4066Sahrens 
4675a0965f35Sbonwick 	spa_init(FREAD | FWRITE);
4676a0965f35Sbonwick 	zfs_init();
4677a0965f35Sbonwick 	zvol_init();
4678a0965f35Sbonwick 
4679a0965f35Sbonwick 	if ((error = mod_install(&modlinkage)) != 0) {
4680a0965f35Sbonwick 		zvol_fini();
4681a0965f35Sbonwick 		zfs_fini();
4682a0965f35Sbonwick 		spa_fini();
4683fa9e4066Sahrens 		return (error);
4684a0965f35Sbonwick 	}
4685fa9e4066Sahrens 
4686ec533521Sfr 	tsd_create(&zfs_fsyncer_key, NULL);
4687f18faf3fSek 	tsd_create(&rrw_tsd_key, NULL);
4688ec533521Sfr 
4689fa9e4066Sahrens 	error = ldi_ident_from_mod(&modlinkage, &zfs_li);
4690fa9e4066Sahrens 	ASSERT(error == 0);
4691ecd6cf80Smarks 	mutex_init(&zfs_share_lock, NULL, MUTEX_DEFAULT, NULL);
4692fa9e4066Sahrens 
4693fa9e4066Sahrens 	return (0);
4694fa9e4066Sahrens }
4695fa9e4066Sahrens 
4696fa9e4066Sahrens int
4697fa9e4066Sahrens _fini(void)
4698fa9e4066Sahrens {
4699fa9e4066Sahrens 	int error;
4700fa9e4066Sahrens 
4701ea8dc4b6Seschrock 	if (spa_busy() || zfs_busy() || zvol_busy() || zio_injection_enabled)
4702fa9e4066Sahrens 		return (EBUSY);
4703fa9e4066Sahrens 
4704fa9e4066Sahrens 	if ((error = mod_remove(&modlinkage)) != 0)
4705fa9e4066Sahrens 		return (error);
4706fa9e4066Sahrens 
4707fa9e4066Sahrens 	zvol_fini();
4708fa9e4066Sahrens 	zfs_fini();
4709fa9e4066Sahrens 	spa_fini();
4710da6c28aaSamw 	if (zfs_nfsshare_inited)
4711ecd6cf80Smarks 		(void) ddi_modclose(nfs_mod);
4712da6c28aaSamw 	if (zfs_smbshare_inited)
4713da6c28aaSamw 		(void) ddi_modclose(smbsrv_mod);
4714da6c28aaSamw 	if (zfs_nfsshare_inited || zfs_smbshare_inited)
4715ecd6cf80Smarks 		(void) ddi_modclose(sharefs_mod);
4716fa9e4066Sahrens 
4717ec533521Sfr 	tsd_destroy(&zfs_fsyncer_key);
4718fa9e4066Sahrens 	ldi_ident_release(zfs_li);
4719fa9e4066Sahrens 	zfs_li = NULL;
4720ecd6cf80Smarks 	mutex_destroy(&zfs_share_lock);
4721fa9e4066Sahrens 
4722fa9e4066Sahrens 	return (error);
4723fa9e4066Sahrens }
4724fa9e4066Sahrens 
4725fa9e4066Sahrens int
4726fa9e4066Sahrens _info(struct modinfo *modinfop)
4727fa9e4066Sahrens {
4728fa9e4066Sahrens 	return (mod_info(&modlinkage, modinfop));
4729fa9e4066Sahrens }
4730