xref: /illumos-gate/usr/src/uts/common/fs/zfs/zfs_ioctl.c (revision a7f53a5629374ca27c5696ace9a1946c2ca050f4)
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>
63c99e4bdcSChris 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
285*a7f53a56SChris Kirby zfs_dozonecheck_impl(const char *dataset, uint64_t zoned, cred_t *cr)
286fa9e4066Sahrens {
287fa9e4066Sahrens 	int writable = 1;
288fa9e4066Sahrens 
289fa9e4066Sahrens 	/*
290fa9e4066Sahrens 	 * The dataset must be visible by this zone -- check this first
291fa9e4066Sahrens 	 * so they don't see EPERM on something they shouldn't know about.
292fa9e4066Sahrens 	 */
293fa9e4066Sahrens 	if (!INGLOBALZONE(curproc) &&
294fa9e4066Sahrens 	    !zone_dataset_visible(dataset, &writable))
295fa9e4066Sahrens 		return (ENOENT);
296fa9e4066Sahrens 
297fa9e4066Sahrens 	if (INGLOBALZONE(curproc)) {
298fa9e4066Sahrens 		/*
299fa9e4066Sahrens 		 * If the fs is zoned, only root can access it from the
300fa9e4066Sahrens 		 * global zone.
301fa9e4066Sahrens 		 */
302fa9e4066Sahrens 		if (secpolicy_zfs(cr) && zoned)
303fa9e4066Sahrens 			return (EPERM);
304fa9e4066Sahrens 	} else {
305fa9e4066Sahrens 		/*
306fa9e4066Sahrens 		 * If we are in a local zone, the 'zoned' property must be set.
307fa9e4066Sahrens 		 */
308fa9e4066Sahrens 		if (!zoned)
309fa9e4066Sahrens 			return (EPERM);
310fa9e4066Sahrens 
311fa9e4066Sahrens 		/* must be writable by this zone */
312fa9e4066Sahrens 		if (!writable)
313fa9e4066Sahrens 			return (EPERM);
314fa9e4066Sahrens 	}
315fa9e4066Sahrens 	return (0);
316fa9e4066Sahrens }
317fa9e4066Sahrens 
318*a7f53a56SChris Kirby static int
319*a7f53a56SChris Kirby zfs_dozonecheck(const char *dataset, cred_t *cr)
320*a7f53a56SChris Kirby {
321*a7f53a56SChris Kirby 	uint64_t zoned;
322*a7f53a56SChris Kirby 
323*a7f53a56SChris Kirby 	if (dsl_prop_get_integer(dataset, "zoned", &zoned, NULL))
324*a7f53a56SChris Kirby 		return (ENOENT);
325*a7f53a56SChris Kirby 
326*a7f53a56SChris Kirby 	return (zfs_dozonecheck_impl(dataset, zoned, cr));
327*a7f53a56SChris Kirby }
328*a7f53a56SChris Kirby 
329*a7f53a56SChris Kirby static int
330*a7f53a56SChris Kirby zfs_dozonecheck_ds(const char *dataset, dsl_dataset_t *ds, cred_t *cr)
331*a7f53a56SChris Kirby {
332*a7f53a56SChris Kirby 	uint64_t zoned;
333*a7f53a56SChris Kirby 
334*a7f53a56SChris Kirby 	rw_enter(&ds->ds_dir->dd_pool->dp_config_rwlock, RW_READER);
335*a7f53a56SChris Kirby 	if (dsl_prop_get_ds(ds, "zoned", 8, 1, &zoned, NULL)) {
336*a7f53a56SChris Kirby 		rw_exit(&ds->ds_dir->dd_pool->dp_config_rwlock);
337*a7f53a56SChris Kirby 		return (ENOENT);
338*a7f53a56SChris Kirby 	}
339*a7f53a56SChris Kirby 	rw_exit(&ds->ds_dir->dd_pool->dp_config_rwlock);
340*a7f53a56SChris Kirby 
341*a7f53a56SChris Kirby 	return (zfs_dozonecheck_impl(dataset, zoned, cr));
342*a7f53a56SChris Kirby }
343*a7f53a56SChris Kirby 
344fa9e4066Sahrens int
345ecd6cf80Smarks zfs_secpolicy_write_perms(const char *name, const char *perm, cred_t *cr)
346fa9e4066Sahrens {
347fa9e4066Sahrens 	int error;
348fa9e4066Sahrens 
349ecd6cf80Smarks 	error = zfs_dozonecheck(name, cr);
350ecd6cf80Smarks 	if (error == 0) {
351ecd6cf80Smarks 		error = secpolicy_zfs(cr);
352db870a07Sahrens 		if (error)
353ecd6cf80Smarks 			error = dsl_deleg_access(name, perm, cr);
354ecd6cf80Smarks 	}
355ecd6cf80Smarks 	return (error);
356ecd6cf80Smarks }
357ecd6cf80Smarks 
358*a7f53a56SChris Kirby int
359*a7f53a56SChris Kirby zfs_secpolicy_write_perms_ds(const char *name, dsl_dataset_t *ds,
360*a7f53a56SChris Kirby     const char *perm, cred_t *cr)
361*a7f53a56SChris Kirby {
362*a7f53a56SChris Kirby 	int error;
363*a7f53a56SChris Kirby 
364*a7f53a56SChris Kirby 	error = zfs_dozonecheck_ds(name, ds, cr);
365*a7f53a56SChris Kirby 	if (error == 0) {
366*a7f53a56SChris Kirby 		error = secpolicy_zfs(cr);
367*a7f53a56SChris Kirby 		if (error)
368*a7f53a56SChris Kirby 			error = dsl_deleg_access_impl(ds, perm, cr);
369*a7f53a56SChris Kirby 	}
370*a7f53a56SChris Kirby 	return (error);
371*a7f53a56SChris Kirby }
372*a7f53a56SChris Kirby 
3734201a95eSRic Aleshire /*
3744201a95eSRic Aleshire  * Policy for setting the security label property.
3754201a95eSRic Aleshire  *
3764201a95eSRic Aleshire  * Returns 0 for success, non-zero for access and other errors.
3774201a95eSRic Aleshire  */
3784201a95eSRic Aleshire static int
37992241e0bSTom Erickson zfs_set_slabel_policy(const char *name, char *strval, cred_t *cr)
3804201a95eSRic Aleshire {
3814201a95eSRic Aleshire 	char		ds_hexsl[MAXNAMELEN];
3824201a95eSRic Aleshire 	bslabel_t	ds_sl, new_sl;
3834201a95eSRic Aleshire 	boolean_t	new_default = FALSE;
3844201a95eSRic Aleshire 	uint64_t	zoned;
3854201a95eSRic Aleshire 	int		needed_priv = -1;
3864201a95eSRic Aleshire 	int		error;
3874201a95eSRic Aleshire 
3884201a95eSRic Aleshire 	/* First get the existing dataset label. */
3894201a95eSRic Aleshire 	error = dsl_prop_get(name, zfs_prop_to_name(ZFS_PROP_MLSLABEL),
3904201a95eSRic Aleshire 	    1, sizeof (ds_hexsl), &ds_hexsl, NULL);
3914201a95eSRic Aleshire 	if (error)
3924201a95eSRic Aleshire 		return (EPERM);
3934201a95eSRic Aleshire 
3944201a95eSRic Aleshire 	if (strcasecmp(strval, ZFS_MLSLABEL_DEFAULT) == 0)
3954201a95eSRic Aleshire 		new_default = TRUE;
3964201a95eSRic Aleshire 
3974201a95eSRic Aleshire 	/* The label must be translatable */
3984201a95eSRic Aleshire 	if (!new_default && (hexstr_to_label(strval, &new_sl) != 0))
3994201a95eSRic Aleshire 		return (EINVAL);
4004201a95eSRic Aleshire 
4014201a95eSRic Aleshire 	/*
4024201a95eSRic Aleshire 	 * In a non-global zone, disallow attempts to set a label that
4034201a95eSRic Aleshire 	 * doesn't match that of the zone; otherwise no other checks
4044201a95eSRic Aleshire 	 * are needed.
4054201a95eSRic Aleshire 	 */
4064201a95eSRic Aleshire 	if (!INGLOBALZONE(curproc)) {
4074201a95eSRic Aleshire 		if (new_default || !blequal(&new_sl, CR_SL(CRED())))
4084201a95eSRic Aleshire 			return (EPERM);
4094201a95eSRic Aleshire 		return (0);
4104201a95eSRic Aleshire 	}
4114201a95eSRic Aleshire 
4124201a95eSRic Aleshire 	/*
4134201a95eSRic Aleshire 	 * For global-zone datasets (i.e., those whose zoned property is
4144201a95eSRic Aleshire 	 * "off", verify that the specified new label is valid for the
4154201a95eSRic Aleshire 	 * global zone.
4164201a95eSRic Aleshire 	 */
4174201a95eSRic Aleshire 	if (dsl_prop_get_integer(name,
4184201a95eSRic Aleshire 	    zfs_prop_to_name(ZFS_PROP_ZONED), &zoned, NULL))
4194201a95eSRic Aleshire 		return (EPERM);
4204201a95eSRic Aleshire 	if (!zoned) {
4214201a95eSRic Aleshire 		if (zfs_check_global_label(name, strval) != 0)
4224201a95eSRic Aleshire 			return (EPERM);
4234201a95eSRic Aleshire 	}
4244201a95eSRic Aleshire 
4254201a95eSRic Aleshire 	/*
4264201a95eSRic Aleshire 	 * If the existing dataset label is nondefault, check if the
4274201a95eSRic Aleshire 	 * dataset is mounted (label cannot be changed while mounted).
4284201a95eSRic Aleshire 	 * Get the zfsvfs; if there isn't one, then the dataset isn't
4294201a95eSRic Aleshire 	 * mounted (or isn't a dataset, doesn't exist, ...).
4304201a95eSRic Aleshire 	 */
4314201a95eSRic Aleshire 	if (strcasecmp(ds_hexsl, ZFS_MLSLABEL_DEFAULT) != 0) {
43292241e0bSTom Erickson 		objset_t *os;
43392241e0bSTom Erickson 		static char *setsl_tag = "setsl_tag";
43492241e0bSTom Erickson 
4354201a95eSRic Aleshire 		/*
4364201a95eSRic Aleshire 		 * Try to own the dataset; abort if there is any error,
4374201a95eSRic Aleshire 		 * (e.g., already mounted, in use, or other error).
4384201a95eSRic Aleshire 		 */
4394201a95eSRic Aleshire 		error = dmu_objset_own(name, DMU_OST_ZFS, B_TRUE,
44092241e0bSTom Erickson 		    setsl_tag, &os);
4414201a95eSRic Aleshire 		if (error)
4424201a95eSRic Aleshire 			return (EPERM);
4434201a95eSRic Aleshire 
44492241e0bSTom Erickson 		dmu_objset_disown(os, setsl_tag);
44592241e0bSTom Erickson 
4464201a95eSRic Aleshire 		if (new_default) {
4474201a95eSRic Aleshire 			needed_priv = PRIV_FILE_DOWNGRADE_SL;
4484201a95eSRic Aleshire 			goto out_check;
4494201a95eSRic Aleshire 		}
4504201a95eSRic Aleshire 
4514201a95eSRic Aleshire 		if (hexstr_to_label(strval, &new_sl) != 0)
4524201a95eSRic Aleshire 			return (EPERM);
4534201a95eSRic Aleshire 
4544201a95eSRic Aleshire 		if (blstrictdom(&ds_sl, &new_sl))
4554201a95eSRic Aleshire 			needed_priv = PRIV_FILE_DOWNGRADE_SL;
4564201a95eSRic Aleshire 		else if (blstrictdom(&new_sl, &ds_sl))
4574201a95eSRic Aleshire 			needed_priv = PRIV_FILE_UPGRADE_SL;
4584201a95eSRic Aleshire 	} else {
4594201a95eSRic Aleshire 		/* dataset currently has a default label */
4604201a95eSRic Aleshire 		if (!new_default)
4614201a95eSRic Aleshire 			needed_priv = PRIV_FILE_UPGRADE_SL;
4624201a95eSRic Aleshire 	}
4634201a95eSRic Aleshire 
4644201a95eSRic Aleshire out_check:
4654201a95eSRic Aleshire 	if (needed_priv != -1)
4664201a95eSRic Aleshire 		return (PRIV_POLICY(cr, needed_priv, B_FALSE, EPERM, NULL));
4674201a95eSRic Aleshire 	return (0);
4684201a95eSRic Aleshire }
4694201a95eSRic Aleshire 
470ecd6cf80Smarks static int
47192241e0bSTom Erickson zfs_secpolicy_setprop(const char *dsname, zfs_prop_t prop, nvpair_t *propval,
47292241e0bSTom Erickson     cred_t *cr)
473ecd6cf80Smarks {
47492241e0bSTom Erickson 	char *strval;
47592241e0bSTom Erickson 
476ecd6cf80Smarks 	/*
477ecd6cf80Smarks 	 * Check permissions for special properties.
478ecd6cf80Smarks 	 */
479ecd6cf80Smarks 	switch (prop) {
480ecd6cf80Smarks 	case ZFS_PROP_ZONED:
481ecd6cf80Smarks 		/*
482ecd6cf80Smarks 		 * Disallow setting of 'zoned' from within a local zone.
483ecd6cf80Smarks 		 */
484ecd6cf80Smarks 		if (!INGLOBALZONE(curproc))
485ecd6cf80Smarks 			return (EPERM);
486ecd6cf80Smarks 		break;
487ecd6cf80Smarks 
488ecd6cf80Smarks 	case ZFS_PROP_QUOTA:
489ecd6cf80Smarks 		if (!INGLOBALZONE(curproc)) {
490ecd6cf80Smarks 			uint64_t zoned;
491ecd6cf80Smarks 			char setpoint[MAXNAMELEN];
492ecd6cf80Smarks 			/*
493ecd6cf80Smarks 			 * Unprivileged users are allowed to modify the
494ecd6cf80Smarks 			 * quota on things *under* (ie. contained by)
495ecd6cf80Smarks 			 * the thing they own.
496ecd6cf80Smarks 			 */
49792241e0bSTom Erickson 			if (dsl_prop_get_integer(dsname, "zoned", &zoned,
498ecd6cf80Smarks 			    setpoint))
499ecd6cf80Smarks 				return (EPERM);
50092241e0bSTom Erickson 			if (!zoned || strlen(dsname) <= strlen(setpoint))
501ecd6cf80Smarks 				return (EPERM);
502ecd6cf80Smarks 		}
503db870a07Sahrens 		break;
5044201a95eSRic Aleshire 
5054201a95eSRic Aleshire 	case ZFS_PROP_MLSLABEL:
5064201a95eSRic Aleshire 		if (!is_system_labeled())
5074201a95eSRic Aleshire 			return (EPERM);
50892241e0bSTom Erickson 
50992241e0bSTom Erickson 		if (nvpair_value_string(propval, &strval) == 0) {
51092241e0bSTom Erickson 			int err;
51192241e0bSTom Erickson 
51292241e0bSTom Erickson 			err = zfs_set_slabel_policy(dsname, strval, CRED());
51392241e0bSTom Erickson 			if (err != 0)
51492241e0bSTom Erickson 				return (err);
51592241e0bSTom Erickson 		}
5164201a95eSRic Aleshire 		break;
517ecd6cf80Smarks 	}
518ecd6cf80Smarks 
51992241e0bSTom Erickson 	return (zfs_secpolicy_write_perms(dsname, zfs_prop_to_name(prop), cr));
520ecd6cf80Smarks }
521ecd6cf80Smarks 
522ecd6cf80Smarks int
523ecd6cf80Smarks zfs_secpolicy_fsacl(zfs_cmd_t *zc, cred_t *cr)
524ecd6cf80Smarks {
525ecd6cf80Smarks 	int error;
526ecd6cf80Smarks 
527ecd6cf80Smarks 	error = zfs_dozonecheck(zc->zc_name, cr);
528ecd6cf80Smarks 	if (error)
529fa9e4066Sahrens 		return (error);
530fa9e4066Sahrens 
531ecd6cf80Smarks 	/*
532ecd6cf80Smarks 	 * permission to set permissions will be evaluated later in
533ecd6cf80Smarks 	 * dsl_deleg_can_allow()
534ecd6cf80Smarks 	 */
535ecd6cf80Smarks 	return (0);
536ecd6cf80Smarks }
537ecd6cf80Smarks 
538ecd6cf80Smarks int
539ecd6cf80Smarks zfs_secpolicy_rollback(zfs_cmd_t *zc, cred_t *cr)
540ecd6cf80Smarks {
541681d9761SEric Taylor 	return (zfs_secpolicy_write_perms(zc->zc_name,
542681d9761SEric Taylor 	    ZFS_DELEG_PERM_ROLLBACK, cr));
543ecd6cf80Smarks }
544ecd6cf80Smarks 
545ecd6cf80Smarks int
546ecd6cf80Smarks zfs_secpolicy_send(zfs_cmd_t *zc, cred_t *cr)
547ecd6cf80Smarks {
548*a7f53a56SChris Kirby 	spa_t *spa;
549*a7f53a56SChris Kirby 	dsl_pool_t *dp;
550*a7f53a56SChris Kirby 	dsl_dataset_t *ds;
551*a7f53a56SChris Kirby 	char *cp;
552*a7f53a56SChris Kirby 	int error;
553*a7f53a56SChris Kirby 
554*a7f53a56SChris Kirby 	/*
555*a7f53a56SChris Kirby 	 * Generate the current snapshot name from the given objsetid, then
556*a7f53a56SChris Kirby 	 * use that name for the secpolicy/zone checks.
557*a7f53a56SChris Kirby 	 */
558*a7f53a56SChris Kirby 	cp = strchr(zc->zc_name, '@');
559*a7f53a56SChris Kirby 	if (cp == NULL)
560*a7f53a56SChris Kirby 		return (EINVAL);
561*a7f53a56SChris Kirby 	error = spa_open(zc->zc_name, &spa, FTAG);
562*a7f53a56SChris Kirby 	if (error)
563*a7f53a56SChris Kirby 		return (error);
564*a7f53a56SChris Kirby 
565*a7f53a56SChris Kirby 	dp = spa_get_dsl(spa);
566*a7f53a56SChris Kirby 	rw_enter(&dp->dp_config_rwlock, RW_READER);
567*a7f53a56SChris Kirby 	error = dsl_dataset_hold_obj(dp, zc->zc_sendobj, FTAG, &ds);
568*a7f53a56SChris Kirby 	rw_exit(&dp->dp_config_rwlock);
569*a7f53a56SChris Kirby 	spa_close(spa, FTAG);
570*a7f53a56SChris Kirby 	if (error)
571*a7f53a56SChris Kirby 		return (error);
572*a7f53a56SChris Kirby 
573*a7f53a56SChris Kirby 	dsl_dataset_name(ds, zc->zc_name);
574*a7f53a56SChris Kirby 
575*a7f53a56SChris Kirby 	error = zfs_secpolicy_write_perms_ds(zc->zc_name, ds,
576*a7f53a56SChris Kirby 	    ZFS_DELEG_PERM_SEND, cr);
577*a7f53a56SChris Kirby 	dsl_dataset_rele(ds, FTAG);
578*a7f53a56SChris Kirby 
579*a7f53a56SChris Kirby 	return (error);
580ecd6cf80Smarks }
581ecd6cf80Smarks 
582743a77edSAlan Wright static int
583743a77edSAlan Wright zfs_secpolicy_deleg_share(zfs_cmd_t *zc, cred_t *cr)
584743a77edSAlan Wright {
585743a77edSAlan Wright 	vnode_t *vp;
586743a77edSAlan Wright 	int error;
587743a77edSAlan Wright 
588743a77edSAlan Wright 	if ((error = lookupname(zc->zc_value, UIO_SYSSPACE,
589743a77edSAlan Wright 	    NO_FOLLOW, NULL, &vp)) != 0)
590743a77edSAlan Wright 		return (error);
591743a77edSAlan Wright 
592743a77edSAlan Wright 	/* Now make sure mntpnt and dataset are ZFS */
593743a77edSAlan Wright 
594743a77edSAlan Wright 	if (vp->v_vfsp->vfs_fstype != zfsfstype ||
595743a77edSAlan Wright 	    (strcmp((char *)refstr_value(vp->v_vfsp->vfs_resource),
596743a77edSAlan Wright 	    zc->zc_name) != 0)) {
597743a77edSAlan Wright 		VN_RELE(vp);
598743a77edSAlan Wright 		return (EPERM);
599743a77edSAlan Wright 	}
600743a77edSAlan Wright 
601743a77edSAlan Wright 	VN_RELE(vp);
602743a77edSAlan Wright 	return (dsl_deleg_access(zc->zc_name,
603743a77edSAlan Wright 	    ZFS_DELEG_PERM_SHARE, cr));
604743a77edSAlan Wright }
605743a77edSAlan Wright 
606ecd6cf80Smarks int
607ecd6cf80Smarks zfs_secpolicy_share(zfs_cmd_t *zc, cred_t *cr)
608ecd6cf80Smarks {
609ecd6cf80Smarks 	if (!INGLOBALZONE(curproc))
610ecd6cf80Smarks 		return (EPERM);
611ecd6cf80Smarks 
6123cb34c60Sahrens 	if (secpolicy_nfs(cr) == 0) {
613ecd6cf80Smarks 		return (0);
614ecd6cf80Smarks 	} else {
615743a77edSAlan Wright 		return (zfs_secpolicy_deleg_share(zc, cr));
616743a77edSAlan Wright 	}
617743a77edSAlan Wright }
618ecd6cf80Smarks 
619743a77edSAlan Wright int
620743a77edSAlan Wright zfs_secpolicy_smb_acl(zfs_cmd_t *zc, cred_t *cr)
621743a77edSAlan Wright {
622743a77edSAlan Wright 	if (!INGLOBALZONE(curproc))
623743a77edSAlan Wright 		return (EPERM);
624ecd6cf80Smarks 
625743a77edSAlan Wright 	if (secpolicy_smb(cr) == 0) {
626743a77edSAlan Wright 		return (0);
627743a77edSAlan Wright 	} else {
628743a77edSAlan Wright 		return (zfs_secpolicy_deleg_share(zc, cr));
629ecd6cf80Smarks 	}
630fa9e4066Sahrens }
631fa9e4066Sahrens 
632fa9e4066Sahrens static int
633ecd6cf80Smarks zfs_get_parent(const char *datasetname, char *parent, int parentsize)
634fa9e4066Sahrens {
635fa9e4066Sahrens 	char *cp;
636fa9e4066Sahrens 
637fa9e4066Sahrens 	/*
638fa9e4066Sahrens 	 * Remove the @bla or /bla from the end of the name to get the parent.
639fa9e4066Sahrens 	 */
640ecd6cf80Smarks 	(void) strncpy(parent, datasetname, parentsize);
641ecd6cf80Smarks 	cp = strrchr(parent, '@');
642fa9e4066Sahrens 	if (cp != NULL) {
643fa9e4066Sahrens 		cp[0] = '\0';
644fa9e4066Sahrens 	} else {
645ecd6cf80Smarks 		cp = strrchr(parent, '/');
646fa9e4066Sahrens 		if (cp == NULL)
647fa9e4066Sahrens 			return (ENOENT);
648fa9e4066Sahrens 		cp[0] = '\0';
649ecd6cf80Smarks 	}
650ecd6cf80Smarks 
651ecd6cf80Smarks 	return (0);
652ecd6cf80Smarks }
653ecd6cf80Smarks 
654ecd6cf80Smarks int
655ecd6cf80Smarks zfs_secpolicy_destroy_perms(const char *name, cred_t *cr)
656ecd6cf80Smarks {
657ecd6cf80Smarks 	int error;
658ecd6cf80Smarks 
659ecd6cf80Smarks 	if ((error = zfs_secpolicy_write_perms(name,
660ecd6cf80Smarks 	    ZFS_DELEG_PERM_MOUNT, cr)) != 0)
661ecd6cf80Smarks 		return (error);
662ecd6cf80Smarks 
663ecd6cf80Smarks 	return (zfs_secpolicy_write_perms(name, ZFS_DELEG_PERM_DESTROY, cr));
664ecd6cf80Smarks }
665ecd6cf80Smarks 
666ecd6cf80Smarks static int
667ecd6cf80Smarks zfs_secpolicy_destroy(zfs_cmd_t *zc, cred_t *cr)
668ecd6cf80Smarks {
669ecd6cf80Smarks 	return (zfs_secpolicy_destroy_perms(zc->zc_name, cr));
670ecd6cf80Smarks }
671ecd6cf80Smarks 
672cbf6f6aaSWilliam Gorrell /*
673cbf6f6aaSWilliam Gorrell  * Destroying snapshots with delegated permissions requires
674cbf6f6aaSWilliam Gorrell  * descendent mount and destroy permissions.
675cbf6f6aaSWilliam Gorrell  * Reassemble the full filesystem@snap name so dsl_deleg_access()
676cbf6f6aaSWilliam Gorrell  * can do the correct permission check.
677cbf6f6aaSWilliam Gorrell  *
678cbf6f6aaSWilliam Gorrell  * Since this routine is used when doing a recursive destroy of snapshots
679cbf6f6aaSWilliam Gorrell  * and destroying snapshots requires descendent permissions, a successfull
680cbf6f6aaSWilliam Gorrell  * check of the top level snapshot applies to snapshots of all descendent
681cbf6f6aaSWilliam Gorrell  * datasets as well.
682cbf6f6aaSWilliam Gorrell  */
683cbf6f6aaSWilliam Gorrell static int
684cbf6f6aaSWilliam Gorrell zfs_secpolicy_destroy_snaps(zfs_cmd_t *zc, cred_t *cr)
685cbf6f6aaSWilliam Gorrell {
686cbf6f6aaSWilliam Gorrell 	int error;
687cbf6f6aaSWilliam Gorrell 	char *dsname;
688cbf6f6aaSWilliam Gorrell 
689cbf6f6aaSWilliam Gorrell 	dsname = kmem_asprintf("%s@%s", zc->zc_name, zc->zc_value);
690cbf6f6aaSWilliam Gorrell 
691cbf6f6aaSWilliam Gorrell 	error = zfs_secpolicy_destroy_perms(dsname, cr);
692cbf6f6aaSWilliam Gorrell 
693cbf6f6aaSWilliam Gorrell 	strfree(dsname);
694cbf6f6aaSWilliam Gorrell 	return (error);
695cbf6f6aaSWilliam Gorrell }
696cbf6f6aaSWilliam Gorrell 
697ecd6cf80Smarks int
698ecd6cf80Smarks zfs_secpolicy_rename_perms(const char *from, const char *to, cred_t *cr)
699ecd6cf80Smarks {
70092241e0bSTom Erickson 	char	parentname[MAXNAMELEN];
701ecd6cf80Smarks 	int	error;
702ecd6cf80Smarks 
703ecd6cf80Smarks 	if ((error = zfs_secpolicy_write_perms(from,
704ecd6cf80Smarks 	    ZFS_DELEG_PERM_RENAME, cr)) != 0)
705ecd6cf80Smarks 		return (error);
706ecd6cf80Smarks 
707ecd6cf80Smarks 	if ((error = zfs_secpolicy_write_perms(from,
708ecd6cf80Smarks 	    ZFS_DELEG_PERM_MOUNT, cr)) != 0)
709ecd6cf80Smarks 		return (error);
710ecd6cf80Smarks 
711ecd6cf80Smarks 	if ((error = zfs_get_parent(to, parentname,
712ecd6cf80Smarks 	    sizeof (parentname))) != 0)
713ecd6cf80Smarks 		return (error);
714ecd6cf80Smarks 
715ecd6cf80Smarks 	if ((error = zfs_secpolicy_write_perms(parentname,
716ecd6cf80Smarks 	    ZFS_DELEG_PERM_CREATE, cr)) != 0)
717ecd6cf80Smarks 		return (error);
718ecd6cf80Smarks 
719ecd6cf80Smarks 	if ((error = zfs_secpolicy_write_perms(parentname,
720ecd6cf80Smarks 	    ZFS_DELEG_PERM_MOUNT, cr)) != 0)
721ecd6cf80Smarks 		return (error);
722ecd6cf80Smarks 
723ecd6cf80Smarks 	return (error);
724ecd6cf80Smarks }
725ecd6cf80Smarks 
726ecd6cf80Smarks static int
727ecd6cf80Smarks zfs_secpolicy_rename(zfs_cmd_t *zc, cred_t *cr)
728ecd6cf80Smarks {
729ecd6cf80Smarks 	return (zfs_secpolicy_rename_perms(zc->zc_name, zc->zc_value, cr));
730ecd6cf80Smarks }
731ecd6cf80Smarks 
732ecd6cf80Smarks static int
733ecd6cf80Smarks zfs_secpolicy_promote(zfs_cmd_t *zc, cred_t *cr)
734ecd6cf80Smarks {
73592241e0bSTom Erickson 	char	parentname[MAXNAMELEN];
736ecd6cf80Smarks 	objset_t *clone;
737ecd6cf80Smarks 	int error;
738ecd6cf80Smarks 
739ecd6cf80Smarks 	error = zfs_secpolicy_write_perms(zc->zc_name,
740ecd6cf80Smarks 	    ZFS_DELEG_PERM_PROMOTE, cr);
741ecd6cf80Smarks 	if (error)
742ecd6cf80Smarks 		return (error);
743ecd6cf80Smarks 
744503ad85cSMatthew Ahrens 	error = dmu_objset_hold(zc->zc_name, FTAG, &clone);
745ecd6cf80Smarks 
746ecd6cf80Smarks 	if (error == 0) {
747ecd6cf80Smarks 		dsl_dataset_t *pclone = NULL;
748ecd6cf80Smarks 		dsl_dir_t *dd;
749503ad85cSMatthew Ahrens 		dd = clone->os_dsl_dataset->ds_dir;
750ecd6cf80Smarks 
751ecd6cf80Smarks 		rw_enter(&dd->dd_pool->dp_config_rwlock, RW_READER);
752745cd3c5Smaybee 		error = dsl_dataset_hold_obj(dd->dd_pool,
753745cd3c5Smaybee 		    dd->dd_phys->dd_origin_obj, FTAG, &pclone);
754ecd6cf80Smarks 		rw_exit(&dd->dd_pool->dp_config_rwlock);
755ecd6cf80Smarks 		if (error) {
756503ad85cSMatthew Ahrens 			dmu_objset_rele(clone, FTAG);
757ecd6cf80Smarks 			return (error);
758ecd6cf80Smarks 		}
759ecd6cf80Smarks 
760ecd6cf80Smarks 		error = zfs_secpolicy_write_perms(zc->zc_name,
761ecd6cf80Smarks 		    ZFS_DELEG_PERM_MOUNT, cr);
762ecd6cf80Smarks 
763ecd6cf80Smarks 		dsl_dataset_name(pclone, parentname);
764503ad85cSMatthew Ahrens 		dmu_objset_rele(clone, FTAG);
765745cd3c5Smaybee 		dsl_dataset_rele(pclone, FTAG);
766ecd6cf80Smarks 		if (error == 0)
767ecd6cf80Smarks 			error = zfs_secpolicy_write_perms(parentname,
768ecd6cf80Smarks 			    ZFS_DELEG_PERM_PROMOTE, cr);
769ecd6cf80Smarks 	}
770ecd6cf80Smarks 	return (error);
771ecd6cf80Smarks }
772ecd6cf80Smarks 
773ecd6cf80Smarks static int
774ecd6cf80Smarks zfs_secpolicy_receive(zfs_cmd_t *zc, cred_t *cr)
775ecd6cf80Smarks {
776ecd6cf80Smarks 	int error;
777ecd6cf80Smarks 
778ecd6cf80Smarks 	if ((error = zfs_secpolicy_write_perms(zc->zc_name,
779ecd6cf80Smarks 	    ZFS_DELEG_PERM_RECEIVE, cr)) != 0)
780ecd6cf80Smarks 		return (error);
781ecd6cf80Smarks 
782ecd6cf80Smarks 	if ((error = zfs_secpolicy_write_perms(zc->zc_name,
783ecd6cf80Smarks 	    ZFS_DELEG_PERM_MOUNT, cr)) != 0)
784ecd6cf80Smarks 		return (error);
785ecd6cf80Smarks 
786ecd6cf80Smarks 	return (zfs_secpolicy_write_perms(zc->zc_name,
787ecd6cf80Smarks 	    ZFS_DELEG_PERM_CREATE, cr));
788ecd6cf80Smarks }
789ecd6cf80Smarks 
790ecd6cf80Smarks int
791ecd6cf80Smarks zfs_secpolicy_snapshot_perms(const char *name, cred_t *cr)
792ecd6cf80Smarks {
793681d9761SEric Taylor 	return (zfs_secpolicy_write_perms(name,
794681d9761SEric Taylor 	    ZFS_DELEG_PERM_SNAPSHOT, cr));
795ecd6cf80Smarks }
796ecd6cf80Smarks 
797ecd6cf80Smarks static int
798ecd6cf80Smarks zfs_secpolicy_snapshot(zfs_cmd_t *zc, cred_t *cr)
799ecd6cf80Smarks {
800ecd6cf80Smarks 
801ecd6cf80Smarks 	return (zfs_secpolicy_snapshot_perms(zc->zc_name, cr));
802ecd6cf80Smarks }
803ecd6cf80Smarks 
804ecd6cf80Smarks static int
805ecd6cf80Smarks zfs_secpolicy_create(zfs_cmd_t *zc, cred_t *cr)
806ecd6cf80Smarks {
80792241e0bSTom Erickson 	char	parentname[MAXNAMELEN];
80892241e0bSTom Erickson 	int	error;
809ecd6cf80Smarks 
810ecd6cf80Smarks 	if ((error = zfs_get_parent(zc->zc_name, parentname,
811ecd6cf80Smarks 	    sizeof (parentname))) != 0)
812ecd6cf80Smarks 		return (error);
813fa9e4066Sahrens 
814ecd6cf80Smarks 	if (zc->zc_value[0] != '\0') {
815ecd6cf80Smarks 		if ((error = zfs_secpolicy_write_perms(zc->zc_value,
816ecd6cf80Smarks 		    ZFS_DELEG_PERM_CLONE, cr)) != 0)
817ecd6cf80Smarks 			return (error);
818fa9e4066Sahrens 	}
819fa9e4066Sahrens 
820ecd6cf80Smarks 	if ((error = zfs_secpolicy_write_perms(parentname,
821ecd6cf80Smarks 	    ZFS_DELEG_PERM_CREATE, cr)) != 0)
822ecd6cf80Smarks 		return (error);
823ecd6cf80Smarks 
824ecd6cf80Smarks 	error = zfs_secpolicy_write_perms(parentname,
825ecd6cf80Smarks 	    ZFS_DELEG_PERM_MOUNT, cr);
826ecd6cf80Smarks 
827ecd6cf80Smarks 	return (error);
828ecd6cf80Smarks }
829ecd6cf80Smarks 
830ecd6cf80Smarks static int
831ecd6cf80Smarks zfs_secpolicy_umount(zfs_cmd_t *zc, cred_t *cr)
832ecd6cf80Smarks {
833ecd6cf80Smarks 	int error;
834ecd6cf80Smarks 
835ecd6cf80Smarks 	error = secpolicy_fs_unmount(cr, NULL);
836ecd6cf80Smarks 	if (error) {
837ecd6cf80Smarks 		error = dsl_deleg_access(zc->zc_name, ZFS_DELEG_PERM_MOUNT, cr);
838ecd6cf80Smarks 	}
839ecd6cf80Smarks 	return (error);
840fa9e4066Sahrens }
841fa9e4066Sahrens 
842fa9e4066Sahrens /*
843fa9e4066Sahrens  * Policy for pool operations - create/destroy pools, add vdevs, etc.  Requires
844fa9e4066Sahrens  * SYS_CONFIG privilege, which is not available in a local zone.
845fa9e4066Sahrens  */
846fa9e4066Sahrens /* ARGSUSED */
847fa9e4066Sahrens static int
848ecd6cf80Smarks zfs_secpolicy_config(zfs_cmd_t *zc, cred_t *cr)
849fa9e4066Sahrens {
850fa9e4066Sahrens 	if (secpolicy_sys_config(cr, B_FALSE) != 0)
851fa9e4066Sahrens 		return (EPERM);
852fa9e4066Sahrens 
853fa9e4066Sahrens 	return (0);
854fa9e4066Sahrens }
855fa9e4066Sahrens 
856ea8dc4b6Seschrock /*
857ea8dc4b6Seschrock  * Policy for fault injection.  Requires all privileges.
858ea8dc4b6Seschrock  */
859ea8dc4b6Seschrock /* ARGSUSED */
860ea8dc4b6Seschrock static int
861ecd6cf80Smarks zfs_secpolicy_inject(zfs_cmd_t *zc, cred_t *cr)
862ea8dc4b6Seschrock {
863ea8dc4b6Seschrock 	return (secpolicy_zinject(cr));
864ea8dc4b6Seschrock }
865ea8dc4b6Seschrock 
866e45ce728Sahrens static int
867e45ce728Sahrens zfs_secpolicy_inherit(zfs_cmd_t *zc, cred_t *cr)
868e45ce728Sahrens {
869e45ce728Sahrens 	zfs_prop_t prop = zfs_name_to_prop(zc->zc_value);
870e45ce728Sahrens 
871990b4856Slling 	if (prop == ZPROP_INVAL) {
872e45ce728Sahrens 		if (!zfs_prop_user(zc->zc_value))
873e45ce728Sahrens 			return (EINVAL);
874e45ce728Sahrens 		return (zfs_secpolicy_write_perms(zc->zc_name,
875e45ce728Sahrens 		    ZFS_DELEG_PERM_USERPROP, cr));
876e45ce728Sahrens 	} else {
87792241e0bSTom Erickson 		return (zfs_secpolicy_setprop(zc->zc_name, prop,
87892241e0bSTom Erickson 		    NULL, cr));
879e45ce728Sahrens 	}
880e45ce728Sahrens }
881e45ce728Sahrens 
88214843421SMatthew Ahrens static int
88314843421SMatthew Ahrens zfs_secpolicy_userspace_one(zfs_cmd_t *zc, cred_t *cr)
88414843421SMatthew Ahrens {
88514843421SMatthew Ahrens 	int err = zfs_secpolicy_read(zc, cr);
88614843421SMatthew Ahrens 	if (err)
88714843421SMatthew Ahrens 		return (err);
88814843421SMatthew Ahrens 
88914843421SMatthew Ahrens 	if (zc->zc_objset_type >= ZFS_NUM_USERQUOTA_PROPS)
89014843421SMatthew Ahrens 		return (EINVAL);
89114843421SMatthew Ahrens 
89214843421SMatthew Ahrens 	if (zc->zc_value[0] == 0) {
89314843421SMatthew Ahrens 		/*
89414843421SMatthew Ahrens 		 * They are asking about a posix uid/gid.  If it's
89514843421SMatthew Ahrens 		 * themself, allow it.
89614843421SMatthew Ahrens 		 */
89714843421SMatthew Ahrens 		if (zc->zc_objset_type == ZFS_PROP_USERUSED ||
89814843421SMatthew Ahrens 		    zc->zc_objset_type == ZFS_PROP_USERQUOTA) {
89914843421SMatthew Ahrens 			if (zc->zc_guid == crgetuid(cr))
90014843421SMatthew Ahrens 				return (0);
90114843421SMatthew Ahrens 		} else {
90214843421SMatthew Ahrens 			if (groupmember(zc->zc_guid, cr))
90314843421SMatthew Ahrens 				return (0);
90414843421SMatthew Ahrens 		}
90514843421SMatthew Ahrens 	}
90614843421SMatthew Ahrens 
90714843421SMatthew Ahrens 	return (zfs_secpolicy_write_perms(zc->zc_name,
90814843421SMatthew Ahrens 	    userquota_perms[zc->zc_objset_type], cr));
90914843421SMatthew Ahrens }
91014843421SMatthew Ahrens 
91114843421SMatthew Ahrens static int
91214843421SMatthew Ahrens zfs_secpolicy_userspace_many(zfs_cmd_t *zc, cred_t *cr)
91314843421SMatthew Ahrens {
91414843421SMatthew Ahrens 	int err = zfs_secpolicy_read(zc, cr);
91514843421SMatthew Ahrens 	if (err)
91614843421SMatthew Ahrens 		return (err);
91714843421SMatthew Ahrens 
91814843421SMatthew Ahrens 	if (zc->zc_objset_type >= ZFS_NUM_USERQUOTA_PROPS)
91914843421SMatthew Ahrens 		return (EINVAL);
92014843421SMatthew Ahrens 
92114843421SMatthew Ahrens 	return (zfs_secpolicy_write_perms(zc->zc_name,
92214843421SMatthew Ahrens 	    userquota_perms[zc->zc_objset_type], cr));
92314843421SMatthew Ahrens }
92414843421SMatthew Ahrens 
92514843421SMatthew Ahrens static int
92614843421SMatthew Ahrens zfs_secpolicy_userspace_upgrade(zfs_cmd_t *zc, cred_t *cr)
92714843421SMatthew Ahrens {
92892241e0bSTom Erickson 	return (zfs_secpolicy_setprop(zc->zc_name, ZFS_PROP_VERSION,
92992241e0bSTom Erickson 	    NULL, cr));
93014843421SMatthew Ahrens }
93114843421SMatthew Ahrens 
932842727c2SChris Kirby static int
933842727c2SChris Kirby zfs_secpolicy_hold(zfs_cmd_t *zc, cred_t *cr)
934842727c2SChris Kirby {
935842727c2SChris Kirby 	return (zfs_secpolicy_write_perms(zc->zc_name,
936842727c2SChris Kirby 	    ZFS_DELEG_PERM_HOLD, cr));
937842727c2SChris Kirby }
938842727c2SChris Kirby 
939842727c2SChris Kirby static int
940842727c2SChris Kirby zfs_secpolicy_release(zfs_cmd_t *zc, cred_t *cr)
941842727c2SChris Kirby {
942842727c2SChris Kirby 	return (zfs_secpolicy_write_perms(zc->zc_name,
943842727c2SChris Kirby 	    ZFS_DELEG_PERM_RELEASE, cr));
944842727c2SChris Kirby }
945842727c2SChris Kirby 
946fa9e4066Sahrens /*
947fa9e4066Sahrens  * Returns the nvlist as specified by the user in the zfs_cmd_t.
948fa9e4066Sahrens  */
949fa9e4066Sahrens static int
950478ed9adSEric Taylor get_nvlist(uint64_t nvl, uint64_t size, int iflag, nvlist_t **nvp)
951fa9e4066Sahrens {
952fa9e4066Sahrens 	char *packed;
953fa9e4066Sahrens 	int error;
954990b4856Slling 	nvlist_t *list = NULL;
955fa9e4066Sahrens 
956fa9e4066Sahrens 	/*
957e9dbad6fSeschrock 	 * Read in and unpack the user-supplied nvlist.
958fa9e4066Sahrens 	 */
959990b4856Slling 	if (size == 0)
960fa9e4066Sahrens 		return (EINVAL);
961fa9e4066Sahrens 
962fa9e4066Sahrens 	packed = kmem_alloc(size, KM_SLEEP);
963fa9e4066Sahrens 
964478ed9adSEric Taylor 	if ((error = ddi_copyin((void *)(uintptr_t)nvl, packed, size,
965478ed9adSEric Taylor 	    iflag)) != 0) {
966fa9e4066Sahrens 		kmem_free(packed, size);
967fa9e4066Sahrens 		return (error);
968fa9e4066Sahrens 	}
969fa9e4066Sahrens 
970990b4856Slling 	if ((error = nvlist_unpack(packed, size, &list, 0)) != 0) {
971fa9e4066Sahrens 		kmem_free(packed, size);
972fa9e4066Sahrens 		return (error);
973fa9e4066Sahrens 	}
974fa9e4066Sahrens 
975fa9e4066Sahrens 	kmem_free(packed, size);
976fa9e4066Sahrens 
977990b4856Slling 	*nvp = list;
978fa9e4066Sahrens 	return (0);
979fa9e4066Sahrens }
980fa9e4066Sahrens 
98192241e0bSTom Erickson static int
98292241e0bSTom Erickson fit_error_list(zfs_cmd_t *zc, nvlist_t **errors)
98392241e0bSTom Erickson {
98492241e0bSTom Erickson 	size_t size;
98592241e0bSTom Erickson 
98692241e0bSTom Erickson 	VERIFY(nvlist_size(*errors, &size, NV_ENCODE_NATIVE) == 0);
98792241e0bSTom Erickson 
98892241e0bSTom Erickson 	if (size > zc->zc_nvlist_dst_size) {
98992241e0bSTom Erickson 		nvpair_t *more_errors;
99092241e0bSTom Erickson 		int n = 0;
99192241e0bSTom Erickson 
99292241e0bSTom Erickson 		if (zc->zc_nvlist_dst_size < 1024)
99392241e0bSTom Erickson 			return (ENOMEM);
99492241e0bSTom Erickson 
99592241e0bSTom Erickson 		VERIFY(nvlist_add_int32(*errors, ZPROP_N_MORE_ERRORS, 0) == 0);
99692241e0bSTom Erickson 		more_errors = nvlist_prev_nvpair(*errors, NULL);
99792241e0bSTom Erickson 
99892241e0bSTom Erickson 		do {
99992241e0bSTom Erickson 			nvpair_t *pair = nvlist_prev_nvpair(*errors,
100092241e0bSTom Erickson 			    more_errors);
100192241e0bSTom Erickson 			VERIFY(nvlist_remove_nvpair(*errors, pair) == 0);
100292241e0bSTom Erickson 			n++;
100392241e0bSTom Erickson 			VERIFY(nvlist_size(*errors, &size,
100492241e0bSTom Erickson 			    NV_ENCODE_NATIVE) == 0);
100592241e0bSTom Erickson 		} while (size > zc->zc_nvlist_dst_size);
100692241e0bSTom Erickson 
100792241e0bSTom Erickson 		VERIFY(nvlist_remove_nvpair(*errors, more_errors) == 0);
100892241e0bSTom Erickson 		VERIFY(nvlist_add_int32(*errors, ZPROP_N_MORE_ERRORS, n) == 0);
100992241e0bSTom Erickson 		ASSERT(nvlist_size(*errors, &size, NV_ENCODE_NATIVE) == 0);
101092241e0bSTom Erickson 		ASSERT(size <= zc->zc_nvlist_dst_size);
101192241e0bSTom Erickson 	}
101292241e0bSTom Erickson 
101392241e0bSTom Erickson 	return (0);
101492241e0bSTom Erickson }
101592241e0bSTom Erickson 
1016e9dbad6fSeschrock static int
1017e9dbad6fSeschrock put_nvlist(zfs_cmd_t *zc, nvlist_t *nvl)
1018e9dbad6fSeschrock {
1019e9dbad6fSeschrock 	char *packed = NULL;
10206e27f868SSam Falkner 	int error = 0;
1021e9dbad6fSeschrock 	size_t size;
1022e9dbad6fSeschrock 
1023e9dbad6fSeschrock 	VERIFY(nvlist_size(nvl, &size, NV_ENCODE_NATIVE) == 0);
1024e9dbad6fSeschrock 
1025e9dbad6fSeschrock 	if (size > zc->zc_nvlist_dst_size) {
1026e9dbad6fSeschrock 		error = ENOMEM;
1027e9dbad6fSeschrock 	} else {
1028da165920Smarks 		packed = kmem_alloc(size, KM_SLEEP);
1029e9dbad6fSeschrock 		VERIFY(nvlist_pack(nvl, &packed, &size, NV_ENCODE_NATIVE,
1030e9dbad6fSeschrock 		    KM_SLEEP) == 0);
10316e27f868SSam Falkner 		if (ddi_copyout(packed, (void *)(uintptr_t)zc->zc_nvlist_dst,
10326e27f868SSam Falkner 		    size, zc->zc_iflags) != 0)
10336e27f868SSam Falkner 			error = EFAULT;
1034e9dbad6fSeschrock 		kmem_free(packed, size);
1035e9dbad6fSeschrock 	}
1036e9dbad6fSeschrock 
1037e9dbad6fSeschrock 	zc->zc_nvlist_dst_size = size;
1038e9dbad6fSeschrock 	return (error);
1039e9dbad6fSeschrock }
1040e9dbad6fSeschrock 
104114843421SMatthew Ahrens static int
1042af4c679fSSean McEnroe getzfsvfs(const char *dsname, zfsvfs_t **zfvp)
104314843421SMatthew Ahrens {
104414843421SMatthew Ahrens 	objset_t *os;
104514843421SMatthew Ahrens 	int error;
104614843421SMatthew Ahrens 
1047503ad85cSMatthew Ahrens 	error = dmu_objset_hold(dsname, FTAG, &os);
104814843421SMatthew Ahrens 	if (error)
104914843421SMatthew Ahrens 		return (error);
1050503ad85cSMatthew Ahrens 	if (dmu_objset_type(os) != DMU_OST_ZFS) {
1051503ad85cSMatthew Ahrens 		dmu_objset_rele(os, FTAG);
1052503ad85cSMatthew Ahrens 		return (EINVAL);
1053503ad85cSMatthew Ahrens 	}
105414843421SMatthew Ahrens 
1055503ad85cSMatthew Ahrens 	mutex_enter(&os->os_user_ptr_lock);
1056af4c679fSSean McEnroe 	*zfvp = dmu_objset_get_user(os);
1057af4c679fSSean McEnroe 	if (*zfvp) {
1058af4c679fSSean McEnroe 		VFS_HOLD((*zfvp)->z_vfs);
105914843421SMatthew Ahrens 	} else {
106014843421SMatthew Ahrens 		error = ESRCH;
106114843421SMatthew Ahrens 	}
1062503ad85cSMatthew Ahrens 	mutex_exit(&os->os_user_ptr_lock);
1063503ad85cSMatthew Ahrens 	dmu_objset_rele(os, FTAG);
106414843421SMatthew Ahrens 	return (error);
106514843421SMatthew Ahrens }
106614843421SMatthew Ahrens 
106714843421SMatthew Ahrens /*
106814843421SMatthew Ahrens  * Find a zfsvfs_t for a mounted filesystem, or create our own, in which
106914843421SMatthew Ahrens  * case its z_vfs will be NULL, and it will be opened as the owner.
107014843421SMatthew Ahrens  */
107114843421SMatthew Ahrens static int
10721412a1a2SMark Shellenbaum zfsvfs_hold(const char *name, void *tag, zfsvfs_t **zfvp, boolean_t writer)
107314843421SMatthew Ahrens {
107414843421SMatthew Ahrens 	int error = 0;
107514843421SMatthew Ahrens 
1076af4c679fSSean McEnroe 	if (getzfsvfs(name, zfvp) != 0)
1077af4c679fSSean McEnroe 		error = zfsvfs_create(name, zfvp);
107814843421SMatthew Ahrens 	if (error == 0) {
10791412a1a2SMark Shellenbaum 		rrw_enter(&(*zfvp)->z_teardown_lock, (writer) ? RW_WRITER :
10801412a1a2SMark Shellenbaum 		    RW_READER, tag);
1081af4c679fSSean McEnroe 		if ((*zfvp)->z_unmounted) {
108214843421SMatthew Ahrens 			/*
108314843421SMatthew Ahrens 			 * XXX we could probably try again, since the unmounting
108414843421SMatthew Ahrens 			 * thread should be just about to disassociate the
108514843421SMatthew Ahrens 			 * objset from the zfsvfs.
108614843421SMatthew Ahrens 			 */
1087af4c679fSSean McEnroe 			rrw_exit(&(*zfvp)->z_teardown_lock, tag);
108814843421SMatthew Ahrens 			return (EBUSY);
108914843421SMatthew Ahrens 		}
109014843421SMatthew Ahrens 	}
109114843421SMatthew Ahrens 	return (error);
109214843421SMatthew Ahrens }
109314843421SMatthew Ahrens 
109414843421SMatthew Ahrens static void
109514843421SMatthew Ahrens zfsvfs_rele(zfsvfs_t *zfsvfs, void *tag)
109614843421SMatthew Ahrens {
109714843421SMatthew Ahrens 	rrw_exit(&zfsvfs->z_teardown_lock, tag);
109814843421SMatthew Ahrens 
109914843421SMatthew Ahrens 	if (zfsvfs->z_vfs) {
110014843421SMatthew Ahrens 		VFS_RELE(zfsvfs->z_vfs);
110114843421SMatthew Ahrens 	} else {
1102503ad85cSMatthew Ahrens 		dmu_objset_disown(zfsvfs->z_os, zfsvfs);
110314843421SMatthew Ahrens 		zfsvfs_free(zfsvfs);
110414843421SMatthew Ahrens 	}
110514843421SMatthew Ahrens }
110614843421SMatthew Ahrens 
1107fa9e4066Sahrens static int
1108fa9e4066Sahrens zfs_ioc_pool_create(zfs_cmd_t *zc)
1109fa9e4066Sahrens {
1110fa9e4066Sahrens 	int error;
1111990b4856Slling 	nvlist_t *config, *props = NULL;
11120a48a24eStimh 	nvlist_t *rootprops = NULL;
11130a48a24eStimh 	nvlist_t *zplprops = NULL;
1114228975ccSek 	char *buf;
1115fa9e4066Sahrens 
1116990b4856Slling 	if (error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size,
1117478ed9adSEric Taylor 	    zc->zc_iflags, &config))
1118fa9e4066Sahrens 		return (error);
11192a6b87f0Sek 
1120990b4856Slling 	if (zc->zc_nvlist_src_size != 0 && (error =
1121478ed9adSEric Taylor 	    get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
1122478ed9adSEric Taylor 	    zc->zc_iflags, &props))) {
1123990b4856Slling 		nvlist_free(config);
1124990b4856Slling 		return (error);
1125990b4856Slling 	}
1126990b4856Slling 
11270a48a24eStimh 	if (props) {
11280a48a24eStimh 		nvlist_t *nvl = NULL;
11290a48a24eStimh 		uint64_t version = SPA_VERSION;
11300a48a24eStimh 
11310a48a24eStimh 		(void) nvlist_lookup_uint64(props,
11320a48a24eStimh 		    zpool_prop_to_name(ZPOOL_PROP_VERSION), &version);
11330a48a24eStimh 		if (version < SPA_VERSION_INITIAL || version > SPA_VERSION) {
11340a48a24eStimh 			error = EINVAL;
11350a48a24eStimh 			goto pool_props_bad;
11360a48a24eStimh 		}
11370a48a24eStimh 		(void) nvlist_lookup_nvlist(props, ZPOOL_ROOTFS_PROPS, &nvl);
11380a48a24eStimh 		if (nvl) {
11390a48a24eStimh 			error = nvlist_dup(nvl, &rootprops, KM_SLEEP);
11400a48a24eStimh 			if (error != 0) {
11410a48a24eStimh 				nvlist_free(config);
11420a48a24eStimh 				nvlist_free(props);
11430a48a24eStimh 				return (error);
11440a48a24eStimh 			}
11450a48a24eStimh 			(void) nvlist_remove_all(props, ZPOOL_ROOTFS_PROPS);
11460a48a24eStimh 		}
11470a48a24eStimh 		VERIFY(nvlist_alloc(&zplprops, NV_UNIQUE_NAME, KM_SLEEP) == 0);
11480a48a24eStimh 		error = zfs_fill_zplprops_root(version, rootprops,
11490a48a24eStimh 		    zplprops, NULL);
11500a48a24eStimh 		if (error)
11510a48a24eStimh 			goto pool_props_bad;
11520a48a24eStimh 	}
11530a48a24eStimh 
11542a6b87f0Sek 	buf = history_str_get(zc);
1155fa9e4066Sahrens 
11560a48a24eStimh 	error = spa_create(zc->zc_name, config, props, buf, zplprops);
11570a48a24eStimh 
11580a48a24eStimh 	/*
11590a48a24eStimh 	 * Set the remaining root properties
11600a48a24eStimh 	 */
116192241e0bSTom Erickson 	if (!error && (error = zfs_set_prop_nvlist(zc->zc_name,
116292241e0bSTom Erickson 	    ZPROP_SRC_LOCAL, rootprops, NULL)) != 0)
11630a48a24eStimh 		(void) spa_destroy(zc->zc_name);
1164fa9e4066Sahrens 
11652a6b87f0Sek 	if (buf != NULL)
11662a6b87f0Sek 		history_str_free(buf);
1167990b4856Slling 
11680a48a24eStimh pool_props_bad:
11690a48a24eStimh 	nvlist_free(rootprops);
11700a48a24eStimh 	nvlist_free(zplprops);
1171fa9e4066Sahrens 	nvlist_free(config);
11720a48a24eStimh 	nvlist_free(props);
1173990b4856Slling 
1174fa9e4066Sahrens 	return (error);
1175fa9e4066Sahrens }
1176fa9e4066Sahrens 
1177fa9e4066Sahrens static int
1178fa9e4066Sahrens zfs_ioc_pool_destroy(zfs_cmd_t *zc)
1179fa9e4066Sahrens {
1180ecd6cf80Smarks 	int error;
1181ecd6cf80Smarks 	zfs_log_history(zc);
1182ecd6cf80Smarks 	error = spa_destroy(zc->zc_name);
1183681d9761SEric Taylor 	if (error == 0)
1184681d9761SEric Taylor 		zvol_remove_minors(zc->zc_name);
1185ecd6cf80Smarks 	return (error);
1186fa9e4066Sahrens }
1187fa9e4066Sahrens 
1188fa9e4066Sahrens static int
1189fa9e4066Sahrens zfs_ioc_pool_import(zfs_cmd_t *zc)
1190fa9e4066Sahrens {
1191990b4856Slling 	nvlist_t *config, *props = NULL;
1192fa9e4066Sahrens 	uint64_t guid;
1193468c413aSTim Haley 	int error;
1194fa9e4066Sahrens 
1195990b4856Slling 	if ((error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size,
1196478ed9adSEric Taylor 	    zc->zc_iflags, &config)) != 0)
1197990b4856Slling 		return (error);
1198990b4856Slling 
1199990b4856Slling 	if (zc->zc_nvlist_src_size != 0 && (error =
1200478ed9adSEric Taylor 	    get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
1201478ed9adSEric Taylor 	    zc->zc_iflags, &props))) {
1202990b4856Slling 		nvlist_free(config);
1203fa9e4066Sahrens 		return (error);
1204990b4856Slling 	}
1205fa9e4066Sahrens 
1206fa9e4066Sahrens 	if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID, &guid) != 0 ||
1207ea8dc4b6Seschrock 	    guid != zc->zc_guid)
1208fa9e4066Sahrens 		error = EINVAL;
1209c5904d13Seschrock 	else if (zc->zc_cookie)
1210468c413aSTim Haley 		error = spa_import_verbatim(zc->zc_name, config, props);
1211fa9e4066Sahrens 	else
1212990b4856Slling 		error = spa_import(zc->zc_name, config, props);
1213fa9e4066Sahrens 
1214468c413aSTim Haley 	if (zc->zc_nvlist_dst != 0)
1215468c413aSTim Haley 		(void) put_nvlist(zc, config);
1216468c413aSTim Haley 
1217fa9e4066Sahrens 	nvlist_free(config);
1218fa9e4066Sahrens 
1219990b4856Slling 	if (props)
1220990b4856Slling 		nvlist_free(props);
1221990b4856Slling 
1222fa9e4066Sahrens 	return (error);
1223fa9e4066Sahrens }
1224fa9e4066Sahrens 
1225fa9e4066Sahrens static int
1226fa9e4066Sahrens zfs_ioc_pool_export(zfs_cmd_t *zc)
1227fa9e4066Sahrens {
1228ecd6cf80Smarks 	int error;
122989a89ebfSlling 	boolean_t force = (boolean_t)zc->zc_cookie;
1230394ab0cbSGeorge Wilson 	boolean_t hardforce = (boolean_t)zc->zc_guid;
123189a89ebfSlling 
1232ecd6cf80Smarks 	zfs_log_history(zc);
1233394ab0cbSGeorge Wilson 	error = spa_export(zc->zc_name, NULL, force, hardforce);
1234681d9761SEric Taylor 	if (error == 0)
1235681d9761SEric Taylor 		zvol_remove_minors(zc->zc_name);
1236ecd6cf80Smarks 	return (error);
1237fa9e4066Sahrens }
1238fa9e4066Sahrens 
1239fa9e4066Sahrens static int
1240fa9e4066Sahrens zfs_ioc_pool_configs(zfs_cmd_t *zc)
1241fa9e4066Sahrens {
1242fa9e4066Sahrens 	nvlist_t *configs;
1243fa9e4066Sahrens 	int error;
1244fa9e4066Sahrens 
1245fa9e4066Sahrens 	if ((configs = spa_all_configs(&zc->zc_cookie)) == NULL)
1246fa9e4066Sahrens 		return (EEXIST);
1247fa9e4066Sahrens 
1248e9dbad6fSeschrock 	error = put_nvlist(zc, configs);
1249fa9e4066Sahrens 
1250fa9e4066Sahrens 	nvlist_free(configs);
1251fa9e4066Sahrens 
1252fa9e4066Sahrens 	return (error);
1253fa9e4066Sahrens }
1254fa9e4066Sahrens 
1255fa9e4066Sahrens static int
1256fa9e4066Sahrens zfs_ioc_pool_stats(zfs_cmd_t *zc)
1257fa9e4066Sahrens {
1258fa9e4066Sahrens 	nvlist_t *config;
1259fa9e4066Sahrens 	int error;
1260ea8dc4b6Seschrock 	int ret = 0;
1261fa9e4066Sahrens 
1262e9dbad6fSeschrock 	error = spa_get_stats(zc->zc_name, &config, zc->zc_value,
1263e9dbad6fSeschrock 	    sizeof (zc->zc_value));
1264fa9e4066Sahrens 
1265fa9e4066Sahrens 	if (config != NULL) {
1266e9dbad6fSeschrock 		ret = put_nvlist(zc, config);
1267fa9e4066Sahrens 		nvlist_free(config);
1268ea8dc4b6Seschrock 
1269ea8dc4b6Seschrock 		/*
1270ea8dc4b6Seschrock 		 * The config may be present even if 'error' is non-zero.
1271ea8dc4b6Seschrock 		 * In this case we return success, and preserve the real errno
1272ea8dc4b6Seschrock 		 * in 'zc_cookie'.
1273ea8dc4b6Seschrock 		 */
1274ea8dc4b6Seschrock 		zc->zc_cookie = error;
1275fa9e4066Sahrens 	} else {
1276ea8dc4b6Seschrock 		ret = error;
1277fa9e4066Sahrens 	}
1278fa9e4066Sahrens 
1279ea8dc4b6Seschrock 	return (ret);
1280fa9e4066Sahrens }
1281fa9e4066Sahrens 
1282fa9e4066Sahrens /*
1283fa9e4066Sahrens  * Try to import the given pool, returning pool stats as appropriate so that
1284fa9e4066Sahrens  * user land knows which devices are available and overall pool health.
1285fa9e4066Sahrens  */
1286fa9e4066Sahrens static int
1287fa9e4066Sahrens zfs_ioc_pool_tryimport(zfs_cmd_t *zc)
1288fa9e4066Sahrens {
1289fa9e4066Sahrens 	nvlist_t *tryconfig, *config;
1290fa9e4066Sahrens 	int error;
1291fa9e4066Sahrens 
1292990b4856Slling 	if ((error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size,
1293478ed9adSEric Taylor 	    zc->zc_iflags, &tryconfig)) != 0)
1294fa9e4066Sahrens 		return (error);
1295fa9e4066Sahrens 
1296fa9e4066Sahrens 	config = spa_tryimport(tryconfig);
1297fa9e4066Sahrens 
1298fa9e4066Sahrens 	nvlist_free(tryconfig);
1299fa9e4066Sahrens 
1300fa9e4066Sahrens 	if (config == NULL)
1301fa9e4066Sahrens 		return (EINVAL);
1302fa9e4066Sahrens 
1303e9dbad6fSeschrock 	error = put_nvlist(zc, config);
1304fa9e4066Sahrens 	nvlist_free(config);
1305fa9e4066Sahrens 
1306fa9e4066Sahrens 	return (error);
1307fa9e4066Sahrens }
1308fa9e4066Sahrens 
13093f9d6ad7SLin Ling /*
13103f9d6ad7SLin Ling  * inputs:
13113f9d6ad7SLin Ling  * zc_name              name of the pool
13123f9d6ad7SLin Ling  * zc_cookie            scan func (pool_scan_func_t)
13133f9d6ad7SLin Ling  */
1314fa9e4066Sahrens static int
13153f9d6ad7SLin Ling zfs_ioc_pool_scan(zfs_cmd_t *zc)
1316fa9e4066Sahrens {
1317fa9e4066Sahrens 	spa_t *spa;
1318fa9e4066Sahrens 	int error;
1319fa9e4066Sahrens 
132006eeb2adSek 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
132106eeb2adSek 		return (error);
132206eeb2adSek 
13233f9d6ad7SLin Ling 	if (zc->zc_cookie == POOL_SCAN_NONE)
13243f9d6ad7SLin Ling 		error = spa_scan_stop(spa);
13253f9d6ad7SLin Ling 	else
13263f9d6ad7SLin Ling 		error = spa_scan(spa, zc->zc_cookie);
132706eeb2adSek 
132806eeb2adSek 	spa_close(spa, FTAG);
132906eeb2adSek 
1330fa9e4066Sahrens 	return (error);
1331fa9e4066Sahrens }
1332fa9e4066Sahrens 
1333fa9e4066Sahrens static int
1334fa9e4066Sahrens zfs_ioc_pool_freeze(zfs_cmd_t *zc)
1335fa9e4066Sahrens {
1336fa9e4066Sahrens 	spa_t *spa;
1337fa9e4066Sahrens 	int error;
1338fa9e4066Sahrens 
1339fa9e4066Sahrens 	error = spa_open(zc->zc_name, &spa, FTAG);
1340fa9e4066Sahrens 	if (error == 0) {
1341fa9e4066Sahrens 		spa_freeze(spa);
1342fa9e4066Sahrens 		spa_close(spa, FTAG);
1343fa9e4066Sahrens 	}
1344fa9e4066Sahrens 	return (error);
1345fa9e4066Sahrens }
1346fa9e4066Sahrens 
1347eaca9bbdSeschrock static int
1348eaca9bbdSeschrock zfs_ioc_pool_upgrade(zfs_cmd_t *zc)
1349eaca9bbdSeschrock {
1350eaca9bbdSeschrock 	spa_t *spa;
1351eaca9bbdSeschrock 	int error;
1352eaca9bbdSeschrock 
135306eeb2adSek 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
135406eeb2adSek 		return (error);
135506eeb2adSek 
1356558d2d50Slling 	if (zc->zc_cookie < spa_version(spa) || zc->zc_cookie > SPA_VERSION) {
1357558d2d50Slling 		spa_close(spa, FTAG);
1358558d2d50Slling 		return (EINVAL);
1359558d2d50Slling 	}
1360558d2d50Slling 
1361990b4856Slling 	spa_upgrade(spa, zc->zc_cookie);
136206eeb2adSek 	spa_close(spa, FTAG);
136306eeb2adSek 
136406eeb2adSek 	return (error);
136506eeb2adSek }
136606eeb2adSek 
136706eeb2adSek static int
136806eeb2adSek zfs_ioc_pool_get_history(zfs_cmd_t *zc)
136906eeb2adSek {
137006eeb2adSek 	spa_t *spa;
137106eeb2adSek 	char *hist_buf;
137206eeb2adSek 	uint64_t size;
137306eeb2adSek 	int error;
137406eeb2adSek 
137506eeb2adSek 	if ((size = zc->zc_history_len) == 0)
137606eeb2adSek 		return (EINVAL);
137706eeb2adSek 
137806eeb2adSek 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
137906eeb2adSek 		return (error);
138006eeb2adSek 
1381e7437265Sahrens 	if (spa_version(spa) < SPA_VERSION_ZPOOL_HISTORY) {
1382d7306b64Sek 		spa_close(spa, FTAG);
1383d7306b64Sek 		return (ENOTSUP);
1384d7306b64Sek 	}
1385d7306b64Sek 
138606eeb2adSek 	hist_buf = kmem_alloc(size, KM_SLEEP);
138706eeb2adSek 	if ((error = spa_history_get(spa, &zc->zc_history_offset,
138806eeb2adSek 	    &zc->zc_history_len, hist_buf)) == 0) {
1389478ed9adSEric Taylor 		error = ddi_copyout(hist_buf,
1390478ed9adSEric Taylor 		    (void *)(uintptr_t)zc->zc_history,
1391478ed9adSEric Taylor 		    zc->zc_history_len, zc->zc_iflags);
139206eeb2adSek 	}
139306eeb2adSek 
139406eeb2adSek 	spa_close(spa, FTAG);
139506eeb2adSek 	kmem_free(hist_buf, size);
139606eeb2adSek 	return (error);
139706eeb2adSek }
139806eeb2adSek 
139955434c77Sek static int
140055434c77Sek zfs_ioc_dsobj_to_dsname(zfs_cmd_t *zc)
140155434c77Sek {
140255434c77Sek 	int error;
140355434c77Sek 
1404b1b8ab34Slling 	if (error = dsl_dsobj_to_dsname(zc->zc_name, zc->zc_obj, zc->zc_value))
140555434c77Sek 		return (error);
140655434c77Sek 
140755434c77Sek 	return (0);
140855434c77Sek }
140955434c77Sek 
1410503ad85cSMatthew Ahrens /*
1411503ad85cSMatthew Ahrens  * inputs:
1412503ad85cSMatthew Ahrens  * zc_name		name of filesystem
1413503ad85cSMatthew Ahrens  * zc_obj		object to find
1414503ad85cSMatthew Ahrens  *
1415503ad85cSMatthew Ahrens  * outputs:
1416503ad85cSMatthew Ahrens  * zc_value		name of object
1417503ad85cSMatthew Ahrens  */
141855434c77Sek static int
141955434c77Sek zfs_ioc_obj_to_path(zfs_cmd_t *zc)
142055434c77Sek {
1421503ad85cSMatthew Ahrens 	objset_t *os;
142255434c77Sek 	int error;
142355434c77Sek 
1424503ad85cSMatthew Ahrens 	/* XXX reading from objset not owned */
1425503ad85cSMatthew Ahrens 	if ((error = dmu_objset_hold(zc->zc_name, FTAG, &os)) != 0)
142655434c77Sek 		return (error);
1427503ad85cSMatthew Ahrens 	if (dmu_objset_type(os) != DMU_OST_ZFS) {
1428503ad85cSMatthew Ahrens 		dmu_objset_rele(os, FTAG);
1429503ad85cSMatthew Ahrens 		return (EINVAL);
1430503ad85cSMatthew Ahrens 	}
1431503ad85cSMatthew Ahrens 	error = zfs_obj_to_path(os, zc->zc_obj, zc->zc_value,
143255434c77Sek 	    sizeof (zc->zc_value));
1433503ad85cSMatthew Ahrens 	dmu_objset_rele(os, FTAG);
143455434c77Sek 
143555434c77Sek 	return (error);
143655434c77Sek }
143755434c77Sek 
1438fa9e4066Sahrens static int
1439fa9e4066Sahrens zfs_ioc_vdev_add(zfs_cmd_t *zc)
1440fa9e4066Sahrens {
1441fa9e4066Sahrens 	spa_t *spa;
1442fa9e4066Sahrens 	int error;
1443e7cbe64fSgw 	nvlist_t *config, **l2cache, **spares;
1444e7cbe64fSgw 	uint_t nl2cache = 0, nspares = 0;
1445fa9e4066Sahrens 
1446fa9e4066Sahrens 	error = spa_open(zc->zc_name, &spa, FTAG);
1447fa9e4066Sahrens 	if (error != 0)
1448fa9e4066Sahrens 		return (error);
1449fa9e4066Sahrens 
1450fa94a07fSbrendan 	error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size,
1451478ed9adSEric Taylor 	    zc->zc_iflags, &config);
1452fa94a07fSbrendan 	(void) nvlist_lookup_nvlist_array(config, ZPOOL_CONFIG_L2CACHE,
1453fa94a07fSbrendan 	    &l2cache, &nl2cache);
1454fa94a07fSbrendan 
1455e7cbe64fSgw 	(void) nvlist_lookup_nvlist_array(config, ZPOOL_CONFIG_SPARES,
1456e7cbe64fSgw 	    &spares, &nspares);
1457e7cbe64fSgw 
1458b1b8ab34Slling 	/*
1459b1b8ab34Slling 	 * A root pool with concatenated devices is not supported.
1460e7cbe64fSgw 	 * Thus, can not add a device to a root pool.
1461e7cbe64fSgw 	 *
1462e7cbe64fSgw 	 * Intent log device can not be added to a rootpool because
1463e7cbe64fSgw 	 * during mountroot, zil is replayed, a seperated log device
1464e7cbe64fSgw 	 * can not be accessed during the mountroot time.
1465e7cbe64fSgw 	 *
1466e7cbe64fSgw 	 * l2cache and spare devices are ok to be added to a rootpool.
1467b1b8ab34Slling 	 */
1468b24ab676SJeff Bonwick 	if (spa_bootfs(spa) != 0 && nl2cache == 0 && nspares == 0) {
14691195e687SMark J Musante 		nvlist_free(config);
1470b1b8ab34Slling 		spa_close(spa, FTAG);
1471b1b8ab34Slling 		return (EDOM);
1472b1b8ab34Slling 	}
1473b1b8ab34Slling 
1474fa94a07fSbrendan 	if (error == 0) {
1475fa9e4066Sahrens 		error = spa_vdev_add(spa, config);
1476fa9e4066Sahrens 		nvlist_free(config);
1477fa9e4066Sahrens 	}
1478fa9e4066Sahrens 	spa_close(spa, FTAG);
1479fa9e4066Sahrens 	return (error);
1480fa9e4066Sahrens }
1481fa9e4066Sahrens 
14823f9d6ad7SLin Ling /*
14833f9d6ad7SLin Ling  * inputs:
14843f9d6ad7SLin Ling  * zc_name		name of the pool
14853f9d6ad7SLin Ling  * zc_nvlist_conf	nvlist of devices to remove
14863f9d6ad7SLin Ling  * zc_cookie		to stop the remove?
14873f9d6ad7SLin Ling  */
1488fa9e4066Sahrens static int
1489fa9e4066Sahrens zfs_ioc_vdev_remove(zfs_cmd_t *zc)
1490fa9e4066Sahrens {
149199653d4eSeschrock 	spa_t *spa;
149299653d4eSeschrock 	int error;
149399653d4eSeschrock 
149499653d4eSeschrock 	error = spa_open(zc->zc_name, &spa, FTAG);
149599653d4eSeschrock 	if (error != 0)
149699653d4eSeschrock 		return (error);
149799653d4eSeschrock 	error = spa_vdev_remove(spa, zc->zc_guid, B_FALSE);
149899653d4eSeschrock 	spa_close(spa, FTAG);
149999653d4eSeschrock 	return (error);
1500fa9e4066Sahrens }
1501fa9e4066Sahrens 
1502fa9e4066Sahrens static int
15033d7072f8Seschrock zfs_ioc_vdev_set_state(zfs_cmd_t *zc)
1504fa9e4066Sahrens {
1505fa9e4066Sahrens 	spa_t *spa;
1506fa9e4066Sahrens 	int error;
15073d7072f8Seschrock 	vdev_state_t newstate = VDEV_STATE_UNKNOWN;
1508fa9e4066Sahrens 
150906eeb2adSek 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
1510fa9e4066Sahrens 		return (error);
15113d7072f8Seschrock 	switch (zc->zc_cookie) {
15123d7072f8Seschrock 	case VDEV_STATE_ONLINE:
15133d7072f8Seschrock 		error = vdev_online(spa, zc->zc_guid, zc->zc_obj, &newstate);
15143d7072f8Seschrock 		break;
1515fa9e4066Sahrens 
15163d7072f8Seschrock 	case VDEV_STATE_OFFLINE:
15173d7072f8Seschrock 		error = vdev_offline(spa, zc->zc_guid, zc->zc_obj);
15183d7072f8Seschrock 		break;
1519fa9e4066Sahrens 
15203d7072f8Seschrock 	case VDEV_STATE_FAULTED:
1521069f55e2SEric Schrock 		if (zc->zc_obj != VDEV_AUX_ERR_EXCEEDED &&
1522069f55e2SEric Schrock 		    zc->zc_obj != VDEV_AUX_EXTERNAL)
1523069f55e2SEric Schrock 			zc->zc_obj = VDEV_AUX_ERR_EXCEEDED;
1524069f55e2SEric Schrock 
1525069f55e2SEric Schrock 		error = vdev_fault(spa, zc->zc_guid, zc->zc_obj);
15263d7072f8Seschrock 		break;
15273d7072f8Seschrock 
15283d7072f8Seschrock 	case VDEV_STATE_DEGRADED:
1529069f55e2SEric Schrock 		if (zc->zc_obj != VDEV_AUX_ERR_EXCEEDED &&
1530069f55e2SEric Schrock 		    zc->zc_obj != VDEV_AUX_EXTERNAL)
1531069f55e2SEric Schrock 			zc->zc_obj = VDEV_AUX_ERR_EXCEEDED;
1532069f55e2SEric Schrock 
1533069f55e2SEric Schrock 		error = vdev_degrade(spa, zc->zc_guid, zc->zc_obj);
15343d7072f8Seschrock 		break;
15353d7072f8Seschrock 
15363d7072f8Seschrock 	default:
15373d7072f8Seschrock 		error = EINVAL;
15383d7072f8Seschrock 	}
15393d7072f8Seschrock 	zc->zc_cookie = newstate;
1540fa9e4066Sahrens 	spa_close(spa, FTAG);
1541fa9e4066Sahrens 	return (error);
1542fa9e4066Sahrens }
1543fa9e4066Sahrens 
1544fa9e4066Sahrens static int
1545fa9e4066Sahrens zfs_ioc_vdev_attach(zfs_cmd_t *zc)
1546fa9e4066Sahrens {
1547fa9e4066Sahrens 	spa_t *spa;
1548fa9e4066Sahrens 	int replacing = zc->zc_cookie;
1549fa9e4066Sahrens 	nvlist_t *config;
1550fa9e4066Sahrens 	int error;
1551fa9e4066Sahrens 
155206eeb2adSek 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
1553fa9e4066Sahrens 		return (error);
1554fa9e4066Sahrens 
1555990b4856Slling 	if ((error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size,
1556478ed9adSEric Taylor 	    zc->zc_iflags, &config)) == 0) {
1557ea8dc4b6Seschrock 		error = spa_vdev_attach(spa, zc->zc_guid, config, replacing);
1558fa9e4066Sahrens 		nvlist_free(config);
1559fa9e4066Sahrens 	}
1560fa9e4066Sahrens 
1561fa9e4066Sahrens 	spa_close(spa, FTAG);
1562fa9e4066Sahrens 	return (error);
1563fa9e4066Sahrens }
1564fa9e4066Sahrens 
1565fa9e4066Sahrens static int
1566fa9e4066Sahrens zfs_ioc_vdev_detach(zfs_cmd_t *zc)
1567fa9e4066Sahrens {
1568fa9e4066Sahrens 	spa_t *spa;
1569fa9e4066Sahrens 	int error;
1570fa9e4066Sahrens 
157106eeb2adSek 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
1572fa9e4066Sahrens 		return (error);
1573fa9e4066Sahrens 
15748ad4d6ddSJeff Bonwick 	error = spa_vdev_detach(spa, zc->zc_guid, 0, B_FALSE);
1575fa9e4066Sahrens 
1576fa9e4066Sahrens 	spa_close(spa, FTAG);
1577fa9e4066Sahrens 	return (error);
1578fa9e4066Sahrens }
1579fa9e4066Sahrens 
15801195e687SMark J Musante static int
15811195e687SMark J Musante zfs_ioc_vdev_split(zfs_cmd_t *zc)
15821195e687SMark J Musante {
15831195e687SMark J Musante 	spa_t *spa;
15841195e687SMark J Musante 	nvlist_t *config, *props = NULL;
15851195e687SMark J Musante 	int error;
15861195e687SMark J Musante 	boolean_t exp = !!(zc->zc_cookie & ZPOOL_EXPORT_AFTER_SPLIT);
15871195e687SMark J Musante 
15881195e687SMark J Musante 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
15891195e687SMark J Musante 		return (error);
15901195e687SMark J Musante 
15911195e687SMark J Musante 	if (error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size,
15921195e687SMark J Musante 	    zc->zc_iflags, &config)) {
15931195e687SMark J Musante 		spa_close(spa, FTAG);
15941195e687SMark J Musante 		return (error);
15951195e687SMark J Musante 	}
15961195e687SMark J Musante 
15971195e687SMark J Musante 	if (zc->zc_nvlist_src_size != 0 && (error =
15981195e687SMark J Musante 	    get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
15991195e687SMark J Musante 	    zc->zc_iflags, &props))) {
16001195e687SMark J Musante 		spa_close(spa, FTAG);
16011195e687SMark J Musante 		nvlist_free(config);
16021195e687SMark J Musante 		return (error);
16031195e687SMark J Musante 	}
16041195e687SMark J Musante 
16051195e687SMark J Musante 	error = spa_vdev_split_mirror(spa, zc->zc_string, config, props, exp);
16061195e687SMark J Musante 
16071195e687SMark J Musante 	spa_close(spa, FTAG);
16081195e687SMark J Musante 
16091195e687SMark J Musante 	nvlist_free(config);
16101195e687SMark J Musante 	nvlist_free(props);
16111195e687SMark J Musante 
16121195e687SMark J Musante 	return (error);
16131195e687SMark J Musante }
16141195e687SMark J Musante 
1615c67d9675Seschrock static int
1616c67d9675Seschrock zfs_ioc_vdev_setpath(zfs_cmd_t *zc)
1617c67d9675Seschrock {
1618c67d9675Seschrock 	spa_t *spa;
1619e9dbad6fSeschrock 	char *path = zc->zc_value;
1620ea8dc4b6Seschrock 	uint64_t guid = zc->zc_guid;
1621c67d9675Seschrock 	int error;
1622c67d9675Seschrock 
1623c67d9675Seschrock 	error = spa_open(zc->zc_name, &spa, FTAG);
1624c67d9675Seschrock 	if (error != 0)
1625c67d9675Seschrock 		return (error);
1626c67d9675Seschrock 
1627c67d9675Seschrock 	error = spa_vdev_setpath(spa, guid, path);
1628c67d9675Seschrock 	spa_close(spa, FTAG);
1629c67d9675Seschrock 	return (error);
1630c67d9675Seschrock }
1631c67d9675Seschrock 
16326809eb4eSEric Schrock static int
16336809eb4eSEric Schrock zfs_ioc_vdev_setfru(zfs_cmd_t *zc)
16346809eb4eSEric Schrock {
16356809eb4eSEric Schrock 	spa_t *spa;
16366809eb4eSEric Schrock 	char *fru = zc->zc_value;
16376809eb4eSEric Schrock 	uint64_t guid = zc->zc_guid;
16386809eb4eSEric Schrock 	int error;
16396809eb4eSEric Schrock 
16406809eb4eSEric Schrock 	error = spa_open(zc->zc_name, &spa, FTAG);
16416809eb4eSEric Schrock 	if (error != 0)
16426809eb4eSEric Schrock 		return (error);
16436809eb4eSEric Schrock 
16446809eb4eSEric Schrock 	error = spa_vdev_setfru(spa, guid, fru);
16456809eb4eSEric Schrock 	spa_close(spa, FTAG);
16466809eb4eSEric Schrock 	return (error);
16476809eb4eSEric Schrock }
16486809eb4eSEric Schrock 
1649fa9e4066Sahrens static int
1650*a7f53a56SChris Kirby zfs_ioc_objset_stats_impl(zfs_cmd_t *zc, objset_t *os)
1651fa9e4066Sahrens {
1652*a7f53a56SChris Kirby 	int error = 0;
16537f7322feSeschrock 	nvlist_t *nv;
1654fa9e4066Sahrens 
1655a2eea2e1Sahrens 	dmu_objset_fast_stat(os, &zc->zc_objset_stats);
1656fa9e4066Sahrens 
16575ad82045Snd 	if (zc->zc_nvlist_dst != 0 &&
165892241e0bSTom Erickson 	    (error = dsl_prop_get_all(os, &nv)) == 0) {
1659a2eea2e1Sahrens 		dmu_objset_stats(os, nv);
1660432f72fdSahrens 		/*
1661bd00f61bSrm 		 * NB: zvol_get_stats() will read the objset contents,
1662432f72fdSahrens 		 * which we aren't supposed to do with a
1663745cd3c5Smaybee 		 * DS_MODE_USER hold, because it could be
1664432f72fdSahrens 		 * inconsistent.  So this is a bit of a workaround...
1665503ad85cSMatthew Ahrens 		 * XXX reading with out owning
1666432f72fdSahrens 		 */
1667e7437265Sahrens 		if (!zc->zc_objset_stats.dds_inconsistent) {
1668e7437265Sahrens 			if (dmu_objset_type(os) == DMU_OST_ZVOL)
1669e7437265Sahrens 				VERIFY(zvol_get_stats(os, nv) == 0);
1670e7437265Sahrens 		}
1671e9dbad6fSeschrock 		error = put_nvlist(zc, nv);
16727f7322feSeschrock 		nvlist_free(nv);
16737f7322feSeschrock 	}
1674fa9e4066Sahrens 
1675*a7f53a56SChris Kirby 	return (error);
1676*a7f53a56SChris Kirby }
1677*a7f53a56SChris Kirby 
1678*a7f53a56SChris Kirby /*
1679*a7f53a56SChris Kirby  * inputs:
1680*a7f53a56SChris Kirby  * zc_name		name of filesystem
1681*a7f53a56SChris Kirby  * zc_nvlist_dst_size	size of buffer for property nvlist
1682*a7f53a56SChris Kirby  *
1683*a7f53a56SChris Kirby  * outputs:
1684*a7f53a56SChris Kirby  * zc_objset_stats	stats
1685*a7f53a56SChris Kirby  * zc_nvlist_dst	property nvlist
1686*a7f53a56SChris Kirby  * zc_nvlist_dst_size	size of property nvlist
1687*a7f53a56SChris Kirby  */
1688*a7f53a56SChris Kirby static int
1689*a7f53a56SChris Kirby zfs_ioc_objset_stats(zfs_cmd_t *zc)
1690*a7f53a56SChris Kirby {
1691*a7f53a56SChris Kirby 	objset_t *os = NULL;
1692*a7f53a56SChris Kirby 	int error;
1693*a7f53a56SChris Kirby 
1694*a7f53a56SChris Kirby 	if (error = dmu_objset_hold(zc->zc_name, FTAG, &os))
1695*a7f53a56SChris Kirby 		return (error);
1696*a7f53a56SChris Kirby 
1697*a7f53a56SChris Kirby 	error = zfs_ioc_objset_stats_impl(zc, os);
1698*a7f53a56SChris Kirby 
1699503ad85cSMatthew Ahrens 	dmu_objset_rele(os, FTAG);
1700*a7f53a56SChris Kirby 
1701fa9e4066Sahrens 	return (error);
1702fa9e4066Sahrens }
1703fa9e4066Sahrens 
170492241e0bSTom Erickson /*
170592241e0bSTom Erickson  * inputs:
170692241e0bSTom Erickson  * zc_name		name of filesystem
170792241e0bSTom Erickson  * zc_nvlist_dst_size	size of buffer for property nvlist
170892241e0bSTom Erickson  *
170992241e0bSTom Erickson  * outputs:
171092241e0bSTom Erickson  * zc_nvlist_dst	received property nvlist
171192241e0bSTom Erickson  * zc_nvlist_dst_size	size of received property nvlist
171292241e0bSTom Erickson  *
171392241e0bSTom Erickson  * Gets received properties (distinct from local properties on or after
171492241e0bSTom Erickson  * SPA_VERSION_RECVD_PROPS) for callers who want to differentiate received from
171592241e0bSTom Erickson  * local property values.
171692241e0bSTom Erickson  */
171792241e0bSTom Erickson static int
171892241e0bSTom Erickson zfs_ioc_objset_recvd_props(zfs_cmd_t *zc)
171992241e0bSTom Erickson {
172092241e0bSTom Erickson 	objset_t *os = NULL;
172192241e0bSTom Erickson 	int error;
172292241e0bSTom Erickson 	nvlist_t *nv;
172392241e0bSTom Erickson 
172492241e0bSTom Erickson 	if (error = dmu_objset_hold(zc->zc_name, FTAG, &os))
172592241e0bSTom Erickson 		return (error);
172692241e0bSTom Erickson 
172792241e0bSTom Erickson 	/*
172892241e0bSTom Erickson 	 * Without this check, we would return local property values if the
172992241e0bSTom Erickson 	 * caller has not already received properties on or after
173092241e0bSTom Erickson 	 * SPA_VERSION_RECVD_PROPS.
173192241e0bSTom Erickson 	 */
173292241e0bSTom Erickson 	if (!dsl_prop_get_hasrecvd(os)) {
173392241e0bSTom Erickson 		dmu_objset_rele(os, FTAG);
173492241e0bSTom Erickson 		return (ENOTSUP);
173592241e0bSTom Erickson 	}
173692241e0bSTom Erickson 
173792241e0bSTom Erickson 	if (zc->zc_nvlist_dst != 0 &&
173892241e0bSTom Erickson 	    (error = dsl_prop_get_received(os, &nv)) == 0) {
173992241e0bSTom Erickson 		error = put_nvlist(zc, nv);
174092241e0bSTom Erickson 		nvlist_free(nv);
174192241e0bSTom Erickson 	}
174292241e0bSTom Erickson 
174392241e0bSTom Erickson 	dmu_objset_rele(os, FTAG);
174492241e0bSTom Erickson 	return (error);
174592241e0bSTom Erickson }
174692241e0bSTom Erickson 
1747de8267e0Stimh static int
1748de8267e0Stimh nvl_add_zplprop(objset_t *os, nvlist_t *props, zfs_prop_t prop)
1749de8267e0Stimh {
1750de8267e0Stimh 	uint64_t value;
1751de8267e0Stimh 	int error;
1752de8267e0Stimh 
1753de8267e0Stimh 	/*
1754de8267e0Stimh 	 * zfs_get_zplprop() will either find a value or give us
1755de8267e0Stimh 	 * the default value (if there is one).
1756de8267e0Stimh 	 */
1757de8267e0Stimh 	if ((error = zfs_get_zplprop(os, prop, &value)) != 0)
1758de8267e0Stimh 		return (error);
1759de8267e0Stimh 	VERIFY(nvlist_add_uint64(props, zfs_prop_to_name(prop), value) == 0);
1760de8267e0Stimh 	return (0);
1761de8267e0Stimh }
1762de8267e0Stimh 
17633cb34c60Sahrens /*
17643cb34c60Sahrens  * inputs:
17653cb34c60Sahrens  * zc_name		name of filesystem
1766de8267e0Stimh  * zc_nvlist_dst_size	size of buffer for zpl property nvlist
17673cb34c60Sahrens  *
17683cb34c60Sahrens  * outputs:
1769de8267e0Stimh  * zc_nvlist_dst	zpl property nvlist
1770de8267e0Stimh  * zc_nvlist_dst_size	size of zpl property nvlist
17713cb34c60Sahrens  */
1772bd00f61bSrm static int
1773de8267e0Stimh zfs_ioc_objset_zplprops(zfs_cmd_t *zc)
1774bd00f61bSrm {
1775de8267e0Stimh 	objset_t *os;
1776de8267e0Stimh 	int err;
1777bd00f61bSrm 
1778503ad85cSMatthew Ahrens 	/* XXX reading without owning */
1779503ad85cSMatthew Ahrens 	if (err = dmu_objset_hold(zc->zc_name, FTAG, &os))
1780de8267e0Stimh 		return (err);
1781bd00f61bSrm 
1782bd00f61bSrm 	dmu_objset_fast_stat(os, &zc->zc_objset_stats);
1783bd00f61bSrm 
1784bd00f61bSrm 	/*
1785de8267e0Stimh 	 * NB: nvl_add_zplprop() will read the objset contents,
1786745cd3c5Smaybee 	 * which we aren't supposed to do with a DS_MODE_USER
1787745cd3c5Smaybee 	 * hold, because it could be inconsistent.
1788bd00f61bSrm 	 */
1789de8267e0Stimh 	if (zc->zc_nvlist_dst != NULL &&
1790de8267e0Stimh 	    !zc->zc_objset_stats.dds_inconsistent &&
1791de8267e0Stimh 	    dmu_objset_type(os) == DMU_OST_ZFS) {
1792de8267e0Stimh 		nvlist_t *nv;
1793de8267e0Stimh 
1794de8267e0Stimh 		VERIFY(nvlist_alloc(&nv, NV_UNIQUE_NAME, KM_SLEEP) == 0);
1795de8267e0Stimh 		if ((err = nvl_add_zplprop(os, nv, ZFS_PROP_VERSION)) == 0 &&
1796de8267e0Stimh 		    (err = nvl_add_zplprop(os, nv, ZFS_PROP_NORMALIZE)) == 0 &&
1797de8267e0Stimh 		    (err = nvl_add_zplprop(os, nv, ZFS_PROP_UTF8ONLY)) == 0 &&
1798de8267e0Stimh 		    (err = nvl_add_zplprop(os, nv, ZFS_PROP_CASE)) == 0)
1799de8267e0Stimh 			err = put_nvlist(zc, nv);
1800de8267e0Stimh 		nvlist_free(nv);
1801de8267e0Stimh 	} else {
1802de8267e0Stimh 		err = ENOENT;
1803de8267e0Stimh 	}
1804503ad85cSMatthew Ahrens 	dmu_objset_rele(os, FTAG);
1805de8267e0Stimh 	return (err);
1806bd00f61bSrm }
1807bd00f61bSrm 
180814843421SMatthew Ahrens static boolean_t
180914843421SMatthew Ahrens dataset_name_hidden(const char *name)
181014843421SMatthew Ahrens {
181114843421SMatthew Ahrens 	/*
181214843421SMatthew Ahrens 	 * Skip over datasets that are not visible in this zone,
181314843421SMatthew Ahrens 	 * internal datasets (which have a $ in their name), and
181414843421SMatthew Ahrens 	 * temporary datasets (which have a % in their name).
181514843421SMatthew Ahrens 	 */
181614843421SMatthew Ahrens 	if (strchr(name, '$') != NULL)
181714843421SMatthew Ahrens 		return (B_TRUE);
181814843421SMatthew Ahrens 	if (strchr(name, '%') != NULL)
181914843421SMatthew Ahrens 		return (B_TRUE);
182014843421SMatthew Ahrens 	if (!INGLOBALZONE(curproc) && !zone_dataset_visible(name, NULL))
182114843421SMatthew Ahrens 		return (B_TRUE);
182214843421SMatthew Ahrens 	return (B_FALSE);
182314843421SMatthew Ahrens }
182414843421SMatthew Ahrens 
1825de8267e0Stimh /*
1826de8267e0Stimh  * inputs:
1827de8267e0Stimh  * zc_name		name of filesystem
1828de8267e0Stimh  * zc_cookie		zap cursor
1829de8267e0Stimh  * zc_nvlist_dst_size	size of buffer for property nvlist
1830de8267e0Stimh  *
1831de8267e0Stimh  * outputs:
1832de8267e0Stimh  * zc_name		name of next filesystem
183314843421SMatthew Ahrens  * zc_cookie		zap cursor
1834de8267e0Stimh  * zc_objset_stats	stats
1835de8267e0Stimh  * zc_nvlist_dst	property nvlist
1836de8267e0Stimh  * zc_nvlist_dst_size	size of property nvlist
1837de8267e0Stimh  */
1838fa9e4066Sahrens static int
1839fa9e4066Sahrens zfs_ioc_dataset_list_next(zfs_cmd_t *zc)
1840fa9e4066Sahrens {
184187e5029aSahrens 	objset_t *os;
1842fa9e4066Sahrens 	int error;
1843fa9e4066Sahrens 	char *p;
1844620252bcSChris Kirby 	size_t orig_len = strlen(zc->zc_name);
1845fa9e4066Sahrens 
1846620252bcSChris Kirby top:
1847503ad85cSMatthew Ahrens 	if (error = dmu_objset_hold(zc->zc_name, FTAG, &os)) {
184887e5029aSahrens 		if (error == ENOENT)
184987e5029aSahrens 			error = ESRCH;
185087e5029aSahrens 		return (error);
1851fa9e4066Sahrens 	}
1852fa9e4066Sahrens 
1853fa9e4066Sahrens 	p = strrchr(zc->zc_name, '/');
1854fa9e4066Sahrens 	if (p == NULL || p[1] != '\0')
1855fa9e4066Sahrens 		(void) strlcat(zc->zc_name, "/", sizeof (zc->zc_name));
1856fa9e4066Sahrens 	p = zc->zc_name + strlen(zc->zc_name);
1857fa9e4066Sahrens 
18585c0b6a79SRich Morris 	/*
18595c0b6a79SRich Morris 	 * Pre-fetch the datasets.  dmu_objset_prefetch() always returns 0
18605c0b6a79SRich Morris 	 * but is not declared void because its called by dmu_objset_find().
18615c0b6a79SRich Morris 	 */
18627f73c863SRich Morris 	if (zc->zc_cookie == 0) {
18637f73c863SRich Morris 		uint64_t cookie = 0;
18647f73c863SRich Morris 		int len = sizeof (zc->zc_name) - (p - zc->zc_name);
18657f73c863SRich Morris 
18667f73c863SRich Morris 		while (dmu_dir_list_next(os, len, p, NULL, &cookie) == 0)
18675c0b6a79SRich Morris 			(void) dmu_objset_prefetch(p, NULL);
18687f73c863SRich Morris 	}
18697f73c863SRich Morris 
1870fa9e4066Sahrens 	do {
187187e5029aSahrens 		error = dmu_dir_list_next(os,
187287e5029aSahrens 		    sizeof (zc->zc_name) - (p - zc->zc_name), p,
187387e5029aSahrens 		    NULL, &zc->zc_cookie);
1874fa9e4066Sahrens 		if (error == ENOENT)
1875fa9e4066Sahrens 			error = ESRCH;
1876681d9761SEric Taylor 	} while (error == 0 && dataset_name_hidden(zc->zc_name) &&
1877681d9761SEric Taylor 	    !(zc->zc_iflags & FKIOCTL));
1878503ad85cSMatthew Ahrens 	dmu_objset_rele(os, FTAG);
1879fa9e4066Sahrens 
1880681d9761SEric Taylor 	/*
1881681d9761SEric Taylor 	 * If it's an internal dataset (ie. with a '$' in its name),
1882681d9761SEric Taylor 	 * don't try to get stats for it, otherwise we'll return ENOENT.
1883681d9761SEric Taylor 	 */
1884620252bcSChris Kirby 	if (error == 0 && strchr(zc->zc_name, '$') == NULL) {
188587e5029aSahrens 		error = zfs_ioc_objset_stats(zc); /* fill in the stats */
1886620252bcSChris Kirby 		if (error == ENOENT) {
1887620252bcSChris Kirby 			/* We lost a race with destroy, get the next one. */
1888620252bcSChris Kirby 			zc->zc_name[orig_len] = '\0';
1889620252bcSChris Kirby 			goto top;
1890620252bcSChris Kirby 		}
1891620252bcSChris Kirby 	}
1892fa9e4066Sahrens 	return (error);
1893fa9e4066Sahrens }
1894fa9e4066Sahrens 
18953cb34c60Sahrens /*
18963cb34c60Sahrens  * inputs:
18973cb34c60Sahrens  * zc_name		name of filesystem
18983cb34c60Sahrens  * zc_cookie		zap cursor
18993cb34c60Sahrens  * zc_nvlist_dst_size	size of buffer for property nvlist
19003cb34c60Sahrens  *
19013cb34c60Sahrens  * outputs:
19023cb34c60Sahrens  * zc_name		name of next snapshot
19033cb34c60Sahrens  * zc_objset_stats	stats
19043cb34c60Sahrens  * zc_nvlist_dst	property nvlist
19053cb34c60Sahrens  * zc_nvlist_dst_size	size of property nvlist
19063cb34c60Sahrens  */
1907fa9e4066Sahrens static int
1908fa9e4066Sahrens zfs_ioc_snapshot_list_next(zfs_cmd_t *zc)
1909fa9e4066Sahrens {
191087e5029aSahrens 	objset_t *os;
1911fa9e4066Sahrens 	int error;
1912fa9e4066Sahrens 
1913620252bcSChris Kirby top:
19147cbf8b43SRich Morris 	if (zc->zc_cookie == 0)
19157cbf8b43SRich Morris 		(void) dmu_objset_find(zc->zc_name, dmu_objset_prefetch,
19167cbf8b43SRich Morris 		    NULL, DS_FIND_SNAPSHOTS);
19177cbf8b43SRich Morris 
1918503ad85cSMatthew Ahrens 	error = dmu_objset_hold(zc->zc_name, FTAG, &os);
1919745cd3c5Smaybee 	if (error)
1920745cd3c5Smaybee 		return (error == ENOENT ? ESRCH : error);
1921fa9e4066Sahrens 
1922b81d61a6Slling 	/*
1923b81d61a6Slling 	 * A dataset name of maximum length cannot have any snapshots,
1924b81d61a6Slling 	 * so exit immediately.
1925b81d61a6Slling 	 */
1926b81d61a6Slling 	if (strlcat(zc->zc_name, "@", sizeof (zc->zc_name)) >= MAXNAMELEN) {
1927503ad85cSMatthew Ahrens 		dmu_objset_rele(os, FTAG);
1928b81d61a6Slling 		return (ESRCH);
1929fa9e4066Sahrens 	}
1930fa9e4066Sahrens 
193187e5029aSahrens 	error = dmu_snapshot_list_next(os,
193287e5029aSahrens 	    sizeof (zc->zc_name) - strlen(zc->zc_name),
1933*a7f53a56SChris Kirby 	    zc->zc_name + strlen(zc->zc_name), &zc->zc_obj, &zc->zc_cookie,
1934*a7f53a56SChris Kirby 	    NULL);
1935*a7f53a56SChris Kirby 
1936620252bcSChris Kirby 	if (error == 0) {
1937*a7f53a56SChris Kirby 		dsl_dataset_t *ds;
1938*a7f53a56SChris Kirby 		dsl_pool_t *dp = os->os_dsl_dataset->ds_dir->dd_pool;
1939*a7f53a56SChris Kirby 
1940*a7f53a56SChris Kirby 		/*
1941*a7f53a56SChris Kirby 		 * Since we probably don't have a hold on this snapshot,
1942*a7f53a56SChris Kirby 		 * it's possible that the objsetid could have been destroyed
1943*a7f53a56SChris Kirby 		 * and reused for a new objset. It's OK if this happens during
1944*a7f53a56SChris Kirby 		 * a zfs send operation, since the new createtxg will be
1945*a7f53a56SChris Kirby 		 * beyond the range we're interested in.
1946*a7f53a56SChris Kirby 		 */
1947*a7f53a56SChris Kirby 		rw_enter(&dp->dp_config_rwlock, RW_READER);
1948*a7f53a56SChris Kirby 		error = dsl_dataset_hold_obj(dp, zc->zc_obj, FTAG, &ds);
1949*a7f53a56SChris Kirby 		rw_exit(&dp->dp_config_rwlock);
1950*a7f53a56SChris Kirby 		if (error) {
1951*a7f53a56SChris Kirby 			if (error == ENOENT) {
1952*a7f53a56SChris Kirby 				/* Racing with destroy, get the next one. */
1953*a7f53a56SChris Kirby 				*strchr(zc->zc_name, '@') = '\0';
1954*a7f53a56SChris Kirby 				dmu_objset_rele(os, FTAG);
1955*a7f53a56SChris Kirby 				goto top;
1956*a7f53a56SChris Kirby 			}
1957*a7f53a56SChris Kirby 		} else {
1958*a7f53a56SChris Kirby 			objset_t *ossnap;
1959*a7f53a56SChris Kirby 
1960*a7f53a56SChris Kirby 			error = dmu_objset_from_ds(ds, &ossnap);
1961*a7f53a56SChris Kirby 			if (error == 0)
1962*a7f53a56SChris Kirby 				error = zfs_ioc_objset_stats_impl(zc, ossnap);
1963*a7f53a56SChris Kirby 			dsl_dataset_rele(ds, FTAG);
1964620252bcSChris Kirby 		}
1965620252bcSChris Kirby 	} else if (error == ENOENT) {
1966745cd3c5Smaybee 		error = ESRCH;
1967620252bcSChris Kirby 	}
1968fa9e4066Sahrens 
1969*a7f53a56SChris Kirby 	dmu_objset_rele(os, FTAG);
19703cb34c60Sahrens 	/* if we failed, undo the @ that we tacked on to zc_name */
1971745cd3c5Smaybee 	if (error)
19723cb34c60Sahrens 		*strchr(zc->zc_name, '@') = '\0';
1973fa9e4066Sahrens 	return (error);
1974fa9e4066Sahrens }
1975fa9e4066Sahrens 
197692241e0bSTom Erickson static int
197792241e0bSTom Erickson zfs_prop_set_userquota(const char *dsname, nvpair_t *pair)
1978fa9e4066Sahrens {
197992241e0bSTom Erickson 	const char *propname = nvpair_name(pair);
198092241e0bSTom Erickson 	uint64_t *valary;
198192241e0bSTom Erickson 	unsigned int vallen;
198292241e0bSTom Erickson 	const char *domain;
1983eeb85002STim Haley 	char *dash;
198492241e0bSTom Erickson 	zfs_userquota_prop_t type;
198592241e0bSTom Erickson 	uint64_t rid;
198692241e0bSTom Erickson 	uint64_t quota;
198792241e0bSTom Erickson 	zfsvfs_t *zfsvfs;
198892241e0bSTom Erickson 	int err;
198992241e0bSTom Erickson 
199092241e0bSTom Erickson 	if (nvpair_type(pair) == DATA_TYPE_NVLIST) {
199192241e0bSTom Erickson 		nvlist_t *attrs;
199292241e0bSTom Erickson 		VERIFY(nvpair_value_nvlist(pair, &attrs) == 0);
1993eeb85002STim Haley 		if (nvlist_lookup_nvpair(attrs, ZPROP_VALUE,
1994eeb85002STim Haley 		    &pair) != 0)
1995eeb85002STim Haley 			return (EINVAL);
199692241e0bSTom Erickson 	}
1997e9dbad6fSeschrock 
1998ecd6cf80Smarks 	/*
1999eeb85002STim Haley 	 * A correctly constructed propname is encoded as
200092241e0bSTom Erickson 	 * userquota@<rid>-<domain>.
2001ecd6cf80Smarks 	 */
2002eeb85002STim Haley 	if ((dash = strchr(propname, '-')) == NULL ||
2003eeb85002STim Haley 	    nvpair_value_uint64_array(pair, &valary, &vallen) != 0 ||
2004eeb85002STim Haley 	    vallen != 3)
2005eeb85002STim Haley 		return (EINVAL);
2006eeb85002STim Haley 
2007eeb85002STim Haley 	domain = dash + 1;
2008eeb85002STim Haley 	type = valary[0];
2009eeb85002STim Haley 	rid = valary[1];
2010eeb85002STim Haley 	quota = valary[2];
2011e9dbad6fSeschrock 
20121412a1a2SMark Shellenbaum 	err = zfsvfs_hold(dsname, FTAG, &zfsvfs, B_FALSE);
201392241e0bSTom Erickson 	if (err == 0) {
201492241e0bSTom Erickson 		err = zfs_set_userquota(zfsvfs, type, domain, rid, quota);
201592241e0bSTom Erickson 		zfsvfs_rele(zfsvfs, FTAG);
201692241e0bSTom Erickson 	}
2017e9dbad6fSeschrock 
201892241e0bSTom Erickson 	return (err);
201992241e0bSTom Erickson }
202014843421SMatthew Ahrens 
202192241e0bSTom Erickson /*
202292241e0bSTom Erickson  * If the named property is one that has a special function to set its value,
202392241e0bSTom Erickson  * return 0 on success and a positive error code on failure; otherwise if it is
202492241e0bSTom Erickson  * not one of the special properties handled by this function, return -1.
202592241e0bSTom Erickson  *
2026eeb85002STim Haley  * XXX: It would be better for callers of the property interface if we handled
202792241e0bSTom Erickson  * these special cases in dsl_prop.c (in the dsl layer).
202892241e0bSTom Erickson  */
202992241e0bSTom Erickson static int
203092241e0bSTom Erickson zfs_prop_set_special(const char *dsname, zprop_source_t source,
203192241e0bSTom Erickson     nvpair_t *pair)
203292241e0bSTom Erickson {
203392241e0bSTom Erickson 	const char *propname = nvpair_name(pair);
203492241e0bSTom Erickson 	zfs_prop_t prop = zfs_name_to_prop(propname);
203592241e0bSTom Erickson 	uint64_t intval;
203692241e0bSTom Erickson 	int err;
2037fa9e4066Sahrens 
203892241e0bSTom Erickson 	if (prop == ZPROP_INVAL) {
203992241e0bSTom Erickson 		if (zfs_prop_userquota(propname))
204092241e0bSTom Erickson 			return (zfs_prop_set_userquota(dsname, pair));
204192241e0bSTom Erickson 		return (-1);
204292241e0bSTom Erickson 	}
204314843421SMatthew Ahrens 
204492241e0bSTom Erickson 	if (nvpair_type(pair) == DATA_TYPE_NVLIST) {
204592241e0bSTom Erickson 		nvlist_t *attrs;
204692241e0bSTom Erickson 		VERIFY(nvpair_value_nvlist(pair, &attrs) == 0);
204792241e0bSTom Erickson 		VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE,
204892241e0bSTom Erickson 		    &pair) == 0);
204992241e0bSTom Erickson 	}
2050db870a07Sahrens 
205192241e0bSTom Erickson 	if (zfs_prop_get_type(prop) == PROP_TYPE_STRING)
205292241e0bSTom Erickson 		return (-1);
2053b24ab676SJeff Bonwick 
205492241e0bSTom Erickson 	VERIFY(0 == nvpair_value_uint64(pair, &intval));
205540feaa91Sahrens 
205692241e0bSTom Erickson 	switch (prop) {
205792241e0bSTom Erickson 	case ZFS_PROP_QUOTA:
205892241e0bSTom Erickson 		err = dsl_dir_set_quota(dsname, source, intval);
205992241e0bSTom Erickson 		break;
206092241e0bSTom Erickson 	case ZFS_PROP_REFQUOTA:
206192241e0bSTom Erickson 		err = dsl_dataset_set_quota(dsname, source, intval);
206292241e0bSTom Erickson 		break;
206392241e0bSTom Erickson 	case ZFS_PROP_RESERVATION:
206492241e0bSTom Erickson 		err = dsl_dir_set_reservation(dsname, source, intval);
206592241e0bSTom Erickson 		break;
206692241e0bSTom Erickson 	case ZFS_PROP_REFRESERVATION:
206792241e0bSTom Erickson 		err = dsl_dataset_set_reservation(dsname, source, intval);
206892241e0bSTom Erickson 		break;
206992241e0bSTom Erickson 	case ZFS_PROP_VOLSIZE:
207092241e0bSTom Erickson 		err = zvol_set_volsize(dsname, ddi_driver_major(zfs_dip),
207192241e0bSTom Erickson 		    intval);
207292241e0bSTom Erickson 		break;
207392241e0bSTom Erickson 	case ZFS_PROP_VERSION:
207492241e0bSTom Erickson 	{
207592241e0bSTom Erickson 		zfsvfs_t *zfsvfs;
20769e6eda55Smarks 
20771412a1a2SMark Shellenbaum 		if ((err = zfsvfs_hold(dsname, FTAG, &zfsvfs, B_TRUE)) != 0)
2078b24ab676SJeff Bonwick 			break;
2079b24ab676SJeff Bonwick 
208092241e0bSTom Erickson 		err = zfs_set_version(zfsvfs, intval);
208192241e0bSTom Erickson 		zfsvfs_rele(zfsvfs, FTAG);
2082d0f3f37eSMark Shellenbaum 
208392241e0bSTom Erickson 		if (err == 0 && intval >= ZPL_VERSION_USERSPACE) {
2084b16da2e2SGeorge Wilson 			zfs_cmd_t *zc;
2085b16da2e2SGeorge Wilson 
2086b16da2e2SGeorge Wilson 			zc = kmem_zalloc(sizeof (zfs_cmd_t), KM_SLEEP);
2087b16da2e2SGeorge Wilson 			(void) strcpy(zc->zc_name, dsname);
2088b16da2e2SGeorge Wilson 			(void) zfs_ioc_userspace_upgrade(zc);
2089b16da2e2SGeorge Wilson 			kmem_free(zc, sizeof (zfs_cmd_t));
209040feaa91Sahrens 		}
209192241e0bSTom Erickson 		break;
2092ecd6cf80Smarks 	}
2093ecd6cf80Smarks 
209492241e0bSTom Erickson 	default:
209592241e0bSTom Erickson 		err = -1;
209692241e0bSTom Erickson 	}
2097e9dbad6fSeschrock 
209892241e0bSTom Erickson 	return (err);
209992241e0bSTom Erickson }
2100a9799022Sck 
210192241e0bSTom Erickson /*
210292241e0bSTom Erickson  * This function is best effort. If it fails to set any of the given properties,
210392241e0bSTom Erickson  * it continues to set as many as it can and returns the first error
210492241e0bSTom Erickson  * encountered. If the caller provides a non-NULL errlist, it also gives the
210592241e0bSTom Erickson  * complete list of names of all the properties it failed to set along with the
210692241e0bSTom Erickson  * corresponding error numbers. The caller is responsible for freeing the
210792241e0bSTom Erickson  * returned errlist.
210892241e0bSTom Erickson  *
210992241e0bSTom Erickson  * If every property is set successfully, zero is returned and the list pointed
211092241e0bSTom Erickson  * at by errlist is NULL.
211192241e0bSTom Erickson  */
211292241e0bSTom Erickson int
211392241e0bSTom Erickson zfs_set_prop_nvlist(const char *dsname, zprop_source_t source, nvlist_t *nvl,
211492241e0bSTom Erickson     nvlist_t **errlist)
211592241e0bSTom Erickson {
211692241e0bSTom Erickson 	nvpair_t *pair;
211792241e0bSTom Erickson 	nvpair_t *propval;
211802e383d1STom Erickson 	int rv = 0;
211992241e0bSTom Erickson 	uint64_t intval;
212092241e0bSTom Erickson 	char *strval;
212192241e0bSTom Erickson 	nvlist_t *genericnvl;
212292241e0bSTom Erickson 	nvlist_t *errors;
212392241e0bSTom Erickson 	nvlist_t *retrynvl;
2124e9dbad6fSeschrock 
212592241e0bSTom Erickson 	VERIFY(nvlist_alloc(&genericnvl, NV_UNIQUE_NAME, KM_SLEEP) == 0);
212692241e0bSTom Erickson 	VERIFY(nvlist_alloc(&errors, NV_UNIQUE_NAME, KM_SLEEP) == 0);
212792241e0bSTom Erickson 	VERIFY(nvlist_alloc(&retrynvl, NV_UNIQUE_NAME, KM_SLEEP) == 0);
2128a9799022Sck 
212992241e0bSTom Erickson retry:
213092241e0bSTom Erickson 	pair = NULL;
213192241e0bSTom Erickson 	while ((pair = nvlist_next_nvpair(nvl, pair)) != NULL) {
213292241e0bSTom Erickson 		const char *propname = nvpair_name(pair);
213392241e0bSTom Erickson 		zfs_prop_t prop = zfs_name_to_prop(propname);
2134cfa69fd2STom Erickson 		int err = 0;
2135e9dbad6fSeschrock 
213692241e0bSTom Erickson 		/* decode the property value */
213792241e0bSTom Erickson 		propval = pair;
213892241e0bSTom Erickson 		if (nvpair_type(pair) == DATA_TYPE_NVLIST) {
213992241e0bSTom Erickson 			nvlist_t *attrs;
214092241e0bSTom Erickson 			VERIFY(nvpair_value_nvlist(pair, &attrs) == 0);
2141eeb85002STim Haley 			if (nvlist_lookup_nvpair(attrs, ZPROP_VALUE,
2142eeb85002STim Haley 			    &propval) != 0)
2143eeb85002STim Haley 				err = EINVAL;
214414843421SMatthew Ahrens 		}
2145e9dbad6fSeschrock 
214692241e0bSTom Erickson 		/* Validate value type */
2147eeb85002STim Haley 		if (err == 0 && prop == ZPROP_INVAL) {
214892241e0bSTom Erickson 			if (zfs_prop_user(propname)) {
214992241e0bSTom Erickson 				if (nvpair_type(propval) != DATA_TYPE_STRING)
215092241e0bSTom Erickson 					err = EINVAL;
215192241e0bSTom Erickson 			} else if (zfs_prop_userquota(propname)) {
215292241e0bSTom Erickson 				if (nvpair_type(propval) !=
215392241e0bSTom Erickson 				    DATA_TYPE_UINT64_ARRAY)
215492241e0bSTom Erickson 					err = EINVAL;
21554201a95eSRic Aleshire 			}
2156eeb85002STim Haley 		} else if (err == 0) {
215792241e0bSTom Erickson 			if (nvpair_type(propval) == DATA_TYPE_STRING) {
215892241e0bSTom Erickson 				if (zfs_prop_get_type(prop) != PROP_TYPE_STRING)
215992241e0bSTom Erickson 					err = EINVAL;
216092241e0bSTom Erickson 			} else if (nvpair_type(propval) == DATA_TYPE_UINT64) {
2161a2eea2e1Sahrens 				const char *unused;
2162a2eea2e1Sahrens 
216392241e0bSTom Erickson 				VERIFY(nvpair_value_uint64(propval,
216492241e0bSTom Erickson 				    &intval) == 0);
2165e9dbad6fSeschrock 
2166e9dbad6fSeschrock 				switch (zfs_prop_get_type(prop)) {
216791ebeef5Sahrens 				case PROP_TYPE_NUMBER:
2168e9dbad6fSeschrock 					break;
216991ebeef5Sahrens 				case PROP_TYPE_STRING:
217092241e0bSTom Erickson 					err = EINVAL;
217192241e0bSTom Erickson 					break;
217291ebeef5Sahrens 				case PROP_TYPE_INDEX:
2173acd76fe5Seschrock 					if (zfs_prop_index_to_string(prop,
217492241e0bSTom Erickson 					    intval, &unused) != 0)
217592241e0bSTom Erickson 						err = EINVAL;
2176e9dbad6fSeschrock 					break;
2177e9dbad6fSeschrock 				default:
2178e7437265Sahrens 					cmn_err(CE_PANIC,
2179e7437265Sahrens 					    "unknown property type");
2180e9dbad6fSeschrock 				}
2181e9dbad6fSeschrock 			} else {
218292241e0bSTom Erickson 				err = EINVAL;
2183e9dbad6fSeschrock 			}
2184e9dbad6fSeschrock 		}
218592241e0bSTom Erickson 
218692241e0bSTom Erickson 		/* Validate permissions */
218792241e0bSTom Erickson 		if (err == 0)
218892241e0bSTom Erickson 			err = zfs_check_settable(dsname, pair, CRED());
218992241e0bSTom Erickson 
219092241e0bSTom Erickson 		if (err == 0) {
219192241e0bSTom Erickson 			err = zfs_prop_set_special(dsname, source, pair);
219292241e0bSTom Erickson 			if (err == -1) {
219392241e0bSTom Erickson 				/*
219492241e0bSTom Erickson 				 * For better performance we build up a list of
219592241e0bSTom Erickson 				 * properties to set in a single transaction.
219692241e0bSTom Erickson 				 */
219792241e0bSTom Erickson 				err = nvlist_add_nvpair(genericnvl, pair);
219892241e0bSTom Erickson 			} else if (err != 0 && nvl != retrynvl) {
219992241e0bSTom Erickson 				/*
220092241e0bSTom Erickson 				 * This may be a spurious error caused by
220192241e0bSTom Erickson 				 * receiving quota and reservation out of order.
220292241e0bSTom Erickson 				 * Try again in a second pass.
220392241e0bSTom Erickson 				 */
220492241e0bSTom Erickson 				err = nvlist_add_nvpair(retrynvl, pair);
220592241e0bSTom Erickson 			}
220692241e0bSTom Erickson 		}
220792241e0bSTom Erickson 
220892241e0bSTom Erickson 		if (err != 0)
220992241e0bSTom Erickson 			VERIFY(nvlist_add_int32(errors, propname, err) == 0);
2210e9dbad6fSeschrock 	}
2211e9dbad6fSeschrock 
221292241e0bSTom Erickson 	if (nvl != retrynvl && !nvlist_empty(retrynvl)) {
221392241e0bSTom Erickson 		nvl = retrynvl;
221492241e0bSTom Erickson 		goto retry;
221592241e0bSTom Erickson 	}
221692241e0bSTom Erickson 
221792241e0bSTom Erickson 	if (!nvlist_empty(genericnvl) &&
221892241e0bSTom Erickson 	    dsl_props_set(dsname, source, genericnvl) != 0) {
221992241e0bSTom Erickson 		/*
222092241e0bSTom Erickson 		 * If this fails, we still want to set as many properties as we
222192241e0bSTom Erickson 		 * can, so try setting them individually.
222292241e0bSTom Erickson 		 */
222392241e0bSTom Erickson 		pair = NULL;
222492241e0bSTom Erickson 		while ((pair = nvlist_next_nvpair(genericnvl, pair)) != NULL) {
222592241e0bSTom Erickson 			const char *propname = nvpair_name(pair);
2226cfa69fd2STom Erickson 			int err = 0;
222792241e0bSTom Erickson 
222892241e0bSTom Erickson 			propval = pair;
222992241e0bSTom Erickson 			if (nvpair_type(pair) == DATA_TYPE_NVLIST) {
223092241e0bSTom Erickson 				nvlist_t *attrs;
223192241e0bSTom Erickson 				VERIFY(nvpair_value_nvlist(pair, &attrs) == 0);
223292241e0bSTom Erickson 				VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE,
223392241e0bSTom Erickson 				    &propval) == 0);
223492241e0bSTom Erickson 			}
223592241e0bSTom Erickson 
223692241e0bSTom Erickson 			if (nvpair_type(propval) == DATA_TYPE_STRING) {
223792241e0bSTom Erickson 				VERIFY(nvpair_value_string(propval,
223892241e0bSTom Erickson 				    &strval) == 0);
223992241e0bSTom Erickson 				err = dsl_prop_set(dsname, propname, source, 1,
224092241e0bSTom Erickson 				    strlen(strval) + 1, strval);
224192241e0bSTom Erickson 			} else {
224292241e0bSTom Erickson 				VERIFY(nvpair_value_uint64(propval,
224392241e0bSTom Erickson 				    &intval) == 0);
224492241e0bSTom Erickson 				err = dsl_prop_set(dsname, propname, source, 8,
224592241e0bSTom Erickson 				    1, &intval);
224692241e0bSTom Erickson 			}
224792241e0bSTom Erickson 
224892241e0bSTom Erickson 			if (err != 0) {
224992241e0bSTom Erickson 				VERIFY(nvlist_add_int32(errors, propname,
225092241e0bSTom Erickson 				    err) == 0);
225192241e0bSTom Erickson 			}
225292241e0bSTom Erickson 		}
22535c0b6a79SRich Morris 	}
22545c0b6a79SRich Morris 	nvlist_free(genericnvl);
225592241e0bSTom Erickson 	nvlist_free(retrynvl);
225692241e0bSTom Erickson 
225792241e0bSTom Erickson 	if ((pair = nvlist_next_nvpair(errors, NULL)) == NULL) {
225892241e0bSTom Erickson 		nvlist_free(errors);
225992241e0bSTom Erickson 		errors = NULL;
226092241e0bSTom Erickson 	} else {
226192241e0bSTom Erickson 		VERIFY(nvpair_value_int32(pair, &rv) == 0);
226292241e0bSTom Erickson 	}
226392241e0bSTom Erickson 
226492241e0bSTom Erickson 	if (errlist == NULL)
226592241e0bSTom Erickson 		nvlist_free(errors);
226692241e0bSTom Erickson 	else
226792241e0bSTom Erickson 		*errlist = errors;
226892241e0bSTom Erickson 
226992241e0bSTom Erickson 	return (rv);
2270fa9e4066Sahrens }
2271fa9e4066Sahrens 
2272ea2f5b9eSMatthew Ahrens /*
2273ea2f5b9eSMatthew Ahrens  * Check that all the properties are valid user properties.
2274ea2f5b9eSMatthew Ahrens  */
2275ea2f5b9eSMatthew Ahrens static int
2276ea2f5b9eSMatthew Ahrens zfs_check_userprops(char *fsname, nvlist_t *nvl)
2277ea2f5b9eSMatthew Ahrens {
227892241e0bSTom Erickson 	nvpair_t *pair = NULL;
2279ea2f5b9eSMatthew Ahrens 	int error = 0;
2280ea2f5b9eSMatthew Ahrens 
228192241e0bSTom Erickson 	while ((pair = nvlist_next_nvpair(nvl, pair)) != NULL) {
228292241e0bSTom Erickson 		const char *propname = nvpair_name(pair);
2283ea2f5b9eSMatthew Ahrens 		char *valstr;
2284ea2f5b9eSMatthew Ahrens 
2285ea2f5b9eSMatthew Ahrens 		if (!zfs_prop_user(propname) ||
228692241e0bSTom Erickson 		    nvpair_type(pair) != DATA_TYPE_STRING)
2287ea2f5b9eSMatthew Ahrens 			return (EINVAL);
2288ea2f5b9eSMatthew Ahrens 
2289ea2f5b9eSMatthew Ahrens 		if (error = zfs_secpolicy_write_perms(fsname,
2290ea2f5b9eSMatthew Ahrens 		    ZFS_DELEG_PERM_USERPROP, CRED()))
2291ea2f5b9eSMatthew Ahrens 			return (error);
2292ea2f5b9eSMatthew Ahrens 
2293ea2f5b9eSMatthew Ahrens 		if (strlen(propname) >= ZAP_MAXNAMELEN)
2294ea2f5b9eSMatthew Ahrens 			return (ENAMETOOLONG);
2295ea2f5b9eSMatthew Ahrens 
229692241e0bSTom Erickson 		VERIFY(nvpair_value_string(pair, &valstr) == 0);
2297ea2f5b9eSMatthew Ahrens 		if (strlen(valstr) >= ZAP_MAXVALUELEN)
2298ea2f5b9eSMatthew Ahrens 			return (E2BIG);
2299ea2f5b9eSMatthew Ahrens 	}
2300ea2f5b9eSMatthew Ahrens 	return (0);
2301ea2f5b9eSMatthew Ahrens }
2302ea2f5b9eSMatthew Ahrens 
230392241e0bSTom Erickson static void
230492241e0bSTom Erickson props_skip(nvlist_t *props, nvlist_t *skipped, nvlist_t **newprops)
230592241e0bSTom Erickson {
230692241e0bSTom Erickson 	nvpair_t *pair;
230792241e0bSTom Erickson 
230892241e0bSTom Erickson 	VERIFY(nvlist_alloc(newprops, NV_UNIQUE_NAME, KM_SLEEP) == 0);
230992241e0bSTom Erickson 
231092241e0bSTom Erickson 	pair = NULL;
231192241e0bSTom Erickson 	while ((pair = nvlist_next_nvpair(props, pair)) != NULL) {
231292241e0bSTom Erickson 		if (nvlist_exists(skipped, nvpair_name(pair)))
231392241e0bSTom Erickson 			continue;
231492241e0bSTom Erickson 
231592241e0bSTom Erickson 		VERIFY(nvlist_add_nvpair(*newprops, pair) == 0);
231692241e0bSTom Erickson 	}
231792241e0bSTom Erickson }
231892241e0bSTom Erickson 
231992241e0bSTom Erickson static int
232092241e0bSTom Erickson clear_received_props(objset_t *os, const char *fs, nvlist_t *props,
232192241e0bSTom Erickson     nvlist_t *skipped)
232292241e0bSTom Erickson {
232392241e0bSTom Erickson 	int err = 0;
232492241e0bSTom Erickson 	nvlist_t *cleared_props = NULL;
232592241e0bSTom Erickson 	props_skip(props, skipped, &cleared_props);
232692241e0bSTom Erickson 	if (!nvlist_empty(cleared_props)) {
232792241e0bSTom Erickson 		/*
232892241e0bSTom Erickson 		 * Acts on local properties until the dataset has received
232992241e0bSTom Erickson 		 * properties at least once on or after SPA_VERSION_RECVD_PROPS.
233092241e0bSTom Erickson 		 */
233192241e0bSTom Erickson 		zprop_source_t flags = (ZPROP_SRC_NONE |
233292241e0bSTom Erickson 		    (dsl_prop_get_hasrecvd(os) ? ZPROP_SRC_RECEIVED : 0));
233392241e0bSTom Erickson 		err = zfs_set_prop_nvlist(fs, flags, cleared_props, NULL);
233492241e0bSTom Erickson 	}
233592241e0bSTom Erickson 	nvlist_free(cleared_props);
233692241e0bSTom Erickson 	return (err);
233792241e0bSTom Erickson }
233892241e0bSTom Erickson 
23393cb34c60Sahrens /*
23403cb34c60Sahrens  * inputs:
23413cb34c60Sahrens  * zc_name		name of filesystem
23425c0b6a79SRich Morris  * zc_value		name of property to set
23433cb34c60Sahrens  * zc_nvlist_src{_size}	nvlist of properties to apply
234492241e0bSTom Erickson  * zc_cookie		received properties flag
23453cb34c60Sahrens  *
234692241e0bSTom Erickson  * outputs:
234792241e0bSTom Erickson  * zc_nvlist_dst{_size} error for each unapplied received property
23483cb34c60Sahrens  */
2349fa9e4066Sahrens static int
2350e9dbad6fSeschrock zfs_ioc_set_prop(zfs_cmd_t *zc)
2351fa9e4066Sahrens {
2352e9dbad6fSeschrock 	nvlist_t *nvl;
235392241e0bSTom Erickson 	boolean_t received = zc->zc_cookie;
235492241e0bSTom Erickson 	zprop_source_t source = (received ? ZPROP_SRC_RECEIVED :
235592241e0bSTom Erickson 	    ZPROP_SRC_LOCAL);
235692241e0bSTom Erickson 	nvlist_t *errors = NULL;
2357e9dbad6fSeschrock 	int error;
2358e9dbad6fSeschrock 
2359990b4856Slling 	if ((error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
2360478ed9adSEric Taylor 	    zc->zc_iflags, &nvl)) != 0)
2361e9dbad6fSeschrock 		return (error);
2362e9dbad6fSeschrock 
236392241e0bSTom Erickson 	if (received) {
2364bb0ade09Sahrens 		nvlist_t *origprops;
2365bb0ade09Sahrens 		objset_t *os;
2366bb0ade09Sahrens 
2367503ad85cSMatthew Ahrens 		if (dmu_objset_hold(zc->zc_name, FTAG, &os) == 0) {
236892241e0bSTom Erickson 			if (dsl_prop_get_received(os, &origprops) == 0) {
236992241e0bSTom Erickson 				(void) clear_received_props(os,
237092241e0bSTom Erickson 				    zc->zc_name, origprops, nvl);
2371bb0ade09Sahrens 				nvlist_free(origprops);
2372bb0ade09Sahrens 			}
237392241e0bSTom Erickson 
237492241e0bSTom Erickson 			dsl_prop_set_hasrecvd(os);
2375503ad85cSMatthew Ahrens 			dmu_objset_rele(os, FTAG);
2376bb0ade09Sahrens 		}
2377bb0ade09Sahrens 	}
2378bb0ade09Sahrens 
237992241e0bSTom Erickson 	error = zfs_set_prop_nvlist(zc->zc_name, source, nvl, &errors);
238092241e0bSTom Erickson 
238192241e0bSTom Erickson 	if (zc->zc_nvlist_dst != NULL && errors != NULL) {
238292241e0bSTom Erickson 		(void) put_nvlist(zc, errors);
238392241e0bSTom Erickson 	}
2384ecd6cf80Smarks 
238592241e0bSTom Erickson 	nvlist_free(errors);
2386e9dbad6fSeschrock 	nvlist_free(nvl);
2387e9dbad6fSeschrock 	return (error);
2388fa9e4066Sahrens }
2389fa9e4066Sahrens 
23903cb34c60Sahrens /*
23913cb34c60Sahrens  * inputs:
23923cb34c60Sahrens  * zc_name		name of filesystem
23933cb34c60Sahrens  * zc_value		name of property to inherit
239492241e0bSTom Erickson  * zc_cookie		revert to received value if TRUE
23953cb34c60Sahrens  *
23963cb34c60Sahrens  * outputs:		none
23973cb34c60Sahrens  */
2398e45ce728Sahrens static int
2399e45ce728Sahrens zfs_ioc_inherit_prop(zfs_cmd_t *zc)
2400e45ce728Sahrens {
240192241e0bSTom Erickson 	const char *propname = zc->zc_value;
240292241e0bSTom Erickson 	zfs_prop_t prop = zfs_name_to_prop(propname);
240392241e0bSTom Erickson 	boolean_t received = zc->zc_cookie;
240492241e0bSTom Erickson 	zprop_source_t source = (received
240592241e0bSTom Erickson 	    ? ZPROP_SRC_NONE		/* revert to received value, if any */
240692241e0bSTom Erickson 	    : ZPROP_SRC_INHERITED);	/* explicitly inherit */
240792241e0bSTom Erickson 
240892241e0bSTom Erickson 	if (received) {
240992241e0bSTom Erickson 		nvlist_t *dummy;
241092241e0bSTom Erickson 		nvpair_t *pair;
241192241e0bSTom Erickson 		zprop_type_t type;
241292241e0bSTom Erickson 		int err;
241392241e0bSTom Erickson 
241492241e0bSTom Erickson 		/*
241592241e0bSTom Erickson 		 * zfs_prop_set_special() expects properties in the form of an
241692241e0bSTom Erickson 		 * nvpair with type info.
241792241e0bSTom Erickson 		 */
241892241e0bSTom Erickson 		if (prop == ZPROP_INVAL) {
241992241e0bSTom Erickson 			if (!zfs_prop_user(propname))
242092241e0bSTom Erickson 				return (EINVAL);
242192241e0bSTom Erickson 
242292241e0bSTom Erickson 			type = PROP_TYPE_STRING;
2423a79992aaSTom Erickson 		} else if (prop == ZFS_PROP_VOLSIZE ||
2424a79992aaSTom Erickson 		    prop == ZFS_PROP_VERSION) {
2425a79992aaSTom Erickson 			return (EINVAL);
242692241e0bSTom Erickson 		} else {
242792241e0bSTom Erickson 			type = zfs_prop_get_type(prop);
242892241e0bSTom Erickson 		}
242992241e0bSTom Erickson 
243092241e0bSTom Erickson 		VERIFY(nvlist_alloc(&dummy, NV_UNIQUE_NAME, KM_SLEEP) == 0);
243192241e0bSTom Erickson 
243292241e0bSTom Erickson 		switch (type) {
243392241e0bSTom Erickson 		case PROP_TYPE_STRING:
243492241e0bSTom Erickson 			VERIFY(0 == nvlist_add_string(dummy, propname, ""));
243592241e0bSTom Erickson 			break;
243692241e0bSTom Erickson 		case PROP_TYPE_NUMBER:
243792241e0bSTom Erickson 		case PROP_TYPE_INDEX:
243892241e0bSTom Erickson 			VERIFY(0 == nvlist_add_uint64(dummy, propname, 0));
243992241e0bSTom Erickson 			break;
244092241e0bSTom Erickson 		default:
244192241e0bSTom Erickson 			nvlist_free(dummy);
244292241e0bSTom Erickson 			return (EINVAL);
244392241e0bSTom Erickson 		}
244492241e0bSTom Erickson 
244592241e0bSTom Erickson 		pair = nvlist_next_nvpair(dummy, NULL);
244692241e0bSTom Erickson 		err = zfs_prop_set_special(zc->zc_name, source, pair);
244792241e0bSTom Erickson 		nvlist_free(dummy);
244892241e0bSTom Erickson 		if (err != -1)
244992241e0bSTom Erickson 			return (err); /* special property already handled */
245092241e0bSTom Erickson 	} else {
245192241e0bSTom Erickson 		/*
245292241e0bSTom Erickson 		 * Only check this in the non-received case. We want to allow
245392241e0bSTom Erickson 		 * 'inherit -S' to revert non-inheritable properties like quota
245492241e0bSTom Erickson 		 * and reservation to the received or default values even though
245592241e0bSTom Erickson 		 * they are not considered inheritable.
245692241e0bSTom Erickson 		 */
245792241e0bSTom Erickson 		if (prop != ZPROP_INVAL && !zfs_prop_inheritable(prop))
245892241e0bSTom Erickson 			return (EINVAL);
245992241e0bSTom Erickson 	}
246092241e0bSTom Erickson 
2461e45ce728Sahrens 	/* the property name has been validated by zfs_secpolicy_inherit() */
246292241e0bSTom Erickson 	return (dsl_prop_set(zc->zc_name, zc->zc_value, source, 0, 0, NULL));
2463e45ce728Sahrens }
2464e45ce728Sahrens 
2465b1b8ab34Slling static int
246611a41203Slling zfs_ioc_pool_set_props(zfs_cmd_t *zc)
2467b1b8ab34Slling {
2468990b4856Slling 	nvlist_t *props;
2469b1b8ab34Slling 	spa_t *spa;
2470990b4856Slling 	int error;
247192241e0bSTom Erickson 	nvpair_t *pair;
2472b1b8ab34Slling 
247392241e0bSTom Erickson 	if (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
247492241e0bSTom Erickson 	    zc->zc_iflags, &props))
2475b1b8ab34Slling 		return (error);
2476b1b8ab34Slling 
2477379c004dSEric Schrock 	/*
2478379c004dSEric Schrock 	 * If the only property is the configfile, then just do a spa_lookup()
2479379c004dSEric Schrock 	 * to handle the faulted case.
2480379c004dSEric Schrock 	 */
248192241e0bSTom Erickson 	pair = nvlist_next_nvpair(props, NULL);
248292241e0bSTom Erickson 	if (pair != NULL && strcmp(nvpair_name(pair),
2483379c004dSEric Schrock 	    zpool_prop_to_name(ZPOOL_PROP_CACHEFILE)) == 0 &&
248492241e0bSTom Erickson 	    nvlist_next_nvpair(props, pair) == NULL) {
2485379c004dSEric Schrock 		mutex_enter(&spa_namespace_lock);
2486379c004dSEric Schrock 		if ((spa = spa_lookup(zc->zc_name)) != NULL) {
2487379c004dSEric Schrock 			spa_configfile_set(spa, props, B_FALSE);
2488379c004dSEric Schrock 			spa_config_sync(spa, B_FALSE, B_TRUE);
2489379c004dSEric Schrock 		}
2490379c004dSEric Schrock 		mutex_exit(&spa_namespace_lock);
2491b693757aSEric Schrock 		if (spa != NULL) {
2492b693757aSEric Schrock 			nvlist_free(props);
2493379c004dSEric Schrock 			return (0);
2494b693757aSEric Schrock 		}
2495379c004dSEric Schrock 	}
2496379c004dSEric Schrock 
2497b1b8ab34Slling 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) {
2498990b4856Slling 		nvlist_free(props);
2499b1b8ab34Slling 		return (error);
2500b1b8ab34Slling 	}
2501b1b8ab34Slling 
2502990b4856Slling 	error = spa_prop_set(spa, props);
2503b1b8ab34Slling 
2504990b4856Slling 	nvlist_free(props);
2505b1b8ab34Slling 	spa_close(spa, FTAG);
2506b1b8ab34Slling 
2507b1b8ab34Slling 	return (error);
2508b1b8ab34Slling }
2509b1b8ab34Slling 
2510b1b8ab34Slling static int
251111a41203Slling zfs_ioc_pool_get_props(zfs_cmd_t *zc)
2512b1b8ab34Slling {
2513b1b8ab34Slling 	spa_t *spa;
2514b1b8ab34Slling 	int error;
2515b1b8ab34Slling 	nvlist_t *nvp = NULL;
2516b1b8ab34Slling 
2517379c004dSEric Schrock 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) {
2518379c004dSEric Schrock 		/*
2519379c004dSEric Schrock 		 * If the pool is faulted, there may be properties we can still
2520379c004dSEric Schrock 		 * get (such as altroot and cachefile), so attempt to get them
2521379c004dSEric Schrock 		 * anyway.
2522379c004dSEric Schrock 		 */
2523379c004dSEric Schrock 		mutex_enter(&spa_namespace_lock);
2524379c004dSEric Schrock 		if ((spa = spa_lookup(zc->zc_name)) != NULL)
2525379c004dSEric Schrock 			error = spa_prop_get(spa, &nvp);
2526379c004dSEric Schrock 		mutex_exit(&spa_namespace_lock);
2527379c004dSEric Schrock 	} else {
2528379c004dSEric Schrock 		error = spa_prop_get(spa, &nvp);
2529379c004dSEric Schrock 		spa_close(spa, FTAG);
2530379c004dSEric Schrock 	}
2531b1b8ab34Slling 
2532b1b8ab34Slling 	if (error == 0 && zc->zc_nvlist_dst != NULL)
2533b1b8ab34Slling 		error = put_nvlist(zc, nvp);
2534b1b8ab34Slling 	else
2535b1b8ab34Slling 		error = EFAULT;
2536b1b8ab34Slling 
2537379c004dSEric Schrock 	nvlist_free(nvp);
2538b1b8ab34Slling 	return (error);
2539b1b8ab34Slling }
2540b1b8ab34Slling 
25413cb34c60Sahrens /*
25423cb34c60Sahrens  * inputs:
25433cb34c60Sahrens  * zc_name		name of filesystem
25443cb34c60Sahrens  * zc_nvlist_src{_size}	nvlist of delegated permissions
25453cb34c60Sahrens  * zc_perm_action	allow/unallow flag
25463cb34c60Sahrens  *
25473cb34c60Sahrens  * outputs:		none
25483cb34c60Sahrens  */
2549ecd6cf80Smarks static int
2550ecd6cf80Smarks zfs_ioc_set_fsacl(zfs_cmd_t *zc)
2551ecd6cf80Smarks {
2552ecd6cf80Smarks 	int error;
2553ecd6cf80Smarks 	nvlist_t *fsaclnv = NULL;
2554ecd6cf80Smarks 
2555990b4856Slling 	if ((error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
2556478ed9adSEric Taylor 	    zc->zc_iflags, &fsaclnv)) != 0)
2557ecd6cf80Smarks 		return (error);
2558ecd6cf80Smarks 
2559ecd6cf80Smarks 	/*
2560ecd6cf80Smarks 	 * Verify nvlist is constructed correctly
2561ecd6cf80Smarks 	 */
2562ecd6cf80Smarks 	if ((error = zfs_deleg_verify_nvlist(fsaclnv)) != 0) {
2563ecd6cf80Smarks 		nvlist_free(fsaclnv);
2564ecd6cf80Smarks 		return (EINVAL);
2565ecd6cf80Smarks 	}
2566ecd6cf80Smarks 
2567ecd6cf80Smarks 	/*
2568ecd6cf80Smarks 	 * If we don't have PRIV_SYS_MOUNT, then validate
2569ecd6cf80Smarks 	 * that user is allowed to hand out each permission in
2570ecd6cf80Smarks 	 * the nvlist(s)
2571ecd6cf80Smarks 	 */
2572ecd6cf80Smarks 
257391ebeef5Sahrens 	error = secpolicy_zfs(CRED());
2574ecd6cf80Smarks 	if (error) {
257591ebeef5Sahrens 		if (zc->zc_perm_action == B_FALSE) {
257691ebeef5Sahrens 			error = dsl_deleg_can_allow(zc->zc_name,
257791ebeef5Sahrens 			    fsaclnv, CRED());
257891ebeef5Sahrens 		} else {
257991ebeef5Sahrens 			error = dsl_deleg_can_unallow(zc->zc_name,
258091ebeef5Sahrens 			    fsaclnv, CRED());
258191ebeef5Sahrens 		}
2582ecd6cf80Smarks 	}
2583ecd6cf80Smarks 
2584ecd6cf80Smarks 	if (error == 0)
2585ecd6cf80Smarks 		error = dsl_deleg_set(zc->zc_name, fsaclnv, zc->zc_perm_action);
2586ecd6cf80Smarks 
2587ecd6cf80Smarks 	nvlist_free(fsaclnv);
2588ecd6cf80Smarks 	return (error);
2589ecd6cf80Smarks }
2590ecd6cf80Smarks 
25913cb34c60Sahrens /*
25923cb34c60Sahrens  * inputs:
25933cb34c60Sahrens  * zc_name		name of filesystem
25943cb34c60Sahrens  *
25953cb34c60Sahrens  * outputs:
25963cb34c60Sahrens  * zc_nvlist_src{_size}	nvlist of delegated permissions
25973cb34c60Sahrens  */
2598ecd6cf80Smarks static int
2599ecd6cf80Smarks zfs_ioc_get_fsacl(zfs_cmd_t *zc)
2600ecd6cf80Smarks {
2601ecd6cf80Smarks 	nvlist_t *nvp;
2602ecd6cf80Smarks 	int error;
2603ecd6cf80Smarks 
2604ecd6cf80Smarks 	if ((error = dsl_deleg_get(zc->zc_name, &nvp)) == 0) {
2605ecd6cf80Smarks 		error = put_nvlist(zc, nvp);
2606ecd6cf80Smarks 		nvlist_free(nvp);
2607ecd6cf80Smarks 	}
2608ecd6cf80Smarks 
2609ecd6cf80Smarks 	return (error);
2610ecd6cf80Smarks }
2611ecd6cf80Smarks 
2612fa9e4066Sahrens /*
2613fa9e4066Sahrens  * Search the vfs list for a specified resource.  Returns a pointer to it
2614fa9e4066Sahrens  * or NULL if no suitable entry is found. The caller of this routine
2615fa9e4066Sahrens  * is responsible for releasing the returned vfs pointer.
2616fa9e4066Sahrens  */
2617fa9e4066Sahrens static vfs_t *
2618fa9e4066Sahrens zfs_get_vfs(const char *resource)
2619fa9e4066Sahrens {
2620fa9e4066Sahrens 	struct vfs *vfsp;
2621fa9e4066Sahrens 	struct vfs *vfs_found = NULL;
2622fa9e4066Sahrens 
2623fa9e4066Sahrens 	vfs_list_read_lock();
2624fa9e4066Sahrens 	vfsp = rootvfs;
2625fa9e4066Sahrens 	do {
2626fa9e4066Sahrens 		if (strcmp(refstr_value(vfsp->vfs_resource), resource) == 0) {
2627fa9e4066Sahrens 			VFS_HOLD(vfsp);
2628fa9e4066Sahrens 			vfs_found = vfsp;
2629fa9e4066Sahrens 			break;
2630fa9e4066Sahrens 		}
2631fa9e4066Sahrens 		vfsp = vfsp->vfs_next;
2632fa9e4066Sahrens 	} while (vfsp != rootvfs);
2633fa9e4066Sahrens 	vfs_list_unlock();
2634fa9e4066Sahrens 	return (vfs_found);
2635fa9e4066Sahrens }
2636fa9e4066Sahrens 
2637ecd6cf80Smarks /* ARGSUSED */
2638fa9e4066Sahrens static void
2639ecd6cf80Smarks zfs_create_cb(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx)
2640fa9e4066Sahrens {
2641da6c28aaSamw 	zfs_creat_t *zct = arg;
2642da6c28aaSamw 
2643de8267e0Stimh 	zfs_create_fs(os, cr, zct->zct_zplprops, tx);
2644da6c28aaSamw }
2645da6c28aaSamw 
2646de8267e0Stimh #define	ZFS_PROP_UNDEFINED	((uint64_t)-1)
2647da6c28aaSamw 
2648da6c28aaSamw /*
2649de8267e0Stimh  * inputs:
26500a48a24eStimh  * createprops		list of properties requested by creator
26510a48a24eStimh  * default_zplver	zpl version to use if unspecified in createprops
26520a48a24eStimh  * fuids_ok		fuids allowed in this version of the spa?
26530a48a24eStimh  * os			parent objset pointer (NULL if root fs)
2654de8267e0Stimh  *
2655de8267e0Stimh  * outputs:
2656de8267e0Stimh  * zplprops	values for the zplprops we attach to the master node object
26570a48a24eStimh  * is_ci	true if requested file system will be purely case-insensitive
2658da6c28aaSamw  *
2659de8267e0Stimh  * Determine the settings for utf8only, normalization and
2660de8267e0Stimh  * casesensitivity.  Specific values may have been requested by the
2661de8267e0Stimh  * creator and/or we can inherit values from the parent dataset.  If
2662de8267e0Stimh  * the file system is of too early a vintage, a creator can not
2663de8267e0Stimh  * request settings for these properties, even if the requested
2664de8267e0Stimh  * setting is the default value.  We don't actually want to create dsl
2665de8267e0Stimh  * properties for these, so remove them from the source nvlist after
2666de8267e0Stimh  * processing.
2667da6c28aaSamw  */
2668da6c28aaSamw static int
266914843421SMatthew Ahrens zfs_fill_zplprops_impl(objset_t *os, uint64_t zplver,
26700a586ceaSMark Shellenbaum     boolean_t fuids_ok, boolean_t sa_ok, nvlist_t *createprops,
26710a586ceaSMark Shellenbaum     nvlist_t *zplprops, boolean_t *is_ci)
2672da6c28aaSamw {
2673de8267e0Stimh 	uint64_t sense = ZFS_PROP_UNDEFINED;
2674de8267e0Stimh 	uint64_t norm = ZFS_PROP_UNDEFINED;
2675de8267e0Stimh 	uint64_t u8 = ZFS_PROP_UNDEFINED;
2676da6c28aaSamw 
2677de8267e0Stimh 	ASSERT(zplprops != NULL);
2678da6c28aaSamw 
2679de8267e0Stimh 	/*
2680de8267e0Stimh 	 * Pull out creator prop choices, if any.
2681de8267e0Stimh 	 */
2682de8267e0Stimh 	if (createprops) {
26830a48a24eStimh 		(void) nvlist_lookup_uint64(createprops,
26840a48a24eStimh 		    zfs_prop_to_name(ZFS_PROP_VERSION), &zplver);
2685de8267e0Stimh 		(void) nvlist_lookup_uint64(createprops,
2686de8267e0Stimh 		    zfs_prop_to_name(ZFS_PROP_NORMALIZE), &norm);
2687de8267e0Stimh 		(void) nvlist_remove_all(createprops,
2688de8267e0Stimh 		    zfs_prop_to_name(ZFS_PROP_NORMALIZE));
2689de8267e0Stimh 		(void) nvlist_lookup_uint64(createprops,
2690de8267e0Stimh 		    zfs_prop_to_name(ZFS_PROP_UTF8ONLY), &u8);
2691de8267e0Stimh 		(void) nvlist_remove_all(createprops,
2692de8267e0Stimh 		    zfs_prop_to_name(ZFS_PROP_UTF8ONLY));
2693de8267e0Stimh 		(void) nvlist_lookup_uint64(createprops,
2694de8267e0Stimh 		    zfs_prop_to_name(ZFS_PROP_CASE), &sense);
2695de8267e0Stimh 		(void) nvlist_remove_all(createprops,
2696de8267e0Stimh 		    zfs_prop_to_name(ZFS_PROP_CASE));
2697de8267e0Stimh 	}
2698da6c28aaSamw 
2699c2a93d44Stimh 	/*
27000a48a24eStimh 	 * If the zpl version requested is whacky or the file system
27010a48a24eStimh 	 * or pool is version is too "young" to support normalization
27020a48a24eStimh 	 * and the creator tried to set a value for one of the props,
27030a48a24eStimh 	 * error out.
2704c2a93d44Stimh 	 */
27050a48a24eStimh 	if ((zplver < ZPL_VERSION_INITIAL || zplver > ZPL_VERSION) ||
27060a48a24eStimh 	    (zplver >= ZPL_VERSION_FUID && !fuids_ok) ||
27070a586ceaSMark Shellenbaum 	    (zplver >= ZPL_VERSION_SA && !sa_ok) ||
27080a48a24eStimh 	    (zplver < ZPL_VERSION_NORMALIZATION &&
2709de8267e0Stimh 	    (norm != ZFS_PROP_UNDEFINED || u8 != ZFS_PROP_UNDEFINED ||
27100a48a24eStimh 	    sense != ZFS_PROP_UNDEFINED)))
2711de8267e0Stimh 		return (ENOTSUP);
2712c2a93d44Stimh 
2713de8267e0Stimh 	/*
2714de8267e0Stimh 	 * Put the version in the zplprops
2715de8267e0Stimh 	 */
2716de8267e0Stimh 	VERIFY(nvlist_add_uint64(zplprops,
2717de8267e0Stimh 	    zfs_prop_to_name(ZFS_PROP_VERSION), zplver) == 0);
2718da6c28aaSamw 
2719de8267e0Stimh 	if (norm == ZFS_PROP_UNDEFINED)
2720de8267e0Stimh 		VERIFY(zfs_get_zplprop(os, ZFS_PROP_NORMALIZE, &norm) == 0);
2721de8267e0Stimh 	VERIFY(nvlist_add_uint64(zplprops,
2722de8267e0Stimh 	    zfs_prop_to_name(ZFS_PROP_NORMALIZE), norm) == 0);
2723da6c28aaSamw 
2724c2a93d44Stimh 	/*
2725de8267e0Stimh 	 * If we're normalizing, names must always be valid UTF-8 strings.
2726c2a93d44Stimh 	 */
2727de8267e0Stimh 	if (norm)
2728de8267e0Stimh 		u8 = 1;
2729de8267e0Stimh 	if (u8 == ZFS_PROP_UNDEFINED)
2730de8267e0Stimh 		VERIFY(zfs_get_zplprop(os, ZFS_PROP_UTF8ONLY, &u8) == 0);
2731de8267e0Stimh 	VERIFY(nvlist_add_uint64(zplprops,
2732de8267e0Stimh 	    zfs_prop_to_name(ZFS_PROP_UTF8ONLY), u8) == 0);
2733de8267e0Stimh 
2734de8267e0Stimh 	if (sense == ZFS_PROP_UNDEFINED)
2735de8267e0Stimh 		VERIFY(zfs_get_zplprop(os, ZFS_PROP_CASE, &sense) == 0);
2736de8267e0Stimh 	VERIFY(nvlist_add_uint64(zplprops,
2737de8267e0Stimh 	    zfs_prop_to_name(ZFS_PROP_CASE), sense) == 0);
2738c2a93d44Stimh 
2739ab04eb8eStimh 	if (is_ci)
2740ab04eb8eStimh 		*is_ci = (sense == ZFS_CASE_INSENSITIVE);
2741ab04eb8eStimh 
2742da6c28aaSamw 	return (0);
2743fa9e4066Sahrens }
2744fa9e4066Sahrens 
27450a48a24eStimh static int
27460a48a24eStimh zfs_fill_zplprops(const char *dataset, nvlist_t *createprops,
27470a48a24eStimh     nvlist_t *zplprops, boolean_t *is_ci)
27480a48a24eStimh {
27490a586ceaSMark Shellenbaum 	boolean_t fuids_ok, sa_ok;
27500a48a24eStimh 	uint64_t zplver = ZPL_VERSION;
27510a48a24eStimh 	objset_t *os = NULL;
27520a48a24eStimh 	char parentname[MAXNAMELEN];
27530a48a24eStimh 	char *cp;
27540a586ceaSMark Shellenbaum 	spa_t *spa;
27550a586ceaSMark Shellenbaum 	uint64_t spa_vers;
27560a48a24eStimh 	int error;
27570a48a24eStimh 
27580a48a24eStimh 	(void) strlcpy(parentname, dataset, sizeof (parentname));
27590a48a24eStimh 	cp = strrchr(parentname, '/');
27600a48a24eStimh 	ASSERT(cp != NULL);
27610a48a24eStimh 	cp[0] = '\0';
27620a48a24eStimh 
27630a586ceaSMark Shellenbaum 	if ((error = spa_open(dataset, &spa, FTAG)) != 0)
27640a586ceaSMark Shellenbaum 		return (error);
27650a586ceaSMark Shellenbaum 
27660a586ceaSMark Shellenbaum 	spa_vers = spa_version(spa);
27670a586ceaSMark Shellenbaum 	spa_close(spa, FTAG);
27680a586ceaSMark Shellenbaum 
27690a586ceaSMark Shellenbaum 	zplver = zfs_zpl_version_map(spa_vers);
27700a586ceaSMark Shellenbaum 	fuids_ok = (zplver >= ZPL_VERSION_FUID);
27710a586ceaSMark Shellenbaum 	sa_ok = (zplver >= ZPL_VERSION_SA);
27720a48a24eStimh 
27730a48a24eStimh 	/*
27740a48a24eStimh 	 * Open parent object set so we can inherit zplprop values.
27750a48a24eStimh 	 */
2776503ad85cSMatthew Ahrens 	if ((error = dmu_objset_hold(parentname, FTAG, &os)) != 0)
27770a48a24eStimh 		return (error);
27780a48a24eStimh 
27790a586ceaSMark Shellenbaum 	error = zfs_fill_zplprops_impl(os, zplver, fuids_ok, sa_ok, createprops,
27800a48a24eStimh 	    zplprops, is_ci);
2781503ad85cSMatthew Ahrens 	dmu_objset_rele(os, FTAG);
27820a48a24eStimh 	return (error);
27830a48a24eStimh }
27840a48a24eStimh 
27850a48a24eStimh static int
27860a48a24eStimh zfs_fill_zplprops_root(uint64_t spa_vers, nvlist_t *createprops,
27870a48a24eStimh     nvlist_t *zplprops, boolean_t *is_ci)
27880a48a24eStimh {
27890a586ceaSMark Shellenbaum 	boolean_t fuids_ok;
27900a586ceaSMark Shellenbaum 	boolean_t sa_ok;
27910a48a24eStimh 	uint64_t zplver = ZPL_VERSION;
27920a48a24eStimh 	int error;
27930a48a24eStimh 
27940a586ceaSMark Shellenbaum 	zplver = zfs_zpl_version_map(spa_vers);
27950a586ceaSMark Shellenbaum 	fuids_ok = (zplver >= ZPL_VERSION_FUID);
27960a586ceaSMark Shellenbaum 	sa_ok = (zplver >= ZPL_VERSION_SA);
27970a48a24eStimh 
27980a586ceaSMark Shellenbaum 	error = zfs_fill_zplprops_impl(NULL, zplver, fuids_ok, sa_ok,
27990a586ceaSMark Shellenbaum 	    createprops, zplprops, is_ci);
28000a48a24eStimh 	return (error);
28010a48a24eStimh }
28020a48a24eStimh 
28033cb34c60Sahrens /*
28043cb34c60Sahrens  * inputs:
28053cb34c60Sahrens  * zc_objset_type	type of objset to create (fs vs zvol)
28063cb34c60Sahrens  * zc_name		name of new objset
28073cb34c60Sahrens  * zc_value		name of snapshot to clone from (may be empty)
28083cb34c60Sahrens  * zc_nvlist_src{_size}	nvlist of properties to apply
28093cb34c60Sahrens  *
2810de8267e0Stimh  * outputs: none
28113cb34c60Sahrens  */
2812fa9e4066Sahrens static int
2813fa9e4066Sahrens zfs_ioc_create(zfs_cmd_t *zc)
2814fa9e4066Sahrens {
2815fa9e4066Sahrens 	objset_t *clone;
2816fa9e4066Sahrens 	int error = 0;
2817da6c28aaSamw 	zfs_creat_t zct;
2818ecd6cf80Smarks 	nvlist_t *nvprops = NULL;
2819ecd6cf80Smarks 	void (*cbfunc)(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx);
2820fa9e4066Sahrens 	dmu_objset_type_t type = zc->zc_objset_type;
2821fa9e4066Sahrens 
2822fa9e4066Sahrens 	switch (type) {
2823fa9e4066Sahrens 
2824fa9e4066Sahrens 	case DMU_OST_ZFS:
2825fa9e4066Sahrens 		cbfunc = zfs_create_cb;
2826fa9e4066Sahrens 		break;
2827fa9e4066Sahrens 
2828fa9e4066Sahrens 	case DMU_OST_ZVOL:
2829fa9e4066Sahrens 		cbfunc = zvol_create_cb;
2830fa9e4066Sahrens 		break;
2831fa9e4066Sahrens 
2832fa9e4066Sahrens 	default:
28331d452cf5Sahrens 		cbfunc = NULL;
2834e7cbe64fSgw 		break;
2835fa9e4066Sahrens 	}
2836f18faf3fSek 	if (strchr(zc->zc_name, '@') ||
2837f18faf3fSek 	    strchr(zc->zc_name, '%'))
28381d452cf5Sahrens 		return (EINVAL);
2839fa9e4066Sahrens 
2840e9dbad6fSeschrock 	if (zc->zc_nvlist_src != NULL &&
2841990b4856Slling 	    (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
2842478ed9adSEric Taylor 	    zc->zc_iflags, &nvprops)) != 0)
2843e9dbad6fSeschrock 		return (error);
2844e9dbad6fSeschrock 
2845de8267e0Stimh 	zct.zct_zplprops = NULL;
2846da6c28aaSamw 	zct.zct_props = nvprops;
2847da6c28aaSamw 
2848e9dbad6fSeschrock 	if (zc->zc_value[0] != '\0') {
2849fa9e4066Sahrens 		/*
2850fa9e4066Sahrens 		 * We're creating a clone of an existing snapshot.
2851fa9e4066Sahrens 		 */
2852e9dbad6fSeschrock 		zc->zc_value[sizeof (zc->zc_value) - 1] = '\0';
2853e9dbad6fSeschrock 		if (dataset_namecheck(zc->zc_value, NULL, NULL) != 0) {
2854ecd6cf80Smarks 			nvlist_free(nvprops);
2855fa9e4066Sahrens 			return (EINVAL);
2856e9dbad6fSeschrock 		}
2857fa9e4066Sahrens 
2858503ad85cSMatthew Ahrens 		error = dmu_objset_hold(zc->zc_value, FTAG, &clone);
2859e9dbad6fSeschrock 		if (error) {
2860ecd6cf80Smarks 			nvlist_free(nvprops);
2861fa9e4066Sahrens 			return (error);
2862e9dbad6fSeschrock 		}
2863ab04eb8eStimh 
2864ae46e4c7SMatthew Ahrens 		error = dmu_objset_clone(zc->zc_name, dmu_objset_ds(clone), 0);
2865503ad85cSMatthew Ahrens 		dmu_objset_rele(clone, FTAG);
2866da6c28aaSamw 		if (error) {
2867da6c28aaSamw 			nvlist_free(nvprops);
2868da6c28aaSamw 			return (error);
2869da6c28aaSamw 		}
2870fa9e4066Sahrens 	} else {
2871ab04eb8eStimh 		boolean_t is_insensitive = B_FALSE;
2872ab04eb8eStimh 
2873e9dbad6fSeschrock 		if (cbfunc == NULL) {
2874ecd6cf80Smarks 			nvlist_free(nvprops);
28751d452cf5Sahrens 			return (EINVAL);
2876e9dbad6fSeschrock 		}
28775c5460e9Seschrock 
2878e9dbad6fSeschrock 		if (type == DMU_OST_ZVOL) {
2879e9dbad6fSeschrock 			uint64_t volsize, volblocksize;
2880e9dbad6fSeschrock 
2881ecd6cf80Smarks 			if (nvprops == NULL ||
2882ecd6cf80Smarks 			    nvlist_lookup_uint64(nvprops,
2883e9dbad6fSeschrock 			    zfs_prop_to_name(ZFS_PROP_VOLSIZE),
2884e9dbad6fSeschrock 			    &volsize) != 0) {
2885ecd6cf80Smarks 				nvlist_free(nvprops);
2886e9dbad6fSeschrock 				return (EINVAL);
2887e9dbad6fSeschrock 			}
2888e9dbad6fSeschrock 
2889ecd6cf80Smarks 			if ((error = nvlist_lookup_uint64(nvprops,
2890e9dbad6fSeschrock 			    zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE),
2891e9dbad6fSeschrock 			    &volblocksize)) != 0 && error != ENOENT) {
2892ecd6cf80Smarks 				nvlist_free(nvprops);
2893e9dbad6fSeschrock 				return (EINVAL);
2894e9dbad6fSeschrock 			}
2895e9dbad6fSeschrock 
2896e9dbad6fSeschrock 			if (error != 0)
2897e9dbad6fSeschrock 				volblocksize = zfs_prop_default_numeric(
2898e9dbad6fSeschrock 				    ZFS_PROP_VOLBLOCKSIZE);
2899e9dbad6fSeschrock 
2900e9dbad6fSeschrock 			if ((error = zvol_check_volblocksize(
2901e9dbad6fSeschrock 			    volblocksize)) != 0 ||
2902e9dbad6fSeschrock 			    (error = zvol_check_volsize(volsize,
2903e9dbad6fSeschrock 			    volblocksize)) != 0) {
2904ecd6cf80Smarks 				nvlist_free(nvprops);
29055c5460e9Seschrock 				return (error);
2906e9dbad6fSeschrock 			}
2907e7437265Sahrens 		} else if (type == DMU_OST_ZFS) {
2908da6c28aaSamw 			int error;
2909da6c28aaSamw 
2910da6c28aaSamw 			/*
2911da6c28aaSamw 			 * We have to have normalization and
2912da6c28aaSamw 			 * case-folding flags correct when we do the
2913da6c28aaSamw 			 * file system creation, so go figure them out
2914de8267e0Stimh 			 * now.
2915da6c28aaSamw 			 */
2916de8267e0Stimh 			VERIFY(nvlist_alloc(&zct.zct_zplprops,
2917de8267e0Stimh 			    NV_UNIQUE_NAME, KM_SLEEP) == 0);
2918de8267e0Stimh 			error = zfs_fill_zplprops(zc->zc_name, nvprops,
29190a48a24eStimh 			    zct.zct_zplprops, &is_insensitive);
2920da6c28aaSamw 			if (error != 0) {
2921da6c28aaSamw 				nvlist_free(nvprops);
2922de8267e0Stimh 				nvlist_free(zct.zct_zplprops);
2923da6c28aaSamw 				return (error);
2924da6c28aaSamw 			}
2925da6c28aaSamw 		}
2926ae46e4c7SMatthew Ahrens 		error = dmu_objset_create(zc->zc_name, type,
2927ab04eb8eStimh 		    is_insensitive ? DS_FLAG_CI_DATASET : 0, cbfunc, &zct);
2928de8267e0Stimh 		nvlist_free(zct.zct_zplprops);
2929fa9e4066Sahrens 	}
2930e9dbad6fSeschrock 
2931e9dbad6fSeschrock 	/*
2932e9dbad6fSeschrock 	 * It would be nice to do this atomically.
2933e9dbad6fSeschrock 	 */
2934e9dbad6fSeschrock 	if (error == 0) {
293592241e0bSTom Erickson 		error = zfs_set_prop_nvlist(zc->zc_name, ZPROP_SRC_LOCAL,
293692241e0bSTom Erickson 		    nvprops, NULL);
293792241e0bSTom Erickson 		if (error != 0)
2938842727c2SChris Kirby 			(void) dmu_objset_destroy(zc->zc_name, B_FALSE);
2939e9dbad6fSeschrock 	}
2940ecd6cf80Smarks 	nvlist_free(nvprops);
2941fa9e4066Sahrens 	return (error);
2942fa9e4066Sahrens }
2943fa9e4066Sahrens 
29443cb34c60Sahrens /*
29453cb34c60Sahrens  * inputs:
29463cb34c60Sahrens  * zc_name	name of filesystem
29473cb34c60Sahrens  * zc_value	short name of snapshot
29483cb34c60Sahrens  * zc_cookie	recursive flag
294914843421SMatthew Ahrens  * zc_nvlist_src[_size] property list
29503cb34c60Sahrens  *
2951681d9761SEric Taylor  * outputs:
2952681d9761SEric Taylor  * zc_value	short snapname (i.e. part after the '@')
29533cb34c60Sahrens  */
2954fa9e4066Sahrens static int
29551d452cf5Sahrens zfs_ioc_snapshot(zfs_cmd_t *zc)
2956fa9e4066Sahrens {
2957bb0ade09Sahrens 	nvlist_t *nvprops = NULL;
2958bb0ade09Sahrens 	int error;
2959bb0ade09Sahrens 	boolean_t recursive = zc->zc_cookie;
2960bb0ade09Sahrens 
2961e9dbad6fSeschrock 	if (snapshot_namecheck(zc->zc_value, NULL, NULL) != 0)
29621d452cf5Sahrens 		return (EINVAL);
2963bb0ade09Sahrens 
2964bb0ade09Sahrens 	if (zc->zc_nvlist_src != NULL &&
2965bb0ade09Sahrens 	    (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
2966478ed9adSEric Taylor 	    zc->zc_iflags, &nvprops)) != 0)
2967bb0ade09Sahrens 		return (error);
2968bb0ade09Sahrens 
2969ea2f5b9eSMatthew Ahrens 	error = zfs_check_userprops(zc->zc_name, nvprops);
2970ea2f5b9eSMatthew Ahrens 	if (error)
2971ea2f5b9eSMatthew Ahrens 		goto out;
2972bb0ade09Sahrens 
297392241e0bSTom Erickson 	if (!nvlist_empty(nvprops) &&
2974ea2f5b9eSMatthew Ahrens 	    zfs_earlier_version(zc->zc_name, SPA_VERSION_SNAP_PROPS)) {
2975ea2f5b9eSMatthew Ahrens 		error = ENOTSUP;
2976ea2f5b9eSMatthew Ahrens 		goto out;
2977bb0ade09Sahrens 	}
2978ea2f5b9eSMatthew Ahrens 
2979ea2f5b9eSMatthew Ahrens 	error = dmu_objset_snapshot(zc->zc_name, zc->zc_value,
2980ea2f5b9eSMatthew Ahrens 	    nvprops, recursive);
2981ea2f5b9eSMatthew Ahrens 
2982ea2f5b9eSMatthew Ahrens out:
2983bb0ade09Sahrens 	nvlist_free(nvprops);
2984bb0ade09Sahrens 	return (error);
29851d452cf5Sahrens }
2986fa9e4066Sahrens 
2987cdf5b4caSmmusante int
2988fd136879SMatthew Ahrens zfs_unmount_snap(const char *name, void *arg)
29891d452cf5Sahrens {
29900b69c2f0Sahrens 	vfs_t *vfsp = NULL;
29911d452cf5Sahrens 
2992745cd3c5Smaybee 	if (arg) {
2993745cd3c5Smaybee 		char *snapname = arg;
2994fd136879SMatthew Ahrens 		char *fullname = kmem_asprintf("%s@%s", name, snapname);
2995fd136879SMatthew Ahrens 		vfsp = zfs_get_vfs(fullname);
2996fd136879SMatthew Ahrens 		strfree(fullname);
29970b69c2f0Sahrens 	} else if (strchr(name, '@')) {
29981d452cf5Sahrens 		vfsp = zfs_get_vfs(name);
29991d452cf5Sahrens 	}
30001d452cf5Sahrens 
30011d452cf5Sahrens 	if (vfsp) {
3002fa9e4066Sahrens 		/*
30031d452cf5Sahrens 		 * Always force the unmount for snapshots.
3004fa9e4066Sahrens 		 */
30051d452cf5Sahrens 		int flag = MS_FORCE;
30061d452cf5Sahrens 		int err;
30071d452cf5Sahrens 
30081d452cf5Sahrens 		if ((err = vn_vfswlock(vfsp->vfs_vnodecovered)) != 0) {
3009fa9e4066Sahrens 			VFS_RELE(vfsp);
30101d452cf5Sahrens 			return (err);
3011fa9e4066Sahrens 		}
30121d452cf5Sahrens 		VFS_RELE(vfsp);
30131d452cf5Sahrens 		if ((err = dounmount(vfsp, flag, kcred)) != 0)
30141d452cf5Sahrens 			return (err);
30151d452cf5Sahrens 	}
30161d452cf5Sahrens 	return (0);
30171d452cf5Sahrens }
30181d452cf5Sahrens 
30193cb34c60Sahrens /*
30203cb34c60Sahrens  * inputs:
3021842727c2SChris Kirby  * zc_name		name of filesystem
3022842727c2SChris Kirby  * zc_value		short name of snapshot
3023842727c2SChris Kirby  * zc_defer_destroy	mark for deferred destroy
30243cb34c60Sahrens  *
30253cb34c60Sahrens  * outputs:	none
30263cb34c60Sahrens  */
30271d452cf5Sahrens static int
30281d452cf5Sahrens zfs_ioc_destroy_snaps(zfs_cmd_t *zc)
30291d452cf5Sahrens {
30301d452cf5Sahrens 	int err;
30311d452cf5Sahrens 
3032e9dbad6fSeschrock 	if (snapshot_namecheck(zc->zc_value, NULL, NULL) != 0)
30331d452cf5Sahrens 		return (EINVAL);
30341d452cf5Sahrens 	err = dmu_objset_find(zc->zc_name,
3035e9dbad6fSeschrock 	    zfs_unmount_snap, zc->zc_value, DS_FIND_CHILDREN);
30361d452cf5Sahrens 	if (err)
30371d452cf5Sahrens 		return (err);
3038842727c2SChris Kirby 	return (dmu_snapshots_destroy(zc->zc_name, zc->zc_value,
3039842727c2SChris Kirby 	    zc->zc_defer_destroy));
30401d452cf5Sahrens }
30411d452cf5Sahrens 
30423cb34c60Sahrens /*
30433cb34c60Sahrens  * inputs:
30443cb34c60Sahrens  * zc_name		name of dataset to destroy
30453cb34c60Sahrens  * zc_objset_type	type of objset
3046842727c2SChris Kirby  * zc_defer_destroy	mark for deferred destroy
30473cb34c60Sahrens  *
30483cb34c60Sahrens  * outputs:		none
30493cb34c60Sahrens  */
30501d452cf5Sahrens static int
30511d452cf5Sahrens zfs_ioc_destroy(zfs_cmd_t *zc)
30521d452cf5Sahrens {
3053681d9761SEric Taylor 	int err;
30541d452cf5Sahrens 	if (strchr(zc->zc_name, '@') && zc->zc_objset_type == DMU_OST_ZFS) {
3055681d9761SEric Taylor 		err = zfs_unmount_snap(zc->zc_name, NULL);
30561d452cf5Sahrens 		if (err)
30571d452cf5Sahrens 			return (err);
3058fa9e4066Sahrens 	}
3059fa9e4066Sahrens 
3060681d9761SEric Taylor 	err = dmu_objset_destroy(zc->zc_name, zc->zc_defer_destroy);
3061681d9761SEric Taylor 	if (zc->zc_objset_type == DMU_OST_ZVOL && err == 0)
30625c987a37SChris Kirby 		(void) zvol_remove_minor(zc->zc_name);
3063681d9761SEric Taylor 	return (err);
3064fa9e4066Sahrens }
3065fa9e4066Sahrens 
30663cb34c60Sahrens /*
30673cb34c60Sahrens  * inputs:
30684ccbb6e7Sahrens  * zc_name	name of dataset to rollback (to most recent snapshot)
30693cb34c60Sahrens  *
30703cb34c60Sahrens  * outputs:	none
30713cb34c60Sahrens  */
3072fa9e4066Sahrens static int
3073fa9e4066Sahrens zfs_ioc_rollback(zfs_cmd_t *zc)
3074fa9e4066Sahrens {
3075ae46e4c7SMatthew Ahrens 	dsl_dataset_t *ds, *clone;
30764ccbb6e7Sahrens 	int error;
3077ae46e4c7SMatthew Ahrens 	zfsvfs_t *zfsvfs;
3078ae46e4c7SMatthew Ahrens 	char *clone_name;
3079ae46e4c7SMatthew Ahrens 
3080ae46e4c7SMatthew Ahrens 	error = dsl_dataset_hold(zc->zc_name, FTAG, &ds);
3081ae46e4c7SMatthew Ahrens 	if (error)
3082ae46e4c7SMatthew Ahrens 		return (error);
3083ae46e4c7SMatthew Ahrens 
3084ae46e4c7SMatthew Ahrens 	/* must not be a snapshot */
3085ae46e4c7SMatthew Ahrens 	if (dsl_dataset_is_snapshot(ds)) {
3086ae46e4c7SMatthew Ahrens 		dsl_dataset_rele(ds, FTAG);
3087ae46e4c7SMatthew Ahrens 		return (EINVAL);
3088ae46e4c7SMatthew Ahrens 	}
3089ae46e4c7SMatthew Ahrens 
3090ae46e4c7SMatthew Ahrens 	/* must have a most recent snapshot */
3091ae46e4c7SMatthew Ahrens 	if (ds->ds_phys->ds_prev_snap_txg < TXG_INITIAL) {
3092ae46e4c7SMatthew Ahrens 		dsl_dataset_rele(ds, FTAG);
3093ae46e4c7SMatthew Ahrens 		return (EINVAL);
3094ae46e4c7SMatthew Ahrens 	}
30954ccbb6e7Sahrens 
30964ccbb6e7Sahrens 	/*
3097ae46e4c7SMatthew Ahrens 	 * Create clone of most recent snapshot.
30984ccbb6e7Sahrens 	 */
3099ae46e4c7SMatthew Ahrens 	clone_name = kmem_asprintf("%s/%%rollback", zc->zc_name);
3100ae46e4c7SMatthew Ahrens 	error = dmu_objset_clone(clone_name, ds->ds_prev, DS_FLAG_INCONSISTENT);
31014ccbb6e7Sahrens 	if (error)
3102ae46e4c7SMatthew Ahrens 		goto out;
31034ccbb6e7Sahrens 
3104503ad85cSMatthew Ahrens 	error = dsl_dataset_own(clone_name, B_TRUE, FTAG, &clone);
3105ae46e4c7SMatthew Ahrens 	if (error)
3106ae46e4c7SMatthew Ahrens 		goto out;
3107ae46e4c7SMatthew Ahrens 
3108ae46e4c7SMatthew Ahrens 	/*
3109ae46e4c7SMatthew Ahrens 	 * Do clone swap.
3110ae46e4c7SMatthew Ahrens 	 */
311114843421SMatthew Ahrens 	if (getzfsvfs(zc->zc_name, &zfsvfs) == 0) {
3112503ad85cSMatthew Ahrens 		error = zfs_suspend_fs(zfsvfs);
311347f263f4Sek 		if (error == 0) {
311447f263f4Sek 			int resume_err;
31154ccbb6e7Sahrens 
3116ae46e4c7SMatthew Ahrens 			if (dsl_dataset_tryown(ds, B_FALSE, FTAG)) {
3117ae46e4c7SMatthew Ahrens 				error = dsl_dataset_clone_swap(clone, ds,
3118ae46e4c7SMatthew Ahrens 				    B_TRUE);
3119ae46e4c7SMatthew Ahrens 				dsl_dataset_disown(ds, FTAG);
3120ae46e4c7SMatthew Ahrens 				ds = NULL;
3121ae46e4c7SMatthew Ahrens 			} else {
3122ae46e4c7SMatthew Ahrens 				error = EBUSY;
3123ae46e4c7SMatthew Ahrens 			}
3124503ad85cSMatthew Ahrens 			resume_err = zfs_resume_fs(zfsvfs, zc->zc_name);
312547f263f4Sek 			error = error ? error : resume_err;
312647f263f4Sek 		}
31274ccbb6e7Sahrens 		VFS_RELE(zfsvfs->z_vfs);
31284ccbb6e7Sahrens 	} else {
3129ae46e4c7SMatthew Ahrens 		if (dsl_dataset_tryown(ds, B_FALSE, FTAG)) {
3130ae46e4c7SMatthew Ahrens 			error = dsl_dataset_clone_swap(clone, ds, B_TRUE);
3131ae46e4c7SMatthew Ahrens 			dsl_dataset_disown(ds, FTAG);
3132ae46e4c7SMatthew Ahrens 			ds = NULL;
3133ae46e4c7SMatthew Ahrens 		} else {
3134ae46e4c7SMatthew Ahrens 			error = EBUSY;
3135ae46e4c7SMatthew Ahrens 		}
31364ccbb6e7Sahrens 	}
31374ccbb6e7Sahrens 
3138ae46e4c7SMatthew Ahrens 	/*
3139ae46e4c7SMatthew Ahrens 	 * Destroy clone (which also closes it).
3140ae46e4c7SMatthew Ahrens 	 */
3141ae46e4c7SMatthew Ahrens 	(void) dsl_dataset_destroy(clone, FTAG, B_FALSE);
3142ae46e4c7SMatthew Ahrens 
3143ae46e4c7SMatthew Ahrens out:
3144ae46e4c7SMatthew Ahrens 	strfree(clone_name);
3145ae46e4c7SMatthew Ahrens 	if (ds)
3146ae46e4c7SMatthew Ahrens 		dsl_dataset_rele(ds, FTAG);
31474ccbb6e7Sahrens 	return (error);
3148fa9e4066Sahrens }
3149fa9e4066Sahrens 
31503cb34c60Sahrens /*
31513cb34c60Sahrens  * inputs:
31523cb34c60Sahrens  * zc_name	old name of dataset
31533cb34c60Sahrens  * zc_value	new name of dataset
31543cb34c60Sahrens  * zc_cookie	recursive flag (only valid for snapshots)
31553cb34c60Sahrens  *
31563cb34c60Sahrens  * outputs:	none
31573cb34c60Sahrens  */
3158fa9e4066Sahrens static int
3159fa9e4066Sahrens zfs_ioc_rename(zfs_cmd_t *zc)
3160fa9e4066Sahrens {
31617f1f55eaSvb 	boolean_t recursive = zc->zc_cookie & 1;
3162cdf5b4caSmmusante 
3163e9dbad6fSeschrock 	zc->zc_value[sizeof (zc->zc_value) - 1] = '\0';
3164f18faf3fSek 	if (dataset_namecheck(zc->zc_value, NULL, NULL) != 0 ||
3165f18faf3fSek 	    strchr(zc->zc_value, '%'))
3166fa9e4066Sahrens 		return (EINVAL);
3167fa9e4066Sahrens 
3168cdf5b4caSmmusante 	/*
3169cdf5b4caSmmusante 	 * Unmount snapshot unless we're doing a recursive rename,
3170cdf5b4caSmmusante 	 * in which case the dataset code figures out which snapshots
3171cdf5b4caSmmusante 	 * to unmount.
3172cdf5b4caSmmusante 	 */
3173cdf5b4caSmmusante 	if (!recursive && strchr(zc->zc_name, '@') != NULL &&
3174fa9e4066Sahrens 	    zc->zc_objset_type == DMU_OST_ZFS) {
31751d452cf5Sahrens 		int err = zfs_unmount_snap(zc->zc_name, NULL);
31761d452cf5Sahrens 		if (err)
31771d452cf5Sahrens 			return (err);
3178fa9e4066Sahrens 	}
3179681d9761SEric Taylor 	if (zc->zc_objset_type == DMU_OST_ZVOL)
3180681d9761SEric Taylor 		(void) zvol_remove_minor(zc->zc_name);
3181cdf5b4caSmmusante 	return (dmu_objset_rename(zc->zc_name, zc->zc_value, recursive));
3182fa9e4066Sahrens }
3183fa9e4066Sahrens 
318492241e0bSTom Erickson static int
318592241e0bSTom Erickson zfs_check_settable(const char *dsname, nvpair_t *pair, cred_t *cr)
318692241e0bSTom Erickson {
318792241e0bSTom Erickson 	const char *propname = nvpair_name(pair);
318892241e0bSTom Erickson 	boolean_t issnap = (strchr(dsname, '@') != NULL);
318992241e0bSTom Erickson 	zfs_prop_t prop = zfs_name_to_prop(propname);
319092241e0bSTom Erickson 	uint64_t intval;
319192241e0bSTom Erickson 	int err;
319292241e0bSTom Erickson 
319392241e0bSTom Erickson 	if (prop == ZPROP_INVAL) {
319492241e0bSTom Erickson 		if (zfs_prop_user(propname)) {
319592241e0bSTom Erickson 			if (err = zfs_secpolicy_write_perms(dsname,
319692241e0bSTom Erickson 			    ZFS_DELEG_PERM_USERPROP, cr))
319792241e0bSTom Erickson 				return (err);
319892241e0bSTom Erickson 			return (0);
319992241e0bSTom Erickson 		}
320092241e0bSTom Erickson 
320192241e0bSTom Erickson 		if (!issnap && zfs_prop_userquota(propname)) {
320292241e0bSTom Erickson 			const char *perm = NULL;
320392241e0bSTom Erickson 			const char *uq_prefix =
320492241e0bSTom Erickson 			    zfs_userquota_prop_prefixes[ZFS_PROP_USERQUOTA];
320592241e0bSTom Erickson 			const char *gq_prefix =
320692241e0bSTom Erickson 			    zfs_userquota_prop_prefixes[ZFS_PROP_GROUPQUOTA];
320792241e0bSTom Erickson 
320892241e0bSTom Erickson 			if (strncmp(propname, uq_prefix,
320992241e0bSTom Erickson 			    strlen(uq_prefix)) == 0) {
321092241e0bSTom Erickson 				perm = ZFS_DELEG_PERM_USERQUOTA;
321192241e0bSTom Erickson 			} else if (strncmp(propname, gq_prefix,
321292241e0bSTom Erickson 			    strlen(gq_prefix)) == 0) {
321392241e0bSTom Erickson 				perm = ZFS_DELEG_PERM_GROUPQUOTA;
321492241e0bSTom Erickson 			} else {
321592241e0bSTom Erickson 				/* USERUSED and GROUPUSED are read-only */
321692241e0bSTom Erickson 				return (EINVAL);
321792241e0bSTom Erickson 			}
321892241e0bSTom Erickson 
321992241e0bSTom Erickson 			if (err = zfs_secpolicy_write_perms(dsname, perm, cr))
322092241e0bSTom Erickson 				return (err);
322192241e0bSTom Erickson 			return (0);
322292241e0bSTom Erickson 		}
322392241e0bSTom Erickson 
322492241e0bSTom Erickson 		return (EINVAL);
322592241e0bSTom Erickson 	}
322692241e0bSTom Erickson 
322792241e0bSTom Erickson 	if (issnap)
322892241e0bSTom Erickson 		return (EINVAL);
322992241e0bSTom Erickson 
323092241e0bSTom Erickson 	if (nvpair_type(pair) == DATA_TYPE_NVLIST) {
323192241e0bSTom Erickson 		/*
323292241e0bSTom Erickson 		 * dsl_prop_get_all_impl() returns properties in this
323392241e0bSTom Erickson 		 * format.
323492241e0bSTom Erickson 		 */
323592241e0bSTom Erickson 		nvlist_t *attrs;
323692241e0bSTom Erickson 		VERIFY(nvpair_value_nvlist(pair, &attrs) == 0);
323792241e0bSTom Erickson 		VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE,
323892241e0bSTom Erickson 		    &pair) == 0);
323992241e0bSTom Erickson 	}
324092241e0bSTom Erickson 
324192241e0bSTom Erickson 	/*
324292241e0bSTom Erickson 	 * Check that this value is valid for this pool version
324392241e0bSTom Erickson 	 */
324492241e0bSTom Erickson 	switch (prop) {
324592241e0bSTom Erickson 	case ZFS_PROP_COMPRESSION:
324692241e0bSTom Erickson 		/*
324792241e0bSTom Erickson 		 * If the user specified gzip compression, make sure
324892241e0bSTom Erickson 		 * the SPA supports it. We ignore any errors here since
324992241e0bSTom Erickson 		 * we'll catch them later.
325092241e0bSTom Erickson 		 */
325192241e0bSTom Erickson 		if (nvpair_type(pair) == DATA_TYPE_UINT64 &&
325292241e0bSTom Erickson 		    nvpair_value_uint64(pair, &intval) == 0) {
325392241e0bSTom Erickson 			if (intval >= ZIO_COMPRESS_GZIP_1 &&
325492241e0bSTom Erickson 			    intval <= ZIO_COMPRESS_GZIP_9 &&
325592241e0bSTom Erickson 			    zfs_earlier_version(dsname,
325692241e0bSTom Erickson 			    SPA_VERSION_GZIP_COMPRESSION)) {
325792241e0bSTom Erickson 				return (ENOTSUP);
325892241e0bSTom Erickson 			}
325992241e0bSTom Erickson 
326092241e0bSTom Erickson 			if (intval == ZIO_COMPRESS_ZLE &&
326192241e0bSTom Erickson 			    zfs_earlier_version(dsname,
326292241e0bSTom Erickson 			    SPA_VERSION_ZLE_COMPRESSION))
326392241e0bSTom Erickson 				return (ENOTSUP);
326492241e0bSTom Erickson 
326592241e0bSTom Erickson 			/*
326692241e0bSTom Erickson 			 * If this is a bootable dataset then
326792241e0bSTom Erickson 			 * verify that the compression algorithm
326892241e0bSTom Erickson 			 * is supported for booting. We must return
326992241e0bSTom Erickson 			 * something other than ENOTSUP since it
327092241e0bSTom Erickson 			 * implies a downrev pool version.
327192241e0bSTom Erickson 			 */
327292241e0bSTom Erickson 			if (zfs_is_bootfs(dsname) &&
327392241e0bSTom Erickson 			    !BOOTFS_COMPRESS_VALID(intval)) {
327492241e0bSTom Erickson 				return (ERANGE);
327592241e0bSTom Erickson 			}
327692241e0bSTom Erickson 		}
327792241e0bSTom Erickson 		break;
327892241e0bSTom Erickson 
327992241e0bSTom Erickson 	case ZFS_PROP_COPIES:
328092241e0bSTom Erickson 		if (zfs_earlier_version(dsname, SPA_VERSION_DITTO_BLOCKS))
328192241e0bSTom Erickson 			return (ENOTSUP);
328292241e0bSTom Erickson 		break;
328392241e0bSTom Erickson 
328492241e0bSTom Erickson 	case ZFS_PROP_DEDUP:
328592241e0bSTom Erickson 		if (zfs_earlier_version(dsname, SPA_VERSION_DEDUP))
328692241e0bSTom Erickson 			return (ENOTSUP);
328792241e0bSTom Erickson 		break;
328892241e0bSTom Erickson 
328992241e0bSTom Erickson 	case ZFS_PROP_SHARESMB:
329092241e0bSTom Erickson 		if (zpl_earlier_version(dsname, ZPL_VERSION_FUID))
329192241e0bSTom Erickson 			return (ENOTSUP);
329292241e0bSTom Erickson 		break;
329392241e0bSTom Erickson 
329492241e0bSTom Erickson 	case ZFS_PROP_ACLINHERIT:
329592241e0bSTom Erickson 		if (nvpair_type(pair) == DATA_TYPE_UINT64 &&
329692241e0bSTom Erickson 		    nvpair_value_uint64(pair, &intval) == 0) {
329792241e0bSTom Erickson 			if (intval == ZFS_ACL_PASSTHROUGH_X &&
329892241e0bSTom Erickson 			    zfs_earlier_version(dsname,
329992241e0bSTom Erickson 			    SPA_VERSION_PASSTHROUGH_X))
330092241e0bSTom Erickson 				return (ENOTSUP);
330192241e0bSTom Erickson 		}
330292241e0bSTom Erickson 		break;
330392241e0bSTom Erickson 	}
330492241e0bSTom Erickson 
330592241e0bSTom Erickson 	return (zfs_secpolicy_setprop(dsname, prop, pair, CRED()));
330692241e0bSTom Erickson }
330792241e0bSTom Erickson 
330892241e0bSTom Erickson /*
330992241e0bSTom Erickson  * Removes properties from the given props list that fail permission checks
331092241e0bSTom Erickson  * needed to clear them and to restore them in case of a receive error. For each
331192241e0bSTom Erickson  * property, make sure we have both set and inherit permissions.
331292241e0bSTom Erickson  *
331392241e0bSTom Erickson  * Returns the first error encountered if any permission checks fail. If the
331492241e0bSTom Erickson  * caller provides a non-NULL errlist, it also gives the complete list of names
331592241e0bSTom Erickson  * of all the properties that failed a permission check along with the
331692241e0bSTom Erickson  * corresponding error numbers. The caller is responsible for freeing the
331792241e0bSTom Erickson  * returned errlist.
331892241e0bSTom Erickson  *
331992241e0bSTom Erickson  * If every property checks out successfully, zero is returned and the list
332092241e0bSTom Erickson  * pointed at by errlist is NULL.
332192241e0bSTom Erickson  */
332292241e0bSTom Erickson static int
332392241e0bSTom Erickson zfs_check_clearable(char *dataset, nvlist_t *props, nvlist_t **errlist)
3324745cd3c5Smaybee {
3325745cd3c5Smaybee 	zfs_cmd_t *zc;
332692241e0bSTom Erickson 	nvpair_t *pair, *next_pair;
332792241e0bSTom Erickson 	nvlist_t *errors;
332892241e0bSTom Erickson 	int err, rv = 0;
3329745cd3c5Smaybee 
3330745cd3c5Smaybee 	if (props == NULL)
333192241e0bSTom Erickson 		return (0);
333292241e0bSTom Erickson 
333392241e0bSTom Erickson 	VERIFY(nvlist_alloc(&errors, NV_UNIQUE_NAME, KM_SLEEP) == 0);
333492241e0bSTom Erickson 
3335745cd3c5Smaybee 	zc = kmem_alloc(sizeof (zfs_cmd_t), KM_SLEEP);
3336745cd3c5Smaybee 	(void) strcpy(zc->zc_name, dataset);
333792241e0bSTom Erickson 	pair = nvlist_next_nvpair(props, NULL);
333892241e0bSTom Erickson 	while (pair != NULL) {
333992241e0bSTom Erickson 		next_pair = nvlist_next_nvpair(props, pair);
334092241e0bSTom Erickson 
334192241e0bSTom Erickson 		(void) strcpy(zc->zc_value, nvpair_name(pair));
334292241e0bSTom Erickson 		if ((err = zfs_check_settable(dataset, pair, CRED())) != 0 ||
334392241e0bSTom Erickson 		    (err = zfs_secpolicy_inherit(zc, CRED())) != 0) {
334492241e0bSTom Erickson 			VERIFY(nvlist_remove_nvpair(props, pair) == 0);
334592241e0bSTom Erickson 			VERIFY(nvlist_add_int32(errors,
334692241e0bSTom Erickson 			    zc->zc_value, err) == 0);
334792241e0bSTom Erickson 		}
334892241e0bSTom Erickson 		pair = next_pair;
3349745cd3c5Smaybee 	}
3350745cd3c5Smaybee 	kmem_free(zc, sizeof (zfs_cmd_t));
335192241e0bSTom Erickson 
335292241e0bSTom Erickson 	if ((pair = nvlist_next_nvpair(errors, NULL)) == NULL) {
335392241e0bSTom Erickson 		nvlist_free(errors);
335492241e0bSTom Erickson 		errors = NULL;
335592241e0bSTom Erickson 	} else {
335692241e0bSTom Erickson 		VERIFY(nvpair_value_int32(pair, &rv) == 0);
335792241e0bSTom Erickson 	}
335892241e0bSTom Erickson 
335992241e0bSTom Erickson 	if (errlist == NULL)
336092241e0bSTom Erickson 		nvlist_free(errors);
336192241e0bSTom Erickson 	else
336292241e0bSTom Erickson 		*errlist = errors;
336392241e0bSTom Erickson 
336492241e0bSTom Erickson 	return (rv);
336592241e0bSTom Erickson }
336692241e0bSTom Erickson 
336792241e0bSTom Erickson static boolean_t
336892241e0bSTom Erickson propval_equals(nvpair_t *p1, nvpair_t *p2)
336992241e0bSTom Erickson {
337092241e0bSTom Erickson 	if (nvpair_type(p1) == DATA_TYPE_NVLIST) {
337192241e0bSTom Erickson 		/* dsl_prop_get_all_impl() format */
337292241e0bSTom Erickson 		nvlist_t *attrs;
337392241e0bSTom Erickson 		VERIFY(nvpair_value_nvlist(p1, &attrs) == 0);
337492241e0bSTom Erickson 		VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE,
337592241e0bSTom Erickson 		    &p1) == 0);
337692241e0bSTom Erickson 	}
337792241e0bSTom Erickson 
337892241e0bSTom Erickson 	if (nvpair_type(p2) == DATA_TYPE_NVLIST) {
337992241e0bSTom Erickson 		nvlist_t *attrs;
338092241e0bSTom Erickson 		VERIFY(nvpair_value_nvlist(p2, &attrs) == 0);
338192241e0bSTom Erickson 		VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE,
338292241e0bSTom Erickson 		    &p2) == 0);
338392241e0bSTom Erickson 	}
338492241e0bSTom Erickson 
338592241e0bSTom Erickson 	if (nvpair_type(p1) != nvpair_type(p2))
338692241e0bSTom Erickson 		return (B_FALSE);
338792241e0bSTom Erickson 
338892241e0bSTom Erickson 	if (nvpair_type(p1) == DATA_TYPE_STRING) {
338992241e0bSTom Erickson 		char *valstr1, *valstr2;
339092241e0bSTom Erickson 
339192241e0bSTom Erickson 		VERIFY(nvpair_value_string(p1, (char **)&valstr1) == 0);
339292241e0bSTom Erickson 		VERIFY(nvpair_value_string(p2, (char **)&valstr2) == 0);
339392241e0bSTom Erickson 		return (strcmp(valstr1, valstr2) == 0);
339492241e0bSTom Erickson 	} else {
339592241e0bSTom Erickson 		uint64_t intval1, intval2;
339692241e0bSTom Erickson 
339792241e0bSTom Erickson 		VERIFY(nvpair_value_uint64(p1, &intval1) == 0);
339892241e0bSTom Erickson 		VERIFY(nvpair_value_uint64(p2, &intval2) == 0);
339992241e0bSTom Erickson 		return (intval1 == intval2);
340092241e0bSTom Erickson 	}
3401745cd3c5Smaybee }
3402745cd3c5Smaybee 
340392241e0bSTom Erickson /*
340492241e0bSTom Erickson  * Remove properties from props if they are not going to change (as determined
340592241e0bSTom Erickson  * by comparison with origprops). Remove them from origprops as well, since we
340692241e0bSTom Erickson  * do not need to clear or restore properties that won't change.
340792241e0bSTom Erickson  */
340892241e0bSTom Erickson static void
340992241e0bSTom Erickson props_reduce(nvlist_t *props, nvlist_t *origprops)
341092241e0bSTom Erickson {
341192241e0bSTom Erickson 	nvpair_t *pair, *next_pair;
341292241e0bSTom Erickson 
341392241e0bSTom Erickson 	if (origprops == NULL)
341492241e0bSTom Erickson 		return; /* all props need to be received */
341592241e0bSTom Erickson 
341692241e0bSTom Erickson 	pair = nvlist_next_nvpair(props, NULL);
341792241e0bSTom Erickson 	while (pair != NULL) {
341892241e0bSTom Erickson 		const char *propname = nvpair_name(pair);
341992241e0bSTom Erickson 		nvpair_t *match;
342092241e0bSTom Erickson 
342192241e0bSTom Erickson 		next_pair = nvlist_next_nvpair(props, pair);
342292241e0bSTom Erickson 
342392241e0bSTom Erickson 		if ((nvlist_lookup_nvpair(origprops, propname,
342492241e0bSTom Erickson 		    &match) != 0) || !propval_equals(pair, match))
342592241e0bSTom Erickson 			goto next; /* need to set received value */
342692241e0bSTom Erickson 
342792241e0bSTom Erickson 		/* don't clear the existing received value */
342892241e0bSTom Erickson 		(void) nvlist_remove_nvpair(origprops, match);
342992241e0bSTom Erickson 		/* don't bother receiving the property */
343092241e0bSTom Erickson 		(void) nvlist_remove_nvpair(props, pair);
343192241e0bSTom Erickson next:
343292241e0bSTom Erickson 		pair = next_pair;
343392241e0bSTom Erickson 	}
343492241e0bSTom Erickson }
343592241e0bSTom Erickson 
343692241e0bSTom Erickson #ifdef	DEBUG
343792241e0bSTom Erickson static boolean_t zfs_ioc_recv_inject_err;
343892241e0bSTom Erickson #endif
343992241e0bSTom Erickson 
34403cb34c60Sahrens /*
34413cb34c60Sahrens  * inputs:
34423cb34c60Sahrens  * zc_name		name of containing filesystem
34433cb34c60Sahrens  * zc_nvlist_src{_size}	nvlist of properties to apply
34443cb34c60Sahrens  * zc_value		name of snapshot to create
34453cb34c60Sahrens  * zc_string		name of clone origin (if DRR_FLAG_CLONE)
34463cb34c60Sahrens  * zc_cookie		file descriptor to recv from
34473cb34c60Sahrens  * zc_begin_record	the BEGIN record of the stream (not byteswapped)
34483cb34c60Sahrens  * zc_guid		force flag
3449c99e4bdcSChris Kirby  * zc_cleanup_fd	cleanup-on-exit file descriptor
3450c99e4bdcSChris Kirby  * zc_action_handle	handle for this guid/ds mapping (or zero on first call)
34513cb34c60Sahrens  *
34523cb34c60Sahrens  * outputs:
34533cb34c60Sahrens  * zc_cookie		number of bytes read
345492241e0bSTom Erickson  * zc_nvlist_dst{_size} error for each unapplied received property
345592241e0bSTom Erickson  * zc_obj		zprop_errflags_t
3456c99e4bdcSChris Kirby  * zc_action_handle	handle for this guid/ds mapping
34573cb34c60Sahrens  */
3458fa9e4066Sahrens static int
34593cb34c60Sahrens zfs_ioc_recv(zfs_cmd_t *zc)
3460fa9e4066Sahrens {
3461fa9e4066Sahrens 	file_t *fp;
3462f18faf3fSek 	objset_t *os;
34633cb34c60Sahrens 	dmu_recv_cookie_t drc;
3464f18faf3fSek 	boolean_t force = (boolean_t)zc->zc_guid;
346592241e0bSTom Erickson 	int fd;
346692241e0bSTom Erickson 	int error = 0;
346792241e0bSTom Erickson 	int props_error = 0;
346892241e0bSTom Erickson 	nvlist_t *errors;
34693cb34c60Sahrens 	offset_t off;
347092241e0bSTom Erickson 	nvlist_t *props = NULL; /* sent properties */
347192241e0bSTom Erickson 	nvlist_t *origprops = NULL; /* existing properties */
34723cb34c60Sahrens 	objset_t *origin = NULL;
34733cb34c60Sahrens 	char *tosnap;
34743cb34c60Sahrens 	char tofs[ZFS_MAXNAMELEN];
347592241e0bSTom Erickson 	boolean_t first_recvd_props = B_FALSE;
3476fa9e4066Sahrens 
34773ccfa83cSahrens 	if (dataset_namecheck(zc->zc_value, NULL, NULL) != 0 ||
3478f18faf3fSek 	    strchr(zc->zc_value, '@') == NULL ||
3479f18faf3fSek 	    strchr(zc->zc_value, '%'))
34803ccfa83cSahrens 		return (EINVAL);
34813ccfa83cSahrens 
34823cb34c60Sahrens 	(void) strcpy(tofs, zc->zc_value);
34833cb34c60Sahrens 	tosnap = strchr(tofs, '@');
348492241e0bSTom Erickson 	*tosnap++ = '\0';
34853cb34c60Sahrens 
34863cb34c60Sahrens 	if (zc->zc_nvlist_src != NULL &&
34873cb34c60Sahrens 	    (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
3488478ed9adSEric Taylor 	    zc->zc_iflags, &props)) != 0)
34893cb34c60Sahrens 		return (error);
34903cb34c60Sahrens 
3491fa9e4066Sahrens 	fd = zc->zc_cookie;
3492fa9e4066Sahrens 	fp = getf(fd);
34933cb34c60Sahrens 	if (fp == NULL) {
34943cb34c60Sahrens 		nvlist_free(props);
3495fa9e4066Sahrens 		return (EBADF);
34963cb34c60Sahrens 	}
3497f18faf3fSek 
349892241e0bSTom Erickson 	VERIFY(nvlist_alloc(&errors, NV_UNIQUE_NAME, KM_SLEEP) == 0);
349992241e0bSTom Erickson 
3500503ad85cSMatthew Ahrens 	if (props && dmu_objset_hold(tofs, FTAG, &os) == 0) {
350192241e0bSTom Erickson 		if ((spa_version(os->os_spa) >= SPA_VERSION_RECVD_PROPS) &&
350292241e0bSTom Erickson 		    !dsl_prop_get_hasrecvd(os)) {
350392241e0bSTom Erickson 			first_recvd_props = B_TRUE;
350492241e0bSTom Erickson 		}
350592241e0bSTom Erickson 
3506745cd3c5Smaybee 		/*
350792241e0bSTom Erickson 		 * If new received properties are supplied, they are to
350892241e0bSTom Erickson 		 * completely replace the existing received properties, so stash
350992241e0bSTom Erickson 		 * away the existing ones.
3510745cd3c5Smaybee 		 */
351192241e0bSTom Erickson 		if (dsl_prop_get_received(os, &origprops) == 0) {
351292241e0bSTom Erickson 			nvlist_t *errlist = NULL;
351392241e0bSTom Erickson 			/*
351492241e0bSTom Erickson 			 * Don't bother writing a property if its value won't
351592241e0bSTom Erickson 			 * change (and avoid the unnecessary security checks).
351692241e0bSTom Erickson 			 *
351792241e0bSTom Erickson 			 * The first receive after SPA_VERSION_RECVD_PROPS is a
351892241e0bSTom Erickson 			 * special case where we blow away all local properties
351992241e0bSTom Erickson 			 * regardless.
352092241e0bSTom Erickson 			 */
352192241e0bSTom Erickson 			if (!first_recvd_props)
352292241e0bSTom Erickson 				props_reduce(props, origprops);
352392241e0bSTom Erickson 			if (zfs_check_clearable(tofs, origprops,
352492241e0bSTom Erickson 			    &errlist) != 0)
352592241e0bSTom Erickson 				(void) nvlist_merge(errors, errlist, 0);
352692241e0bSTom Erickson 			nvlist_free(errlist);
352792241e0bSTom Erickson 		}
3528745cd3c5Smaybee 
3529503ad85cSMatthew Ahrens 		dmu_objset_rele(os, FTAG);
3530f18faf3fSek 	}
3531f18faf3fSek 
35323cb34c60Sahrens 	if (zc->zc_string[0]) {
3533503ad85cSMatthew Ahrens 		error = dmu_objset_hold(zc->zc_string, FTAG, &origin);
3534745cd3c5Smaybee 		if (error)
3535745cd3c5Smaybee 			goto out;
35363cb34c60Sahrens 	}
35373cb34c60Sahrens 
35389e69d7d0SLori Alt 	error = dmu_recv_begin(tofs, tosnap, zc->zc_top_ds,
35399e69d7d0SLori Alt 	    &zc->zc_begin_record, force, origin, &drc);
35403cb34c60Sahrens 	if (origin)
3541503ad85cSMatthew Ahrens 		dmu_objset_rele(origin, FTAG);
3542745cd3c5Smaybee 	if (error)
3543745cd3c5Smaybee 		goto out;
3544f18faf3fSek 
3545f18faf3fSek 	/*
354692241e0bSTom Erickson 	 * Set properties before we receive the stream so that they are applied
354792241e0bSTom Erickson 	 * to the new data. Note that we must call dmu_recv_stream() if
354892241e0bSTom Erickson 	 * dmu_recv_begin() succeeds.
3549f18faf3fSek 	 */
35503cb34c60Sahrens 	if (props) {
355192241e0bSTom Erickson 		nvlist_t *errlist;
355292241e0bSTom Erickson 
355392241e0bSTom Erickson 		if (dmu_objset_from_ds(drc.drc_logical_ds, &os) == 0) {
355492241e0bSTom Erickson 			if (drc.drc_newfs) {
355592241e0bSTom Erickson 				if (spa_version(os->os_spa) >=
355692241e0bSTom Erickson 				    SPA_VERSION_RECVD_PROPS)
355792241e0bSTom Erickson 					first_recvd_props = B_TRUE;
355892241e0bSTom Erickson 			} else if (origprops != NULL) {
355992241e0bSTom Erickson 				if (clear_received_props(os, tofs, origprops,
356092241e0bSTom Erickson 				    first_recvd_props ? NULL : props) != 0)
356192241e0bSTom Erickson 					zc->zc_obj |= ZPROP_ERR_NOCLEAR;
356292241e0bSTom Erickson 			} else {
356392241e0bSTom Erickson 				zc->zc_obj |= ZPROP_ERR_NOCLEAR;
356492241e0bSTom Erickson 			}
356592241e0bSTom Erickson 			dsl_prop_set_hasrecvd(os);
356692241e0bSTom Erickson 		} else if (!drc.drc_newfs) {
356792241e0bSTom Erickson 			zc->zc_obj |= ZPROP_ERR_NOCLEAR;
356892241e0bSTom Erickson 		}
356992241e0bSTom Erickson 
357092241e0bSTom Erickson 		(void) zfs_set_prop_nvlist(tofs, ZPROP_SRC_RECEIVED,
357192241e0bSTom Erickson 		    props, &errlist);
357292241e0bSTom Erickson 		(void) nvlist_merge(errors, errlist, 0);
357392241e0bSTom Erickson 		nvlist_free(errlist);
357492241e0bSTom Erickson 	}
357592241e0bSTom Erickson 
357692241e0bSTom Erickson 	if (fit_error_list(zc, &errors) != 0 || put_nvlist(zc, errors) != 0) {
3577745cd3c5Smaybee 		/*
357892241e0bSTom Erickson 		 * Caller made zc->zc_nvlist_dst less than the minimum expected
357992241e0bSTom Erickson 		 * size or supplied an invalid address.
3580745cd3c5Smaybee 		 */
358192241e0bSTom Erickson 		props_error = EINVAL;
35823cb34c60Sahrens 	}
35833cb34c60Sahrens 
35843cb34c60Sahrens 	off = fp->f_offset;
3585c99e4bdcSChris Kirby 	error = dmu_recv_stream(&drc, fp->f_vnode, &off, zc->zc_cleanup_fd,
3586c99e4bdcSChris Kirby 	    &zc->zc_action_handle);
3587a2eea2e1Sahrens 
3588f4b94bdeSMatthew Ahrens 	if (error == 0) {
3589f4b94bdeSMatthew Ahrens 		zfsvfs_t *zfsvfs = NULL;
3590745cd3c5Smaybee 
3591f4b94bdeSMatthew Ahrens 		if (getzfsvfs(tofs, &zfsvfs) == 0) {
3592f4b94bdeSMatthew Ahrens 			/* online recv */
3593f4b94bdeSMatthew Ahrens 			int end_err;
3594745cd3c5Smaybee 
3595503ad85cSMatthew Ahrens 			error = zfs_suspend_fs(zfsvfs);
3596f4b94bdeSMatthew Ahrens 			/*
3597f4b94bdeSMatthew Ahrens 			 * If the suspend fails, then the recv_end will
3598f4b94bdeSMatthew Ahrens 			 * likely also fail, and clean up after itself.
3599f4b94bdeSMatthew Ahrens 			 */
3600f4b94bdeSMatthew Ahrens 			end_err = dmu_recv_end(&drc);
36015c703fceSGeorge Wilson 			if (error == 0)
36025c703fceSGeorge Wilson 				error = zfs_resume_fs(zfsvfs, tofs);
3603f4b94bdeSMatthew Ahrens 			error = error ? error : end_err;
3604f4b94bdeSMatthew Ahrens 			VFS_RELE(zfsvfs->z_vfs);
3605745cd3c5Smaybee 		} else {
3606f4b94bdeSMatthew Ahrens 			error = dmu_recv_end(&drc);
36073cb34c60Sahrens 		}
360847f263f4Sek 	}
36093cb34c60Sahrens 
36103cb34c60Sahrens 	zc->zc_cookie = off - fp->f_offset;
36113cb34c60Sahrens 	if (VOP_SEEK(fp->f_vnode, fp->f_offset, &off, NULL) == 0)
36123cb34c60Sahrens 		fp->f_offset = off;
3613a2eea2e1Sahrens 
361492241e0bSTom Erickson #ifdef	DEBUG
361592241e0bSTom Erickson 	if (zfs_ioc_recv_inject_err) {
361692241e0bSTom Erickson 		zfs_ioc_recv_inject_err = B_FALSE;
361792241e0bSTom Erickson 		error = 1;
361892241e0bSTom Erickson 	}
361992241e0bSTom Erickson #endif
3620745cd3c5Smaybee 	/*
3621745cd3c5Smaybee 	 * On error, restore the original props.
3622745cd3c5Smaybee 	 */
3623745cd3c5Smaybee 	if (error && props) {
362492241e0bSTom Erickson 		if (dmu_objset_hold(tofs, FTAG, &os) == 0) {
362592241e0bSTom Erickson 			if (clear_received_props(os, tofs, props, NULL) != 0) {
362692241e0bSTom Erickson 				/*
362792241e0bSTom Erickson 				 * We failed to clear the received properties.
362892241e0bSTom Erickson 				 * Since we may have left a $recvd value on the
362992241e0bSTom Erickson 				 * system, we can't clear the $hasrecvd flag.
363092241e0bSTom Erickson 				 */
363192241e0bSTom Erickson 				zc->zc_obj |= ZPROP_ERR_NORESTORE;
363292241e0bSTom Erickson 			} else if (first_recvd_props) {
363392241e0bSTom Erickson 				dsl_prop_unset_hasrecvd(os);
363492241e0bSTom Erickson 			}
363592241e0bSTom Erickson 			dmu_objset_rele(os, FTAG);
363692241e0bSTom Erickson 		} else if (!drc.drc_newfs) {
363792241e0bSTom Erickson 			/* We failed to clear the received properties. */
363892241e0bSTom Erickson 			zc->zc_obj |= ZPROP_ERR_NORESTORE;
363992241e0bSTom Erickson 		}
364092241e0bSTom Erickson 
364192241e0bSTom Erickson 		if (origprops == NULL && !drc.drc_newfs) {
364292241e0bSTom Erickson 			/* We failed to stash the original properties. */
364392241e0bSTom Erickson 			zc->zc_obj |= ZPROP_ERR_NORESTORE;
364492241e0bSTom Erickson 		}
364592241e0bSTom Erickson 
364692241e0bSTom Erickson 		/*
364792241e0bSTom Erickson 		 * dsl_props_set() will not convert RECEIVED to LOCAL on or
364892241e0bSTom Erickson 		 * after SPA_VERSION_RECVD_PROPS, so we need to specify LOCAL
364992241e0bSTom Erickson 		 * explictly if we're restoring local properties cleared in the
365092241e0bSTom Erickson 		 * first new-style receive.
365192241e0bSTom Erickson 		 */
365292241e0bSTom Erickson 		if (origprops != NULL &&
365392241e0bSTom Erickson 		    zfs_set_prop_nvlist(tofs, (first_recvd_props ?
365492241e0bSTom Erickson 		    ZPROP_SRC_LOCAL : ZPROP_SRC_RECEIVED),
365592241e0bSTom Erickson 		    origprops, NULL) != 0) {
365692241e0bSTom Erickson 			/*
365792241e0bSTom Erickson 			 * We stashed the original properties but failed to
365892241e0bSTom Erickson 			 * restore them.
365992241e0bSTom Erickson 			 */
366092241e0bSTom Erickson 			zc->zc_obj |= ZPROP_ERR_NORESTORE;
366192241e0bSTom Erickson 		}
3662745cd3c5Smaybee 	}
3663745cd3c5Smaybee out:
3664745cd3c5Smaybee 	nvlist_free(props);
3665745cd3c5Smaybee 	nvlist_free(origprops);
366692241e0bSTom Erickson 	nvlist_free(errors);
3667fa9e4066Sahrens 	releasef(fd);
366892241e0bSTom Erickson 
366992241e0bSTom Erickson 	if (error == 0)
367092241e0bSTom Erickson 		error = props_error;
367192241e0bSTom Erickson 
3672fa9e4066Sahrens 	return (error);
3673fa9e4066Sahrens }
3674fa9e4066Sahrens 
36753cb34c60Sahrens /*
36763cb34c60Sahrens  * inputs:
36773cb34c60Sahrens  * zc_name	name of snapshot to send
36783cb34c60Sahrens  * zc_cookie	file descriptor to send stream to
3679*a7f53a56SChris Kirby  * zc_obj	fromorigin flag (mutually exclusive with zc_fromobj)
3680*a7f53a56SChris Kirby  * zc_sendobj	objsetid of snapshot to send
3681*a7f53a56SChris Kirby  * zc_fromobj	objsetid of incremental fromsnap (may be zero)
36823cb34c60Sahrens  *
36833cb34c60Sahrens  * outputs: none
36843cb34c60Sahrens  */
3685fa9e4066Sahrens static int
36863cb34c60Sahrens zfs_ioc_send(zfs_cmd_t *zc)
3687fa9e4066Sahrens {
3688fa9e4066Sahrens 	objset_t *fromsnap = NULL;
3689fa9e4066Sahrens 	objset_t *tosnap;
3690fa9e4066Sahrens 	file_t *fp;
3691fa9e4066Sahrens 	int error;
36923cb34c60Sahrens 	offset_t off;
3693*a7f53a56SChris Kirby 	dsl_dataset_t *ds;
3694*a7f53a56SChris Kirby 	dsl_dataset_t *dsfrom = NULL;
3695*a7f53a56SChris Kirby 	spa_t *spa;
3696*a7f53a56SChris Kirby 	dsl_pool_t *dp;
3697fa9e4066Sahrens 
3698*a7f53a56SChris Kirby 	error = spa_open(zc->zc_name, &spa, FTAG);
3699fa9e4066Sahrens 	if (error)
3700fa9e4066Sahrens 		return (error);
3701fa9e4066Sahrens 
3702*a7f53a56SChris Kirby 	dp = spa_get_dsl(spa);
3703*a7f53a56SChris Kirby 	rw_enter(&dp->dp_config_rwlock, RW_READER);
3704*a7f53a56SChris Kirby 	error = dsl_dataset_hold_obj(dp, zc->zc_sendobj, FTAG, &ds);
3705*a7f53a56SChris Kirby 	rw_exit(&dp->dp_config_rwlock);
3706*a7f53a56SChris Kirby 	if (error) {
3707*a7f53a56SChris Kirby 		spa_close(spa, FTAG);
3708*a7f53a56SChris Kirby 		return (error);
3709*a7f53a56SChris Kirby 	}
3710*a7f53a56SChris Kirby 
3711*a7f53a56SChris Kirby 	error = dmu_objset_from_ds(ds, &tosnap);
3712*a7f53a56SChris Kirby 	if (error) {
3713*a7f53a56SChris Kirby 		dsl_dataset_rele(ds, FTAG);
3714*a7f53a56SChris Kirby 		spa_close(spa, FTAG);
3715*a7f53a56SChris Kirby 		return (error);
3716*a7f53a56SChris Kirby 	}
3717*a7f53a56SChris Kirby 
3718*a7f53a56SChris Kirby 	if (zc->zc_fromobj != 0) {
3719*a7f53a56SChris Kirby 		rw_enter(&dp->dp_config_rwlock, RW_READER);
3720*a7f53a56SChris Kirby 		error = dsl_dataset_hold_obj(dp, zc->zc_fromobj, FTAG, &dsfrom);
3721*a7f53a56SChris Kirby 		rw_exit(&dp->dp_config_rwlock);
3722*a7f53a56SChris Kirby 		spa_close(spa, FTAG);
3723*a7f53a56SChris Kirby 		if (error) {
3724*a7f53a56SChris Kirby 			dsl_dataset_rele(ds, FTAG);
3725*a7f53a56SChris Kirby 			return (error);
3726*a7f53a56SChris Kirby 		}
3727*a7f53a56SChris Kirby 		error = dmu_objset_from_ds(dsfrom, &fromsnap);
3728fa9e4066Sahrens 		if (error) {
3729*a7f53a56SChris Kirby 			dsl_dataset_rele(dsfrom, FTAG);
3730*a7f53a56SChris Kirby 			dsl_dataset_rele(ds, FTAG);
3731fa9e4066Sahrens 			return (error);
3732fa9e4066Sahrens 		}
3733*a7f53a56SChris Kirby 	} else {
3734*a7f53a56SChris Kirby 		spa_close(spa, FTAG);
3735fa9e4066Sahrens 	}
3736fa9e4066Sahrens 
3737fa9e4066Sahrens 	fp = getf(zc->zc_cookie);
3738fa9e4066Sahrens 	if (fp == NULL) {
3739*a7f53a56SChris Kirby 		dsl_dataset_rele(ds, FTAG);
3740*a7f53a56SChris Kirby 		if (dsfrom)
3741*a7f53a56SChris Kirby 			dsl_dataset_rele(dsfrom, FTAG);
3742fa9e4066Sahrens 		return (EBADF);
3743fa9e4066Sahrens 	}
3744fa9e4066Sahrens 
37453cb34c60Sahrens 	off = fp->f_offset;
37463cb34c60Sahrens 	error = dmu_sendbackup(tosnap, fromsnap, zc->zc_obj, fp->f_vnode, &off);
3747fa9e4066Sahrens 
37483cb34c60Sahrens 	if (VOP_SEEK(fp->f_vnode, fp->f_offset, &off, NULL) == 0)
37493cb34c60Sahrens 		fp->f_offset = off;
3750fa9e4066Sahrens 	releasef(zc->zc_cookie);
3751*a7f53a56SChris Kirby 	if (dsfrom)
3752*a7f53a56SChris Kirby 		dsl_dataset_rele(dsfrom, FTAG);
3753*a7f53a56SChris Kirby 	dsl_dataset_rele(ds, FTAG);
3754fa9e4066Sahrens 	return (error);
3755fa9e4066Sahrens }
3756fa9e4066Sahrens 
3757ea8dc4b6Seschrock static int
3758ea8dc4b6Seschrock zfs_ioc_inject_fault(zfs_cmd_t *zc)
3759ea8dc4b6Seschrock {
3760ea8dc4b6Seschrock 	int id, error;
3761ea8dc4b6Seschrock 
3762ea8dc4b6Seschrock 	error = zio_inject_fault(zc->zc_name, (int)zc->zc_guid, &id,
3763ea8dc4b6Seschrock 	    &zc->zc_inject_record);
3764ea8dc4b6Seschrock 
3765ea8dc4b6Seschrock 	if (error == 0)
3766ea8dc4b6Seschrock 		zc->zc_guid = (uint64_t)id;
3767ea8dc4b6Seschrock 
3768ea8dc4b6Seschrock 	return (error);
3769ea8dc4b6Seschrock }
3770ea8dc4b6Seschrock 
3771ea8dc4b6Seschrock static int
3772ea8dc4b6Seschrock zfs_ioc_clear_fault(zfs_cmd_t *zc)
3773ea8dc4b6Seschrock {
3774ea8dc4b6Seschrock 	return (zio_clear_fault((int)zc->zc_guid));
3775ea8dc4b6Seschrock }
3776ea8dc4b6Seschrock 
3777ea8dc4b6Seschrock static int
3778ea8dc4b6Seschrock zfs_ioc_inject_list_next(zfs_cmd_t *zc)
3779ea8dc4b6Seschrock {
3780ea8dc4b6Seschrock 	int id = (int)zc->zc_guid;
3781ea8dc4b6Seschrock 	int error;
3782ea8dc4b6Seschrock 
3783ea8dc4b6Seschrock 	error = zio_inject_list_next(&id, zc->zc_name, sizeof (zc->zc_name),
3784ea8dc4b6Seschrock 	    &zc->zc_inject_record);
3785ea8dc4b6Seschrock 
3786ea8dc4b6Seschrock 	zc->zc_guid = id;
3787ea8dc4b6Seschrock 
3788ea8dc4b6Seschrock 	return (error);
3789ea8dc4b6Seschrock }
3790ea8dc4b6Seschrock 
3791ea8dc4b6Seschrock static int
3792ea8dc4b6Seschrock zfs_ioc_error_log(zfs_cmd_t *zc)
3793ea8dc4b6Seschrock {
3794ea8dc4b6Seschrock 	spa_t *spa;
3795ea8dc4b6Seschrock 	int error;
3796e9dbad6fSeschrock 	size_t count = (size_t)zc->zc_nvlist_dst_size;
3797ea8dc4b6Seschrock 
3798ea8dc4b6Seschrock 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
3799ea8dc4b6Seschrock 		return (error);
3800ea8dc4b6Seschrock 
3801e9dbad6fSeschrock 	error = spa_get_errlog(spa, (void *)(uintptr_t)zc->zc_nvlist_dst,
3802ea8dc4b6Seschrock 	    &count);
3803ea8dc4b6Seschrock 	if (error == 0)
3804e9dbad6fSeschrock 		zc->zc_nvlist_dst_size = count;
3805ea8dc4b6Seschrock 	else
3806e9dbad6fSeschrock 		zc->zc_nvlist_dst_size = spa_get_errlog_size(spa);
3807ea8dc4b6Seschrock 
3808ea8dc4b6Seschrock 	spa_close(spa, FTAG);
3809ea8dc4b6Seschrock 
3810ea8dc4b6Seschrock 	return (error);
3811ea8dc4b6Seschrock }
3812ea8dc4b6Seschrock 
3813ea8dc4b6Seschrock static int
3814ea8dc4b6Seschrock zfs_ioc_clear(zfs_cmd_t *zc)
3815ea8dc4b6Seschrock {
3816ea8dc4b6Seschrock 	spa_t *spa;
3817ea8dc4b6Seschrock 	vdev_t *vd;
3818bb8b5132Sek 	int error;
3819ea8dc4b6Seschrock 
3820b87f3af3Sperrin 	/*
3821b87f3af3Sperrin 	 * On zpool clear we also fix up missing slogs
3822b87f3af3Sperrin 	 */
3823b87f3af3Sperrin 	mutex_enter(&spa_namespace_lock);
3824b87f3af3Sperrin 	spa = spa_lookup(zc->zc_name);
3825b87f3af3Sperrin 	if (spa == NULL) {
3826b87f3af3Sperrin 		mutex_exit(&spa_namespace_lock);
3827b87f3af3Sperrin 		return (EIO);
3828b87f3af3Sperrin 	}
3829b24ab676SJeff Bonwick 	if (spa_get_log_state(spa) == SPA_LOG_MISSING) {
3830b87f3af3Sperrin 		/* we need to let spa_open/spa_load clear the chains */
3831b24ab676SJeff Bonwick 		spa_set_log_state(spa, SPA_LOG_CLEAR);
3832b87f3af3Sperrin 	}
3833468c413aSTim Haley 	spa->spa_last_open_failed = 0;
3834b87f3af3Sperrin 	mutex_exit(&spa_namespace_lock);
3835b87f3af3Sperrin 
3836c8ee1847SVictor Latushkin 	if (zc->zc_cookie & ZPOOL_NO_REWIND) {
3837468c413aSTim Haley 		error = spa_open(zc->zc_name, &spa, FTAG);
3838468c413aSTim Haley 	} else {
3839468c413aSTim Haley 		nvlist_t *policy;
3840468c413aSTim Haley 		nvlist_t *config = NULL;
3841468c413aSTim Haley 
3842468c413aSTim Haley 		if (zc->zc_nvlist_src == NULL)
3843468c413aSTim Haley 			return (EINVAL);
3844468c413aSTim Haley 
3845468c413aSTim Haley 		if ((error = get_nvlist(zc->zc_nvlist_src,
3846468c413aSTim Haley 		    zc->zc_nvlist_src_size, zc->zc_iflags, &policy)) == 0) {
3847468c413aSTim Haley 			error = spa_open_rewind(zc->zc_name, &spa, FTAG,
3848468c413aSTim Haley 			    policy, &config);
3849468c413aSTim Haley 			if (config != NULL) {
3850468c413aSTim Haley 				(void) put_nvlist(zc, config);
3851468c413aSTim Haley 				nvlist_free(config);
3852468c413aSTim Haley 			}
3853468c413aSTim Haley 			nvlist_free(policy);
3854468c413aSTim Haley 		}
3855468c413aSTim Haley 	}
3856468c413aSTim Haley 
3857468c413aSTim Haley 	if (error)
3858ea8dc4b6Seschrock 		return (error);
3859ea8dc4b6Seschrock 
38608f18d1faSGeorge Wilson 	spa_vdev_state_enter(spa, SCL_NONE);
3861ea8dc4b6Seschrock 
3862e9dbad6fSeschrock 	if (zc->zc_guid == 0) {
3863ea8dc4b6Seschrock 		vd = NULL;
3864c5904d13Seschrock 	} else {
3865c5904d13Seschrock 		vd = spa_lookup_by_guid(spa, zc->zc_guid, B_TRUE);
3866fa94a07fSbrendan 		if (vd == NULL) {
3867e14bb325SJeff Bonwick 			(void) spa_vdev_state_exit(spa, NULL, ENODEV);
3868fa94a07fSbrendan 			spa_close(spa, FTAG);
3869fa94a07fSbrendan 			return (ENODEV);
3870fa94a07fSbrendan 		}
3871ea8dc4b6Seschrock 	}
3872ea8dc4b6Seschrock 
3873e14bb325SJeff Bonwick 	vdev_clear(spa, vd);
3874e14bb325SJeff Bonwick 
3875e14bb325SJeff Bonwick 	(void) spa_vdev_state_exit(spa, NULL, 0);
3876ea8dc4b6Seschrock 
3877e14bb325SJeff Bonwick 	/*
3878e14bb325SJeff Bonwick 	 * Resume any suspended I/Os.
3879e14bb325SJeff Bonwick 	 */
388054d692b7SGeorge Wilson 	if (zio_resume(spa) != 0)
388154d692b7SGeorge Wilson 		error = EIO;
3882ea8dc4b6Seschrock 
3883ea8dc4b6Seschrock 	spa_close(spa, FTAG);
3884ea8dc4b6Seschrock 
388554d692b7SGeorge Wilson 	return (error);
3886ea8dc4b6Seschrock }
3887ea8dc4b6Seschrock 
38883cb34c60Sahrens /*
38893cb34c60Sahrens  * inputs:
38903cb34c60Sahrens  * zc_name	name of filesystem
38913cb34c60Sahrens  * zc_value	name of origin snapshot
38923cb34c60Sahrens  *
3893681d9761SEric Taylor  * outputs:
3894681d9761SEric Taylor  * zc_string	name of conflicting snapshot, if there is one
38953cb34c60Sahrens  */
389699653d4eSeschrock static int
389799653d4eSeschrock zfs_ioc_promote(zfs_cmd_t *zc)
389899653d4eSeschrock {
38990b69c2f0Sahrens 	char *cp;
39000b69c2f0Sahrens 
39010b69c2f0Sahrens 	/*
39020b69c2f0Sahrens 	 * We don't need to unmount *all* the origin fs's snapshots, but
39030b69c2f0Sahrens 	 * it's easier.
39040b69c2f0Sahrens 	 */
3905e9dbad6fSeschrock 	cp = strchr(zc->zc_value, '@');
39060b69c2f0Sahrens 	if (cp)
39070b69c2f0Sahrens 		*cp = '\0';
3908e9dbad6fSeschrock 	(void) dmu_objset_find(zc->zc_value,
39090b69c2f0Sahrens 	    zfs_unmount_snap, NULL, DS_FIND_SNAPSHOTS);
3910681d9761SEric Taylor 	return (dsl_dataset_promote(zc->zc_name, zc->zc_string));
391199653d4eSeschrock }
391299653d4eSeschrock 
391314843421SMatthew Ahrens /*
391414843421SMatthew Ahrens  * Retrieve a single {user|group}{used|quota}@... property.
391514843421SMatthew Ahrens  *
391614843421SMatthew Ahrens  * inputs:
391714843421SMatthew Ahrens  * zc_name	name of filesystem
391814843421SMatthew Ahrens  * zc_objset_type zfs_userquota_prop_t
391914843421SMatthew Ahrens  * zc_value	domain name (eg. "S-1-234-567-89")
392014843421SMatthew Ahrens  * zc_guid	RID/UID/GID
392114843421SMatthew Ahrens  *
392214843421SMatthew Ahrens  * outputs:
392314843421SMatthew Ahrens  * zc_cookie	property value
392414843421SMatthew Ahrens  */
392514843421SMatthew Ahrens static int
392614843421SMatthew Ahrens zfs_ioc_userspace_one(zfs_cmd_t *zc)
392714843421SMatthew Ahrens {
392814843421SMatthew Ahrens 	zfsvfs_t *zfsvfs;
392914843421SMatthew Ahrens 	int error;
393014843421SMatthew Ahrens 
393114843421SMatthew Ahrens 	if (zc->zc_objset_type >= ZFS_NUM_USERQUOTA_PROPS)
393214843421SMatthew Ahrens 		return (EINVAL);
393314843421SMatthew Ahrens 
39341412a1a2SMark Shellenbaum 	error = zfsvfs_hold(zc->zc_name, FTAG, &zfsvfs, B_FALSE);
393514843421SMatthew Ahrens 	if (error)
393614843421SMatthew Ahrens 		return (error);
393714843421SMatthew Ahrens 
393814843421SMatthew Ahrens 	error = zfs_userspace_one(zfsvfs,
393914843421SMatthew Ahrens 	    zc->zc_objset_type, zc->zc_value, zc->zc_guid, &zc->zc_cookie);
394014843421SMatthew Ahrens 	zfsvfs_rele(zfsvfs, FTAG);
394114843421SMatthew Ahrens 
394214843421SMatthew Ahrens 	return (error);
394314843421SMatthew Ahrens }
394414843421SMatthew Ahrens 
394514843421SMatthew Ahrens /*
394614843421SMatthew Ahrens  * inputs:
394714843421SMatthew Ahrens  * zc_name		name of filesystem
394814843421SMatthew Ahrens  * zc_cookie		zap cursor
394914843421SMatthew Ahrens  * zc_objset_type	zfs_userquota_prop_t
395014843421SMatthew Ahrens  * zc_nvlist_dst[_size] buffer to fill (not really an nvlist)
395114843421SMatthew Ahrens  *
395214843421SMatthew Ahrens  * outputs:
395314843421SMatthew Ahrens  * zc_nvlist_dst[_size]	data buffer (array of zfs_useracct_t)
395414843421SMatthew Ahrens  * zc_cookie	zap cursor
395514843421SMatthew Ahrens  */
395614843421SMatthew Ahrens static int
395714843421SMatthew Ahrens zfs_ioc_userspace_many(zfs_cmd_t *zc)
395814843421SMatthew Ahrens {
395914843421SMatthew Ahrens 	zfsvfs_t *zfsvfs;
3960eeb85002STim Haley 	int bufsize = zc->zc_nvlist_dst_size;
396114843421SMatthew Ahrens 
3962eeb85002STim Haley 	if (bufsize <= 0)
3963eeb85002STim Haley 		return (ENOMEM);
3964eeb85002STim Haley 
39651412a1a2SMark Shellenbaum 	int error = zfsvfs_hold(zc->zc_name, FTAG, &zfsvfs, B_FALSE);
396614843421SMatthew Ahrens 	if (error)
396714843421SMatthew Ahrens 		return (error);
396814843421SMatthew Ahrens 
396914843421SMatthew Ahrens 	void *buf = kmem_alloc(bufsize, KM_SLEEP);
397014843421SMatthew Ahrens 
397114843421SMatthew Ahrens 	error = zfs_userspace_many(zfsvfs, zc->zc_objset_type, &zc->zc_cookie,
397214843421SMatthew Ahrens 	    buf, &zc->zc_nvlist_dst_size);
397314843421SMatthew Ahrens 
397414843421SMatthew Ahrens 	if (error == 0) {
397514843421SMatthew Ahrens 		error = xcopyout(buf,
397614843421SMatthew Ahrens 		    (void *)(uintptr_t)zc->zc_nvlist_dst,
397714843421SMatthew Ahrens 		    zc->zc_nvlist_dst_size);
397814843421SMatthew Ahrens 	}
397914843421SMatthew Ahrens 	kmem_free(buf, bufsize);
398014843421SMatthew Ahrens 	zfsvfs_rele(zfsvfs, FTAG);
398114843421SMatthew Ahrens 
398214843421SMatthew Ahrens 	return (error);
398314843421SMatthew Ahrens }
398414843421SMatthew Ahrens 
398514843421SMatthew Ahrens /*
398614843421SMatthew Ahrens  * inputs:
398714843421SMatthew Ahrens  * zc_name		name of filesystem
398814843421SMatthew Ahrens  *
398914843421SMatthew Ahrens  * outputs:
399014843421SMatthew Ahrens  * none
399114843421SMatthew Ahrens  */
399214843421SMatthew Ahrens static int
399314843421SMatthew Ahrens zfs_ioc_userspace_upgrade(zfs_cmd_t *zc)
399414843421SMatthew Ahrens {
399514843421SMatthew Ahrens 	objset_t *os;
39961195e687SMark J Musante 	int error = 0;
399714843421SMatthew Ahrens 	zfsvfs_t *zfsvfs;
399814843421SMatthew Ahrens 
399914843421SMatthew Ahrens 	if (getzfsvfs(zc->zc_name, &zfsvfs) == 0) {
4000503ad85cSMatthew Ahrens 		if (!dmu_objset_userused_enabled(zfsvfs->z_os)) {
400114843421SMatthew Ahrens 			/*
400214843421SMatthew Ahrens 			 * If userused is not enabled, it may be because the
400314843421SMatthew Ahrens 			 * objset needs to be closed & reopened (to grow the
400414843421SMatthew Ahrens 			 * objset_phys_t).  Suspend/resume the fs will do that.
400514843421SMatthew Ahrens 			 */
4006503ad85cSMatthew Ahrens 			error = zfs_suspend_fs(zfsvfs);
4007503ad85cSMatthew Ahrens 			if (error == 0)
4008503ad85cSMatthew Ahrens 				error = zfs_resume_fs(zfsvfs, zc->zc_name);
400914843421SMatthew Ahrens 		}
401014843421SMatthew Ahrens 		if (error == 0)
401114843421SMatthew Ahrens 			error = dmu_objset_userspace_upgrade(zfsvfs->z_os);
401214843421SMatthew Ahrens 		VFS_RELE(zfsvfs->z_vfs);
401314843421SMatthew Ahrens 	} else {
4014503ad85cSMatthew Ahrens 		/* XXX kind of reading contents without owning */
4015503ad85cSMatthew Ahrens 		error = dmu_objset_hold(zc->zc_name, FTAG, &os);
401614843421SMatthew Ahrens 		if (error)
401714843421SMatthew Ahrens 			return (error);
401814843421SMatthew Ahrens 
401914843421SMatthew Ahrens 		error = dmu_objset_userspace_upgrade(os);
4020503ad85cSMatthew Ahrens 		dmu_objset_rele(os, FTAG);
402114843421SMatthew Ahrens 	}
402214843421SMatthew Ahrens 
402314843421SMatthew Ahrens 	return (error);
402414843421SMatthew Ahrens }
402514843421SMatthew Ahrens 
4026ecd6cf80Smarks /*
4027ecd6cf80Smarks  * We don't want to have a hard dependency
4028ecd6cf80Smarks  * against some special symbols in sharefs
4029da6c28aaSamw  * nfs, and smbsrv.  Determine them if needed when
4030ecd6cf80Smarks  * the first file system is shared.
4031da6c28aaSamw  * Neither sharefs, nfs or smbsrv are unloadable modules.
4032ecd6cf80Smarks  */
4033da6c28aaSamw int (*znfsexport_fs)(void *arg);
4034ecd6cf80Smarks int (*zshare_fs)(enum sharefs_sys_op, share_t *, uint32_t);
4035da6c28aaSamw int (*zsmbexport_fs)(void *arg, boolean_t add_share);
4036da6c28aaSamw 
4037da6c28aaSamw int zfs_nfsshare_inited;
4038da6c28aaSamw int zfs_smbshare_inited;
4039ecd6cf80Smarks 
4040ecd6cf80Smarks ddi_modhandle_t nfs_mod;
4041ecd6cf80Smarks ddi_modhandle_t sharefs_mod;
4042da6c28aaSamw ddi_modhandle_t smbsrv_mod;
4043ecd6cf80Smarks kmutex_t zfs_share_lock;
4044ecd6cf80Smarks 
4045da6c28aaSamw static int
4046da6c28aaSamw zfs_init_sharefs()
4047da6c28aaSamw {
4048da6c28aaSamw 	int error;
4049da6c28aaSamw 
4050da6c28aaSamw 	ASSERT(MUTEX_HELD(&zfs_share_lock));
4051da6c28aaSamw 	/* Both NFS and SMB shares also require sharetab support. */
4052da6c28aaSamw 	if (sharefs_mod == NULL && ((sharefs_mod =
4053da6c28aaSamw 	    ddi_modopen("fs/sharefs",
4054da6c28aaSamw 	    KRTLD_MODE_FIRST, &error)) == NULL)) {
4055da6c28aaSamw 		return (ENOSYS);
4056da6c28aaSamw 	}
4057da6c28aaSamw 	if (zshare_fs == NULL && ((zshare_fs =
4058da6c28aaSamw 	    (int (*)(enum sharefs_sys_op, share_t *, uint32_t))
4059da6c28aaSamw 	    ddi_modsym(sharefs_mod, "sharefs_impl", &error)) == NULL)) {
4060da6c28aaSamw 		return (ENOSYS);
4061da6c28aaSamw 	}
4062da6c28aaSamw 	return (0);
4063da6c28aaSamw }
4064da6c28aaSamw 
4065ecd6cf80Smarks static int
4066ecd6cf80Smarks zfs_ioc_share(zfs_cmd_t *zc)
4067ecd6cf80Smarks {
4068ecd6cf80Smarks 	int error;
4069ecd6cf80Smarks 	int opcode;
4070ecd6cf80Smarks 
4071da6c28aaSamw 	switch (zc->zc_share.z_sharetype) {
4072da6c28aaSamw 	case ZFS_SHARE_NFS:
4073da6c28aaSamw 	case ZFS_UNSHARE_NFS:
4074da6c28aaSamw 		if (zfs_nfsshare_inited == 0) {
4075da6c28aaSamw 			mutex_enter(&zfs_share_lock);
4076da6c28aaSamw 			if (nfs_mod == NULL && ((nfs_mod = ddi_modopen("fs/nfs",
4077da6c28aaSamw 			    KRTLD_MODE_FIRST, &error)) == NULL)) {
4078da6c28aaSamw 				mutex_exit(&zfs_share_lock);
4079da6c28aaSamw 				return (ENOSYS);
4080da6c28aaSamw 			}
4081da6c28aaSamw 			if (znfsexport_fs == NULL &&
4082da6c28aaSamw 			    ((znfsexport_fs = (int (*)(void *))
4083da6c28aaSamw 			    ddi_modsym(nfs_mod,
4084da6c28aaSamw 			    "nfs_export", &error)) == NULL)) {
4085da6c28aaSamw 				mutex_exit(&zfs_share_lock);
4086da6c28aaSamw 				return (ENOSYS);
4087da6c28aaSamw 			}
4088da6c28aaSamw 			error = zfs_init_sharefs();
4089da6c28aaSamw 			if (error) {
4090da6c28aaSamw 				mutex_exit(&zfs_share_lock);
4091da6c28aaSamw 				return (ENOSYS);
4092da6c28aaSamw 			}
4093da6c28aaSamw 			zfs_nfsshare_inited = 1;
4094ecd6cf80Smarks 			mutex_exit(&zfs_share_lock);
4095ecd6cf80Smarks 		}
4096da6c28aaSamw 		break;
4097da6c28aaSamw 	case ZFS_SHARE_SMB:
4098da6c28aaSamw 	case ZFS_UNSHARE_SMB:
4099da6c28aaSamw 		if (zfs_smbshare_inited == 0) {
4100da6c28aaSamw 			mutex_enter(&zfs_share_lock);
4101da6c28aaSamw 			if (smbsrv_mod == NULL && ((smbsrv_mod =
4102da6c28aaSamw 			    ddi_modopen("drv/smbsrv",
4103da6c28aaSamw 			    KRTLD_MODE_FIRST, &error)) == NULL)) {
4104da6c28aaSamw 				mutex_exit(&zfs_share_lock);
4105da6c28aaSamw 				return (ENOSYS);
4106da6c28aaSamw 			}
4107da6c28aaSamw 			if (zsmbexport_fs == NULL && ((zsmbexport_fs =
4108da6c28aaSamw 			    (int (*)(void *, boolean_t))ddi_modsym(smbsrv_mod,
4109faa1795aSjb 			    "smb_server_share", &error)) == NULL)) {
4110da6c28aaSamw 				mutex_exit(&zfs_share_lock);
4111da6c28aaSamw 				return (ENOSYS);
4112da6c28aaSamw 			}
4113da6c28aaSamw 			error = zfs_init_sharefs();
4114da6c28aaSamw 			if (error) {
4115da6c28aaSamw 				mutex_exit(&zfs_share_lock);
4116da6c28aaSamw 				return (ENOSYS);
4117da6c28aaSamw 			}
4118da6c28aaSamw 			zfs_smbshare_inited = 1;
4119ecd6cf80Smarks 			mutex_exit(&zfs_share_lock);
4120ecd6cf80Smarks 		}
4121da6c28aaSamw 		break;
4122da6c28aaSamw 	default:
4123da6c28aaSamw 		return (EINVAL);
4124da6c28aaSamw 	}
4125ecd6cf80Smarks 
4126da6c28aaSamw 	switch (zc->zc_share.z_sharetype) {
4127da6c28aaSamw 	case ZFS_SHARE_NFS:
4128da6c28aaSamw 	case ZFS_UNSHARE_NFS:
4129da6c28aaSamw 		if (error =
4130da6c28aaSamw 		    znfsexport_fs((void *)
4131da6c28aaSamw 		    (uintptr_t)zc->zc_share.z_exportdata))
4132da6c28aaSamw 			return (error);
4133da6c28aaSamw 		break;
4134da6c28aaSamw 	case ZFS_SHARE_SMB:
4135da6c28aaSamw 	case ZFS_UNSHARE_SMB:
4136da6c28aaSamw 		if (error = zsmbexport_fs((void *)
4137da6c28aaSamw 		    (uintptr_t)zc->zc_share.z_exportdata,
4138da6c28aaSamw 		    zc->zc_share.z_sharetype == ZFS_SHARE_SMB ?
4139743a77edSAlan Wright 		    B_TRUE: B_FALSE)) {
4140da6c28aaSamw 			return (error);
4141ecd6cf80Smarks 		}
4142da6c28aaSamw 		break;
4143ecd6cf80Smarks 	}
4144ecd6cf80Smarks 
4145da6c28aaSamw 	opcode = (zc->zc_share.z_sharetype == ZFS_SHARE_NFS ||
4146da6c28aaSamw 	    zc->zc_share.z_sharetype == ZFS_SHARE_SMB) ?
4147ecd6cf80Smarks 	    SHAREFS_ADD : SHAREFS_REMOVE;
4148ecd6cf80Smarks 
4149da6c28aaSamw 	/*
4150da6c28aaSamw 	 * Add or remove share from sharetab
4151da6c28aaSamw 	 */
4152ecd6cf80Smarks 	error = zshare_fs(opcode,
4153ecd6cf80Smarks 	    (void *)(uintptr_t)zc->zc_share.z_sharedata,
4154ecd6cf80Smarks 	    zc->zc_share.z_sharemax);
4155ecd6cf80Smarks 
4156ecd6cf80Smarks 	return (error);
4157ecd6cf80Smarks 
4158ecd6cf80Smarks }
4159ecd6cf80Smarks 
4160743a77edSAlan Wright ace_t full_access[] = {
4161743a77edSAlan Wright 	{(uid_t)-1, ACE_ALL_PERMS, ACE_EVERYONE, 0}
4162743a77edSAlan Wright };
4163743a77edSAlan Wright 
4164743a77edSAlan Wright /*
4165743a77edSAlan Wright  * Remove all ACL files in shares dir
4166743a77edSAlan Wright  */
4167743a77edSAlan Wright static int
4168743a77edSAlan Wright zfs_smb_acl_purge(znode_t *dzp)
4169743a77edSAlan Wright {
4170743a77edSAlan Wright 	zap_cursor_t	zc;
4171743a77edSAlan Wright 	zap_attribute_t	zap;
4172743a77edSAlan Wright 	zfsvfs_t *zfsvfs = dzp->z_zfsvfs;
4173743a77edSAlan Wright 	int error;
4174743a77edSAlan Wright 
4175743a77edSAlan Wright 	for (zap_cursor_init(&zc, zfsvfs->z_os, dzp->z_id);
4176743a77edSAlan Wright 	    (error = zap_cursor_retrieve(&zc, &zap)) == 0;
4177743a77edSAlan Wright 	    zap_cursor_advance(&zc)) {
4178743a77edSAlan Wright 		if ((error = VOP_REMOVE(ZTOV(dzp), zap.za_name, kcred,
4179743a77edSAlan Wright 		    NULL, 0)) != 0)
4180743a77edSAlan Wright 			break;
4181743a77edSAlan Wright 	}
4182743a77edSAlan Wright 	zap_cursor_fini(&zc);
4183743a77edSAlan Wright 	return (error);
4184743a77edSAlan Wright }
4185743a77edSAlan Wright 
4186743a77edSAlan Wright static int
4187743a77edSAlan Wright zfs_ioc_smb_acl(zfs_cmd_t *zc)
4188743a77edSAlan Wright {
4189743a77edSAlan Wright 	vnode_t *vp;
4190743a77edSAlan Wright 	znode_t *dzp;
4191743a77edSAlan Wright 	vnode_t *resourcevp = NULL;
4192743a77edSAlan Wright 	znode_t *sharedir;
4193743a77edSAlan Wright 	zfsvfs_t *zfsvfs;
4194743a77edSAlan Wright 	nvlist_t *nvlist;
4195743a77edSAlan Wright 	char *src, *target;
4196743a77edSAlan Wright 	vattr_t vattr;
4197743a77edSAlan Wright 	vsecattr_t vsec;
4198743a77edSAlan Wright 	int error = 0;
4199743a77edSAlan Wright 
4200743a77edSAlan Wright 	if ((error = lookupname(zc->zc_value, UIO_SYSSPACE,
4201743a77edSAlan Wright 	    NO_FOLLOW, NULL, &vp)) != 0)
4202743a77edSAlan Wright 		return (error);
4203743a77edSAlan Wright 
4204743a77edSAlan Wright 	/* Now make sure mntpnt and dataset are ZFS */
4205743a77edSAlan Wright 
4206743a77edSAlan Wright 	if (vp->v_vfsp->vfs_fstype != zfsfstype ||
4207743a77edSAlan Wright 	    (strcmp((char *)refstr_value(vp->v_vfsp->vfs_resource),
4208743a77edSAlan Wright 	    zc->zc_name) != 0)) {
4209743a77edSAlan Wright 		VN_RELE(vp);
4210743a77edSAlan Wright 		return (EINVAL);
4211743a77edSAlan Wright 	}
4212743a77edSAlan Wright 
4213743a77edSAlan Wright 	dzp = VTOZ(vp);
4214743a77edSAlan Wright 	zfsvfs = dzp->z_zfsvfs;
4215743a77edSAlan Wright 	ZFS_ENTER(zfsvfs);
4216743a77edSAlan Wright 
42179e1320c0SMark Shellenbaum 	/*
42189e1320c0SMark Shellenbaum 	 * Create share dir if its missing.
42199e1320c0SMark Shellenbaum 	 */
42209e1320c0SMark Shellenbaum 	mutex_enter(&zfsvfs->z_lock);
42219e1320c0SMark Shellenbaum 	if (zfsvfs->z_shares_dir == 0) {
42229e1320c0SMark Shellenbaum 		dmu_tx_t *tx;
42239e1320c0SMark Shellenbaum 
42249e1320c0SMark Shellenbaum 		tx = dmu_tx_create(zfsvfs->z_os);
42259e1320c0SMark Shellenbaum 		dmu_tx_hold_zap(tx, MASTER_NODE_OBJ, TRUE,
42269e1320c0SMark Shellenbaum 		    ZFS_SHARES_DIR);
42279e1320c0SMark Shellenbaum 		dmu_tx_hold_zap(tx, DMU_NEW_OBJECT, FALSE, NULL);
42289e1320c0SMark Shellenbaum 		error = dmu_tx_assign(tx, TXG_WAIT);
42299e1320c0SMark Shellenbaum 		if (error) {
42309e1320c0SMark Shellenbaum 			dmu_tx_abort(tx);
42319e1320c0SMark Shellenbaum 		} else {
42329e1320c0SMark Shellenbaum 			error = zfs_create_share_dir(zfsvfs, tx);
42339e1320c0SMark Shellenbaum 			dmu_tx_commit(tx);
42349e1320c0SMark Shellenbaum 		}
42359e1320c0SMark Shellenbaum 		if (error) {
42369e1320c0SMark Shellenbaum 			mutex_exit(&zfsvfs->z_lock);
42379e1320c0SMark Shellenbaum 			VN_RELE(vp);
42389e1320c0SMark Shellenbaum 			ZFS_EXIT(zfsvfs);
42399e1320c0SMark Shellenbaum 			return (error);
42409e1320c0SMark Shellenbaum 		}
42419e1320c0SMark Shellenbaum 	}
42429e1320c0SMark Shellenbaum 	mutex_exit(&zfsvfs->z_lock);
42439e1320c0SMark Shellenbaum 
42449e1320c0SMark Shellenbaum 	ASSERT(zfsvfs->z_shares_dir);
4245743a77edSAlan Wright 	if ((error = zfs_zget(zfsvfs, zfsvfs->z_shares_dir, &sharedir)) != 0) {
42469e1320c0SMark Shellenbaum 		VN_RELE(vp);
4247743a77edSAlan Wright 		ZFS_EXIT(zfsvfs);
4248743a77edSAlan Wright 		return (error);
4249743a77edSAlan Wright 	}
4250743a77edSAlan Wright 
4251743a77edSAlan Wright 	switch (zc->zc_cookie) {
4252743a77edSAlan Wright 	case ZFS_SMB_ACL_ADD:
4253743a77edSAlan Wright 		vattr.va_mask = AT_MODE|AT_UID|AT_GID|AT_TYPE;
4254743a77edSAlan Wright 		vattr.va_type = VREG;
4255743a77edSAlan Wright 		vattr.va_mode = S_IFREG|0777;
4256743a77edSAlan Wright 		vattr.va_uid = 0;
4257743a77edSAlan Wright 		vattr.va_gid = 0;
4258743a77edSAlan Wright 
4259743a77edSAlan Wright 		vsec.vsa_mask = VSA_ACE;
4260743a77edSAlan Wright 		vsec.vsa_aclentp = &full_access;
4261743a77edSAlan Wright 		vsec.vsa_aclentsz = sizeof (full_access);
4262743a77edSAlan Wright 		vsec.vsa_aclcnt = 1;
4263743a77edSAlan Wright 
4264743a77edSAlan Wright 		error = VOP_CREATE(ZTOV(sharedir), zc->zc_string,
4265743a77edSAlan Wright 		    &vattr, EXCL, 0, &resourcevp, kcred, 0, NULL, &vsec);
4266743a77edSAlan Wright 		if (resourcevp)
4267743a77edSAlan Wright 			VN_RELE(resourcevp);
4268743a77edSAlan Wright 		break;
4269743a77edSAlan Wright 
4270743a77edSAlan Wright 	case ZFS_SMB_ACL_REMOVE:
4271743a77edSAlan Wright 		error = VOP_REMOVE(ZTOV(sharedir), zc->zc_string, kcred,
4272743a77edSAlan Wright 		    NULL, 0);
4273743a77edSAlan Wright 		break;
4274743a77edSAlan Wright 
4275743a77edSAlan Wright 	case ZFS_SMB_ACL_RENAME:
4276743a77edSAlan Wright 		if ((error = get_nvlist(zc->zc_nvlist_src,
4277478ed9adSEric Taylor 		    zc->zc_nvlist_src_size, zc->zc_iflags, &nvlist)) != 0) {
4278743a77edSAlan Wright 			VN_RELE(vp);
4279743a77edSAlan Wright 			ZFS_EXIT(zfsvfs);
4280743a77edSAlan Wright 			return (error);
4281743a77edSAlan Wright 		}
4282743a77edSAlan Wright 		if (nvlist_lookup_string(nvlist, ZFS_SMB_ACL_SRC, &src) ||
4283743a77edSAlan Wright 		    nvlist_lookup_string(nvlist, ZFS_SMB_ACL_TARGET,
4284743a77edSAlan Wright 		    &target)) {
4285743a77edSAlan Wright 			VN_RELE(vp);
428689459e17SMark Shellenbaum 			VN_RELE(ZTOV(sharedir));
4287743a77edSAlan Wright 			ZFS_EXIT(zfsvfs);
42881195e687SMark J Musante 			nvlist_free(nvlist);
4289743a77edSAlan Wright 			return (error);
4290743a77edSAlan Wright 		}
4291743a77edSAlan Wright 		error = VOP_RENAME(ZTOV(sharedir), src, ZTOV(sharedir), target,
4292743a77edSAlan Wright 		    kcred, NULL, 0);
4293743a77edSAlan Wright 		nvlist_free(nvlist);
4294743a77edSAlan Wright 		break;
4295743a77edSAlan Wright 
4296743a77edSAlan Wright 	case ZFS_SMB_ACL_PURGE:
4297743a77edSAlan Wright 		error = zfs_smb_acl_purge(sharedir);
4298743a77edSAlan Wright 		break;
4299743a77edSAlan Wright 
4300743a77edSAlan Wright 	default:
4301743a77edSAlan Wright 		error = EINVAL;
4302743a77edSAlan Wright 		break;
4303743a77edSAlan Wright 	}
4304743a77edSAlan Wright 
4305743a77edSAlan Wright 	VN_RELE(vp);
4306743a77edSAlan Wright 	VN_RELE(ZTOV(sharedir));
4307743a77edSAlan Wright 
4308743a77edSAlan Wright 	ZFS_EXIT(zfsvfs);
4309743a77edSAlan Wright 
4310743a77edSAlan Wright 	return (error);
4311743a77edSAlan Wright }
4312743a77edSAlan Wright 
4313842727c2SChris Kirby /*
4314842727c2SChris Kirby  * inputs:
4315c99e4bdcSChris Kirby  * zc_name		name of filesystem
4316c99e4bdcSChris Kirby  * zc_value		short name of snap
4317c99e4bdcSChris Kirby  * zc_string		user-supplied tag for this hold
4318c99e4bdcSChris Kirby  * zc_cookie		recursive flag
4319c99e4bdcSChris Kirby  * zc_temphold		set if hold is temporary
4320c99e4bdcSChris Kirby  * zc_cleanup_fd	cleanup-on-exit file descriptor for calling process
4321*a7f53a56SChris Kirby  * zc_sendobj		if non-zero, the objid for zc_name@zc_value
4322*a7f53a56SChris Kirby  * zc_createtxg		if zc_sendobj is non-zero, snap must have zc_createtxg
4323842727c2SChris Kirby  *
4324842727c2SChris Kirby  * outputs:		none
4325842727c2SChris Kirby  */
4326842727c2SChris Kirby static int
4327842727c2SChris Kirby zfs_ioc_hold(zfs_cmd_t *zc)
4328842727c2SChris Kirby {
4329842727c2SChris Kirby 	boolean_t recursive = zc->zc_cookie;
4330*a7f53a56SChris Kirby 	spa_t *spa;
4331*a7f53a56SChris Kirby 	dsl_pool_t *dp;
4332*a7f53a56SChris Kirby 	dsl_dataset_t *ds;
4333*a7f53a56SChris Kirby 	int error;
4334*a7f53a56SChris Kirby 	minor_t minor = 0;
4335842727c2SChris Kirby 
4336842727c2SChris Kirby 	if (snapshot_namecheck(zc->zc_value, NULL, NULL) != 0)
4337842727c2SChris Kirby 		return (EINVAL);
4338842727c2SChris Kirby 
4339*a7f53a56SChris Kirby 	if (zc->zc_sendobj == 0) {
4340*a7f53a56SChris Kirby 		return (dsl_dataset_user_hold(zc->zc_name, zc->zc_value,
4341*a7f53a56SChris Kirby 		    zc->zc_string, recursive, zc->zc_temphold,
4342*a7f53a56SChris Kirby 		    zc->zc_cleanup_fd));
4343*a7f53a56SChris Kirby 	}
4344*a7f53a56SChris Kirby 
4345*a7f53a56SChris Kirby 	if (recursive)
4346*a7f53a56SChris Kirby 		return (EINVAL);
4347*a7f53a56SChris Kirby 
4348*a7f53a56SChris Kirby 	error = spa_open(zc->zc_name, &spa, FTAG);
4349*a7f53a56SChris Kirby 	if (error)
4350*a7f53a56SChris Kirby 		return (error);
4351*a7f53a56SChris Kirby 
4352*a7f53a56SChris Kirby 	dp = spa_get_dsl(spa);
4353*a7f53a56SChris Kirby 	rw_enter(&dp->dp_config_rwlock, RW_READER);
4354*a7f53a56SChris Kirby 	error = dsl_dataset_hold_obj(dp, zc->zc_sendobj, FTAG, &ds);
4355*a7f53a56SChris Kirby 	rw_exit(&dp->dp_config_rwlock);
4356*a7f53a56SChris Kirby 	spa_close(spa, FTAG);
4357*a7f53a56SChris Kirby 	if (error)
4358*a7f53a56SChris Kirby 		return (error);
4359*a7f53a56SChris Kirby 
4360*a7f53a56SChris Kirby 	/*
4361*a7f53a56SChris Kirby 	 * Until we have a hold on this snapshot, it's possible that
4362*a7f53a56SChris Kirby 	 * zc_sendobj could've been destroyed and reused as part
4363*a7f53a56SChris Kirby 	 * of a later txg.  Make sure we're looking at the right object.
4364*a7f53a56SChris Kirby 	 */
4365*a7f53a56SChris Kirby 	if (zc->zc_createtxg != ds->ds_phys->ds_creation_txg) {
4366*a7f53a56SChris Kirby 		dsl_dataset_rele(ds, FTAG);
4367*a7f53a56SChris Kirby 		return (ENOENT);
4368*a7f53a56SChris Kirby 	}
4369*a7f53a56SChris Kirby 
4370*a7f53a56SChris Kirby 	if (zc->zc_cleanup_fd != -1 && zc->zc_temphold) {
4371*a7f53a56SChris Kirby 		error = zfs_onexit_fd_hold(zc->zc_cleanup_fd, &minor);
4372*a7f53a56SChris Kirby 		if (error) {
4373*a7f53a56SChris Kirby 			dsl_dataset_rele(ds, FTAG);
4374*a7f53a56SChris Kirby 			return (error);
4375*a7f53a56SChris Kirby 		}
4376*a7f53a56SChris Kirby 	}
4377*a7f53a56SChris Kirby 
4378*a7f53a56SChris Kirby 	error = dsl_dataset_user_hold_for_send(ds, zc->zc_string,
4379*a7f53a56SChris Kirby 	    zc->zc_temphold);
4380*a7f53a56SChris Kirby 	if (minor != 0) {
4381*a7f53a56SChris Kirby 		if (error == 0) {
4382*a7f53a56SChris Kirby 			dsl_register_onexit_hold_cleanup(ds, zc->zc_string,
4383*a7f53a56SChris Kirby 			    minor);
4384*a7f53a56SChris Kirby 		}
4385*a7f53a56SChris Kirby 		zfs_onexit_fd_rele(zc->zc_cleanup_fd);
4386*a7f53a56SChris Kirby 	}
4387*a7f53a56SChris Kirby 	dsl_dataset_rele(ds, FTAG);
4388*a7f53a56SChris Kirby 
4389*a7f53a56SChris Kirby 	return (error);
4390842727c2SChris Kirby }
4391842727c2SChris Kirby 
4392842727c2SChris Kirby /*
4393842727c2SChris Kirby  * inputs:
4394c99e4bdcSChris Kirby  * zc_name	name of dataset from which we're releasing a user hold
4395842727c2SChris Kirby  * zc_value	short name of snap
4396c99e4bdcSChris Kirby  * zc_string	user-supplied tag for this hold
4397842727c2SChris Kirby  * zc_cookie	recursive flag
4398842727c2SChris Kirby  *
4399c99e4bdcSChris Kirby  * outputs:	none
4400842727c2SChris Kirby  */
4401842727c2SChris Kirby static int
4402842727c2SChris Kirby zfs_ioc_release(zfs_cmd_t *zc)
4403842727c2SChris Kirby {
4404842727c2SChris Kirby 	boolean_t recursive = zc->zc_cookie;
4405842727c2SChris Kirby 
4406842727c2SChris Kirby 	if (snapshot_namecheck(zc->zc_value, NULL, NULL) != 0)
4407842727c2SChris Kirby 		return (EINVAL);
4408842727c2SChris Kirby 
4409842727c2SChris Kirby 	return (dsl_dataset_user_release(zc->zc_name, zc->zc_value,
4410842727c2SChris Kirby 	    zc->zc_string, recursive));
4411842727c2SChris Kirby }
4412842727c2SChris Kirby 
4413842727c2SChris Kirby /*
4414842727c2SChris Kirby  * inputs:
4415842727c2SChris Kirby  * zc_name		name of filesystem
4416842727c2SChris Kirby  *
4417842727c2SChris Kirby  * outputs:
4418842727c2SChris Kirby  * zc_nvlist_src{_size}	nvlist of snapshot holds
4419842727c2SChris Kirby  */
4420842727c2SChris Kirby static int
4421842727c2SChris Kirby zfs_ioc_get_holds(zfs_cmd_t *zc)
4422842727c2SChris Kirby {
4423842727c2SChris Kirby 	nvlist_t *nvp;
4424842727c2SChris Kirby 	int error;
4425842727c2SChris Kirby 
4426842727c2SChris Kirby 	if ((error = dsl_dataset_get_holds(zc->zc_name, &nvp)) == 0) {
4427842727c2SChris Kirby 		error = put_nvlist(zc, nvp);
4428842727c2SChris Kirby 		nvlist_free(nvp);
4429842727c2SChris Kirby 	}
4430842727c2SChris Kirby 
4431842727c2SChris Kirby 	return (error);
4432842727c2SChris Kirby }
4433842727c2SChris Kirby 
4434ecd6cf80Smarks /*
44352a6b87f0Sek  * pool create, destroy, and export don't log the history as part of
44362a6b87f0Sek  * zfsdev_ioctl, but rather zfs_ioc_pool_create, and zfs_ioc_pool_export
44372a6b87f0Sek  * do the logging of those commands.
4438ecd6cf80Smarks  */
4439fa9e4066Sahrens static zfs_ioc_vec_t zfs_ioc_vec[] = {
444054d692b7SGeorge Wilson 	{ zfs_ioc_pool_create, zfs_secpolicy_config, POOL_NAME, B_FALSE,
444154d692b7SGeorge Wilson 	    B_FALSE },
444254d692b7SGeorge Wilson 	{ zfs_ioc_pool_destroy,	zfs_secpolicy_config, POOL_NAME, B_FALSE,
444354d692b7SGeorge Wilson 	    B_FALSE },
444454d692b7SGeorge Wilson 	{ zfs_ioc_pool_import, zfs_secpolicy_config, POOL_NAME, B_TRUE,
444554d692b7SGeorge Wilson 	    B_FALSE },
444654d692b7SGeorge Wilson 	{ zfs_ioc_pool_export, zfs_secpolicy_config, POOL_NAME, B_FALSE,
444754d692b7SGeorge Wilson 	    B_FALSE },
444854d692b7SGeorge Wilson 	{ zfs_ioc_pool_configs,	zfs_secpolicy_none, NO_NAME, B_FALSE,
444954d692b7SGeorge Wilson 	    B_FALSE },
445054d692b7SGeorge Wilson 	{ zfs_ioc_pool_stats, zfs_secpolicy_read, POOL_NAME, B_FALSE,
445154d692b7SGeorge Wilson 	    B_FALSE },
445254d692b7SGeorge Wilson 	{ zfs_ioc_pool_tryimport, zfs_secpolicy_config, NO_NAME, B_FALSE,
445354d692b7SGeorge Wilson 	    B_FALSE },
44543f9d6ad7SLin Ling 	{ zfs_ioc_pool_scan, zfs_secpolicy_config, POOL_NAME, B_TRUE,
445554d692b7SGeorge Wilson 	    B_TRUE },
445654d692b7SGeorge Wilson 	{ zfs_ioc_pool_freeze, zfs_secpolicy_config, NO_NAME, B_FALSE,
445754d692b7SGeorge Wilson 	    B_FALSE },
445854d692b7SGeorge Wilson 	{ zfs_ioc_pool_upgrade,	zfs_secpolicy_config, POOL_NAME, B_TRUE,
445954d692b7SGeorge Wilson 	    B_TRUE },
446054d692b7SGeorge Wilson 	{ zfs_ioc_pool_get_history, zfs_secpolicy_config, POOL_NAME, B_FALSE,
446154d692b7SGeorge Wilson 	    B_FALSE },
446254d692b7SGeorge Wilson 	{ zfs_ioc_vdev_add, zfs_secpolicy_config, POOL_NAME, B_TRUE,
446354d692b7SGeorge Wilson 	    B_TRUE },
446454d692b7SGeorge Wilson 	{ zfs_ioc_vdev_remove, zfs_secpolicy_config, POOL_NAME, B_TRUE,
446554d692b7SGeorge Wilson 	    B_TRUE },
446654d692b7SGeorge Wilson 	{ zfs_ioc_vdev_set_state, zfs_secpolicy_config,	POOL_NAME, B_TRUE,
446754d692b7SGeorge Wilson 	    B_FALSE },
446854d692b7SGeorge Wilson 	{ zfs_ioc_vdev_attach, zfs_secpolicy_config, POOL_NAME, B_TRUE,
446954d692b7SGeorge Wilson 	    B_TRUE },
447054d692b7SGeorge Wilson 	{ zfs_ioc_vdev_detach, zfs_secpolicy_config, POOL_NAME, B_TRUE,
447154d692b7SGeorge Wilson 	    B_TRUE },
447254d692b7SGeorge Wilson 	{ zfs_ioc_vdev_setpath,	zfs_secpolicy_config, POOL_NAME, B_FALSE,
447354d692b7SGeorge Wilson 	    B_TRUE },
44746809eb4eSEric Schrock 	{ zfs_ioc_vdev_setfru,	zfs_secpolicy_config, POOL_NAME, B_FALSE,
44756809eb4eSEric Schrock 	    B_TRUE },
447654d692b7SGeorge Wilson 	{ zfs_ioc_objset_stats,	zfs_secpolicy_read, DATASET_NAME, B_FALSE,
4477cf2859fcSSanjeev Bagewadi 	    B_TRUE },
447854d692b7SGeorge Wilson 	{ zfs_ioc_objset_zplprops, zfs_secpolicy_read, DATASET_NAME, B_FALSE,
447954d692b7SGeorge Wilson 	    B_FALSE },
448054d692b7SGeorge Wilson 	{ zfs_ioc_dataset_list_next, zfs_secpolicy_read, DATASET_NAME, B_FALSE,
4481cf2859fcSSanjeev Bagewadi 	    B_TRUE },
448254d692b7SGeorge Wilson 	{ zfs_ioc_snapshot_list_next, zfs_secpolicy_read, DATASET_NAME, B_FALSE,
4483cf2859fcSSanjeev Bagewadi 	    B_TRUE },
448454d692b7SGeorge Wilson 	{ zfs_ioc_set_prop, zfs_secpolicy_none, DATASET_NAME, B_TRUE, B_TRUE },
448554d692b7SGeorge Wilson 	{ zfs_ioc_create, zfs_secpolicy_create, DATASET_NAME, B_TRUE, B_TRUE },
448654d692b7SGeorge Wilson 	{ zfs_ioc_destroy, zfs_secpolicy_destroy, DATASET_NAME, B_TRUE,
448754d692b7SGeorge Wilson 	    B_TRUE},
448854d692b7SGeorge Wilson 	{ zfs_ioc_rollback, zfs_secpolicy_rollback, DATASET_NAME, B_TRUE,
448954d692b7SGeorge Wilson 	    B_TRUE },
449054d692b7SGeorge Wilson 	{ zfs_ioc_rename, zfs_secpolicy_rename,	DATASET_NAME, B_TRUE, B_TRUE },
449154d692b7SGeorge Wilson 	{ zfs_ioc_recv, zfs_secpolicy_receive, DATASET_NAME, B_TRUE, B_TRUE },
449254d692b7SGeorge Wilson 	{ zfs_ioc_send, zfs_secpolicy_send, DATASET_NAME, B_TRUE, B_FALSE },
449354d692b7SGeorge Wilson 	{ zfs_ioc_inject_fault,	zfs_secpolicy_inject, NO_NAME, B_FALSE,
449454d692b7SGeorge Wilson 	    B_FALSE },
449554d692b7SGeorge Wilson 	{ zfs_ioc_clear_fault, zfs_secpolicy_inject, NO_NAME, B_FALSE,
449654d692b7SGeorge Wilson 	    B_FALSE },
449754d692b7SGeorge Wilson 	{ zfs_ioc_inject_list_next, zfs_secpolicy_inject, NO_NAME, B_FALSE,
449854d692b7SGeorge Wilson 	    B_FALSE },
449954d692b7SGeorge Wilson 	{ zfs_ioc_error_log, zfs_secpolicy_inject, POOL_NAME, B_FALSE,
450054d692b7SGeorge Wilson 	    B_FALSE },
450154d692b7SGeorge Wilson 	{ zfs_ioc_clear, zfs_secpolicy_config, POOL_NAME, B_TRUE, B_FALSE },
450254d692b7SGeorge Wilson 	{ zfs_ioc_promote, zfs_secpolicy_promote, DATASET_NAME, B_TRUE,
450354d692b7SGeorge Wilson 	    B_TRUE },
4504cbf6f6aaSWilliam Gorrell 	{ zfs_ioc_destroy_snaps, zfs_secpolicy_destroy_snaps, DATASET_NAME,
4505cbf6f6aaSWilliam Gorrell 	    B_TRUE, B_TRUE },
450654d692b7SGeorge Wilson 	{ zfs_ioc_snapshot, zfs_secpolicy_snapshot, DATASET_NAME, B_TRUE,
450754d692b7SGeorge Wilson 	    B_TRUE },
450854d692b7SGeorge Wilson 	{ zfs_ioc_dsobj_to_dsname, zfs_secpolicy_config, POOL_NAME, B_FALSE,
450954d692b7SGeorge Wilson 	    B_FALSE },
45106e8a0f56SGeorge Wilson 	{ zfs_ioc_obj_to_path, zfs_secpolicy_config, DATASET_NAME, B_FALSE,
45116e8a0f56SGeorge Wilson 	    B_TRUE },
451254d692b7SGeorge Wilson 	{ zfs_ioc_pool_set_props, zfs_secpolicy_config,	POOL_NAME, B_TRUE,
451354d692b7SGeorge Wilson 	    B_TRUE },
451454d692b7SGeorge Wilson 	{ zfs_ioc_pool_get_props, zfs_secpolicy_read, POOL_NAME, B_FALSE,
451554d692b7SGeorge Wilson 	    B_FALSE },
451654d692b7SGeorge Wilson 	{ zfs_ioc_set_fsacl, zfs_secpolicy_fsacl, DATASET_NAME, B_TRUE,
451754d692b7SGeorge Wilson 	    B_TRUE },
451854d692b7SGeorge Wilson 	{ zfs_ioc_get_fsacl, zfs_secpolicy_read, DATASET_NAME, B_FALSE,
451954d692b7SGeorge Wilson 	    B_FALSE },
452054d692b7SGeorge Wilson 	{ zfs_ioc_share, zfs_secpolicy_share, DATASET_NAME, B_FALSE, B_FALSE },
452154d692b7SGeorge Wilson 	{ zfs_ioc_inherit_prop, zfs_secpolicy_inherit, DATASET_NAME, B_TRUE,
452254d692b7SGeorge Wilson 	    B_TRUE },
452354d692b7SGeorge Wilson 	{ zfs_ioc_smb_acl, zfs_secpolicy_smb_acl, DATASET_NAME, B_FALSE,
452414843421SMatthew Ahrens 	    B_FALSE },
452514843421SMatthew Ahrens 	{ zfs_ioc_userspace_one, zfs_secpolicy_userspace_one,
452614843421SMatthew Ahrens 	    DATASET_NAME, B_FALSE, B_FALSE },
452714843421SMatthew Ahrens 	{ zfs_ioc_userspace_many, zfs_secpolicy_userspace_many,
452814843421SMatthew Ahrens 	    DATASET_NAME, B_FALSE, B_FALSE },
452914843421SMatthew Ahrens 	{ zfs_ioc_userspace_upgrade, zfs_secpolicy_userspace_upgrade,
453014843421SMatthew Ahrens 	    DATASET_NAME, B_FALSE, B_TRUE },
4531842727c2SChris Kirby 	{ zfs_ioc_hold, zfs_secpolicy_hold, DATASET_NAME, B_TRUE, B_TRUE },
4532842727c2SChris Kirby 	{ zfs_ioc_release, zfs_secpolicy_release, DATASET_NAME, B_TRUE,
4533842727c2SChris Kirby 	    B_TRUE },
4534842727c2SChris Kirby 	{ zfs_ioc_get_holds, zfs_secpolicy_read, DATASET_NAME, B_FALSE,
453592241e0bSTom Erickson 	    B_TRUE },
453692241e0bSTom Erickson 	{ zfs_ioc_objset_recvd_props, zfs_secpolicy_read, DATASET_NAME, B_FALSE,
45371195e687SMark J Musante 	    B_FALSE },
45381195e687SMark J Musante 	{ zfs_ioc_vdev_split, zfs_secpolicy_config, POOL_NAME, B_TRUE,
45391195e687SMark J Musante 	    B_TRUE }
4540fa9e4066Sahrens };
4541fa9e4066Sahrens 
454254d692b7SGeorge Wilson int
454354d692b7SGeorge Wilson pool_status_check(const char *name, zfs_ioc_namecheck_t type)
454454d692b7SGeorge Wilson {
454554d692b7SGeorge Wilson 	spa_t *spa;
454654d692b7SGeorge Wilson 	int error;
454754d692b7SGeorge Wilson 
454854d692b7SGeorge Wilson 	ASSERT(type == POOL_NAME || type == DATASET_NAME);
454954d692b7SGeorge Wilson 
455014843421SMatthew Ahrens 	error = spa_open(name, &spa, FTAG);
455154d692b7SGeorge Wilson 	if (error == 0) {
455254d692b7SGeorge Wilson 		if (spa_suspended(spa))
455354d692b7SGeorge Wilson 			error = EAGAIN;
455454d692b7SGeorge Wilson 		spa_close(spa, FTAG);
455554d692b7SGeorge Wilson 	}
455654d692b7SGeorge Wilson 	return (error);
455754d692b7SGeorge Wilson }
455854d692b7SGeorge Wilson 
4559c99e4bdcSChris Kirby /*
4560c99e4bdcSChris Kirby  * Find a free minor number.
4561c99e4bdcSChris Kirby  */
4562c99e4bdcSChris Kirby minor_t
4563c99e4bdcSChris Kirby zfsdev_minor_alloc(void)
4564c99e4bdcSChris Kirby {
4565c99e4bdcSChris Kirby 	static minor_t last_minor;
4566c99e4bdcSChris Kirby 	minor_t m;
4567c99e4bdcSChris Kirby 
4568c99e4bdcSChris Kirby 	ASSERT(MUTEX_HELD(&zfsdev_state_lock));
4569c99e4bdcSChris Kirby 
4570c99e4bdcSChris Kirby 	for (m = last_minor + 1; m != last_minor; m++) {
4571c99e4bdcSChris Kirby 		if (m > ZFSDEV_MAX_MINOR)
4572c99e4bdcSChris Kirby 			m = 1;
4573c99e4bdcSChris Kirby 		if (ddi_get_soft_state(zfsdev_state, m) == NULL) {
4574c99e4bdcSChris Kirby 			last_minor = m;
4575c99e4bdcSChris Kirby 			return (m);
4576c99e4bdcSChris Kirby 		}
4577c99e4bdcSChris Kirby 	}
4578c99e4bdcSChris Kirby 
4579c99e4bdcSChris Kirby 	return (0);
4580c99e4bdcSChris Kirby }
4581c99e4bdcSChris Kirby 
4582c99e4bdcSChris Kirby static int
4583c99e4bdcSChris Kirby zfs_ctldev_init(dev_t *devp)
4584c99e4bdcSChris Kirby {
4585c99e4bdcSChris Kirby 	minor_t minor;
4586c99e4bdcSChris Kirby 	zfs_soft_state_t *zs;
4587c99e4bdcSChris Kirby 
4588c99e4bdcSChris Kirby 	ASSERT(MUTEX_HELD(&zfsdev_state_lock));
4589c99e4bdcSChris Kirby 	ASSERT(getminor(*devp) == 0);
4590c99e4bdcSChris Kirby 
4591c99e4bdcSChris Kirby 	minor = zfsdev_minor_alloc();
4592c99e4bdcSChris Kirby 	if (minor == 0)
4593c99e4bdcSChris Kirby 		return (ENXIO);
4594c99e4bdcSChris Kirby 
4595c99e4bdcSChris Kirby 	if (ddi_soft_state_zalloc(zfsdev_state, minor) != DDI_SUCCESS)
4596c99e4bdcSChris Kirby 		return (EAGAIN);
4597c99e4bdcSChris Kirby 
4598c99e4bdcSChris Kirby 	*devp = makedevice(getemajor(*devp), minor);
4599c99e4bdcSChris Kirby 
4600c99e4bdcSChris Kirby 	zs = ddi_get_soft_state(zfsdev_state, minor);
4601c99e4bdcSChris Kirby 	zs->zss_type = ZSST_CTLDEV;
4602c99e4bdcSChris Kirby 	zfs_onexit_init((zfs_onexit_t **)&zs->zss_data);
4603c99e4bdcSChris Kirby 
4604c99e4bdcSChris Kirby 	return (0);
4605c99e4bdcSChris Kirby }
4606c99e4bdcSChris Kirby 
4607c99e4bdcSChris Kirby static void
4608c99e4bdcSChris Kirby zfs_ctldev_destroy(zfs_onexit_t *zo, minor_t minor)
4609c99e4bdcSChris Kirby {
4610c99e4bdcSChris Kirby 	ASSERT(MUTEX_HELD(&zfsdev_state_lock));
4611c99e4bdcSChris Kirby 
4612c99e4bdcSChris Kirby 	zfs_onexit_destroy(zo);
4613c99e4bdcSChris Kirby 	ddi_soft_state_free(zfsdev_state, minor);
4614c99e4bdcSChris Kirby }
4615c99e4bdcSChris Kirby 
4616c99e4bdcSChris Kirby void *
4617c99e4bdcSChris Kirby zfsdev_get_soft_state(minor_t minor, enum zfs_soft_state_type which)
4618c99e4bdcSChris Kirby {
4619c99e4bdcSChris Kirby 	zfs_soft_state_t *zp;
4620c99e4bdcSChris Kirby 
4621c99e4bdcSChris Kirby 	zp = ddi_get_soft_state(zfsdev_state, minor);
4622c99e4bdcSChris Kirby 	if (zp == NULL || zp->zss_type != which)
4623c99e4bdcSChris Kirby 		return (NULL);
4624c99e4bdcSChris Kirby 
4625c99e4bdcSChris Kirby 	return (zp->zss_data);
4626c99e4bdcSChris Kirby }
4627c99e4bdcSChris Kirby 
4628c99e4bdcSChris Kirby static int
4629c99e4bdcSChris Kirby zfsdev_open(dev_t *devp, int flag, int otyp, cred_t *cr)
4630c99e4bdcSChris Kirby {
4631c99e4bdcSChris Kirby 	int error = 0;
4632c99e4bdcSChris Kirby 
4633c99e4bdcSChris Kirby 	if (getminor(*devp) != 0)
4634c99e4bdcSChris Kirby 		return (zvol_open(devp, flag, otyp, cr));
4635c99e4bdcSChris Kirby 
4636c99e4bdcSChris Kirby 	/* This is the control device. Allocate a new minor if requested. */
4637c99e4bdcSChris Kirby 	if (flag & FEXCL) {
4638c99e4bdcSChris Kirby 		mutex_enter(&zfsdev_state_lock);
4639c99e4bdcSChris Kirby 		error = zfs_ctldev_init(devp);
4640c99e4bdcSChris Kirby 		mutex_exit(&zfsdev_state_lock);
4641c99e4bdcSChris Kirby 	}
4642c99e4bdcSChris Kirby 
4643c99e4bdcSChris Kirby 	return (error);
4644c99e4bdcSChris Kirby }
4645c99e4bdcSChris Kirby 
4646c99e4bdcSChris Kirby static int
4647c99e4bdcSChris Kirby zfsdev_close(dev_t dev, int flag, int otyp, cred_t *cr)
4648c99e4bdcSChris Kirby {
4649c99e4bdcSChris Kirby 	zfs_onexit_t *zo;
4650c99e4bdcSChris Kirby 	minor_t minor = getminor(dev);
4651c99e4bdcSChris Kirby 
4652c99e4bdcSChris Kirby 	if (minor == 0)
4653c99e4bdcSChris Kirby 		return (0);
4654c99e4bdcSChris Kirby 
4655c99e4bdcSChris Kirby 	mutex_enter(&zfsdev_state_lock);
4656c99e4bdcSChris Kirby 	zo = zfsdev_get_soft_state(minor, ZSST_CTLDEV);
4657c99e4bdcSChris Kirby 	if (zo == NULL) {
4658c99e4bdcSChris Kirby 		mutex_exit(&zfsdev_state_lock);
4659c99e4bdcSChris Kirby 		return (zvol_close(dev, flag, otyp, cr));
4660c99e4bdcSChris Kirby 	}
4661c99e4bdcSChris Kirby 	zfs_ctldev_destroy(zo, minor);
4662c99e4bdcSChris Kirby 	mutex_exit(&zfsdev_state_lock);
4663c99e4bdcSChris Kirby 
4664c99e4bdcSChris Kirby 	return (0);
4665c99e4bdcSChris Kirby }
4666c99e4bdcSChris Kirby 
4667fa9e4066Sahrens static int
4668fa9e4066Sahrens zfsdev_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *cr, int *rvalp)
4669fa9e4066Sahrens {
4670fa9e4066Sahrens 	zfs_cmd_t *zc;
4671fa9e4066Sahrens 	uint_t vec;
46721d452cf5Sahrens 	int error, rc;
4673c99e4bdcSChris Kirby 	minor_t minor = getminor(dev);
4674fa9e4066Sahrens 
4675c99e4bdcSChris Kirby 	if (minor != 0 &&
4676c99e4bdcSChris Kirby 	    zfsdev_get_soft_state(minor, ZSST_CTLDEV) == NULL)
4677fa9e4066Sahrens 		return (zvol_ioctl(dev, cmd, arg, flag, cr, rvalp));
4678fa9e4066Sahrens 
4679fa9e4066Sahrens 	vec = cmd - ZFS_IOC;
468091ebeef5Sahrens 	ASSERT3U(getmajor(dev), ==, ddi_driver_major(zfs_dip));
4681fa9e4066Sahrens 
4682fa9e4066Sahrens 	if (vec >= sizeof (zfs_ioc_vec) / sizeof (zfs_ioc_vec[0]))
4683fa9e4066Sahrens 		return (EINVAL);
4684fa9e4066Sahrens 
4685fa9e4066Sahrens 	zc = kmem_zalloc(sizeof (zfs_cmd_t), KM_SLEEP);
4686fa9e4066Sahrens 
4687478ed9adSEric Taylor 	error = ddi_copyin((void *)arg, zc, sizeof (zfs_cmd_t), flag);
46886e27f868SSam Falkner 	if (error != 0)
46896e27f868SSam Falkner 		error = EFAULT;
4690fa9e4066Sahrens 
4691681d9761SEric Taylor 	if ((error == 0) && !(flag & FKIOCTL))
4692ecd6cf80Smarks 		error = zfs_ioc_vec[vec].zvec_secpolicy(zc, cr);
4693fa9e4066Sahrens 
4694fa9e4066Sahrens 	/*
4695fa9e4066Sahrens 	 * Ensure that all pool/dataset names are valid before we pass down to
4696fa9e4066Sahrens 	 * the lower layers.
4697fa9e4066Sahrens 	 */
4698fa9e4066Sahrens 	if (error == 0) {
4699fa9e4066Sahrens 		zc->zc_name[sizeof (zc->zc_name) - 1] = '\0';
4700478ed9adSEric Taylor 		zc->zc_iflags = flag & FKIOCTL;
4701fa9e4066Sahrens 		switch (zfs_ioc_vec[vec].zvec_namecheck) {
4702e7437265Sahrens 		case POOL_NAME:
4703fa9e4066Sahrens 			if (pool_namecheck(zc->zc_name, NULL, NULL) != 0)
4704fa9e4066Sahrens 				error = EINVAL;
470554d692b7SGeorge Wilson 			if (zfs_ioc_vec[vec].zvec_pool_check)
470654d692b7SGeorge Wilson 				error = pool_status_check(zc->zc_name,
470754d692b7SGeorge Wilson 				    zfs_ioc_vec[vec].zvec_namecheck);
4708fa9e4066Sahrens 			break;
4709fa9e4066Sahrens 
4710e7437265Sahrens 		case DATASET_NAME:
4711fa9e4066Sahrens 			if (dataset_namecheck(zc->zc_name, NULL, NULL) != 0)
4712fa9e4066Sahrens 				error = EINVAL;
471354d692b7SGeorge Wilson 			if (zfs_ioc_vec[vec].zvec_pool_check)
471454d692b7SGeorge Wilson 				error = pool_status_check(zc->zc_name,
471554d692b7SGeorge Wilson 				    zfs_ioc_vec[vec].zvec_namecheck);
4716fa9e4066Sahrens 			break;
47175ad82045Snd 
4718e7437265Sahrens 		case NO_NAME:
47195ad82045Snd 			break;
4720fa9e4066Sahrens 		}
4721fa9e4066Sahrens 	}
4722fa9e4066Sahrens 
4723fa9e4066Sahrens 	if (error == 0)
4724fa9e4066Sahrens 		error = zfs_ioc_vec[vec].zvec_func(zc);
4725fa9e4066Sahrens 
4726478ed9adSEric Taylor 	rc = ddi_copyout(zc, (void *)arg, sizeof (zfs_cmd_t), flag);
4727ecd6cf80Smarks 	if (error == 0) {
47286e27f868SSam Falkner 		if (rc != 0)
47296e27f868SSam Falkner 			error = EFAULT;
473014843421SMatthew Ahrens 		if (zfs_ioc_vec[vec].zvec_his_log)
4731ecd6cf80Smarks 			zfs_log_history(zc);
4732ecd6cf80Smarks 	}
4733fa9e4066Sahrens 
4734fa9e4066Sahrens 	kmem_free(zc, sizeof (zfs_cmd_t));
4735fa9e4066Sahrens 	return (error);
4736fa9e4066Sahrens }
4737fa9e4066Sahrens 
4738fa9e4066Sahrens static int
4739fa9e4066Sahrens zfs_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
4740fa9e4066Sahrens {
4741fa9e4066Sahrens 	if (cmd != DDI_ATTACH)
4742fa9e4066Sahrens 		return (DDI_FAILURE);
4743fa9e4066Sahrens 
4744fa9e4066Sahrens 	if (ddi_create_minor_node(dip, "zfs", S_IFCHR, 0,
4745fa9e4066Sahrens 	    DDI_PSEUDO, 0) == DDI_FAILURE)
4746fa9e4066Sahrens 		return (DDI_FAILURE);
4747fa9e4066Sahrens 
4748fa9e4066Sahrens 	zfs_dip = dip;
4749fa9e4066Sahrens 
4750fa9e4066Sahrens 	ddi_report_dev(dip);
4751fa9e4066Sahrens 
4752fa9e4066Sahrens 	return (DDI_SUCCESS);
4753fa9e4066Sahrens }
4754fa9e4066Sahrens 
4755fa9e4066Sahrens static int
4756fa9e4066Sahrens zfs_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
4757fa9e4066Sahrens {
4758fa9e4066Sahrens 	if (spa_busy() || zfs_busy() || zvol_busy())
4759fa9e4066Sahrens 		return (DDI_FAILURE);
4760fa9e4066Sahrens 
4761fa9e4066Sahrens 	if (cmd != DDI_DETACH)
4762fa9e4066Sahrens 		return (DDI_FAILURE);
4763fa9e4066Sahrens 
4764fa9e4066Sahrens 	zfs_dip = NULL;
4765fa9e4066Sahrens 
4766fa9e4066Sahrens 	ddi_prop_remove_all(dip);
4767fa9e4066Sahrens 	ddi_remove_minor_node(dip, NULL);
4768fa9e4066Sahrens 
4769fa9e4066Sahrens 	return (DDI_SUCCESS);
4770fa9e4066Sahrens }
4771fa9e4066Sahrens 
4772fa9e4066Sahrens /*ARGSUSED*/
4773fa9e4066Sahrens static int
4774fa9e4066Sahrens zfs_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
4775fa9e4066Sahrens {
4776fa9e4066Sahrens 	switch (infocmd) {
4777fa9e4066Sahrens 	case DDI_INFO_DEVT2DEVINFO:
4778fa9e4066Sahrens 		*result = zfs_dip;
4779fa9e4066Sahrens 		return (DDI_SUCCESS);
4780fa9e4066Sahrens 
4781fa9e4066Sahrens 	case DDI_INFO_DEVT2INSTANCE:
4782a0965f35Sbonwick 		*result = (void *)0;
4783fa9e4066Sahrens 		return (DDI_SUCCESS);
4784fa9e4066Sahrens 	}
4785fa9e4066Sahrens 
4786fa9e4066Sahrens 	return (DDI_FAILURE);
4787fa9e4066Sahrens }
4788fa9e4066Sahrens 
4789fa9e4066Sahrens /*
4790fa9e4066Sahrens  * OK, so this is a little weird.
4791fa9e4066Sahrens  *
4792fa9e4066Sahrens  * /dev/zfs is the control node, i.e. minor 0.
4793fa9e4066Sahrens  * /dev/zvol/[r]dsk/pool/dataset are the zvols, minor > 0.
4794fa9e4066Sahrens  *
4795fa9e4066Sahrens  * /dev/zfs has basically nothing to do except serve up ioctls,
4796fa9e4066Sahrens  * so most of the standard driver entry points are in zvol.c.
4797fa9e4066Sahrens  */
4798fa9e4066Sahrens static struct cb_ops zfs_cb_ops = {
4799c99e4bdcSChris Kirby 	zfsdev_open,	/* open */
4800c99e4bdcSChris Kirby 	zfsdev_close,	/* close */
4801fa9e4066Sahrens 	zvol_strategy,	/* strategy */
4802fa9e4066Sahrens 	nodev,		/* print */
4803e7cbe64fSgw 	zvol_dump,	/* dump */
4804fa9e4066Sahrens 	zvol_read,	/* read */
4805fa9e4066Sahrens 	zvol_write,	/* write */
4806fa9e4066Sahrens 	zfsdev_ioctl,	/* ioctl */
4807fa9e4066Sahrens 	nodev,		/* devmap */
4808fa9e4066Sahrens 	nodev,		/* mmap */
4809fa9e4066Sahrens 	nodev,		/* segmap */
4810fa9e4066Sahrens 	nochpoll,	/* poll */
4811fa9e4066Sahrens 	ddi_prop_op,	/* prop_op */
4812fa9e4066Sahrens 	NULL,		/* streamtab */
4813fa9e4066Sahrens 	D_NEW | D_MP | D_64BIT,		/* Driver compatibility flag */
4814fa9e4066Sahrens 	CB_REV,		/* version */
4815feb08c6bSbillm 	nodev,		/* async read */
4816feb08c6bSbillm 	nodev,		/* async write */
4817fa9e4066Sahrens };
4818fa9e4066Sahrens 
4819fa9e4066Sahrens static struct dev_ops zfs_dev_ops = {
4820fa9e4066Sahrens 	DEVO_REV,	/* version */
4821fa9e4066Sahrens 	0,		/* refcnt */
4822fa9e4066Sahrens 	zfs_info,	/* info */
4823fa9e4066Sahrens 	nulldev,	/* identify */
4824fa9e4066Sahrens 	nulldev,	/* probe */
4825fa9e4066Sahrens 	zfs_attach,	/* attach */
4826fa9e4066Sahrens 	zfs_detach,	/* detach */
4827fa9e4066Sahrens 	nodev,		/* reset */
4828fa9e4066Sahrens 	&zfs_cb_ops,	/* driver operations */
482919397407SSherry Moore 	NULL,		/* no bus operations */
483019397407SSherry Moore 	NULL,		/* power */
483119397407SSherry Moore 	ddi_quiesce_not_needed,	/* quiesce */
4832fa9e4066Sahrens };
4833fa9e4066Sahrens 
4834fa9e4066Sahrens static struct modldrv zfs_modldrv = {
483519397407SSherry Moore 	&mod_driverops,
483619397407SSherry Moore 	"ZFS storage pool",
483719397407SSherry Moore 	&zfs_dev_ops
4838fa9e4066Sahrens };
4839fa9e4066Sahrens 
4840fa9e4066Sahrens static struct modlinkage modlinkage = {
4841fa9e4066Sahrens 	MODREV_1,
4842fa9e4066Sahrens 	(void *)&zfs_modlfs,
4843fa9e4066Sahrens 	(void *)&zfs_modldrv,
4844fa9e4066Sahrens 	NULL
4845fa9e4066Sahrens };
4846fa9e4066Sahrens 
4847ec533521Sfr 
4848ec533521Sfr uint_t zfs_fsyncer_key;
4849f18faf3fSek extern uint_t rrw_tsd_key;
4850ec533521Sfr 
4851fa9e4066Sahrens int
4852fa9e4066Sahrens _init(void)
4853fa9e4066Sahrens {
4854fa9e4066Sahrens 	int error;
4855fa9e4066Sahrens 
4856a0965f35Sbonwick 	spa_init(FREAD | FWRITE);
4857a0965f35Sbonwick 	zfs_init();
4858a0965f35Sbonwick 	zvol_init();
4859a0965f35Sbonwick 
4860a0965f35Sbonwick 	if ((error = mod_install(&modlinkage)) != 0) {
4861a0965f35Sbonwick 		zvol_fini();
4862a0965f35Sbonwick 		zfs_fini();
4863a0965f35Sbonwick 		spa_fini();
4864fa9e4066Sahrens 		return (error);
4865a0965f35Sbonwick 	}
4866fa9e4066Sahrens 
4867ec533521Sfr 	tsd_create(&zfs_fsyncer_key, NULL);
4868f18faf3fSek 	tsd_create(&rrw_tsd_key, NULL);
4869ec533521Sfr 
4870fa9e4066Sahrens 	error = ldi_ident_from_mod(&modlinkage, &zfs_li);
4871fa9e4066Sahrens 	ASSERT(error == 0);
4872ecd6cf80Smarks 	mutex_init(&zfs_share_lock, NULL, MUTEX_DEFAULT, NULL);
4873fa9e4066Sahrens 
4874fa9e4066Sahrens 	return (0);
4875fa9e4066Sahrens }
4876fa9e4066Sahrens 
4877fa9e4066Sahrens int
4878fa9e4066Sahrens _fini(void)
4879fa9e4066Sahrens {
4880fa9e4066Sahrens 	int error;
4881fa9e4066Sahrens 
4882ea8dc4b6Seschrock 	if (spa_busy() || zfs_busy() || zvol_busy() || zio_injection_enabled)
4883fa9e4066Sahrens 		return (EBUSY);
4884fa9e4066Sahrens 
4885fa9e4066Sahrens 	if ((error = mod_remove(&modlinkage)) != 0)
4886fa9e4066Sahrens 		return (error);
4887fa9e4066Sahrens 
4888fa9e4066Sahrens 	zvol_fini();
4889fa9e4066Sahrens 	zfs_fini();
4890fa9e4066Sahrens 	spa_fini();
4891da6c28aaSamw 	if (zfs_nfsshare_inited)
4892ecd6cf80Smarks 		(void) ddi_modclose(nfs_mod);
4893da6c28aaSamw 	if (zfs_smbshare_inited)
4894da6c28aaSamw 		(void) ddi_modclose(smbsrv_mod);
4895da6c28aaSamw 	if (zfs_nfsshare_inited || zfs_smbshare_inited)
4896ecd6cf80Smarks 		(void) ddi_modclose(sharefs_mod);
4897fa9e4066Sahrens 
4898ec533521Sfr 	tsd_destroy(&zfs_fsyncer_key);
4899fa9e4066Sahrens 	ldi_ident_release(zfs_li);
4900fa9e4066Sahrens 	zfs_li = NULL;
4901ecd6cf80Smarks 	mutex_destroy(&zfs_share_lock);
4902fa9e4066Sahrens 
4903fa9e4066Sahrens 	return (error);
4904fa9e4066Sahrens }
4905fa9e4066Sahrens 
4906fa9e4066Sahrens int
4907fa9e4066Sahrens _info(struct modinfo *modinfop)
4908fa9e4066Sahrens {
4909fa9e4066Sahrens 	return (mod_info(&modlinkage, modinfop));
4910fa9e4066Sahrens }
4911