xref: /onnv-gate/usr/src/uts/common/fs/zfs/zfs_ioctl.c (revision 12949:b521d551715f)
1789Sahrens /*
2789Sahrens  * CDDL HEADER START
3789Sahrens  *
4789Sahrens  * The contents of this file are subject to the terms of the
51485Slling  * Common Development and Distribution License (the "License").
61485Slling  * You may not use this file except in compliance with the License.
7789Sahrens  *
8789Sahrens  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9789Sahrens  * or http://www.opensolaris.org/os/licensing.
10789Sahrens  * See the License for the specific language governing permissions
11789Sahrens  * and limitations under the License.
12789Sahrens  *
13789Sahrens  * When distributing Covered Code, include this CDDL HEADER in each
14789Sahrens  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15789Sahrens  * If applicable, add the following below this CDDL HEADER, with the
16789Sahrens  * fields enclosed by brackets "[]" replaced with your own identifying
17789Sahrens  * information: Portions Copyright [yyyy] [name of copyright owner]
18789Sahrens  *
19789Sahrens  * CDDL HEADER END
20789Sahrens  */
21789Sahrens /*
2212296SLin.Ling@Sun.COM  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
23789Sahrens  */
24789Sahrens 
25789Sahrens #include <sys/types.h>
26789Sahrens #include <sys/param.h>
27789Sahrens #include <sys/errno.h>
28789Sahrens #include <sys/uio.h>
29789Sahrens #include <sys/buf.h>
30789Sahrens #include <sys/modctl.h>
31789Sahrens #include <sys/open.h>
32789Sahrens #include <sys/file.h>
33789Sahrens #include <sys/kmem.h>
34789Sahrens #include <sys/conf.h>
35789Sahrens #include <sys/cmn_err.h>
36789Sahrens #include <sys/stat.h>
37789Sahrens #include <sys/zfs_ioctl.h>
3810972SRic.Aleshire@Sun.COM #include <sys/zfs_vfsops.h>
395331Samw #include <sys/zfs_znode.h>
40789Sahrens #include <sys/zap.h>
41789Sahrens #include <sys/spa.h>
423912Slling #include <sys/spa_impl.h>
43789Sahrens #include <sys/vdev.h>
4410972SRic.Aleshire@Sun.COM #include <sys/priv_impl.h>
45789Sahrens #include <sys/dmu.h>
46789Sahrens #include <sys/dsl_dir.h>
47789Sahrens #include <sys/dsl_dataset.h>
48789Sahrens #include <sys/dsl_prop.h>
494543Smarks #include <sys/dsl_deleg.h>
504543Smarks #include <sys/dmu_objset.h>
51789Sahrens #include <sys/ddi.h>
52789Sahrens #include <sys/sunddi.h>
53789Sahrens #include <sys/sunldi.h>
54789Sahrens #include <sys/policy.h>
55789Sahrens #include <sys/zone.h>
56789Sahrens #include <sys/nvpair.h>
57789Sahrens #include <sys/pathname.h>
58789Sahrens #include <sys/mount.h>
59789Sahrens #include <sys/sdt.h>
60789Sahrens #include <sys/fs/zfs.h>
61789Sahrens #include <sys/zfs_ctldir.h>
625331Samw #include <sys/zfs_dir.h>
6312527SChris.Kirby@oracle.com #include <sys/zfs_onexit.h>
642885Sahrens #include <sys/zvol.h>
6512296SLin.Ling@Sun.COM #include <sys/dsl_scan.h>
664543Smarks #include <sharefs/share.h>
675326Sek110237 #include <sys/dmu_objset.h>
68789Sahrens 
69789Sahrens #include "zfs_namecheck.h"
702676Seschrock #include "zfs_prop.h"
714543Smarks #include "zfs_deleg.h"
7211935SMark.Shellenbaum@Sun.COM #include "zfs_comutil.h"
73789Sahrens 
74789Sahrens extern struct modlfs zfs_modlfs;
75789Sahrens 
76789Sahrens extern void zfs_init(void);
77789Sahrens extern void zfs_fini(void);
78789Sahrens 
79789Sahrens ldi_ident_t zfs_li = NULL;
80789Sahrens dev_info_t *zfs_dip;
81789Sahrens 
82789Sahrens typedef int zfs_ioc_func_t(zfs_cmd_t *);
834543Smarks typedef int zfs_secpolicy_func_t(zfs_cmd_t *, cred_t *);
84789Sahrens 
859234SGeorge.Wilson@Sun.COM typedef enum {
869234SGeorge.Wilson@Sun.COM 	NO_NAME,
879234SGeorge.Wilson@Sun.COM 	POOL_NAME,
889234SGeorge.Wilson@Sun.COM 	DATASET_NAME
899234SGeorge.Wilson@Sun.COM } zfs_ioc_namecheck_t;
909234SGeorge.Wilson@Sun.COM 
91789Sahrens typedef struct zfs_ioc_vec {
92789Sahrens 	zfs_ioc_func_t		*zvec_func;
93789Sahrens 	zfs_secpolicy_func_t	*zvec_secpolicy;
949234SGeorge.Wilson@Sun.COM 	zfs_ioc_namecheck_t	zvec_namecheck;
954543Smarks 	boolean_t		zvec_his_log;
969234SGeorge.Wilson@Sun.COM 	boolean_t		zvec_pool_check;
97789Sahrens } zfs_ioc_vec_t;
98789Sahrens 
999396SMatthew.Ahrens@Sun.COM /* This array is indexed by zfs_userquota_prop_t */
1009396SMatthew.Ahrens@Sun.COM static const char *userquota_perms[] = {
1019396SMatthew.Ahrens@Sun.COM 	ZFS_DELEG_PERM_USERUSED,
1029396SMatthew.Ahrens@Sun.COM 	ZFS_DELEG_PERM_USERQUOTA,
1039396SMatthew.Ahrens@Sun.COM 	ZFS_DELEG_PERM_GROUPUSED,
1049396SMatthew.Ahrens@Sun.COM 	ZFS_DELEG_PERM_GROUPQUOTA,
1059396SMatthew.Ahrens@Sun.COM };
1069396SMatthew.Ahrens@Sun.COM 
1079396SMatthew.Ahrens@Sun.COM static int zfs_ioc_userspace_upgrade(zfs_cmd_t *zc);
10811022STom.Erickson@Sun.COM static int zfs_check_settable(const char *name, nvpair_t *property,
10911022STom.Erickson@Sun.COM     cred_t *cr);
11011022STom.Erickson@Sun.COM static int zfs_check_clearable(char *dataset, nvlist_t *props,
11111022STom.Erickson@Sun.COM     nvlist_t **errors);
1127184Stimh static int zfs_fill_zplprops_root(uint64_t, nvlist_t *, nvlist_t *,
1137184Stimh     boolean_t *);
11411022STom.Erickson@Sun.COM int zfs_set_prop_nvlist(const char *, zprop_source_t, nvlist_t *, nvlist_t **);
1157184Stimh 
116789Sahrens /* _NOTE(PRINTFLIKE(4)) - this is printf-like, but lint is too whiney */
117789Sahrens void
118789Sahrens __dprintf(const char *file, const char *func, int line, const char *fmt, ...)
119789Sahrens {
120789Sahrens 	const char *newfile;
12112296SLin.Ling@Sun.COM 	char buf[512];
122789Sahrens 	va_list adx;
123789Sahrens 
124789Sahrens 	/*
125789Sahrens 	 * Get rid of annoying "../common/" prefix to filename.
126789Sahrens 	 */
127789Sahrens 	newfile = strrchr(file, '/');
128789Sahrens 	if (newfile != NULL) {
129789Sahrens 		newfile = newfile + 1; /* Get rid of leading / */
130789Sahrens 	} else {
131789Sahrens 		newfile = file;
132789Sahrens 	}
133789Sahrens 
134789Sahrens 	va_start(adx, fmt);
135789Sahrens 	(void) vsnprintf(buf, sizeof (buf), fmt, adx);
136789Sahrens 	va_end(adx);
137789Sahrens 
138789Sahrens 	/*
139789Sahrens 	 * To get this data, use the zfs-dprintf probe as so:
140789Sahrens 	 * dtrace -q -n 'zfs-dprintf \
141789Sahrens 	 *	/stringof(arg0) == "dbuf.c"/ \
142789Sahrens 	 *	{printf("%s: %s", stringof(arg1), stringof(arg3))}'
143789Sahrens 	 * arg0 = file name
144789Sahrens 	 * arg1 = function name
145789Sahrens 	 * arg2 = line number
146789Sahrens 	 * arg3 = message
147789Sahrens 	 */
148789Sahrens 	DTRACE_PROBE4(zfs__dprintf,
149789Sahrens 	    char *, newfile, char *, func, int, line, char *, buf);
150789Sahrens }
151789Sahrens 
1524543Smarks static void
1534715Sek110237 history_str_free(char *buf)
1544715Sek110237 {
1554715Sek110237 	kmem_free(buf, HIS_MAX_RECORD_LEN);
1564715Sek110237 }
1574715Sek110237 
1584715Sek110237 static char *
1594715Sek110237 history_str_get(zfs_cmd_t *zc)
1604715Sek110237 {
1614715Sek110237 	char *buf;
1624715Sek110237 
1634715Sek110237 	if (zc->zc_history == NULL)
1644715Sek110237 		return (NULL);
1654715Sek110237 
1664715Sek110237 	buf = kmem_alloc(HIS_MAX_RECORD_LEN, KM_SLEEP);
1674715Sek110237 	if (copyinstr((void *)(uintptr_t)zc->zc_history,
1684715Sek110237 	    buf, HIS_MAX_RECORD_LEN, NULL) != 0) {
1694715Sek110237 		history_str_free(buf);
1704715Sek110237 		return (NULL);
1714715Sek110237 	}
1724715Sek110237 
1734715Sek110237 	buf[HIS_MAX_RECORD_LEN -1] = '\0';
1744715Sek110237 
1754715Sek110237 	return (buf);
1764715Sek110237 }
1774715Sek110237 
1785375Stimh /*
1797042Sgw25295  * Check to see if the named dataset is currently defined as bootable
1807042Sgw25295  */
1817042Sgw25295 static boolean_t
1827042Sgw25295 zfs_is_bootfs(const char *name)
1837042Sgw25295 {
18410298SMatthew.Ahrens@Sun.COM 	objset_t *os;
18510298SMatthew.Ahrens@Sun.COM 
18610298SMatthew.Ahrens@Sun.COM 	if (dmu_objset_hold(name, FTAG, &os) == 0) {
18710298SMatthew.Ahrens@Sun.COM 		boolean_t ret;
18810922SJeff.Bonwick@Sun.COM 		ret = (dmu_objset_id(os) == spa_bootfs(dmu_objset_spa(os)));
18910298SMatthew.Ahrens@Sun.COM 		dmu_objset_rele(os, FTAG);
19010298SMatthew.Ahrens@Sun.COM 		return (ret);
1917042Sgw25295 	}
19210298SMatthew.Ahrens@Sun.COM 	return (B_FALSE);
1937042Sgw25295 }
1947042Sgw25295 
1957042Sgw25295 /*
1967184Stimh  * zfs_earlier_version
1975375Stimh  *
1985375Stimh  *	Return non-zero if the spa version is less than requested version.
1995375Stimh  */
2005331Samw static int
2017184Stimh zfs_earlier_version(const char *name, int version)
2025331Samw {
2035331Samw 	spa_t *spa;
2045331Samw 
2055331Samw 	if (spa_open(name, &spa, FTAG) == 0) {
2065331Samw 		if (spa_version(spa) < version) {
2075331Samw 			spa_close(spa, FTAG);
2085331Samw 			return (1);
2095331Samw 		}
2105331Samw 		spa_close(spa, FTAG);
2115331Samw 	}
2125331Samw 	return (0);
2135331Samw }
2145331Samw 
2155977Smarks /*
2166689Smaybee  * zpl_earlier_version
2175977Smarks  *
2186689Smaybee  * Return TRUE if the ZPL version is less than requested version.
2195977Smarks  */
2206689Smaybee static boolean_t
2216689Smaybee zpl_earlier_version(const char *name, int version)
2225977Smarks {
2235977Smarks 	objset_t *os;
2246689Smaybee 	boolean_t rc = B_TRUE;
2255977Smarks 
22610298SMatthew.Ahrens@Sun.COM 	if (dmu_objset_hold(name, FTAG, &os) == 0) {
2276689Smaybee 		uint64_t zplversion;
2286689Smaybee 
22910298SMatthew.Ahrens@Sun.COM 		if (dmu_objset_type(os) != DMU_OST_ZFS) {
23010298SMatthew.Ahrens@Sun.COM 			dmu_objset_rele(os, FTAG);
23110298SMatthew.Ahrens@Sun.COM 			return (B_TRUE);
23210298SMatthew.Ahrens@Sun.COM 		}
23310298SMatthew.Ahrens@Sun.COM 		/* XXX reading from non-owned objset */
2346689Smaybee 		if (zfs_get_zplprop(os, ZFS_PROP_VERSION, &zplversion) == 0)
2356689Smaybee 			rc = zplversion < version;
23610298SMatthew.Ahrens@Sun.COM 		dmu_objset_rele(os, FTAG);
2375977Smarks 	}
2385977Smarks 	return (rc);
2395977Smarks }
2405977Smarks 
2414715Sek110237 static void
2424543Smarks zfs_log_history(zfs_cmd_t *zc)
2434543Smarks {
2444543Smarks 	spa_t *spa;
2454603Sahrens 	char *buf;
2464543Smarks 
2474715Sek110237 	if ((buf = history_str_get(zc)) == NULL)
2484577Sahrens 		return;
2494577Sahrens 
2504715Sek110237 	if (spa_open(zc->zc_name, &spa, FTAG) == 0) {
2514715Sek110237 		if (spa_version(spa) >= SPA_VERSION_ZPOOL_HISTORY)
2524715Sek110237 			(void) spa_history_log(spa, buf, LOG_CMD_NORMAL);
2534715Sek110237 		spa_close(spa, FTAG);
2544543Smarks 	}
2554715Sek110237 	history_str_free(buf);
2564543Smarks }
2574543Smarks 
258789Sahrens /*
259789Sahrens  * Policy for top-level read operations (list pools).  Requires no privileges,
260789Sahrens  * and can be used in the local zone, as there is no associated dataset.
261789Sahrens  */
262789Sahrens /* ARGSUSED */
263789Sahrens static int
2644543Smarks zfs_secpolicy_none(zfs_cmd_t *zc, cred_t *cr)
265789Sahrens {
266789Sahrens 	return (0);
267789Sahrens }
268789Sahrens 
269789Sahrens /*
270789Sahrens  * Policy for dataset read operations (list children, get statistics).  Requires
271789Sahrens  * no privileges, but must be visible in the local zone.
272789Sahrens  */
273789Sahrens /* ARGSUSED */
274789Sahrens static int
2754543Smarks zfs_secpolicy_read(zfs_cmd_t *zc, cred_t *cr)
276789Sahrens {
277789Sahrens 	if (INGLOBALZONE(curproc) ||
2784543Smarks 	    zone_dataset_visible(zc->zc_name, NULL))
279789Sahrens 		return (0);
280789Sahrens 
281789Sahrens 	return (ENOENT);
282789Sahrens }
283789Sahrens 
284789Sahrens static int
28512786SChris.Kirby@oracle.com zfs_dozonecheck_impl(const char *dataset, uint64_t zoned, cred_t *cr)
286789Sahrens {
287789Sahrens 	int writable = 1;
288789Sahrens 
289789Sahrens 	/*
290789Sahrens 	 * The dataset must be visible by this zone -- check this first
291789Sahrens 	 * so they don't see EPERM on something they shouldn't know about.
292789Sahrens 	 */
293789Sahrens 	if (!INGLOBALZONE(curproc) &&
294789Sahrens 	    !zone_dataset_visible(dataset, &writable))
295789Sahrens 		return (ENOENT);
296789Sahrens 
297789Sahrens 	if (INGLOBALZONE(curproc)) {
298789Sahrens 		/*
299789Sahrens 		 * If the fs is zoned, only root can access it from the
300789Sahrens 		 * global zone.
301789Sahrens 		 */
302789Sahrens 		if (secpolicy_zfs(cr) && zoned)
303789Sahrens 			return (EPERM);
304789Sahrens 	} else {
305789Sahrens 		/*
306789Sahrens 		 * If we are in a local zone, the 'zoned' property must be set.
307789Sahrens 		 */
308789Sahrens 		if (!zoned)
309789Sahrens 			return (EPERM);
310789Sahrens 
311789Sahrens 		/* must be writable by this zone */
312789Sahrens 		if (!writable)
313789Sahrens 			return (EPERM);
314789Sahrens 	}
315789Sahrens 	return (0);
316789Sahrens }
317789Sahrens 
31812786SChris.Kirby@oracle.com static int
31912786SChris.Kirby@oracle.com zfs_dozonecheck(const char *dataset, cred_t *cr)
32012786SChris.Kirby@oracle.com {
32112786SChris.Kirby@oracle.com 	uint64_t zoned;
32212786SChris.Kirby@oracle.com 
32312786SChris.Kirby@oracle.com 	if (dsl_prop_get_integer(dataset, "zoned", &zoned, NULL))
32412786SChris.Kirby@oracle.com 		return (ENOENT);
32512786SChris.Kirby@oracle.com 
32612786SChris.Kirby@oracle.com 	return (zfs_dozonecheck_impl(dataset, zoned, cr));
32712786SChris.Kirby@oracle.com }
32812786SChris.Kirby@oracle.com 
32912786SChris.Kirby@oracle.com static int
33012786SChris.Kirby@oracle.com zfs_dozonecheck_ds(const char *dataset, dsl_dataset_t *ds, cred_t *cr)
33112786SChris.Kirby@oracle.com {
33212786SChris.Kirby@oracle.com 	uint64_t zoned;
33312786SChris.Kirby@oracle.com 
33412786SChris.Kirby@oracle.com 	rw_enter(&ds->ds_dir->dd_pool->dp_config_rwlock, RW_READER);
33512786SChris.Kirby@oracle.com 	if (dsl_prop_get_ds(ds, "zoned", 8, 1, &zoned, NULL)) {
33612786SChris.Kirby@oracle.com 		rw_exit(&ds->ds_dir->dd_pool->dp_config_rwlock);
33712786SChris.Kirby@oracle.com 		return (ENOENT);
33812786SChris.Kirby@oracle.com 	}
33912786SChris.Kirby@oracle.com 	rw_exit(&ds->ds_dir->dd_pool->dp_config_rwlock);
34012786SChris.Kirby@oracle.com 
34112786SChris.Kirby@oracle.com 	return (zfs_dozonecheck_impl(dataset, zoned, cr));
34212786SChris.Kirby@oracle.com }
34312786SChris.Kirby@oracle.com 
344789Sahrens int
3454543Smarks zfs_secpolicy_write_perms(const char *name, const char *perm, cred_t *cr)
346789Sahrens {
347789Sahrens 	int error;
348789Sahrens 
3494543Smarks 	error = zfs_dozonecheck(name, cr);
3504543Smarks 	if (error == 0) {
3514543Smarks 		error = secpolicy_zfs(cr);
3524670Sahrens 		if (error)
3534543Smarks 			error = dsl_deleg_access(name, perm, cr);
3544543Smarks 	}
3554543Smarks 	return (error);
3564543Smarks }
3574543Smarks 
35812786SChris.Kirby@oracle.com int
35912786SChris.Kirby@oracle.com zfs_secpolicy_write_perms_ds(const char *name, dsl_dataset_t *ds,
36012786SChris.Kirby@oracle.com     const char *perm, cred_t *cr)
36112786SChris.Kirby@oracle.com {
36212786SChris.Kirby@oracle.com 	int error;
36312786SChris.Kirby@oracle.com 
36412786SChris.Kirby@oracle.com 	error = zfs_dozonecheck_ds(name, ds, cr);
36512786SChris.Kirby@oracle.com 	if (error == 0) {
36612786SChris.Kirby@oracle.com 		error = secpolicy_zfs(cr);
36712786SChris.Kirby@oracle.com 		if (error)
36812786SChris.Kirby@oracle.com 			error = dsl_deleg_access_impl(ds, perm, cr);
36912786SChris.Kirby@oracle.com 	}
37012786SChris.Kirby@oracle.com 	return (error);
37112786SChris.Kirby@oracle.com }
37212786SChris.Kirby@oracle.com 
37310972SRic.Aleshire@Sun.COM /*
37410972SRic.Aleshire@Sun.COM  * Policy for setting the security label property.
37510972SRic.Aleshire@Sun.COM  *
37610972SRic.Aleshire@Sun.COM  * Returns 0 for success, non-zero for access and other errors.
37710972SRic.Aleshire@Sun.COM  */
37810972SRic.Aleshire@Sun.COM static int
37911022STom.Erickson@Sun.COM zfs_set_slabel_policy(const char *name, char *strval, cred_t *cr)
38010972SRic.Aleshire@Sun.COM {
38110972SRic.Aleshire@Sun.COM 	char		ds_hexsl[MAXNAMELEN];
38210972SRic.Aleshire@Sun.COM 	bslabel_t	ds_sl, new_sl;
38310972SRic.Aleshire@Sun.COM 	boolean_t	new_default = FALSE;
38410972SRic.Aleshire@Sun.COM 	uint64_t	zoned;
38510972SRic.Aleshire@Sun.COM 	int		needed_priv = -1;
38610972SRic.Aleshire@Sun.COM 	int		error;
38710972SRic.Aleshire@Sun.COM 
38810972SRic.Aleshire@Sun.COM 	/* First get the existing dataset label. */
38910972SRic.Aleshire@Sun.COM 	error = dsl_prop_get(name, zfs_prop_to_name(ZFS_PROP_MLSLABEL),
39010972SRic.Aleshire@Sun.COM 	    1, sizeof (ds_hexsl), &ds_hexsl, NULL);
39110972SRic.Aleshire@Sun.COM 	if (error)
39210972SRic.Aleshire@Sun.COM 		return (EPERM);
39310972SRic.Aleshire@Sun.COM 
39410972SRic.Aleshire@Sun.COM 	if (strcasecmp(strval, ZFS_MLSLABEL_DEFAULT) == 0)
39510972SRic.Aleshire@Sun.COM 		new_default = TRUE;
39610972SRic.Aleshire@Sun.COM 
39710972SRic.Aleshire@Sun.COM 	/* The label must be translatable */
39810972SRic.Aleshire@Sun.COM 	if (!new_default && (hexstr_to_label(strval, &new_sl) != 0))
39910972SRic.Aleshire@Sun.COM 		return (EINVAL);
40010972SRic.Aleshire@Sun.COM 
40110972SRic.Aleshire@Sun.COM 	/*
40210972SRic.Aleshire@Sun.COM 	 * In a non-global zone, disallow attempts to set a label that
40310972SRic.Aleshire@Sun.COM 	 * doesn't match that of the zone; otherwise no other checks
40410972SRic.Aleshire@Sun.COM 	 * are needed.
40510972SRic.Aleshire@Sun.COM 	 */
40610972SRic.Aleshire@Sun.COM 	if (!INGLOBALZONE(curproc)) {
40710972SRic.Aleshire@Sun.COM 		if (new_default || !blequal(&new_sl, CR_SL(CRED())))
40810972SRic.Aleshire@Sun.COM 			return (EPERM);
40910972SRic.Aleshire@Sun.COM 		return (0);
41010972SRic.Aleshire@Sun.COM 	}
41110972SRic.Aleshire@Sun.COM 
41210972SRic.Aleshire@Sun.COM 	/*
41310972SRic.Aleshire@Sun.COM 	 * For global-zone datasets (i.e., those whose zoned property is
41410972SRic.Aleshire@Sun.COM 	 * "off", verify that the specified new label is valid for the
41510972SRic.Aleshire@Sun.COM 	 * global zone.
41610972SRic.Aleshire@Sun.COM 	 */
41710972SRic.Aleshire@Sun.COM 	if (dsl_prop_get_integer(name,
41810972SRic.Aleshire@Sun.COM 	    zfs_prop_to_name(ZFS_PROP_ZONED), &zoned, NULL))
41910972SRic.Aleshire@Sun.COM 		return (EPERM);
42010972SRic.Aleshire@Sun.COM 	if (!zoned) {
42110972SRic.Aleshire@Sun.COM 		if (zfs_check_global_label(name, strval) != 0)
42210972SRic.Aleshire@Sun.COM 			return (EPERM);
42310972SRic.Aleshire@Sun.COM 	}
42410972SRic.Aleshire@Sun.COM 
42510972SRic.Aleshire@Sun.COM 	/*
42610972SRic.Aleshire@Sun.COM 	 * If the existing dataset label is nondefault, check if the
42710972SRic.Aleshire@Sun.COM 	 * dataset is mounted (label cannot be changed while mounted).
42810972SRic.Aleshire@Sun.COM 	 * Get the zfsvfs; if there isn't one, then the dataset isn't
42910972SRic.Aleshire@Sun.COM 	 * mounted (or isn't a dataset, doesn't exist, ...).
43010972SRic.Aleshire@Sun.COM 	 */
43110972SRic.Aleshire@Sun.COM 	if (strcasecmp(ds_hexsl, ZFS_MLSLABEL_DEFAULT) != 0) {
43211022STom.Erickson@Sun.COM 		objset_t *os;
43311022STom.Erickson@Sun.COM 		static char *setsl_tag = "setsl_tag";
43411022STom.Erickson@Sun.COM 
43510972SRic.Aleshire@Sun.COM 		/*
43610972SRic.Aleshire@Sun.COM 		 * Try to own the dataset; abort if there is any error,
43710972SRic.Aleshire@Sun.COM 		 * (e.g., already mounted, in use, or other error).
43810972SRic.Aleshire@Sun.COM 		 */
43910972SRic.Aleshire@Sun.COM 		error = dmu_objset_own(name, DMU_OST_ZFS, B_TRUE,
44011022STom.Erickson@Sun.COM 		    setsl_tag, &os);
44110972SRic.Aleshire@Sun.COM 		if (error)
44210972SRic.Aleshire@Sun.COM 			return (EPERM);
44310972SRic.Aleshire@Sun.COM 
44411022STom.Erickson@Sun.COM 		dmu_objset_disown(os, setsl_tag);
44511022STom.Erickson@Sun.COM 
44610972SRic.Aleshire@Sun.COM 		if (new_default) {
44710972SRic.Aleshire@Sun.COM 			needed_priv = PRIV_FILE_DOWNGRADE_SL;
44810972SRic.Aleshire@Sun.COM 			goto out_check;
44910972SRic.Aleshire@Sun.COM 		}
45010972SRic.Aleshire@Sun.COM 
45110972SRic.Aleshire@Sun.COM 		if (hexstr_to_label(strval, &new_sl) != 0)
45210972SRic.Aleshire@Sun.COM 			return (EPERM);
45310972SRic.Aleshire@Sun.COM 
45410972SRic.Aleshire@Sun.COM 		if (blstrictdom(&ds_sl, &new_sl))
45510972SRic.Aleshire@Sun.COM 			needed_priv = PRIV_FILE_DOWNGRADE_SL;
45610972SRic.Aleshire@Sun.COM 		else if (blstrictdom(&new_sl, &ds_sl))
45710972SRic.Aleshire@Sun.COM 			needed_priv = PRIV_FILE_UPGRADE_SL;
45810972SRic.Aleshire@Sun.COM 	} else {
45910972SRic.Aleshire@Sun.COM 		/* dataset currently has a default label */
46010972SRic.Aleshire@Sun.COM 		if (!new_default)
46110972SRic.Aleshire@Sun.COM 			needed_priv = PRIV_FILE_UPGRADE_SL;
46210972SRic.Aleshire@Sun.COM 	}
46310972SRic.Aleshire@Sun.COM 
46410972SRic.Aleshire@Sun.COM out_check:
46510972SRic.Aleshire@Sun.COM 	if (needed_priv != -1)
46610972SRic.Aleshire@Sun.COM 		return (PRIV_POLICY(cr, needed_priv, B_FALSE, EPERM, NULL));
46710972SRic.Aleshire@Sun.COM 	return (0);
46810972SRic.Aleshire@Sun.COM }
46910972SRic.Aleshire@Sun.COM 
4704543Smarks static int
47111022STom.Erickson@Sun.COM zfs_secpolicy_setprop(const char *dsname, zfs_prop_t prop, nvpair_t *propval,
47211022STom.Erickson@Sun.COM     cred_t *cr)
4734543Smarks {
47411022STom.Erickson@Sun.COM 	char *strval;
47511022STom.Erickson@Sun.COM 
4764543Smarks 	/*
4774543Smarks 	 * Check permissions for special properties.
4784543Smarks 	 */
4794543Smarks 	switch (prop) {
4804543Smarks 	case ZFS_PROP_ZONED:
4814543Smarks 		/*
4824543Smarks 		 * Disallow setting of 'zoned' from within a local zone.
4834543Smarks 		 */
4844543Smarks 		if (!INGLOBALZONE(curproc))
4854543Smarks 			return (EPERM);
4864543Smarks 		break;
487789Sahrens 
4884543Smarks 	case ZFS_PROP_QUOTA:
4894543Smarks 		if (!INGLOBALZONE(curproc)) {
4904543Smarks 			uint64_t zoned;
4914543Smarks 			char setpoint[MAXNAMELEN];
4924543Smarks 			/*
4934543Smarks 			 * Unprivileged users are allowed to modify the
4944543Smarks 			 * quota on things *under* (ie. contained by)
4954543Smarks 			 * the thing they own.
4964543Smarks 			 */
49711022STom.Erickson@Sun.COM 			if (dsl_prop_get_integer(dsname, "zoned", &zoned,
4984543Smarks 			    setpoint))
4994543Smarks 				return (EPERM);
50011022STom.Erickson@Sun.COM 			if (!zoned || strlen(dsname) <= strlen(setpoint))
5014543Smarks 				return (EPERM);
5024543Smarks 		}
5034670Sahrens 		break;
50410972SRic.Aleshire@Sun.COM 
50510972SRic.Aleshire@Sun.COM 	case ZFS_PROP_MLSLABEL:
50610972SRic.Aleshire@Sun.COM 		if (!is_system_labeled())
50710972SRic.Aleshire@Sun.COM 			return (EPERM);
50811022STom.Erickson@Sun.COM 
50911022STom.Erickson@Sun.COM 		if (nvpair_value_string(propval, &strval) == 0) {
51011022STom.Erickson@Sun.COM 			int err;
51111022STom.Erickson@Sun.COM 
51211022STom.Erickson@Sun.COM 			err = zfs_set_slabel_policy(dsname, strval, CRED());
51311022STom.Erickson@Sun.COM 			if (err != 0)
51411022STom.Erickson@Sun.COM 				return (err);
51511022STom.Erickson@Sun.COM 		}
51610972SRic.Aleshire@Sun.COM 		break;
5174543Smarks 	}
5184543Smarks 
51911022STom.Erickson@Sun.COM 	return (zfs_secpolicy_write_perms(dsname, zfs_prop_to_name(prop), cr));
520789Sahrens }
521789Sahrens 
5224543Smarks int
5234543Smarks zfs_secpolicy_fsacl(zfs_cmd_t *zc, cred_t *cr)
5244543Smarks {
5254543Smarks 	int error;
5264543Smarks 
5274543Smarks 	error = zfs_dozonecheck(zc->zc_name, cr);
5284543Smarks 	if (error)
5294543Smarks 		return (error);
5304543Smarks 
5314543Smarks 	/*
5324543Smarks 	 * permission to set permissions will be evaluated later in
5334543Smarks 	 * dsl_deleg_can_allow()
5344543Smarks 	 */
5354543Smarks 	return (0);
5364543Smarks }
5374543Smarks 
5384543Smarks int
5394543Smarks zfs_secpolicy_rollback(zfs_cmd_t *zc, cred_t *cr)
5404543Smarks {
54110588SEric.Taylor@Sun.COM 	return (zfs_secpolicy_write_perms(zc->zc_name,
54210588SEric.Taylor@Sun.COM 	    ZFS_DELEG_PERM_ROLLBACK, cr));
5434543Smarks }
5444543Smarks 
5454543Smarks int
5464543Smarks zfs_secpolicy_send(zfs_cmd_t *zc, cred_t *cr)
5474543Smarks {
54812786SChris.Kirby@oracle.com 	spa_t *spa;
54912786SChris.Kirby@oracle.com 	dsl_pool_t *dp;
55012786SChris.Kirby@oracle.com 	dsl_dataset_t *ds;
55112786SChris.Kirby@oracle.com 	char *cp;
55212786SChris.Kirby@oracle.com 	int error;
55312786SChris.Kirby@oracle.com 
55412786SChris.Kirby@oracle.com 	/*
55512786SChris.Kirby@oracle.com 	 * Generate the current snapshot name from the given objsetid, then
55612786SChris.Kirby@oracle.com 	 * use that name for the secpolicy/zone checks.
55712786SChris.Kirby@oracle.com 	 */
55812786SChris.Kirby@oracle.com 	cp = strchr(zc->zc_name, '@');
55912786SChris.Kirby@oracle.com 	if (cp == NULL)
56012786SChris.Kirby@oracle.com 		return (EINVAL);
56112786SChris.Kirby@oracle.com 	error = spa_open(zc->zc_name, &spa, FTAG);
56212786SChris.Kirby@oracle.com 	if (error)
56312786SChris.Kirby@oracle.com 		return (error);
56412786SChris.Kirby@oracle.com 
56512786SChris.Kirby@oracle.com 	dp = spa_get_dsl(spa);
56612786SChris.Kirby@oracle.com 	rw_enter(&dp->dp_config_rwlock, RW_READER);
56712786SChris.Kirby@oracle.com 	error = dsl_dataset_hold_obj(dp, zc->zc_sendobj, FTAG, &ds);
56812786SChris.Kirby@oracle.com 	rw_exit(&dp->dp_config_rwlock);
56912786SChris.Kirby@oracle.com 	spa_close(spa, FTAG);
57012786SChris.Kirby@oracle.com 	if (error)
57112786SChris.Kirby@oracle.com 		return (error);
57212786SChris.Kirby@oracle.com 
57312786SChris.Kirby@oracle.com 	dsl_dataset_name(ds, zc->zc_name);
57412786SChris.Kirby@oracle.com 
57512786SChris.Kirby@oracle.com 	error = zfs_secpolicy_write_perms_ds(zc->zc_name, ds,
57612786SChris.Kirby@oracle.com 	    ZFS_DELEG_PERM_SEND, cr);
57712786SChris.Kirby@oracle.com 	dsl_dataset_rele(ds, FTAG);
57812786SChris.Kirby@oracle.com 
57912786SChris.Kirby@oracle.com 	return (error);
5804543Smarks }
5814543Smarks 
5828845Samw@Sun.COM static int
5838845Samw@Sun.COM zfs_secpolicy_deleg_share(zfs_cmd_t *zc, cred_t *cr)
5848845Samw@Sun.COM {
5858845Samw@Sun.COM 	vnode_t *vp;
5868845Samw@Sun.COM 	int error;
5878845Samw@Sun.COM 
5888845Samw@Sun.COM 	if ((error = lookupname(zc->zc_value, UIO_SYSSPACE,
5898845Samw@Sun.COM 	    NO_FOLLOW, NULL, &vp)) != 0)
5908845Samw@Sun.COM 		return (error);
5918845Samw@Sun.COM 
5928845Samw@Sun.COM 	/* Now make sure mntpnt and dataset are ZFS */
5938845Samw@Sun.COM 
5948845Samw@Sun.COM 	if (vp->v_vfsp->vfs_fstype != zfsfstype ||
5958845Samw@Sun.COM 	    (strcmp((char *)refstr_value(vp->v_vfsp->vfs_resource),
5968845Samw@Sun.COM 	    zc->zc_name) != 0)) {
5978845Samw@Sun.COM 		VN_RELE(vp);
5988845Samw@Sun.COM 		return (EPERM);
5998845Samw@Sun.COM 	}
6008845Samw@Sun.COM 
6018845Samw@Sun.COM 	VN_RELE(vp);
6028845Samw@Sun.COM 	return (dsl_deleg_access(zc->zc_name,
6038845Samw@Sun.COM 	    ZFS_DELEG_PERM_SHARE, cr));
6048845Samw@Sun.COM }
6058845Samw@Sun.COM 
6064543Smarks int
6074543Smarks zfs_secpolicy_share(zfs_cmd_t *zc, cred_t *cr)
6084543Smarks {
6094543Smarks 	if (!INGLOBALZONE(curproc))
6104543Smarks 		return (EPERM);
6114543Smarks 
6125367Sahrens 	if (secpolicy_nfs(cr) == 0) {
6134543Smarks 		return (0);
6144543Smarks 	} else {
6158845Samw@Sun.COM 		return (zfs_secpolicy_deleg_share(zc, cr));
6168845Samw@Sun.COM 	}
6178845Samw@Sun.COM }
6188845Samw@Sun.COM 
6198845Samw@Sun.COM int
6208845Samw@Sun.COM zfs_secpolicy_smb_acl(zfs_cmd_t *zc, cred_t *cr)
6218845Samw@Sun.COM {
6228845Samw@Sun.COM 	if (!INGLOBALZONE(curproc))
6238845Samw@Sun.COM 		return (EPERM);
6248845Samw@Sun.COM 
6258845Samw@Sun.COM 	if (secpolicy_smb(cr) == 0) {
6268845Samw@Sun.COM 		return (0);
6278845Samw@Sun.COM 	} else {
6288845Samw@Sun.COM 		return (zfs_secpolicy_deleg_share(zc, cr));
6294543Smarks 	}
6304543Smarks }
6314543Smarks 
632789Sahrens static int
6334543Smarks zfs_get_parent(const char *datasetname, char *parent, int parentsize)
634789Sahrens {
635789Sahrens 	char *cp;
636789Sahrens 
637789Sahrens 	/*
638789Sahrens 	 * Remove the @bla or /bla from the end of the name to get the parent.
639789Sahrens 	 */
6404543Smarks 	(void) strncpy(parent, datasetname, parentsize);
6414543Smarks 	cp = strrchr(parent, '@');
642789Sahrens 	if (cp != NULL) {
643789Sahrens 		cp[0] = '\0';
644789Sahrens 	} else {
6454543Smarks 		cp = strrchr(parent, '/');
646789Sahrens 		if (cp == NULL)
647789Sahrens 			return (ENOENT);
648789Sahrens 		cp[0] = '\0';
649789Sahrens 	}
650789Sahrens 
6514543Smarks 	return (0);
6524543Smarks }
6534543Smarks 
6544543Smarks int
6554543Smarks zfs_secpolicy_destroy_perms(const char *name, cred_t *cr)
6564543Smarks {
6574543Smarks 	int error;
6584543Smarks 
6594543Smarks 	if ((error = zfs_secpolicy_write_perms(name,
6604543Smarks 	    ZFS_DELEG_PERM_MOUNT, cr)) != 0)
6614543Smarks 		return (error);
6624543Smarks 
6634543Smarks 	return (zfs_secpolicy_write_perms(name, ZFS_DELEG_PERM_DESTROY, cr));
6644543Smarks }
6654543Smarks 
6664543Smarks static int
6674543Smarks zfs_secpolicy_destroy(zfs_cmd_t *zc, cred_t *cr)
6684543Smarks {
6694543Smarks 	return (zfs_secpolicy_destroy_perms(zc->zc_name, cr));
6704543Smarks }
6714543Smarks 
6724543Smarks /*
67311314Swilliam.gorrell@sun.com  * Destroying snapshots with delegated permissions requires
67411314Swilliam.gorrell@sun.com  * descendent mount and destroy permissions.
67511314Swilliam.gorrell@sun.com  * Reassemble the full filesystem@snap name so dsl_deleg_access()
67611314Swilliam.gorrell@sun.com  * can do the correct permission check.
67711314Swilliam.gorrell@sun.com  *
67811314Swilliam.gorrell@sun.com  * Since this routine is used when doing a recursive destroy of snapshots
67911314Swilliam.gorrell@sun.com  * and destroying snapshots requires descendent permissions, a successfull
68011314Swilliam.gorrell@sun.com  * check of the top level snapshot applies to snapshots of all descendent
68111314Swilliam.gorrell@sun.com  * datasets as well.
68211314Swilliam.gorrell@sun.com  */
68311314Swilliam.gorrell@sun.com static int
68411314Swilliam.gorrell@sun.com zfs_secpolicy_destroy_snaps(zfs_cmd_t *zc, cred_t *cr)
68511314Swilliam.gorrell@sun.com {
68611314Swilliam.gorrell@sun.com 	int error;
68711314Swilliam.gorrell@sun.com 	char *dsname;
68811314Swilliam.gorrell@sun.com 
68911314Swilliam.gorrell@sun.com 	dsname = kmem_asprintf("%s@%s", zc->zc_name, zc->zc_value);
69011314Swilliam.gorrell@sun.com 
69111314Swilliam.gorrell@sun.com 	error = zfs_secpolicy_destroy_perms(dsname, cr);
69211314Swilliam.gorrell@sun.com 
69311314Swilliam.gorrell@sun.com 	strfree(dsname);
69411314Swilliam.gorrell@sun.com 	return (error);
69511314Swilliam.gorrell@sun.com }
69611314Swilliam.gorrell@sun.com 
6974543Smarks int
6984543Smarks zfs_secpolicy_rename_perms(const char *from, const char *to, cred_t *cr)
6994543Smarks {
70011022STom.Erickson@Sun.COM 	char	parentname[MAXNAMELEN];
7014543Smarks 	int	error;
7024543Smarks 
7034543Smarks 	if ((error = zfs_secpolicy_write_perms(from,
7044543Smarks 	    ZFS_DELEG_PERM_RENAME, cr)) != 0)
7054543Smarks 		return (error);
7064543Smarks 
7074543Smarks 	if ((error = zfs_secpolicy_write_perms(from,
7084543Smarks 	    ZFS_DELEG_PERM_MOUNT, cr)) != 0)
7094543Smarks 		return (error);
7104543Smarks 
7114543Smarks 	if ((error = zfs_get_parent(to, parentname,
7124543Smarks 	    sizeof (parentname))) != 0)
7134543Smarks 		return (error);
7144543Smarks 
7154543Smarks 	if ((error = zfs_secpolicy_write_perms(parentname,
7164543Smarks 	    ZFS_DELEG_PERM_CREATE, cr)) != 0)
7174543Smarks 		return (error);
7184543Smarks 
7194543Smarks 	if ((error = zfs_secpolicy_write_perms(parentname,
7204543Smarks 	    ZFS_DELEG_PERM_MOUNT, cr)) != 0)
7214543Smarks 		return (error);
7224543Smarks 
7234543Smarks 	return (error);
7244543Smarks }
7254543Smarks 
7264543Smarks static int
7274543Smarks zfs_secpolicy_rename(zfs_cmd_t *zc, cred_t *cr)
7284543Smarks {
7294543Smarks 	return (zfs_secpolicy_rename_perms(zc->zc_name, zc->zc_value, cr));
7304543Smarks }
7314543Smarks 
7324543Smarks static int
7334543Smarks zfs_secpolicy_promote(zfs_cmd_t *zc, cred_t *cr)
7344543Smarks {
73511022STom.Erickson@Sun.COM 	char	parentname[MAXNAMELEN];
7364543Smarks 	objset_t *clone;
7374543Smarks 	int error;
7384543Smarks 
7394543Smarks 	error = zfs_secpolicy_write_perms(zc->zc_name,
7404543Smarks 	    ZFS_DELEG_PERM_PROMOTE, cr);
7414543Smarks 	if (error)
7424543Smarks 		return (error);
7434543Smarks 
74410298SMatthew.Ahrens@Sun.COM 	error = dmu_objset_hold(zc->zc_name, FTAG, &clone);
7454543Smarks 
7464543Smarks 	if (error == 0) {
7474543Smarks 		dsl_dataset_t *pclone = NULL;
7484543Smarks 		dsl_dir_t *dd;
74910298SMatthew.Ahrens@Sun.COM 		dd = clone->os_dsl_dataset->ds_dir;
7504543Smarks 
7514543Smarks 		rw_enter(&dd->dd_pool->dp_config_rwlock, RW_READER);
7526689Smaybee 		error = dsl_dataset_hold_obj(dd->dd_pool,
7536689Smaybee 		    dd->dd_phys->dd_origin_obj, FTAG, &pclone);
7544543Smarks 		rw_exit(&dd->dd_pool->dp_config_rwlock);
7554543Smarks 		if (error) {
75610298SMatthew.Ahrens@Sun.COM 			dmu_objset_rele(clone, FTAG);
7574543Smarks 			return (error);
7584543Smarks 		}
7594543Smarks 
7604543Smarks 		error = zfs_secpolicy_write_perms(zc->zc_name,
7614543Smarks 		    ZFS_DELEG_PERM_MOUNT, cr);
7624543Smarks 
7634543Smarks 		dsl_dataset_name(pclone, parentname);
76410298SMatthew.Ahrens@Sun.COM 		dmu_objset_rele(clone, FTAG);
7656689Smaybee 		dsl_dataset_rele(pclone, FTAG);
7664543Smarks 		if (error == 0)
7674543Smarks 			error = zfs_secpolicy_write_perms(parentname,
7684543Smarks 			    ZFS_DELEG_PERM_PROMOTE, cr);
7694543Smarks 	}
7704543Smarks 	return (error);
7714543Smarks }
7724543Smarks 
7734543Smarks static int
7744543Smarks zfs_secpolicy_receive(zfs_cmd_t *zc, cred_t *cr)
7754543Smarks {
7764543Smarks 	int error;
7774543Smarks 
7784543Smarks 	if ((error = zfs_secpolicy_write_perms(zc->zc_name,
7794543Smarks 	    ZFS_DELEG_PERM_RECEIVE, cr)) != 0)
7804543Smarks 		return (error);
7814543Smarks 
7824543Smarks 	if ((error = zfs_secpolicy_write_perms(zc->zc_name,
7834543Smarks 	    ZFS_DELEG_PERM_MOUNT, cr)) != 0)
7844543Smarks 		return (error);
7854543Smarks 
7864543Smarks 	return (zfs_secpolicy_write_perms(zc->zc_name,
7874543Smarks 	    ZFS_DELEG_PERM_CREATE, cr));
7884543Smarks }
7894543Smarks 
7904543Smarks int
7914543Smarks zfs_secpolicy_snapshot_perms(const char *name, cred_t *cr)
7924543Smarks {
79310588SEric.Taylor@Sun.COM 	return (zfs_secpolicy_write_perms(name,
79410588SEric.Taylor@Sun.COM 	    ZFS_DELEG_PERM_SNAPSHOT, cr));
7954543Smarks }
7964543Smarks 
7974543Smarks static int
7984543Smarks zfs_secpolicy_snapshot(zfs_cmd_t *zc, cred_t *cr)
7994543Smarks {
8004543Smarks 
8014543Smarks 	return (zfs_secpolicy_snapshot_perms(zc->zc_name, cr));
8024543Smarks }
8034543Smarks 
8044543Smarks static int
8054543Smarks zfs_secpolicy_create(zfs_cmd_t *zc, cred_t *cr)
8064543Smarks {
80711022STom.Erickson@Sun.COM 	char	parentname[MAXNAMELEN];
80811022STom.Erickson@Sun.COM 	int	error;
8094543Smarks 
8104543Smarks 	if ((error = zfs_get_parent(zc->zc_name, parentname,
8114543Smarks 	    sizeof (parentname))) != 0)
8124543Smarks 		return (error);
8134543Smarks 
8144543Smarks 	if (zc->zc_value[0] != '\0') {
8154543Smarks 		if ((error = zfs_secpolicy_write_perms(zc->zc_value,
8164543Smarks 		    ZFS_DELEG_PERM_CLONE, cr)) != 0)
8174543Smarks 			return (error);
8184543Smarks 	}
8194543Smarks 
8204543Smarks 	if ((error = zfs_secpolicy_write_perms(parentname,
8214543Smarks 	    ZFS_DELEG_PERM_CREATE, cr)) != 0)
8224543Smarks 		return (error);
8234543Smarks 
8244543Smarks 	error = zfs_secpolicy_write_perms(parentname,
8254543Smarks 	    ZFS_DELEG_PERM_MOUNT, cr);
8264543Smarks 
8274543Smarks 	return (error);
8284543Smarks }
8294543Smarks 
8304543Smarks static int
8314543Smarks zfs_secpolicy_umount(zfs_cmd_t *zc, cred_t *cr)
8324543Smarks {
8334543Smarks 	int error;
8344543Smarks 
8354543Smarks 	error = secpolicy_fs_unmount(cr, NULL);
8364543Smarks 	if (error) {
8374543Smarks 		error = dsl_deleg_access(zc->zc_name, ZFS_DELEG_PERM_MOUNT, cr);
8384543Smarks 	}
8394543Smarks 	return (error);
840789Sahrens }
841789Sahrens 
842789Sahrens /*
843789Sahrens  * Policy for pool operations - create/destroy pools, add vdevs, etc.  Requires
844789Sahrens  * SYS_CONFIG privilege, which is not available in a local zone.
845789Sahrens  */
846789Sahrens /* ARGSUSED */
847789Sahrens static int
8484543Smarks zfs_secpolicy_config(zfs_cmd_t *zc, cred_t *cr)
849789Sahrens {
850789Sahrens 	if (secpolicy_sys_config(cr, B_FALSE) != 0)
851789Sahrens 		return (EPERM);
852789Sahrens 
853789Sahrens 	return (0);
854789Sahrens }
855789Sahrens 
856789Sahrens /*
8571544Seschrock  * Policy for fault injection.  Requires all privileges.
8581544Seschrock  */
8591544Seschrock /* ARGSUSED */
8601544Seschrock static int
8614543Smarks zfs_secpolicy_inject(zfs_cmd_t *zc, cred_t *cr)
8621544Seschrock {
8631544Seschrock 	return (secpolicy_zinject(cr));
8641544Seschrock }
8651544Seschrock 
8664849Sahrens static int
8674849Sahrens zfs_secpolicy_inherit(zfs_cmd_t *zc, cred_t *cr)
8684849Sahrens {
8694849Sahrens 	zfs_prop_t prop = zfs_name_to_prop(zc->zc_value);
8704849Sahrens 
8715094Slling 	if (prop == ZPROP_INVAL) {
8724849Sahrens 		if (!zfs_prop_user(zc->zc_value))
8734849Sahrens 			return (EINVAL);
8744849Sahrens 		return (zfs_secpolicy_write_perms(zc->zc_name,
8754849Sahrens 		    ZFS_DELEG_PERM_USERPROP, cr));
8764849Sahrens 	} else {
87711022STom.Erickson@Sun.COM 		return (zfs_secpolicy_setprop(zc->zc_name, prop,
87811022STom.Erickson@Sun.COM 		    NULL, cr));
8794849Sahrens 	}
8804849Sahrens }
8814849Sahrens 
8829396SMatthew.Ahrens@Sun.COM static int
8839396SMatthew.Ahrens@Sun.COM zfs_secpolicy_userspace_one(zfs_cmd_t *zc, cred_t *cr)
8849396SMatthew.Ahrens@Sun.COM {
8859396SMatthew.Ahrens@Sun.COM 	int err = zfs_secpolicy_read(zc, cr);
8869396SMatthew.Ahrens@Sun.COM 	if (err)
8879396SMatthew.Ahrens@Sun.COM 		return (err);
8889396SMatthew.Ahrens@Sun.COM 
8899396SMatthew.Ahrens@Sun.COM 	if (zc->zc_objset_type >= ZFS_NUM_USERQUOTA_PROPS)
8909396SMatthew.Ahrens@Sun.COM 		return (EINVAL);
8919396SMatthew.Ahrens@Sun.COM 
8929396SMatthew.Ahrens@Sun.COM 	if (zc->zc_value[0] == 0) {
8939396SMatthew.Ahrens@Sun.COM 		/*
8949396SMatthew.Ahrens@Sun.COM 		 * They are asking about a posix uid/gid.  If it's
8959396SMatthew.Ahrens@Sun.COM 		 * themself, allow it.
8969396SMatthew.Ahrens@Sun.COM 		 */
8979396SMatthew.Ahrens@Sun.COM 		if (zc->zc_objset_type == ZFS_PROP_USERUSED ||
8989396SMatthew.Ahrens@Sun.COM 		    zc->zc_objset_type == ZFS_PROP_USERQUOTA) {
8999396SMatthew.Ahrens@Sun.COM 			if (zc->zc_guid == crgetuid(cr))
9009396SMatthew.Ahrens@Sun.COM 				return (0);
9019396SMatthew.Ahrens@Sun.COM 		} else {
9029396SMatthew.Ahrens@Sun.COM 			if (groupmember(zc->zc_guid, cr))
9039396SMatthew.Ahrens@Sun.COM 				return (0);
9049396SMatthew.Ahrens@Sun.COM 		}
9059396SMatthew.Ahrens@Sun.COM 	}
9069396SMatthew.Ahrens@Sun.COM 
9079396SMatthew.Ahrens@Sun.COM 	return (zfs_secpolicy_write_perms(zc->zc_name,
9089396SMatthew.Ahrens@Sun.COM 	    userquota_perms[zc->zc_objset_type], cr));
9099396SMatthew.Ahrens@Sun.COM }
9109396SMatthew.Ahrens@Sun.COM 
9119396SMatthew.Ahrens@Sun.COM static int
9129396SMatthew.Ahrens@Sun.COM zfs_secpolicy_userspace_many(zfs_cmd_t *zc, cred_t *cr)
9139396SMatthew.Ahrens@Sun.COM {
9149396SMatthew.Ahrens@Sun.COM 	int err = zfs_secpolicy_read(zc, cr);
9159396SMatthew.Ahrens@Sun.COM 	if (err)
9169396SMatthew.Ahrens@Sun.COM 		return (err);
9179396SMatthew.Ahrens@Sun.COM 
9189396SMatthew.Ahrens@Sun.COM 	if (zc->zc_objset_type >= ZFS_NUM_USERQUOTA_PROPS)
9199396SMatthew.Ahrens@Sun.COM 		return (EINVAL);
9209396SMatthew.Ahrens@Sun.COM 
9219396SMatthew.Ahrens@Sun.COM 	return (zfs_secpolicy_write_perms(zc->zc_name,
9229396SMatthew.Ahrens@Sun.COM 	    userquota_perms[zc->zc_objset_type], cr));
9239396SMatthew.Ahrens@Sun.COM }
9249396SMatthew.Ahrens@Sun.COM 
9259396SMatthew.Ahrens@Sun.COM static int
9269396SMatthew.Ahrens@Sun.COM zfs_secpolicy_userspace_upgrade(zfs_cmd_t *zc, cred_t *cr)
9279396SMatthew.Ahrens@Sun.COM {
92811022STom.Erickson@Sun.COM 	return (zfs_secpolicy_setprop(zc->zc_name, ZFS_PROP_VERSION,
92911022STom.Erickson@Sun.COM 	    NULL, cr));
9309396SMatthew.Ahrens@Sun.COM }
9319396SMatthew.Ahrens@Sun.COM 
93210242Schris.kirby@sun.com static int
93310242Schris.kirby@sun.com zfs_secpolicy_hold(zfs_cmd_t *zc, cred_t *cr)
93410242Schris.kirby@sun.com {
93510242Schris.kirby@sun.com 	return (zfs_secpolicy_write_perms(zc->zc_name,
93610242Schris.kirby@sun.com 	    ZFS_DELEG_PERM_HOLD, cr));
93710242Schris.kirby@sun.com }
93810242Schris.kirby@sun.com 
93910242Schris.kirby@sun.com static int
94010242Schris.kirby@sun.com zfs_secpolicy_release(zfs_cmd_t *zc, cred_t *cr)
94110242Schris.kirby@sun.com {
94210242Schris.kirby@sun.com 	return (zfs_secpolicy_write_perms(zc->zc_name,
94310242Schris.kirby@sun.com 	    ZFS_DELEG_PERM_RELEASE, cr));
94410242Schris.kirby@sun.com }
94510242Schris.kirby@sun.com 
9461544Seschrock /*
947789Sahrens  * Returns the nvlist as specified by the user in the zfs_cmd_t.
948789Sahrens  */
949789Sahrens static int
9509643SEric.Taylor@Sun.COM get_nvlist(uint64_t nvl, uint64_t size, int iflag, nvlist_t **nvp)
951789Sahrens {
952789Sahrens 	char *packed;
953789Sahrens 	int error;
9545094Slling 	nvlist_t *list = NULL;
955789Sahrens 
956789Sahrens 	/*
9572676Seschrock 	 * Read in and unpack the user-supplied nvlist.
958789Sahrens 	 */
9595094Slling 	if (size == 0)
960789Sahrens 		return (EINVAL);
961789Sahrens 
962789Sahrens 	packed = kmem_alloc(size, KM_SLEEP);
963789Sahrens 
9649643SEric.Taylor@Sun.COM 	if ((error = ddi_copyin((void *)(uintptr_t)nvl, packed, size,
9659643SEric.Taylor@Sun.COM 	    iflag)) != 0) {
966789Sahrens 		kmem_free(packed, size);
967789Sahrens 		return (error);
968789Sahrens 	}
969789Sahrens 
9705094Slling 	if ((error = nvlist_unpack(packed, size, &list, 0)) != 0) {
971789Sahrens 		kmem_free(packed, size);
972789Sahrens 		return (error);
973789Sahrens 	}
974789Sahrens 
975789Sahrens 	kmem_free(packed, size);
976789Sahrens 
9775094Slling 	*nvp = list;
978789Sahrens 	return (0);
979789Sahrens }
980789Sahrens 
981789Sahrens static int
98211022STom.Erickson@Sun.COM fit_error_list(zfs_cmd_t *zc, nvlist_t **errors)
98311022STom.Erickson@Sun.COM {
98411022STom.Erickson@Sun.COM 	size_t size;
98511022STom.Erickson@Sun.COM 
98611022STom.Erickson@Sun.COM 	VERIFY(nvlist_size(*errors, &size, NV_ENCODE_NATIVE) == 0);
98711022STom.Erickson@Sun.COM 
98811022STom.Erickson@Sun.COM 	if (size > zc->zc_nvlist_dst_size) {
98911022STom.Erickson@Sun.COM 		nvpair_t *more_errors;
99011022STom.Erickson@Sun.COM 		int n = 0;
99111022STom.Erickson@Sun.COM 
99211022STom.Erickson@Sun.COM 		if (zc->zc_nvlist_dst_size < 1024)
99311022STom.Erickson@Sun.COM 			return (ENOMEM);
99411022STom.Erickson@Sun.COM 
99511022STom.Erickson@Sun.COM 		VERIFY(nvlist_add_int32(*errors, ZPROP_N_MORE_ERRORS, 0) == 0);
99611022STom.Erickson@Sun.COM 		more_errors = nvlist_prev_nvpair(*errors, NULL);
99711022STom.Erickson@Sun.COM 
99811022STom.Erickson@Sun.COM 		do {
99911022STom.Erickson@Sun.COM 			nvpair_t *pair = nvlist_prev_nvpair(*errors,
100011022STom.Erickson@Sun.COM 			    more_errors);
100111022STom.Erickson@Sun.COM 			VERIFY(nvlist_remove_nvpair(*errors, pair) == 0);
100211022STom.Erickson@Sun.COM 			n++;
100311022STom.Erickson@Sun.COM 			VERIFY(nvlist_size(*errors, &size,
100411022STom.Erickson@Sun.COM 			    NV_ENCODE_NATIVE) == 0);
100511022STom.Erickson@Sun.COM 		} while (size > zc->zc_nvlist_dst_size);
100611022STom.Erickson@Sun.COM 
100711022STom.Erickson@Sun.COM 		VERIFY(nvlist_remove_nvpair(*errors, more_errors) == 0);
100811022STom.Erickson@Sun.COM 		VERIFY(nvlist_add_int32(*errors, ZPROP_N_MORE_ERRORS, n) == 0);
100911022STom.Erickson@Sun.COM 		ASSERT(nvlist_size(*errors, &size, NV_ENCODE_NATIVE) == 0);
101011022STom.Erickson@Sun.COM 		ASSERT(size <= zc->zc_nvlist_dst_size);
101111022STom.Erickson@Sun.COM 	}
101211022STom.Erickson@Sun.COM 
101311022STom.Erickson@Sun.COM 	return (0);
101411022STom.Erickson@Sun.COM }
101511022STom.Erickson@Sun.COM 
101611022STom.Erickson@Sun.COM static int
10172676Seschrock put_nvlist(zfs_cmd_t *zc, nvlist_t *nvl)
10182676Seschrock {
10192676Seschrock 	char *packed = NULL;
102011807SSam.Falkner@Sun.COM 	int error = 0;
10212676Seschrock 	size_t size;
10222676Seschrock 
10232676Seschrock 	VERIFY(nvlist_size(nvl, &size, NV_ENCODE_NATIVE) == 0);
10242676Seschrock 
10252676Seschrock 	if (size > zc->zc_nvlist_dst_size) {
10262676Seschrock 		error = ENOMEM;
10272676Seschrock 	} else {
10284611Smarks 		packed = kmem_alloc(size, KM_SLEEP);
10292676Seschrock 		VERIFY(nvlist_pack(nvl, &packed, &size, NV_ENCODE_NATIVE,
10302676Seschrock 		    KM_SLEEP) == 0);
103111807SSam.Falkner@Sun.COM 		if (ddi_copyout(packed, (void *)(uintptr_t)zc->zc_nvlist_dst,
103211807SSam.Falkner@Sun.COM 		    size, zc->zc_iflags) != 0)
103311807SSam.Falkner@Sun.COM 			error = EFAULT;
10342676Seschrock 		kmem_free(packed, size);
10352676Seschrock 	}
10362676Seschrock 
10372676Seschrock 	zc->zc_nvlist_dst_size = size;
10382676Seschrock 	return (error);
10392676Seschrock }
10402676Seschrock 
10412676Seschrock static int
104211185SSean.McEnroe@Sun.COM getzfsvfs(const char *dsname, zfsvfs_t **zfvp)
10439396SMatthew.Ahrens@Sun.COM {
10449396SMatthew.Ahrens@Sun.COM 	objset_t *os;
10459396SMatthew.Ahrens@Sun.COM 	int error;
10469396SMatthew.Ahrens@Sun.COM 
104710298SMatthew.Ahrens@Sun.COM 	error = dmu_objset_hold(dsname, FTAG, &os);
10489396SMatthew.Ahrens@Sun.COM 	if (error)
10499396SMatthew.Ahrens@Sun.COM 		return (error);
105010298SMatthew.Ahrens@Sun.COM 	if (dmu_objset_type(os) != DMU_OST_ZFS) {
105110298SMatthew.Ahrens@Sun.COM 		dmu_objset_rele(os, FTAG);
105210298SMatthew.Ahrens@Sun.COM 		return (EINVAL);
105310298SMatthew.Ahrens@Sun.COM 	}
105410298SMatthew.Ahrens@Sun.COM 
105510298SMatthew.Ahrens@Sun.COM 	mutex_enter(&os->os_user_ptr_lock);
105611185SSean.McEnroe@Sun.COM 	*zfvp = dmu_objset_get_user(os);
105711185SSean.McEnroe@Sun.COM 	if (*zfvp) {
105811185SSean.McEnroe@Sun.COM 		VFS_HOLD((*zfvp)->z_vfs);
10599396SMatthew.Ahrens@Sun.COM 	} else {
10609396SMatthew.Ahrens@Sun.COM 		error = ESRCH;
10619396SMatthew.Ahrens@Sun.COM 	}
106210298SMatthew.Ahrens@Sun.COM 	mutex_exit(&os->os_user_ptr_lock);
106310298SMatthew.Ahrens@Sun.COM 	dmu_objset_rele(os, FTAG);
10649396SMatthew.Ahrens@Sun.COM 	return (error);
10659396SMatthew.Ahrens@Sun.COM }
10669396SMatthew.Ahrens@Sun.COM 
10679396SMatthew.Ahrens@Sun.COM /*
10689396SMatthew.Ahrens@Sun.COM  * Find a zfsvfs_t for a mounted filesystem, or create our own, in which
10699396SMatthew.Ahrens@Sun.COM  * case its z_vfs will be NULL, and it will be opened as the owner.
10709396SMatthew.Ahrens@Sun.COM  */
10719396SMatthew.Ahrens@Sun.COM static int
107212620SMark.Shellenbaum@Oracle.COM zfsvfs_hold(const char *name, void *tag, zfsvfs_t **zfvp, boolean_t writer)
10739396SMatthew.Ahrens@Sun.COM {
10749396SMatthew.Ahrens@Sun.COM 	int error = 0;
10759396SMatthew.Ahrens@Sun.COM 
107611185SSean.McEnroe@Sun.COM 	if (getzfsvfs(name, zfvp) != 0)
107711185SSean.McEnroe@Sun.COM 		error = zfsvfs_create(name, zfvp);
10789396SMatthew.Ahrens@Sun.COM 	if (error == 0) {
107912620SMark.Shellenbaum@Oracle.COM 		rrw_enter(&(*zfvp)->z_teardown_lock, (writer) ? RW_WRITER :
108012620SMark.Shellenbaum@Oracle.COM 		    RW_READER, tag);
108111185SSean.McEnroe@Sun.COM 		if ((*zfvp)->z_unmounted) {
10829396SMatthew.Ahrens@Sun.COM 			/*
10839396SMatthew.Ahrens@Sun.COM 			 * XXX we could probably try again, since the unmounting
10849396SMatthew.Ahrens@Sun.COM 			 * thread should be just about to disassociate the
10859396SMatthew.Ahrens@Sun.COM 			 * objset from the zfsvfs.
10869396SMatthew.Ahrens@Sun.COM 			 */
108711185SSean.McEnroe@Sun.COM 			rrw_exit(&(*zfvp)->z_teardown_lock, tag);
10889396SMatthew.Ahrens@Sun.COM 			return (EBUSY);
10899396SMatthew.Ahrens@Sun.COM 		}
10909396SMatthew.Ahrens@Sun.COM 	}
10919396SMatthew.Ahrens@Sun.COM 	return (error);
10929396SMatthew.Ahrens@Sun.COM }
10939396SMatthew.Ahrens@Sun.COM 
10949396SMatthew.Ahrens@Sun.COM static void
10959396SMatthew.Ahrens@Sun.COM zfsvfs_rele(zfsvfs_t *zfsvfs, void *tag)
10969396SMatthew.Ahrens@Sun.COM {
10979396SMatthew.Ahrens@Sun.COM 	rrw_exit(&zfsvfs->z_teardown_lock, tag);
10989396SMatthew.Ahrens@Sun.COM 
10999396SMatthew.Ahrens@Sun.COM 	if (zfsvfs->z_vfs) {
11009396SMatthew.Ahrens@Sun.COM 		VFS_RELE(zfsvfs->z_vfs);
11019396SMatthew.Ahrens@Sun.COM 	} else {
110210298SMatthew.Ahrens@Sun.COM 		dmu_objset_disown(zfsvfs->z_os, zfsvfs);
11039396SMatthew.Ahrens@Sun.COM 		zfsvfs_free(zfsvfs);
11049396SMatthew.Ahrens@Sun.COM 	}
11059396SMatthew.Ahrens@Sun.COM }
11069396SMatthew.Ahrens@Sun.COM 
11079396SMatthew.Ahrens@Sun.COM static int
1108789Sahrens zfs_ioc_pool_create(zfs_cmd_t *zc)
1109789Sahrens {
1110789Sahrens 	int error;
11115094Slling 	nvlist_t *config, *props = NULL;
11127184Stimh 	nvlist_t *rootprops = NULL;
11137184Stimh 	nvlist_t *zplprops = NULL;
11144715Sek110237 	char *buf;
1115789Sahrens 
11165094Slling 	if (error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size,
11179643SEric.Taylor@Sun.COM 	    zc->zc_iflags, &config))
11184988Sek110237 		return (error);
11194715Sek110237 
11205094Slling 	if (zc->zc_nvlist_src_size != 0 && (error =
11219643SEric.Taylor@Sun.COM 	    get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
11229643SEric.Taylor@Sun.COM 	    zc->zc_iflags, &props))) {
11235094Slling 		nvlist_free(config);
11245094Slling 		return (error);
11255094Slling 	}
11265094Slling 
11277184Stimh 	if (props) {
11287184Stimh 		nvlist_t *nvl = NULL;
11297184Stimh 		uint64_t version = SPA_VERSION;
11307184Stimh 
11317184Stimh 		(void) nvlist_lookup_uint64(props,
11327184Stimh 		    zpool_prop_to_name(ZPOOL_PROP_VERSION), &version);
11337184Stimh 		if (version < SPA_VERSION_INITIAL || version > SPA_VERSION) {
11347184Stimh 			error = EINVAL;
11357184Stimh 			goto pool_props_bad;
11367184Stimh 		}
11377184Stimh 		(void) nvlist_lookup_nvlist(props, ZPOOL_ROOTFS_PROPS, &nvl);
11387184Stimh 		if (nvl) {
11397184Stimh 			error = nvlist_dup(nvl, &rootprops, KM_SLEEP);
11407184Stimh 			if (error != 0) {
11417184Stimh 				nvlist_free(config);
11427184Stimh 				nvlist_free(props);
11437184Stimh 				return (error);
11447184Stimh 			}
11457184Stimh 			(void) nvlist_remove_all(props, ZPOOL_ROOTFS_PROPS);
11467184Stimh 		}
11477184Stimh 		VERIFY(nvlist_alloc(&zplprops, NV_UNIQUE_NAME, KM_SLEEP) == 0);
11487184Stimh 		error = zfs_fill_zplprops_root(version, rootprops,
11497184Stimh 		    zplprops, NULL);
11507184Stimh 		if (error)
11517184Stimh 			goto pool_props_bad;
11527184Stimh 	}
11537184Stimh 
11544988Sek110237 	buf = history_str_get(zc);
1155789Sahrens 
11567184Stimh 	error = spa_create(zc->zc_name, config, props, buf, zplprops);
11577184Stimh 
11587184Stimh 	/*
11597184Stimh 	 * Set the remaining root properties
11607184Stimh 	 */
116111022STom.Erickson@Sun.COM 	if (!error && (error = zfs_set_prop_nvlist(zc->zc_name,
116211022STom.Erickson@Sun.COM 	    ZPROP_SRC_LOCAL, rootprops, NULL)) != 0)
11637184Stimh 		(void) spa_destroy(zc->zc_name);
1164789Sahrens 
11654988Sek110237 	if (buf != NULL)
11664988Sek110237 		history_str_free(buf);
11675094Slling 
11687184Stimh pool_props_bad:
11697184Stimh 	nvlist_free(rootprops);
11707184Stimh 	nvlist_free(zplprops);
1171789Sahrens 	nvlist_free(config);
11727184Stimh 	nvlist_free(props);
11735094Slling 
1174789Sahrens 	return (error);
1175789Sahrens }
1176789Sahrens 
1177789Sahrens static int
1178789Sahrens zfs_ioc_pool_destroy(zfs_cmd_t *zc)
1179789Sahrens {
11804543Smarks 	int error;
11814543Smarks 	zfs_log_history(zc);
11824543Smarks 	error = spa_destroy(zc->zc_name);
118310588SEric.Taylor@Sun.COM 	if (error == 0)
118410588SEric.Taylor@Sun.COM 		zvol_remove_minors(zc->zc_name);
11854543Smarks 	return (error);
1186789Sahrens }
1187789Sahrens 
1188789Sahrens static int
1189789Sahrens zfs_ioc_pool_import(zfs_cmd_t *zc)
1190789Sahrens {
11915094Slling 	nvlist_t *config, *props = NULL;
1192789Sahrens 	uint64_t guid;
119310921STim.Haley@Sun.COM 	int error;
1194789Sahrens 
11955094Slling 	if ((error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size,
11969643SEric.Taylor@Sun.COM 	    zc->zc_iflags, &config)) != 0)
1197789Sahrens 		return (error);
1198789Sahrens 
11995094Slling 	if (zc->zc_nvlist_src_size != 0 && (error =
12009643SEric.Taylor@Sun.COM 	    get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
12019643SEric.Taylor@Sun.COM 	    zc->zc_iflags, &props))) {
12025094Slling 		nvlist_free(config);
12035094Slling 		return (error);
12045094Slling 	}
12055094Slling 
1206789Sahrens 	if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID, &guid) != 0 ||
12071544Seschrock 	    guid != zc->zc_guid)
1208789Sahrens 		error = EINVAL;
1209789Sahrens 	else
1210*12949SGeorge.Wilson@Sun.COM 		error = spa_import(zc->zc_name, config, props, zc->zc_cookie);
1211*12949SGeorge.Wilson@Sun.COM 
1212*12949SGeorge.Wilson@Sun.COM 	if (zc->zc_nvlist_dst != 0) {
1213*12949SGeorge.Wilson@Sun.COM 		int err;
1214*12949SGeorge.Wilson@Sun.COM 
1215*12949SGeorge.Wilson@Sun.COM 		if ((err = put_nvlist(zc, config)) != 0)
1216*12949SGeorge.Wilson@Sun.COM 			error = err;
1217*12949SGeorge.Wilson@Sun.COM 	}
121810921STim.Haley@Sun.COM 
1219789Sahrens 	nvlist_free(config);
1220789Sahrens 
12215094Slling 	if (props)
12225094Slling 		nvlist_free(props);
12235094Slling 
1224789Sahrens 	return (error);
1225789Sahrens }
1226789Sahrens 
1227789Sahrens static int
1228789Sahrens zfs_ioc_pool_export(zfs_cmd_t *zc)
1229789Sahrens {
12304543Smarks 	int error;
12317214Slling 	boolean_t force = (boolean_t)zc->zc_cookie;
12328211SGeorge.Wilson@Sun.COM 	boolean_t hardforce = (boolean_t)zc->zc_guid;
12337214Slling 
12344543Smarks 	zfs_log_history(zc);
12358211SGeorge.Wilson@Sun.COM 	error = spa_export(zc->zc_name, NULL, force, hardforce);
123610588SEric.Taylor@Sun.COM 	if (error == 0)
123710588SEric.Taylor@Sun.COM 		zvol_remove_minors(zc->zc_name);
12384543Smarks 	return (error);
1239789Sahrens }
1240789Sahrens 
1241789Sahrens static int
1242789Sahrens zfs_ioc_pool_configs(zfs_cmd_t *zc)
1243789Sahrens {
1244789Sahrens 	nvlist_t *configs;
1245789Sahrens 	int error;
1246789Sahrens 
1247789Sahrens 	if ((configs = spa_all_configs(&zc->zc_cookie)) == NULL)
1248789Sahrens 		return (EEXIST);
1249789Sahrens 
12502676Seschrock 	error = put_nvlist(zc, configs);
1251789Sahrens 
1252789Sahrens 	nvlist_free(configs);
1253789Sahrens 
1254789Sahrens 	return (error);
1255789Sahrens }
1256789Sahrens 
1257789Sahrens static int
1258789Sahrens zfs_ioc_pool_stats(zfs_cmd_t *zc)
1259789Sahrens {
1260789Sahrens 	nvlist_t *config;
1261789Sahrens 	int error;
12621544Seschrock 	int ret = 0;
1263789Sahrens 
12642676Seschrock 	error = spa_get_stats(zc->zc_name, &config, zc->zc_value,
12652676Seschrock 	    sizeof (zc->zc_value));
1266789Sahrens 
1267789Sahrens 	if (config != NULL) {
12682676Seschrock 		ret = put_nvlist(zc, config);
1269789Sahrens 		nvlist_free(config);
12701544Seschrock 
12711544Seschrock 		/*
12721544Seschrock 		 * The config may be present even if 'error' is non-zero.
12731544Seschrock 		 * In this case we return success, and preserve the real errno
12741544Seschrock 		 * in 'zc_cookie'.
12751544Seschrock 		 */
12761544Seschrock 		zc->zc_cookie = error;
1277789Sahrens 	} else {
12781544Seschrock 		ret = error;
1279789Sahrens 	}
1280789Sahrens 
12811544Seschrock 	return (ret);
1282789Sahrens }
1283789Sahrens 
1284789Sahrens /*
1285789Sahrens  * Try to import the given pool, returning pool stats as appropriate so that
1286789Sahrens  * user land knows which devices are available and overall pool health.
1287789Sahrens  */
1288789Sahrens static int
1289789Sahrens zfs_ioc_pool_tryimport(zfs_cmd_t *zc)
1290789Sahrens {
1291789Sahrens 	nvlist_t *tryconfig, *config;
1292789Sahrens 	int error;
1293789Sahrens 
12945094Slling 	if ((error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size,
12959643SEric.Taylor@Sun.COM 	    zc->zc_iflags, &tryconfig)) != 0)
1296789Sahrens 		return (error);
1297789Sahrens 
1298789Sahrens 	config = spa_tryimport(tryconfig);
1299789Sahrens 
1300789Sahrens 	nvlist_free(tryconfig);
1301789Sahrens 
1302789Sahrens 	if (config == NULL)
1303789Sahrens 		return (EINVAL);
1304789Sahrens 
13052676Seschrock 	error = put_nvlist(zc, config);
1306789Sahrens 	nvlist_free(config);
1307789Sahrens 
1308789Sahrens 	return (error);
1309789Sahrens }
1310789Sahrens 
131112296SLin.Ling@Sun.COM /*
131212296SLin.Ling@Sun.COM  * inputs:
131312296SLin.Ling@Sun.COM  * zc_name              name of the pool
131412296SLin.Ling@Sun.COM  * zc_cookie            scan func (pool_scan_func_t)
131512296SLin.Ling@Sun.COM  */
1316789Sahrens static int
131712296SLin.Ling@Sun.COM zfs_ioc_pool_scan(zfs_cmd_t *zc)
1318789Sahrens {
1319789Sahrens 	spa_t *spa;
1320789Sahrens 	int error;
1321789Sahrens 
13222926Sek110237 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
13232926Sek110237 		return (error);
13242926Sek110237 
132512296SLin.Ling@Sun.COM 	if (zc->zc_cookie == POOL_SCAN_NONE)
132612296SLin.Ling@Sun.COM 		error = spa_scan_stop(spa);
132712296SLin.Ling@Sun.COM 	else
132812296SLin.Ling@Sun.COM 		error = spa_scan(spa, zc->zc_cookie);
13292926Sek110237 
13302926Sek110237 	spa_close(spa, FTAG);
13312926Sek110237 
1332789Sahrens 	return (error);
1333789Sahrens }
1334789Sahrens 
1335789Sahrens static int
1336789Sahrens zfs_ioc_pool_freeze(zfs_cmd_t *zc)
1337789Sahrens {
1338789Sahrens 	spa_t *spa;
1339789Sahrens 	int error;
1340789Sahrens 
1341789Sahrens 	error = spa_open(zc->zc_name, &spa, FTAG);
1342789Sahrens 	if (error == 0) {
1343789Sahrens 		spa_freeze(spa);
1344789Sahrens 		spa_close(spa, FTAG);
1345789Sahrens 	}
1346789Sahrens 	return (error);
1347789Sahrens }
1348789Sahrens 
1349789Sahrens static int
13501760Seschrock zfs_ioc_pool_upgrade(zfs_cmd_t *zc)
13511760Seschrock {
13521760Seschrock 	spa_t *spa;
13531760Seschrock 	int error;
13541760Seschrock 
13552926Sek110237 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
13562926Sek110237 		return (error);
13572926Sek110237 
13585118Slling 	if (zc->zc_cookie < spa_version(spa) || zc->zc_cookie > SPA_VERSION) {
13595118Slling 		spa_close(spa, FTAG);
13605118Slling 		return (EINVAL);
13615118Slling 	}
13625118Slling 
13635094Slling 	spa_upgrade(spa, zc->zc_cookie);
13642926Sek110237 	spa_close(spa, FTAG);
13652926Sek110237 
13662926Sek110237 	return (error);
13672926Sek110237 }
13682926Sek110237 
13692926Sek110237 static int
13702926Sek110237 zfs_ioc_pool_get_history(zfs_cmd_t *zc)
13712926Sek110237 {
13722926Sek110237 	spa_t *spa;
13732926Sek110237 	char *hist_buf;
13742926Sek110237 	uint64_t size;
13752926Sek110237 	int error;
13762926Sek110237 
13772926Sek110237 	if ((size = zc->zc_history_len) == 0)
13782926Sek110237 		return (EINVAL);
13792926Sek110237 
13802926Sek110237 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
13812926Sek110237 		return (error);
13822926Sek110237 
13834577Sahrens 	if (spa_version(spa) < SPA_VERSION_ZPOOL_HISTORY) {
13843863Sek110237 		spa_close(spa, FTAG);
13853863Sek110237 		return (ENOTSUP);
13863863Sek110237 	}
13873863Sek110237 
13882926Sek110237 	hist_buf = kmem_alloc(size, KM_SLEEP);
13892926Sek110237 	if ((error = spa_history_get(spa, &zc->zc_history_offset,
13902926Sek110237 	    &zc->zc_history_len, hist_buf)) == 0) {
13919643SEric.Taylor@Sun.COM 		error = ddi_copyout(hist_buf,
13929643SEric.Taylor@Sun.COM 		    (void *)(uintptr_t)zc->zc_history,
13939643SEric.Taylor@Sun.COM 		    zc->zc_history_len, zc->zc_iflags);
13942926Sek110237 	}
13952926Sek110237 
13962926Sek110237 	spa_close(spa, FTAG);
13972926Sek110237 	kmem_free(hist_buf, size);
13982926Sek110237 	return (error);
13992926Sek110237 }
14002926Sek110237 
14012926Sek110237 static int
14023444Sek110237 zfs_ioc_dsobj_to_dsname(zfs_cmd_t *zc)
14033444Sek110237 {
14043444Sek110237 	int error;
14053444Sek110237 
14063912Slling 	if (error = dsl_dsobj_to_dsname(zc->zc_name, zc->zc_obj, zc->zc_value))
14073444Sek110237 		return (error);
14083444Sek110237 
14093444Sek110237 	return (0);
14103444Sek110237 }
14113444Sek110237 
141210298SMatthew.Ahrens@Sun.COM /*
141310298SMatthew.Ahrens@Sun.COM  * inputs:
141410298SMatthew.Ahrens@Sun.COM  * zc_name		name of filesystem
141510298SMatthew.Ahrens@Sun.COM  * zc_obj		object to find
141610298SMatthew.Ahrens@Sun.COM  *
141710298SMatthew.Ahrens@Sun.COM  * outputs:
141810298SMatthew.Ahrens@Sun.COM  * zc_value		name of object
141910298SMatthew.Ahrens@Sun.COM  */
14203444Sek110237 static int
14213444Sek110237 zfs_ioc_obj_to_path(zfs_cmd_t *zc)
14223444Sek110237 {
142310298SMatthew.Ahrens@Sun.COM 	objset_t *os;
14243444Sek110237 	int error;
14253444Sek110237 
142610298SMatthew.Ahrens@Sun.COM 	/* XXX reading from objset not owned */
142710298SMatthew.Ahrens@Sun.COM 	if ((error = dmu_objset_hold(zc->zc_name, FTAG, &os)) != 0)
14283444Sek110237 		return (error);
142910298SMatthew.Ahrens@Sun.COM 	if (dmu_objset_type(os) != DMU_OST_ZFS) {
143010298SMatthew.Ahrens@Sun.COM 		dmu_objset_rele(os, FTAG);
143110298SMatthew.Ahrens@Sun.COM 		return (EINVAL);
143210298SMatthew.Ahrens@Sun.COM 	}
143310298SMatthew.Ahrens@Sun.COM 	error = zfs_obj_to_path(os, zc->zc_obj, zc->zc_value,
14343444Sek110237 	    sizeof (zc->zc_value));
143510298SMatthew.Ahrens@Sun.COM 	dmu_objset_rele(os, FTAG);
14363444Sek110237 
14373444Sek110237 	return (error);
14383444Sek110237 }
14393444Sek110237 
14403444Sek110237 static int
1441789Sahrens zfs_ioc_vdev_add(zfs_cmd_t *zc)
1442789Sahrens {
1443789Sahrens 	spa_t *spa;
1444789Sahrens 	int error;
14456423Sgw25295 	nvlist_t *config, **l2cache, **spares;
14466423Sgw25295 	uint_t nl2cache = 0, nspares = 0;
1447789Sahrens 
1448789Sahrens 	error = spa_open(zc->zc_name, &spa, FTAG);
1449789Sahrens 	if (error != 0)
1450789Sahrens 		return (error);
1451789Sahrens 
14525450Sbrendan 	error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size,
14539643SEric.Taylor@Sun.COM 	    zc->zc_iflags, &config);
14545450Sbrendan 	(void) nvlist_lookup_nvlist_array(config, ZPOOL_CONFIG_L2CACHE,
14555450Sbrendan 	    &l2cache, &nl2cache);
14565450Sbrendan 
14576423Sgw25295 	(void) nvlist_lookup_nvlist_array(config, ZPOOL_CONFIG_SPARES,
14586423Sgw25295 	    &spares, &nspares);
14596423Sgw25295 
14603912Slling 	/*
14613912Slling 	 * A root pool with concatenated devices is not supported.
14626423Sgw25295 	 * Thus, can not add a device to a root pool.
14636423Sgw25295 	 *
14646423Sgw25295 	 * Intent log device can not be added to a rootpool because
14656423Sgw25295 	 * during mountroot, zil is replayed, a seperated log device
14666423Sgw25295 	 * can not be accessed during the mountroot time.
14676423Sgw25295 	 *
14686423Sgw25295 	 * l2cache and spare devices are ok to be added to a rootpool.
14693912Slling 	 */
147010922SJeff.Bonwick@Sun.COM 	if (spa_bootfs(spa) != 0 && nl2cache == 0 && nspares == 0) {
147111422SMark.Musante@Sun.COM 		nvlist_free(config);
14723912Slling 		spa_close(spa, FTAG);
14733912Slling 		return (EDOM);
14743912Slling 	}
14753912Slling 
14765450Sbrendan 	if (error == 0) {
1477789Sahrens 		error = spa_vdev_add(spa, config);
1478789Sahrens 		nvlist_free(config);
1479789Sahrens 	}
1480789Sahrens 	spa_close(spa, FTAG);
1481789Sahrens 	return (error);
1482789Sahrens }
1483789Sahrens 
148412296SLin.Ling@Sun.COM /*
148512296SLin.Ling@Sun.COM  * inputs:
148612296SLin.Ling@Sun.COM  * zc_name		name of the pool
148712296SLin.Ling@Sun.COM  * zc_nvlist_conf	nvlist of devices to remove
148812296SLin.Ling@Sun.COM  * zc_cookie		to stop the remove?
148912296SLin.Ling@Sun.COM  */
1490789Sahrens static int
1491789Sahrens zfs_ioc_vdev_remove(zfs_cmd_t *zc)
1492789Sahrens {
14932082Seschrock 	spa_t *spa;
14942082Seschrock 	int error;
14952082Seschrock 
14962082Seschrock 	error = spa_open(zc->zc_name, &spa, FTAG);
14972082Seschrock 	if (error != 0)
14982082Seschrock 		return (error);
14992082Seschrock 	error = spa_vdev_remove(spa, zc->zc_guid, B_FALSE);
15002082Seschrock 	spa_close(spa, FTAG);
15012082Seschrock 	return (error);
1502789Sahrens }
1503789Sahrens 
1504789Sahrens static int
15054451Seschrock zfs_ioc_vdev_set_state(zfs_cmd_t *zc)
1506789Sahrens {
1507789Sahrens 	spa_t *spa;
1508789Sahrens 	int error;
15094451Seschrock 	vdev_state_t newstate = VDEV_STATE_UNKNOWN;
1510789Sahrens 
15112926Sek110237 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
1512789Sahrens 		return (error);
15134451Seschrock 	switch (zc->zc_cookie) {
15144451Seschrock 	case VDEV_STATE_ONLINE:
15154451Seschrock 		error = vdev_online(spa, zc->zc_guid, zc->zc_obj, &newstate);
15164451Seschrock 		break;
15174451Seschrock 
15184451Seschrock 	case VDEV_STATE_OFFLINE:
15194451Seschrock 		error = vdev_offline(spa, zc->zc_guid, zc->zc_obj);
15204451Seschrock 		break;
1521789Sahrens 
15224451Seschrock 	case VDEV_STATE_FAULTED:
152310817SEric.Schrock@Sun.COM 		if (zc->zc_obj != VDEV_AUX_ERR_EXCEEDED &&
152410817SEric.Schrock@Sun.COM 		    zc->zc_obj != VDEV_AUX_EXTERNAL)
152510817SEric.Schrock@Sun.COM 			zc->zc_obj = VDEV_AUX_ERR_EXCEEDED;
152610817SEric.Schrock@Sun.COM 
152710817SEric.Schrock@Sun.COM 		error = vdev_fault(spa, zc->zc_guid, zc->zc_obj);
15284451Seschrock 		break;
1529789Sahrens 
15304451Seschrock 	case VDEV_STATE_DEGRADED:
153110817SEric.Schrock@Sun.COM 		if (zc->zc_obj != VDEV_AUX_ERR_EXCEEDED &&
153210817SEric.Schrock@Sun.COM 		    zc->zc_obj != VDEV_AUX_EXTERNAL)
153310817SEric.Schrock@Sun.COM 			zc->zc_obj = VDEV_AUX_ERR_EXCEEDED;
153410817SEric.Schrock@Sun.COM 
153510817SEric.Schrock@Sun.COM 		error = vdev_degrade(spa, zc->zc_guid, zc->zc_obj);
15364451Seschrock 		break;
15374451Seschrock 
15384451Seschrock 	default:
15394451Seschrock 		error = EINVAL;
15404451Seschrock 	}
15414451Seschrock 	zc->zc_cookie = newstate;
1542789Sahrens 	spa_close(spa, FTAG);
1543789Sahrens 	return (error);
1544789Sahrens }
1545789Sahrens 
1546789Sahrens static int
1547789Sahrens zfs_ioc_vdev_attach(zfs_cmd_t *zc)
1548789Sahrens {
1549789Sahrens 	spa_t *spa;
1550789Sahrens 	int replacing = zc->zc_cookie;
1551789Sahrens 	nvlist_t *config;
1552789Sahrens 	int error;
1553789Sahrens 
15542926Sek110237 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
1555789Sahrens 		return (error);
1556789Sahrens 
15575094Slling 	if ((error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size,
15589643SEric.Taylor@Sun.COM 	    zc->zc_iflags, &config)) == 0) {
15591544Seschrock 		error = spa_vdev_attach(spa, zc->zc_guid, config, replacing);
1560789Sahrens 		nvlist_free(config);
1561789Sahrens 	}
1562789Sahrens 
1563789Sahrens 	spa_close(spa, FTAG);
1564789Sahrens 	return (error);
1565789Sahrens }
1566789Sahrens 
1567789Sahrens static int
1568789Sahrens zfs_ioc_vdev_detach(zfs_cmd_t *zc)
1569789Sahrens {
1570789Sahrens 	spa_t *spa;
1571789Sahrens 	int error;
1572789Sahrens 
15732926Sek110237 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
1574789Sahrens 		return (error);
1575789Sahrens 
15768241SJeff.Bonwick@Sun.COM 	error = spa_vdev_detach(spa, zc->zc_guid, 0, B_FALSE);
1577789Sahrens 
1578789Sahrens 	spa_close(spa, FTAG);
1579789Sahrens 	return (error);
1580789Sahrens }
1581789Sahrens 
1582789Sahrens static int
158311422SMark.Musante@Sun.COM zfs_ioc_vdev_split(zfs_cmd_t *zc)
158411422SMark.Musante@Sun.COM {
158511422SMark.Musante@Sun.COM 	spa_t *spa;
158611422SMark.Musante@Sun.COM 	nvlist_t *config, *props = NULL;
158711422SMark.Musante@Sun.COM 	int error;
158811422SMark.Musante@Sun.COM 	boolean_t exp = !!(zc->zc_cookie & ZPOOL_EXPORT_AFTER_SPLIT);
158911422SMark.Musante@Sun.COM 
159011422SMark.Musante@Sun.COM 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
159111422SMark.Musante@Sun.COM 		return (error);
159211422SMark.Musante@Sun.COM 
159311422SMark.Musante@Sun.COM 	if (error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size,
159411422SMark.Musante@Sun.COM 	    zc->zc_iflags, &config)) {
159511422SMark.Musante@Sun.COM 		spa_close(spa, FTAG);
159611422SMark.Musante@Sun.COM 		return (error);
159711422SMark.Musante@Sun.COM 	}
159811422SMark.Musante@Sun.COM 
159911422SMark.Musante@Sun.COM 	if (zc->zc_nvlist_src_size != 0 && (error =
160011422SMark.Musante@Sun.COM 	    get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
160111422SMark.Musante@Sun.COM 	    zc->zc_iflags, &props))) {
160211422SMark.Musante@Sun.COM 		spa_close(spa, FTAG);
160311422SMark.Musante@Sun.COM 		nvlist_free(config);
160411422SMark.Musante@Sun.COM 		return (error);
160511422SMark.Musante@Sun.COM 	}
160611422SMark.Musante@Sun.COM 
160711422SMark.Musante@Sun.COM 	error = spa_vdev_split_mirror(spa, zc->zc_string, config, props, exp);
160811422SMark.Musante@Sun.COM 
160911422SMark.Musante@Sun.COM 	spa_close(spa, FTAG);
161011422SMark.Musante@Sun.COM 
161111422SMark.Musante@Sun.COM 	nvlist_free(config);
161211422SMark.Musante@Sun.COM 	nvlist_free(props);
161311422SMark.Musante@Sun.COM 
161411422SMark.Musante@Sun.COM 	return (error);
161511422SMark.Musante@Sun.COM }
161611422SMark.Musante@Sun.COM 
161711422SMark.Musante@Sun.COM static int
16181354Seschrock zfs_ioc_vdev_setpath(zfs_cmd_t *zc)
16191354Seschrock {
16201354Seschrock 	spa_t *spa;
16212676Seschrock 	char *path = zc->zc_value;
16221544Seschrock 	uint64_t guid = zc->zc_guid;
16231354Seschrock 	int error;
16241354Seschrock 
16251354Seschrock 	error = spa_open(zc->zc_name, &spa, FTAG);
16261354Seschrock 	if (error != 0)
16271354Seschrock 		return (error);
16281354Seschrock 
16291354Seschrock 	error = spa_vdev_setpath(spa, guid, path);
16301354Seschrock 	spa_close(spa, FTAG);
16311354Seschrock 	return (error);
16321354Seschrock }
16331354Seschrock 
16349425SEric.Schrock@Sun.COM static int
16359425SEric.Schrock@Sun.COM zfs_ioc_vdev_setfru(zfs_cmd_t *zc)
16369425SEric.Schrock@Sun.COM {
16379425SEric.Schrock@Sun.COM 	spa_t *spa;
16389425SEric.Schrock@Sun.COM 	char *fru = zc->zc_value;
16399425SEric.Schrock@Sun.COM 	uint64_t guid = zc->zc_guid;
16409425SEric.Schrock@Sun.COM 	int error;
16419425SEric.Schrock@Sun.COM 
16429425SEric.Schrock@Sun.COM 	error = spa_open(zc->zc_name, &spa, FTAG);
16439425SEric.Schrock@Sun.COM 	if (error != 0)
16449425SEric.Schrock@Sun.COM 		return (error);
16459425SEric.Schrock@Sun.COM 
16469425SEric.Schrock@Sun.COM 	error = spa_vdev_setfru(spa, guid, fru);
16479425SEric.Schrock@Sun.COM 	spa_close(spa, FTAG);
16489425SEric.Schrock@Sun.COM 	return (error);
16499425SEric.Schrock@Sun.COM }
16509425SEric.Schrock@Sun.COM 
16511354Seschrock static int
165212786SChris.Kirby@oracle.com zfs_ioc_objset_stats_impl(zfs_cmd_t *zc, objset_t *os)
1653789Sahrens {
165412786SChris.Kirby@oracle.com 	int error = 0;
16551356Seschrock 	nvlist_t *nv;
1656789Sahrens 
16572885Sahrens 	dmu_objset_fast_stat(os, &zc->zc_objset_stats);
1658789Sahrens 
16592856Snd150628 	if (zc->zc_nvlist_dst != 0 &&
166011022STom.Erickson@Sun.COM 	    (error = dsl_prop_get_all(os, &nv)) == 0) {
16612885Sahrens 		dmu_objset_stats(os, nv);
16623087Sahrens 		/*
16635147Srm160521 		 * NB: zvol_get_stats() will read the objset contents,
16643087Sahrens 		 * which we aren't supposed to do with a
16656689Smaybee 		 * DS_MODE_USER hold, because it could be
16663087Sahrens 		 * inconsistent.  So this is a bit of a workaround...
166710298SMatthew.Ahrens@Sun.COM 		 * XXX reading with out owning
16683087Sahrens 		 */
16694577Sahrens 		if (!zc->zc_objset_stats.dds_inconsistent) {
16704577Sahrens 			if (dmu_objset_type(os) == DMU_OST_ZVOL)
16714577Sahrens 				VERIFY(zvol_get_stats(os, nv) == 0);
16724577Sahrens 		}
16732676Seschrock 		error = put_nvlist(zc, nv);
16741356Seschrock 		nvlist_free(nv);
16751356Seschrock 	}
1676789Sahrens 
167712786SChris.Kirby@oracle.com 	return (error);
167812786SChris.Kirby@oracle.com }
167912786SChris.Kirby@oracle.com 
168012786SChris.Kirby@oracle.com /*
168112786SChris.Kirby@oracle.com  * inputs:
168212786SChris.Kirby@oracle.com  * zc_name		name of filesystem
168312786SChris.Kirby@oracle.com  * zc_nvlist_dst_size	size of buffer for property nvlist
168412786SChris.Kirby@oracle.com  *
168512786SChris.Kirby@oracle.com  * outputs:
168612786SChris.Kirby@oracle.com  * zc_objset_stats	stats
168712786SChris.Kirby@oracle.com  * zc_nvlist_dst	property nvlist
168812786SChris.Kirby@oracle.com  * zc_nvlist_dst_size	size of property nvlist
168912786SChris.Kirby@oracle.com  */
169012786SChris.Kirby@oracle.com static int
169112786SChris.Kirby@oracle.com zfs_ioc_objset_stats(zfs_cmd_t *zc)
169212786SChris.Kirby@oracle.com {
169312786SChris.Kirby@oracle.com 	objset_t *os = NULL;
169412786SChris.Kirby@oracle.com 	int error;
169512786SChris.Kirby@oracle.com 
169612786SChris.Kirby@oracle.com 	if (error = dmu_objset_hold(zc->zc_name, FTAG, &os))
169712786SChris.Kirby@oracle.com 		return (error);
169812786SChris.Kirby@oracle.com 
169912786SChris.Kirby@oracle.com 	error = zfs_ioc_objset_stats_impl(zc, os);
170012786SChris.Kirby@oracle.com 
170110298SMatthew.Ahrens@Sun.COM 	dmu_objset_rele(os, FTAG);
170212786SChris.Kirby@oracle.com 
1703789Sahrens 	return (error);
1704789Sahrens }
1705789Sahrens 
170611022STom.Erickson@Sun.COM /*
170711022STom.Erickson@Sun.COM  * inputs:
170811022STom.Erickson@Sun.COM  * zc_name		name of filesystem
170911022STom.Erickson@Sun.COM  * zc_nvlist_dst_size	size of buffer for property nvlist
171011022STom.Erickson@Sun.COM  *
171111022STom.Erickson@Sun.COM  * outputs:
171211022STom.Erickson@Sun.COM  * zc_nvlist_dst	received property nvlist
171311022STom.Erickson@Sun.COM  * zc_nvlist_dst_size	size of received property nvlist
171411022STom.Erickson@Sun.COM  *
171511022STom.Erickson@Sun.COM  * Gets received properties (distinct from local properties on or after
171611022STom.Erickson@Sun.COM  * SPA_VERSION_RECVD_PROPS) for callers who want to differentiate received from
171711022STom.Erickson@Sun.COM  * local property values.
171811022STom.Erickson@Sun.COM  */
171911022STom.Erickson@Sun.COM static int
172011022STom.Erickson@Sun.COM zfs_ioc_objset_recvd_props(zfs_cmd_t *zc)
172111022STom.Erickson@Sun.COM {
172211022STom.Erickson@Sun.COM 	objset_t *os = NULL;
172311022STom.Erickson@Sun.COM 	int error;
172411022STom.Erickson@Sun.COM 	nvlist_t *nv;
172511022STom.Erickson@Sun.COM 
172611022STom.Erickson@Sun.COM 	if (error = dmu_objset_hold(zc->zc_name, FTAG, &os))
172711022STom.Erickson@Sun.COM 		return (error);
172811022STom.Erickson@Sun.COM 
172911022STom.Erickson@Sun.COM 	/*
173011022STom.Erickson@Sun.COM 	 * Without this check, we would return local property values if the
173111022STom.Erickson@Sun.COM 	 * caller has not already received properties on or after
173211022STom.Erickson@Sun.COM 	 * SPA_VERSION_RECVD_PROPS.
173311022STom.Erickson@Sun.COM 	 */
173411022STom.Erickson@Sun.COM 	if (!dsl_prop_get_hasrecvd(os)) {
173511022STom.Erickson@Sun.COM 		dmu_objset_rele(os, FTAG);
173611022STom.Erickson@Sun.COM 		return (ENOTSUP);
173711022STom.Erickson@Sun.COM 	}
173811022STom.Erickson@Sun.COM 
173911022STom.Erickson@Sun.COM 	if (zc->zc_nvlist_dst != 0 &&
174011022STom.Erickson@Sun.COM 	    (error = dsl_prop_get_received(os, &nv)) == 0) {
174111022STom.Erickson@Sun.COM 		error = put_nvlist(zc, nv);
174211022STom.Erickson@Sun.COM 		nvlist_free(nv);
174311022STom.Erickson@Sun.COM 	}
174411022STom.Erickson@Sun.COM 
174511022STom.Erickson@Sun.COM 	dmu_objset_rele(os, FTAG);
174611022STom.Erickson@Sun.COM 	return (error);
174711022STom.Erickson@Sun.COM }
174811022STom.Erickson@Sun.COM 
17495498Stimh static int
17505498Stimh nvl_add_zplprop(objset_t *os, nvlist_t *props, zfs_prop_t prop)
17515498Stimh {
17525498Stimh 	uint64_t value;
17535498Stimh 	int error;
17545498Stimh 
17555498Stimh 	/*
17565498Stimh 	 * zfs_get_zplprop() will either find a value or give us
17575498Stimh 	 * the default value (if there is one).
17585498Stimh 	 */
17595498Stimh 	if ((error = zfs_get_zplprop(os, prop, &value)) != 0)
17605498Stimh 		return (error);
17615498Stimh 	VERIFY(nvlist_add_uint64(props, zfs_prop_to_name(prop), value) == 0);
17625498Stimh 	return (0);
17635498Stimh }
17645498Stimh 
17655498Stimh /*
17665498Stimh  * inputs:
17675498Stimh  * zc_name		name of filesystem
17685498Stimh  * zc_nvlist_dst_size	size of buffer for zpl property nvlist
17695498Stimh  *
17705498Stimh  * outputs:
17715498Stimh  * zc_nvlist_dst	zpl property nvlist
17725498Stimh  * zc_nvlist_dst_size	size of zpl property nvlist
17735498Stimh  */
17745498Stimh static int
17755498Stimh zfs_ioc_objset_zplprops(zfs_cmd_t *zc)
17765498Stimh {
17775498Stimh 	objset_t *os;
17785498Stimh 	int err;
17795498Stimh 
178010298SMatthew.Ahrens@Sun.COM 	/* XXX reading without owning */
178110298SMatthew.Ahrens@Sun.COM 	if (err = dmu_objset_hold(zc->zc_name, FTAG, &os))
17825498Stimh 		return (err);
17835498Stimh 
17845498Stimh 	dmu_objset_fast_stat(os, &zc->zc_objset_stats);
17855498Stimh 
17865498Stimh 	/*
17875498Stimh 	 * NB: nvl_add_zplprop() will read the objset contents,
17886689Smaybee 	 * which we aren't supposed to do with a DS_MODE_USER
17896689Smaybee 	 * hold, because it could be inconsistent.
17905498Stimh 	 */
17915498Stimh 	if (zc->zc_nvlist_dst != NULL &&
17925498Stimh 	    !zc->zc_objset_stats.dds_inconsistent &&
17935498Stimh 	    dmu_objset_type(os) == DMU_OST_ZFS) {
17945498Stimh 		nvlist_t *nv;
17955498Stimh 
17965498Stimh 		VERIFY(nvlist_alloc(&nv, NV_UNIQUE_NAME, KM_SLEEP) == 0);
17975498Stimh 		if ((err = nvl_add_zplprop(os, nv, ZFS_PROP_VERSION)) == 0 &&
17985498Stimh 		    (err = nvl_add_zplprop(os, nv, ZFS_PROP_NORMALIZE)) == 0 &&
17995498Stimh 		    (err = nvl_add_zplprop(os, nv, ZFS_PROP_UTF8ONLY)) == 0 &&
18005498Stimh 		    (err = nvl_add_zplprop(os, nv, ZFS_PROP_CASE)) == 0)
18015498Stimh 			err = put_nvlist(zc, nv);
18025498Stimh 		nvlist_free(nv);
18035498Stimh 	} else {
18045498Stimh 		err = ENOENT;
18055498Stimh 	}
180610298SMatthew.Ahrens@Sun.COM 	dmu_objset_rele(os, FTAG);
18075498Stimh 	return (err);
18085498Stimh }
18095498Stimh 
18109396SMatthew.Ahrens@Sun.COM static boolean_t
18119396SMatthew.Ahrens@Sun.COM dataset_name_hidden(const char *name)
18129396SMatthew.Ahrens@Sun.COM {
18139396SMatthew.Ahrens@Sun.COM 	/*
18149396SMatthew.Ahrens@Sun.COM 	 * Skip over datasets that are not visible in this zone,
18159396SMatthew.Ahrens@Sun.COM 	 * internal datasets (which have a $ in their name), and
18169396SMatthew.Ahrens@Sun.COM 	 * temporary datasets (which have a % in their name).
18179396SMatthew.Ahrens@Sun.COM 	 */
18189396SMatthew.Ahrens@Sun.COM 	if (strchr(name, '$') != NULL)
18199396SMatthew.Ahrens@Sun.COM 		return (B_TRUE);
18209396SMatthew.Ahrens@Sun.COM 	if (strchr(name, '%') != NULL)
18219396SMatthew.Ahrens@Sun.COM 		return (B_TRUE);
18229396SMatthew.Ahrens@Sun.COM 	if (!INGLOBALZONE(curproc) && !zone_dataset_visible(name, NULL))
18239396SMatthew.Ahrens@Sun.COM 		return (B_TRUE);
18249396SMatthew.Ahrens@Sun.COM 	return (B_FALSE);
18259396SMatthew.Ahrens@Sun.COM }
18269396SMatthew.Ahrens@Sun.COM 
18275367Sahrens /*
18285367Sahrens  * inputs:
18295367Sahrens  * zc_name		name of filesystem
18305367Sahrens  * zc_cookie		zap cursor
18315367Sahrens  * zc_nvlist_dst_size	size of buffer for property nvlist
18325367Sahrens  *
18335367Sahrens  * outputs:
18345367Sahrens  * zc_name		name of next filesystem
18359396SMatthew.Ahrens@Sun.COM  * zc_cookie		zap cursor
18365367Sahrens  * zc_objset_stats	stats
18375367Sahrens  * zc_nvlist_dst	property nvlist
18385367Sahrens  * zc_nvlist_dst_size	size of property nvlist
18395367Sahrens  */
1840789Sahrens static int
1841789Sahrens zfs_ioc_dataset_list_next(zfs_cmd_t *zc)
1842789Sahrens {
1843885Sahrens 	objset_t *os;
1844789Sahrens 	int error;
1845789Sahrens 	char *p;
184611546SChris.Kirby@sun.com 	size_t orig_len = strlen(zc->zc_name);
184711546SChris.Kirby@sun.com 
184811546SChris.Kirby@sun.com top:
184910298SMatthew.Ahrens@Sun.COM 	if (error = dmu_objset_hold(zc->zc_name, FTAG, &os)) {
1850885Sahrens 		if (error == ENOENT)
1851885Sahrens 			error = ESRCH;
1852885Sahrens 		return (error);
1853789Sahrens 	}
1854789Sahrens 
1855789Sahrens 	p = strrchr(zc->zc_name, '/');
1856789Sahrens 	if (p == NULL || p[1] != '\0')
1857789Sahrens 		(void) strlcat(zc->zc_name, "/", sizeof (zc->zc_name));
1858789Sahrens 	p = zc->zc_name + strlen(zc->zc_name);
1859789Sahrens 
18608697SRichard.Morris@Sun.COM 	/*
18618697SRichard.Morris@Sun.COM 	 * Pre-fetch the datasets.  dmu_objset_prefetch() always returns 0
18628697SRichard.Morris@Sun.COM 	 * but is not declared void because its called by dmu_objset_find().
18638697SRichard.Morris@Sun.COM 	 */
18648415SRichard.Morris@Sun.COM 	if (zc->zc_cookie == 0) {
18658415SRichard.Morris@Sun.COM 		uint64_t cookie = 0;
18668415SRichard.Morris@Sun.COM 		int len = sizeof (zc->zc_name) - (p - zc->zc_name);
18678415SRichard.Morris@Sun.COM 
18688415SRichard.Morris@Sun.COM 		while (dmu_dir_list_next(os, len, p, NULL, &cookie) == 0)
18698697SRichard.Morris@Sun.COM 			(void) dmu_objset_prefetch(p, NULL);
18708415SRichard.Morris@Sun.COM 	}
18718415SRichard.Morris@Sun.COM 
1872789Sahrens 	do {
1873885Sahrens 		error = dmu_dir_list_next(os,
1874885Sahrens 		    sizeof (zc->zc_name) - (p - zc->zc_name), p,
1875885Sahrens 		    NULL, &zc->zc_cookie);
1876789Sahrens 		if (error == ENOENT)
1877789Sahrens 			error = ESRCH;
187810588SEric.Taylor@Sun.COM 	} while (error == 0 && dataset_name_hidden(zc->zc_name) &&
187910588SEric.Taylor@Sun.COM 	    !(zc->zc_iflags & FKIOCTL));
188010298SMatthew.Ahrens@Sun.COM 	dmu_objset_rele(os, FTAG);
1881789Sahrens 
188210588SEric.Taylor@Sun.COM 	/*
188310588SEric.Taylor@Sun.COM 	 * If it's an internal dataset (ie. with a '$' in its name),
188410588SEric.Taylor@Sun.COM 	 * don't try to get stats for it, otherwise we'll return ENOENT.
188510588SEric.Taylor@Sun.COM 	 */
188611546SChris.Kirby@sun.com 	if (error == 0 && strchr(zc->zc_name, '$') == NULL) {
1887885Sahrens 		error = zfs_ioc_objset_stats(zc); /* fill in the stats */
188811546SChris.Kirby@sun.com 		if (error == ENOENT) {
188911546SChris.Kirby@sun.com 			/* We lost a race with destroy, get the next one. */
189011546SChris.Kirby@sun.com 			zc->zc_name[orig_len] = '\0';
189111546SChris.Kirby@sun.com 			goto top;
189211546SChris.Kirby@sun.com 		}
189311546SChris.Kirby@sun.com 	}
1894789Sahrens 	return (error);
1895789Sahrens }
1896789Sahrens 
18975367Sahrens /*
18985367Sahrens  * inputs:
18995367Sahrens  * zc_name		name of filesystem
19005367Sahrens  * zc_cookie		zap cursor
19015367Sahrens  * zc_nvlist_dst_size	size of buffer for property nvlist
19025367Sahrens  *
19035367Sahrens  * outputs:
19045367Sahrens  * zc_name		name of next snapshot
19055367Sahrens  * zc_objset_stats	stats
19065367Sahrens  * zc_nvlist_dst	property nvlist
19075367Sahrens  * zc_nvlist_dst_size	size of property nvlist
19085367Sahrens  */
1909789Sahrens static int
1910789Sahrens zfs_ioc_snapshot_list_next(zfs_cmd_t *zc)
1911789Sahrens {
1912885Sahrens 	objset_t *os;
1913789Sahrens 	int error;
1914789Sahrens 
191511546SChris.Kirby@sun.com top:
191610474SRichard.Morris@Sun.COM 	if (zc->zc_cookie == 0)
191710474SRichard.Morris@Sun.COM 		(void) dmu_objset_find(zc->zc_name, dmu_objset_prefetch,
191810474SRichard.Morris@Sun.COM 		    NULL, DS_FIND_SNAPSHOTS);
191910474SRichard.Morris@Sun.COM 
192010298SMatthew.Ahrens@Sun.COM 	error = dmu_objset_hold(zc->zc_name, FTAG, &os);
19216689Smaybee 	if (error)
19226689Smaybee 		return (error == ENOENT ? ESRCH : error);
1923789Sahrens 
19241003Slling 	/*
19251003Slling 	 * A dataset name of maximum length cannot have any snapshots,
19261003Slling 	 * so exit immediately.
19271003Slling 	 */
19281003Slling 	if (strlcat(zc->zc_name, "@", sizeof (zc->zc_name)) >= MAXNAMELEN) {
192910298SMatthew.Ahrens@Sun.COM 		dmu_objset_rele(os, FTAG);
19301003Slling 		return (ESRCH);
1931789Sahrens 	}
1932789Sahrens 
1933885Sahrens 	error = dmu_snapshot_list_next(os,
1934885Sahrens 	    sizeof (zc->zc_name) - strlen(zc->zc_name),
193512786SChris.Kirby@oracle.com 	    zc->zc_name + strlen(zc->zc_name), &zc->zc_obj, &zc->zc_cookie,
193612786SChris.Kirby@oracle.com 	    NULL);
193712786SChris.Kirby@oracle.com 
193811546SChris.Kirby@sun.com 	if (error == 0) {
193912786SChris.Kirby@oracle.com 		dsl_dataset_t *ds;
194012786SChris.Kirby@oracle.com 		dsl_pool_t *dp = os->os_dsl_dataset->ds_dir->dd_pool;
194112786SChris.Kirby@oracle.com 
194212786SChris.Kirby@oracle.com 		/*
194312786SChris.Kirby@oracle.com 		 * Since we probably don't have a hold on this snapshot,
194412786SChris.Kirby@oracle.com 		 * it's possible that the objsetid could have been destroyed
194512786SChris.Kirby@oracle.com 		 * and reused for a new objset. It's OK if this happens during
194612786SChris.Kirby@oracle.com 		 * a zfs send operation, since the new createtxg will be
194712786SChris.Kirby@oracle.com 		 * beyond the range we're interested in.
194812786SChris.Kirby@oracle.com 		 */
194912786SChris.Kirby@oracle.com 		rw_enter(&dp->dp_config_rwlock, RW_READER);
195012786SChris.Kirby@oracle.com 		error = dsl_dataset_hold_obj(dp, zc->zc_obj, FTAG, &ds);
195112786SChris.Kirby@oracle.com 		rw_exit(&dp->dp_config_rwlock);
195212786SChris.Kirby@oracle.com 		if (error) {
195312786SChris.Kirby@oracle.com 			if (error == ENOENT) {
195412786SChris.Kirby@oracle.com 				/* Racing with destroy, get the next one. */
195512786SChris.Kirby@oracle.com 				*strchr(zc->zc_name, '@') = '\0';
195612786SChris.Kirby@oracle.com 				dmu_objset_rele(os, FTAG);
195712786SChris.Kirby@oracle.com 				goto top;
195812786SChris.Kirby@oracle.com 			}
195912786SChris.Kirby@oracle.com 		} else {
196012786SChris.Kirby@oracle.com 			objset_t *ossnap;
196112786SChris.Kirby@oracle.com 
196212786SChris.Kirby@oracle.com 			error = dmu_objset_from_ds(ds, &ossnap);
196312786SChris.Kirby@oracle.com 			if (error == 0)
196412786SChris.Kirby@oracle.com 				error = zfs_ioc_objset_stats_impl(zc, ossnap);
196512786SChris.Kirby@oracle.com 			dsl_dataset_rele(ds, FTAG);
196611546SChris.Kirby@sun.com 		}
196711546SChris.Kirby@sun.com 	} else if (error == ENOENT) {
19686689Smaybee 		error = ESRCH;
196911546SChris.Kirby@sun.com 	}
1970789Sahrens 
197112786SChris.Kirby@oracle.com 	dmu_objset_rele(os, FTAG);
19725367Sahrens 	/* if we failed, undo the @ that we tacked on to zc_name */
19736689Smaybee 	if (error)
19745367Sahrens 		*strchr(zc->zc_name, '@') = '\0';
1975789Sahrens 	return (error);
1976789Sahrens }
1977789Sahrens 
197811022STom.Erickson@Sun.COM static int
197911022STom.Erickson@Sun.COM zfs_prop_set_userquota(const char *dsname, nvpair_t *pair)
198011022STom.Erickson@Sun.COM {
198111022STom.Erickson@Sun.COM 	const char *propname = nvpair_name(pair);
198211022STom.Erickson@Sun.COM 	uint64_t *valary;
198311022STom.Erickson@Sun.COM 	unsigned int vallen;
198411022STom.Erickson@Sun.COM 	const char *domain;
198511933STim.Haley@Sun.COM 	char *dash;
198611022STom.Erickson@Sun.COM 	zfs_userquota_prop_t type;
198711022STom.Erickson@Sun.COM 	uint64_t rid;
198811022STom.Erickson@Sun.COM 	uint64_t quota;
198911022STom.Erickson@Sun.COM 	zfsvfs_t *zfsvfs;
199011022STom.Erickson@Sun.COM 	int err;
199111022STom.Erickson@Sun.COM 
199211022STom.Erickson@Sun.COM 	if (nvpair_type(pair) == DATA_TYPE_NVLIST) {
199311022STom.Erickson@Sun.COM 		nvlist_t *attrs;
199411022STom.Erickson@Sun.COM 		VERIFY(nvpair_value_nvlist(pair, &attrs) == 0);
199511933STim.Haley@Sun.COM 		if (nvlist_lookup_nvpair(attrs, ZPROP_VALUE,
199611933STim.Haley@Sun.COM 		    &pair) != 0)
199711933STim.Haley@Sun.COM 			return (EINVAL);
199811022STom.Erickson@Sun.COM 	}
199911022STom.Erickson@Sun.COM 
200011933STim.Haley@Sun.COM 	/*
200111933STim.Haley@Sun.COM 	 * A correctly constructed propname is encoded as
200211933STim.Haley@Sun.COM 	 * userquota@<rid>-<domain>.
200311933STim.Haley@Sun.COM 	 */
200411933STim.Haley@Sun.COM 	if ((dash = strchr(propname, '-')) == NULL ||
200511933STim.Haley@Sun.COM 	    nvpair_value_uint64_array(pair, &valary, &vallen) != 0 ||
200611933STim.Haley@Sun.COM 	    vallen != 3)
200711933STim.Haley@Sun.COM 		return (EINVAL);
200811933STim.Haley@Sun.COM 
200911933STim.Haley@Sun.COM 	domain = dash + 1;
201011022STom.Erickson@Sun.COM 	type = valary[0];
201111022STom.Erickson@Sun.COM 	rid = valary[1];
201211022STom.Erickson@Sun.COM 	quota = valary[2];
201311022STom.Erickson@Sun.COM 
201412620SMark.Shellenbaum@Oracle.COM 	err = zfsvfs_hold(dsname, FTAG, &zfsvfs, B_FALSE);
201511022STom.Erickson@Sun.COM 	if (err == 0) {
201611022STom.Erickson@Sun.COM 		err = zfs_set_userquota(zfsvfs, type, domain, rid, quota);
201711022STom.Erickson@Sun.COM 		zfsvfs_rele(zfsvfs, FTAG);
201811022STom.Erickson@Sun.COM 	}
201911022STom.Erickson@Sun.COM 
202011022STom.Erickson@Sun.COM 	return (err);
202111022STom.Erickson@Sun.COM }
202211022STom.Erickson@Sun.COM 
202311022STom.Erickson@Sun.COM /*
202411022STom.Erickson@Sun.COM  * If the named property is one that has a special function to set its value,
202511022STom.Erickson@Sun.COM  * return 0 on success and a positive error code on failure; otherwise if it is
202611022STom.Erickson@Sun.COM  * not one of the special properties handled by this function, return -1.
202711022STom.Erickson@Sun.COM  *
202811933STim.Haley@Sun.COM  * XXX: It would be better for callers of the property interface if we handled
202911022STom.Erickson@Sun.COM  * these special cases in dsl_prop.c (in the dsl layer).
203011022STom.Erickson@Sun.COM  */
203111022STom.Erickson@Sun.COM static int
203211022STom.Erickson@Sun.COM zfs_prop_set_special(const char *dsname, zprop_source_t source,
203311022STom.Erickson@Sun.COM     nvpair_t *pair)
2034789Sahrens {
203511022STom.Erickson@Sun.COM 	const char *propname = nvpair_name(pair);
203611022STom.Erickson@Sun.COM 	zfs_prop_t prop = zfs_name_to_prop(propname);
203711022STom.Erickson@Sun.COM 	uint64_t intval;
203811022STom.Erickson@Sun.COM 	int err;
203911022STom.Erickson@Sun.COM 
204011022STom.Erickson@Sun.COM 	if (prop == ZPROP_INVAL) {
204111022STom.Erickson@Sun.COM 		if (zfs_prop_userquota(propname))
204211022STom.Erickson@Sun.COM 			return (zfs_prop_set_userquota(dsname, pair));
204311022STom.Erickson@Sun.COM 		return (-1);
204411022STom.Erickson@Sun.COM 	}
204511022STom.Erickson@Sun.COM 
204611022STom.Erickson@Sun.COM 	if (nvpair_type(pair) == DATA_TYPE_NVLIST) {
204711022STom.Erickson@Sun.COM 		nvlist_t *attrs;
204811022STom.Erickson@Sun.COM 		VERIFY(nvpair_value_nvlist(pair, &attrs) == 0);
204911022STom.Erickson@Sun.COM 		VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE,
205011022STom.Erickson@Sun.COM 		    &pair) == 0);
205111022STom.Erickson@Sun.COM 	}
205211022STom.Erickson@Sun.COM 
205311022STom.Erickson@Sun.COM 	if (zfs_prop_get_type(prop) == PROP_TYPE_STRING)
205411022STom.Erickson@Sun.COM 		return (-1);
205511022STom.Erickson@Sun.COM 
205611022STom.Erickson@Sun.COM 	VERIFY(0 == nvpair_value_uint64(pair, &intval));
205711022STom.Erickson@Sun.COM 
205811022STom.Erickson@Sun.COM 	switch (prop) {
205911022STom.Erickson@Sun.COM 	case ZFS_PROP_QUOTA:
206011022STom.Erickson@Sun.COM 		err = dsl_dir_set_quota(dsname, source, intval);
206111022STom.Erickson@Sun.COM 		break;
206211022STom.Erickson@Sun.COM 	case ZFS_PROP_REFQUOTA:
206311022STom.Erickson@Sun.COM 		err = dsl_dataset_set_quota(dsname, source, intval);
206411022STom.Erickson@Sun.COM 		break;
206511022STom.Erickson@Sun.COM 	case ZFS_PROP_RESERVATION:
206611022STom.Erickson@Sun.COM 		err = dsl_dir_set_reservation(dsname, source, intval);
206711022STom.Erickson@Sun.COM 		break;
206811022STom.Erickson@Sun.COM 	case ZFS_PROP_REFRESERVATION:
206911022STom.Erickson@Sun.COM 		err = dsl_dataset_set_reservation(dsname, source, intval);
207011022STom.Erickson@Sun.COM 		break;
207111022STom.Erickson@Sun.COM 	case ZFS_PROP_VOLSIZE:
207211022STom.Erickson@Sun.COM 		err = zvol_set_volsize(dsname, ddi_driver_major(zfs_dip),
207311022STom.Erickson@Sun.COM 		    intval);
207411022STom.Erickson@Sun.COM 		break;
207511022STom.Erickson@Sun.COM 	case ZFS_PROP_VERSION:
207611022STom.Erickson@Sun.COM 	{
207711022STom.Erickson@Sun.COM 		zfsvfs_t *zfsvfs;
207811022STom.Erickson@Sun.COM 
207912620SMark.Shellenbaum@Oracle.COM 		if ((err = zfsvfs_hold(dsname, FTAG, &zfsvfs, B_TRUE)) != 0)
208011022STom.Erickson@Sun.COM 			break;
208111022STom.Erickson@Sun.COM 
208211022STom.Erickson@Sun.COM 		err = zfs_set_version(zfsvfs, intval);
208311022STom.Erickson@Sun.COM 		zfsvfs_rele(zfsvfs, FTAG);
208411022STom.Erickson@Sun.COM 
208511022STom.Erickson@Sun.COM 		if (err == 0 && intval >= ZPL_VERSION_USERSPACE) {
208611147SGeorge.Wilson@Sun.COM 			zfs_cmd_t *zc;
208711147SGeorge.Wilson@Sun.COM 
208811147SGeorge.Wilson@Sun.COM 			zc = kmem_zalloc(sizeof (zfs_cmd_t), KM_SLEEP);
208911147SGeorge.Wilson@Sun.COM 			(void) strcpy(zc->zc_name, dsname);
209011147SGeorge.Wilson@Sun.COM 			(void) zfs_ioc_userspace_upgrade(zc);
209111147SGeorge.Wilson@Sun.COM 			kmem_free(zc, sizeof (zfs_cmd_t));
209211022STom.Erickson@Sun.COM 		}
209311022STom.Erickson@Sun.COM 		break;
209411022STom.Erickson@Sun.COM 	}
209511022STom.Erickson@Sun.COM 
209611022STom.Erickson@Sun.COM 	default:
209711022STom.Erickson@Sun.COM 		err = -1;
209811022STom.Erickson@Sun.COM 	}
209911022STom.Erickson@Sun.COM 
210011022STom.Erickson@Sun.COM 	return (err);
210111022STom.Erickson@Sun.COM }
210211022STom.Erickson@Sun.COM 
210311022STom.Erickson@Sun.COM /*
210411022STom.Erickson@Sun.COM  * This function is best effort. If it fails to set any of the given properties,
210511022STom.Erickson@Sun.COM  * it continues to set as many as it can and returns the first error
210611022STom.Erickson@Sun.COM  * encountered. If the caller provides a non-NULL errlist, it also gives the
210711022STom.Erickson@Sun.COM  * complete list of names of all the properties it failed to set along with the
210811022STom.Erickson@Sun.COM  * corresponding error numbers. The caller is responsible for freeing the
210911022STom.Erickson@Sun.COM  * returned errlist.
211011022STom.Erickson@Sun.COM  *
211111022STom.Erickson@Sun.COM  * If every property is set successfully, zero is returned and the list pointed
211211022STom.Erickson@Sun.COM  * at by errlist is NULL.
211311022STom.Erickson@Sun.COM  */
211411022STom.Erickson@Sun.COM int
211511022STom.Erickson@Sun.COM zfs_set_prop_nvlist(const char *dsname, zprop_source_t source, nvlist_t *nvl,
211611022STom.Erickson@Sun.COM     nvlist_t **errlist)
211711022STom.Erickson@Sun.COM {
211811022STom.Erickson@Sun.COM 	nvpair_t *pair;
211911022STom.Erickson@Sun.COM 	nvpair_t *propval;
212011045STom.Erickson@Sun.COM 	int rv = 0;
21212676Seschrock 	uint64_t intval;
21222676Seschrock 	char *strval;
21238697SRichard.Morris@Sun.COM 	nvlist_t *genericnvl;
212411022STom.Erickson@Sun.COM 	nvlist_t *errors;
212511022STom.Erickson@Sun.COM 	nvlist_t *retrynvl;
21264543Smarks 
21278697SRichard.Morris@Sun.COM 	VERIFY(nvlist_alloc(&genericnvl, NV_UNIQUE_NAME, KM_SLEEP) == 0);
212811022STom.Erickson@Sun.COM 	VERIFY(nvlist_alloc(&errors, NV_UNIQUE_NAME, KM_SLEEP) == 0);
212911022STom.Erickson@Sun.COM 	VERIFY(nvlist_alloc(&retrynvl, NV_UNIQUE_NAME, KM_SLEEP) == 0);
213011022STom.Erickson@Sun.COM 
213111022STom.Erickson@Sun.COM retry:
213211022STom.Erickson@Sun.COM 	pair = NULL;
213311022STom.Erickson@Sun.COM 	while ((pair = nvlist_next_nvpair(nvl, pair)) != NULL) {
213411022STom.Erickson@Sun.COM 		const char *propname = nvpair_name(pair);
21354670Sahrens 		zfs_prop_t prop = zfs_name_to_prop(propname);
213611181STom.Erickson@Sun.COM 		int err = 0;
21374543Smarks 
213811022STom.Erickson@Sun.COM 		/* decode the property value */
213911022STom.Erickson@Sun.COM 		propval = pair;
214011022STom.Erickson@Sun.COM 		if (nvpair_type(pair) == DATA_TYPE_NVLIST) {
214111022STom.Erickson@Sun.COM 			nvlist_t *attrs;
214211022STom.Erickson@Sun.COM 			VERIFY(nvpair_value_nvlist(pair, &attrs) == 0);
214311933STim.Haley@Sun.COM 			if (nvlist_lookup_nvpair(attrs, ZPROP_VALUE,
214411933STim.Haley@Sun.COM 			    &propval) != 0)
214511933STim.Haley@Sun.COM 				err = EINVAL;
21464543Smarks 		}
21472676Seschrock 
214811022STom.Erickson@Sun.COM 		/* Validate value type */
214911933STim.Haley@Sun.COM 		if (err == 0 && prop == ZPROP_INVAL) {
215011022STom.Erickson@Sun.COM 			if (zfs_prop_user(propname)) {
215111022STom.Erickson@Sun.COM 				if (nvpair_type(propval) != DATA_TYPE_STRING)
215211022STom.Erickson@Sun.COM 					err = EINVAL;
215311022STom.Erickson@Sun.COM 			} else if (zfs_prop_userquota(propname)) {
215411022STom.Erickson@Sun.COM 				if (nvpair_type(propval) !=
215511022STom.Erickson@Sun.COM 				    DATA_TYPE_UINT64_ARRAY)
215611022STom.Erickson@Sun.COM 					err = EINVAL;
21579396SMatthew.Ahrens@Sun.COM 			}
215811933STim.Haley@Sun.COM 		} else if (err == 0) {
215911022STom.Erickson@Sun.COM 			if (nvpair_type(propval) == DATA_TYPE_STRING) {
216011022STom.Erickson@Sun.COM 				if (zfs_prop_get_type(prop) != PROP_TYPE_STRING)
216111022STom.Erickson@Sun.COM 					err = EINVAL;
216211022STom.Erickson@Sun.COM 			} else if (nvpair_type(propval) == DATA_TYPE_UINT64) {
21632885Sahrens 				const char *unused;
21642885Sahrens 
216511022STom.Erickson@Sun.COM 				VERIFY(nvpair_value_uint64(propval,
216611022STom.Erickson@Sun.COM 				    &intval) == 0);
21672676Seschrock 
21682676Seschrock 				switch (zfs_prop_get_type(prop)) {
21694787Sahrens 				case PROP_TYPE_NUMBER:
21702676Seschrock 					break;
21714787Sahrens 				case PROP_TYPE_STRING:
217211022STom.Erickson@Sun.COM 					err = EINVAL;
217311022STom.Erickson@Sun.COM 					break;
21744787Sahrens 				case PROP_TYPE_INDEX:
21752717Seschrock 					if (zfs_prop_index_to_string(prop,
217611022STom.Erickson@Sun.COM 					    intval, &unused) != 0)
217711022STom.Erickson@Sun.COM 						err = EINVAL;
21782676Seschrock 					break;
21792676Seschrock 				default:
21804577Sahrens 					cmn_err(CE_PANIC,
21814577Sahrens 					    "unknown property type");
21822676Seschrock 				}
21832676Seschrock 			} else {
218411022STom.Erickson@Sun.COM 				err = EINVAL;
218511022STom.Erickson@Sun.COM 			}
218611022STom.Erickson@Sun.COM 		}
218711022STom.Erickson@Sun.COM 
218811022STom.Erickson@Sun.COM 		/* Validate permissions */
218911022STom.Erickson@Sun.COM 		if (err == 0)
219011022STom.Erickson@Sun.COM 			err = zfs_check_settable(dsname, pair, CRED());
219111022STom.Erickson@Sun.COM 
219211022STom.Erickson@Sun.COM 		if (err == 0) {
219311022STom.Erickson@Sun.COM 			err = zfs_prop_set_special(dsname, source, pair);
219411022STom.Erickson@Sun.COM 			if (err == -1) {
219511022STom.Erickson@Sun.COM 				/*
219611022STom.Erickson@Sun.COM 				 * For better performance we build up a list of
219711022STom.Erickson@Sun.COM 				 * properties to set in a single transaction.
219811022STom.Erickson@Sun.COM 				 */
219911022STom.Erickson@Sun.COM 				err = nvlist_add_nvpair(genericnvl, pair);
220011022STom.Erickson@Sun.COM 			} else if (err != 0 && nvl != retrynvl) {
220111022STom.Erickson@Sun.COM 				/*
220211022STom.Erickson@Sun.COM 				 * This may be a spurious error caused by
220311022STom.Erickson@Sun.COM 				 * receiving quota and reservation out of order.
220411022STom.Erickson@Sun.COM 				 * Try again in a second pass.
220511022STom.Erickson@Sun.COM 				 */
220611022STom.Erickson@Sun.COM 				err = nvlist_add_nvpair(retrynvl, pair);
22072676Seschrock 			}
220811022STom.Erickson@Sun.COM 		}
220911022STom.Erickson@Sun.COM 
221011022STom.Erickson@Sun.COM 		if (err != 0)
221111022STom.Erickson@Sun.COM 			VERIFY(nvlist_add_int32(errors, propname, err) == 0);
221211022STom.Erickson@Sun.COM 	}
221311022STom.Erickson@Sun.COM 
221411022STom.Erickson@Sun.COM 	if (nvl != retrynvl && !nvlist_empty(retrynvl)) {
221511022STom.Erickson@Sun.COM 		nvl = retrynvl;
221611022STom.Erickson@Sun.COM 		goto retry;
221711022STom.Erickson@Sun.COM 	}
221811022STom.Erickson@Sun.COM 
221911022STom.Erickson@Sun.COM 	if (!nvlist_empty(genericnvl) &&
222011022STom.Erickson@Sun.COM 	    dsl_props_set(dsname, source, genericnvl) != 0) {
222111022STom.Erickson@Sun.COM 		/*
222211022STom.Erickson@Sun.COM 		 * If this fails, we still want to set as many properties as we
222311022STom.Erickson@Sun.COM 		 * can, so try setting them individually.
222411022STom.Erickson@Sun.COM 		 */
222511022STom.Erickson@Sun.COM 		pair = NULL;
222611022STom.Erickson@Sun.COM 		while ((pair = nvlist_next_nvpair(genericnvl, pair)) != NULL) {
222711022STom.Erickson@Sun.COM 			const char *propname = nvpair_name(pair);
222811181STom.Erickson@Sun.COM 			int err = 0;
222911022STom.Erickson@Sun.COM 
223011022STom.Erickson@Sun.COM 			propval = pair;
223111022STom.Erickson@Sun.COM 			if (nvpair_type(pair) == DATA_TYPE_NVLIST) {
223211022STom.Erickson@Sun.COM 				nvlist_t *attrs;
223311022STom.Erickson@Sun.COM 				VERIFY(nvpair_value_nvlist(pair, &attrs) == 0);
223411022STom.Erickson@Sun.COM 				VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE,
223511022STom.Erickson@Sun.COM 				    &propval) == 0);
223611022STom.Erickson@Sun.COM 			}
223711022STom.Erickson@Sun.COM 
223811022STom.Erickson@Sun.COM 			if (nvpair_type(propval) == DATA_TYPE_STRING) {
223911022STom.Erickson@Sun.COM 				VERIFY(nvpair_value_string(propval,
224011022STom.Erickson@Sun.COM 				    &strval) == 0);
224111022STom.Erickson@Sun.COM 				err = dsl_prop_set(dsname, propname, source, 1,
224211022STom.Erickson@Sun.COM 				    strlen(strval) + 1, strval);
224311022STom.Erickson@Sun.COM 			} else {
224411022STom.Erickson@Sun.COM 				VERIFY(nvpair_value_uint64(propval,
224511022STom.Erickson@Sun.COM 				    &intval) == 0);
224611022STom.Erickson@Sun.COM 				err = dsl_prop_set(dsname, propname, source, 8,
224711022STom.Erickson@Sun.COM 				    1, &intval);
224811022STom.Erickson@Sun.COM 			}
224911022STom.Erickson@Sun.COM 
225011022STom.Erickson@Sun.COM 			if (err != 0) {
225111022STom.Erickson@Sun.COM 				VERIFY(nvlist_add_int32(errors, propname,
225211022STom.Erickson@Sun.COM 				    err) == 0);
225311022STom.Erickson@Sun.COM 			}
22542676Seschrock 		}
22552676Seschrock 	}
225611022STom.Erickson@Sun.COM 	nvlist_free(genericnvl);
225711022STom.Erickson@Sun.COM 	nvlist_free(retrynvl);
225811022STom.Erickson@Sun.COM 
225911022STom.Erickson@Sun.COM 	if ((pair = nvlist_next_nvpair(errors, NULL)) == NULL) {
226011022STom.Erickson@Sun.COM 		nvlist_free(errors);
226111022STom.Erickson@Sun.COM 		errors = NULL;
226211022STom.Erickson@Sun.COM 	} else {
226311022STom.Erickson@Sun.COM 		VERIFY(nvpair_value_int32(pair, &rv) == 0);
22648697SRichard.Morris@Sun.COM 	}
226511022STom.Erickson@Sun.COM 
226611022STom.Erickson@Sun.COM 	if (errlist == NULL)
226711022STom.Erickson@Sun.COM 		nvlist_free(errors);
226811022STom.Erickson@Sun.COM 	else
226911022STom.Erickson@Sun.COM 		*errlist = errors;
227011022STom.Erickson@Sun.COM 
227111022STom.Erickson@Sun.COM 	return (rv);
2272789Sahrens }
2273789Sahrens 
22745367Sahrens /*
22759355SMatthew.Ahrens@Sun.COM  * Check that all the properties are valid user properties.
22769355SMatthew.Ahrens@Sun.COM  */
22779355SMatthew.Ahrens@Sun.COM static int
22789355SMatthew.Ahrens@Sun.COM zfs_check_userprops(char *fsname, nvlist_t *nvl)
22799355SMatthew.Ahrens@Sun.COM {
228011022STom.Erickson@Sun.COM 	nvpair_t *pair = NULL;
22819355SMatthew.Ahrens@Sun.COM 	int error = 0;
22829355SMatthew.Ahrens@Sun.COM 
228311022STom.Erickson@Sun.COM 	while ((pair = nvlist_next_nvpair(nvl, pair)) != NULL) {
228411022STom.Erickson@Sun.COM 		const char *propname = nvpair_name(pair);
22859355SMatthew.Ahrens@Sun.COM 		char *valstr;
22869355SMatthew.Ahrens@Sun.COM 
22879355SMatthew.Ahrens@Sun.COM 		if (!zfs_prop_user(propname) ||
228811022STom.Erickson@Sun.COM 		    nvpair_type(pair) != DATA_TYPE_STRING)
22899355SMatthew.Ahrens@Sun.COM 			return (EINVAL);
22909355SMatthew.Ahrens@Sun.COM 
22919355SMatthew.Ahrens@Sun.COM 		if (error = zfs_secpolicy_write_perms(fsname,
22929355SMatthew.Ahrens@Sun.COM 		    ZFS_DELEG_PERM_USERPROP, CRED()))
22939355SMatthew.Ahrens@Sun.COM 			return (error);
22949355SMatthew.Ahrens@Sun.COM 
22959355SMatthew.Ahrens@Sun.COM 		if (strlen(propname) >= ZAP_MAXNAMELEN)
22969355SMatthew.Ahrens@Sun.COM 			return (ENAMETOOLONG);
22979355SMatthew.Ahrens@Sun.COM 
229811022STom.Erickson@Sun.COM 		VERIFY(nvpair_value_string(pair, &valstr) == 0);
22999355SMatthew.Ahrens@Sun.COM 		if (strlen(valstr) >= ZAP_MAXVALUELEN)
23009355SMatthew.Ahrens@Sun.COM 			return (E2BIG);
23019355SMatthew.Ahrens@Sun.COM 	}
23029355SMatthew.Ahrens@Sun.COM 	return (0);
23039355SMatthew.Ahrens@Sun.COM }
23049355SMatthew.Ahrens@Sun.COM 
230511022STom.Erickson@Sun.COM static void
230611022STom.Erickson@Sun.COM props_skip(nvlist_t *props, nvlist_t *skipped, nvlist_t **newprops)
230711022STom.Erickson@Sun.COM {
230811022STom.Erickson@Sun.COM 	nvpair_t *pair;
230911022STom.Erickson@Sun.COM 
231011022STom.Erickson@Sun.COM 	VERIFY(nvlist_alloc(newprops, NV_UNIQUE_NAME, KM_SLEEP) == 0);
231111022STom.Erickson@Sun.COM 
231211022STom.Erickson@Sun.COM 	pair = NULL;
231311022STom.Erickson@Sun.COM 	while ((pair = nvlist_next_nvpair(props, pair)) != NULL) {
231411022STom.Erickson@Sun.COM 		if (nvlist_exists(skipped, nvpair_name(pair)))
231511022STom.Erickson@Sun.COM 			continue;
231611022STom.Erickson@Sun.COM 
231711022STom.Erickson@Sun.COM 		VERIFY(nvlist_add_nvpair(*newprops, pair) == 0);
231811022STom.Erickson@Sun.COM 	}
231911022STom.Erickson@Sun.COM }
232011022STom.Erickson@Sun.COM 
232111022STom.Erickson@Sun.COM static int
232211022STom.Erickson@Sun.COM clear_received_props(objset_t *os, const char *fs, nvlist_t *props,
232311022STom.Erickson@Sun.COM     nvlist_t *skipped)
232411022STom.Erickson@Sun.COM {
232511022STom.Erickson@Sun.COM 	int err = 0;
232611022STom.Erickson@Sun.COM 	nvlist_t *cleared_props = NULL;
232711022STom.Erickson@Sun.COM 	props_skip(props, skipped, &cleared_props);
232811022STom.Erickson@Sun.COM 	if (!nvlist_empty(cleared_props)) {
232911022STom.Erickson@Sun.COM 		/*
233011022STom.Erickson@Sun.COM 		 * Acts on local properties until the dataset has received
233111022STom.Erickson@Sun.COM 		 * properties at least once on or after SPA_VERSION_RECVD_PROPS.
233211022STom.Erickson@Sun.COM 		 */
233311022STom.Erickson@Sun.COM 		zprop_source_t flags = (ZPROP_SRC_NONE |
233411022STom.Erickson@Sun.COM 		    (dsl_prop_get_hasrecvd(os) ? ZPROP_SRC_RECEIVED : 0));
233511022STom.Erickson@Sun.COM 		err = zfs_set_prop_nvlist(fs, flags, cleared_props, NULL);
233611022STom.Erickson@Sun.COM 	}
233711022STom.Erickson@Sun.COM 	nvlist_free(cleared_props);
233811022STom.Erickson@Sun.COM 	return (err);
233911022STom.Erickson@Sun.COM }
234011022STom.Erickson@Sun.COM 
23419355SMatthew.Ahrens@Sun.COM /*
23425367Sahrens  * inputs:
23435367Sahrens  * zc_name		name of filesystem
23448697SRichard.Morris@Sun.COM  * zc_value		name of property to set
23455367Sahrens  * zc_nvlist_src{_size}	nvlist of properties to apply
234611022STom.Erickson@Sun.COM  * zc_cookie		received properties flag
23475367Sahrens  *
234811022STom.Erickson@Sun.COM  * outputs:
234911022STom.Erickson@Sun.COM  * zc_nvlist_dst{_size} error for each unapplied received property
23505367Sahrens  */
2351789Sahrens static int
23522676Seschrock zfs_ioc_set_prop(zfs_cmd_t *zc)
2353789Sahrens {
23542676Seschrock 	nvlist_t *nvl;
235511022STom.Erickson@Sun.COM 	boolean_t received = zc->zc_cookie;
235611022STom.Erickson@Sun.COM 	zprop_source_t source = (received ? ZPROP_SRC_RECEIVED :
235711022STom.Erickson@Sun.COM 	    ZPROP_SRC_LOCAL);
235811022STom.Erickson@Sun.COM 	nvlist_t *errors = NULL;
23592676Seschrock 	int error;
2360789Sahrens 
23615094Slling 	if ((error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
23629643SEric.Taylor@Sun.COM 	    zc->zc_iflags, &nvl)) != 0)
23632676Seschrock 		return (error);
23642676Seschrock 
236511022STom.Erickson@Sun.COM 	if (received) {
23667265Sahrens 		nvlist_t *origprops;
23677265Sahrens 		objset_t *os;
23687265Sahrens 
236910298SMatthew.Ahrens@Sun.COM 		if (dmu_objset_hold(zc->zc_name, FTAG, &os) == 0) {
237011022STom.Erickson@Sun.COM 			if (dsl_prop_get_received(os, &origprops) == 0) {
237111022STom.Erickson@Sun.COM 				(void) clear_received_props(os,
237211022STom.Erickson@Sun.COM 				    zc->zc_name, origprops, nvl);
23737265Sahrens 				nvlist_free(origprops);
23747265Sahrens 			}
237511022STom.Erickson@Sun.COM 
237611022STom.Erickson@Sun.COM 			dsl_prop_set_hasrecvd(os);
237710298SMatthew.Ahrens@Sun.COM 			dmu_objset_rele(os, FTAG);
23787265Sahrens 		}
23797265Sahrens 	}
23807265Sahrens 
238111022STom.Erickson@Sun.COM 	error = zfs_set_prop_nvlist(zc->zc_name, source, nvl, &errors);
238211022STom.Erickson@Sun.COM 
238311022STom.Erickson@Sun.COM 	if (zc->zc_nvlist_dst != NULL && errors != NULL) {
238411022STom.Erickson@Sun.COM 		(void) put_nvlist(zc, errors);
238511022STom.Erickson@Sun.COM 	}
238611022STom.Erickson@Sun.COM 
238711022STom.Erickson@Sun.COM 	nvlist_free(errors);
23882676Seschrock 	nvlist_free(nvl);
23892676Seschrock 	return (error);
2390789Sahrens }
2391789Sahrens 
23925367Sahrens /*
23935367Sahrens  * inputs:
23945367Sahrens  * zc_name		name of filesystem
23955367Sahrens  * zc_value		name of property to inherit
239611022STom.Erickson@Sun.COM  * zc_cookie		revert to received value if TRUE
23975367Sahrens  *
23985367Sahrens  * outputs:		none
23995367Sahrens  */
2400789Sahrens static int
24014849Sahrens zfs_ioc_inherit_prop(zfs_cmd_t *zc)
24024849Sahrens {
240311022STom.Erickson@Sun.COM 	const char *propname = zc->zc_value;
240411022STom.Erickson@Sun.COM 	zfs_prop_t prop = zfs_name_to_prop(propname);
240511022STom.Erickson@Sun.COM 	boolean_t received = zc->zc_cookie;
240611022STom.Erickson@Sun.COM 	zprop_source_t source = (received
240711022STom.Erickson@Sun.COM 	    ? ZPROP_SRC_NONE		/* revert to received value, if any */
240811022STom.Erickson@Sun.COM 	    : ZPROP_SRC_INHERITED);	/* explicitly inherit */
240911022STom.Erickson@Sun.COM 
241011022STom.Erickson@Sun.COM 	if (received) {
241111022STom.Erickson@Sun.COM 		nvlist_t *dummy;
241211022STom.Erickson@Sun.COM 		nvpair_t *pair;
241311022STom.Erickson@Sun.COM 		zprop_type_t type;
241411022STom.Erickson@Sun.COM 		int err;
241511022STom.Erickson@Sun.COM 
241611022STom.Erickson@Sun.COM 		/*
241711022STom.Erickson@Sun.COM 		 * zfs_prop_set_special() expects properties in the form of an
241811022STom.Erickson@Sun.COM 		 * nvpair with type info.
241911022STom.Erickson@Sun.COM 		 */
242011022STom.Erickson@Sun.COM 		if (prop == ZPROP_INVAL) {
242111022STom.Erickson@Sun.COM 			if (!zfs_prop_user(propname))
242211022STom.Erickson@Sun.COM 				return (EINVAL);
242311022STom.Erickson@Sun.COM 
242411022STom.Erickson@Sun.COM 			type = PROP_TYPE_STRING;
242511515STom.Erickson@Sun.COM 		} else if (prop == ZFS_PROP_VOLSIZE ||
242611515STom.Erickson@Sun.COM 		    prop == ZFS_PROP_VERSION) {
242711515STom.Erickson@Sun.COM 			return (EINVAL);
242811022STom.Erickson@Sun.COM 		} else {
242911022STom.Erickson@Sun.COM 			type = zfs_prop_get_type(prop);
243011022STom.Erickson@Sun.COM 		}
243111022STom.Erickson@Sun.COM 
243211022STom.Erickson@Sun.COM 		VERIFY(nvlist_alloc(&dummy, NV_UNIQUE_NAME, KM_SLEEP) == 0);
243311022STom.Erickson@Sun.COM 
243411022STom.Erickson@Sun.COM 		switch (type) {
243511022STom.Erickson@Sun.COM 		case PROP_TYPE_STRING:
243611022STom.Erickson@Sun.COM 			VERIFY(0 == nvlist_add_string(dummy, propname, ""));
243711022STom.Erickson@Sun.COM 			break;
243811022STom.Erickson@Sun.COM 		case PROP_TYPE_NUMBER:
243911022STom.Erickson@Sun.COM 		case PROP_TYPE_INDEX:
244011022STom.Erickson@Sun.COM 			VERIFY(0 == nvlist_add_uint64(dummy, propname, 0));
244111022STom.Erickson@Sun.COM 			break;
244211022STom.Erickson@Sun.COM 		default:
244311022STom.Erickson@Sun.COM 			nvlist_free(dummy);
244411022STom.Erickson@Sun.COM 			return (EINVAL);
244511022STom.Erickson@Sun.COM 		}
244611022STom.Erickson@Sun.COM 
244711022STom.Erickson@Sun.COM 		pair = nvlist_next_nvpair(dummy, NULL);
244811022STom.Erickson@Sun.COM 		err = zfs_prop_set_special(zc->zc_name, source, pair);
244911022STom.Erickson@Sun.COM 		nvlist_free(dummy);
245011022STom.Erickson@Sun.COM 		if (err != -1)
245111022STom.Erickson@Sun.COM 			return (err); /* special property already handled */
245211022STom.Erickson@Sun.COM 	} else {
245311022STom.Erickson@Sun.COM 		/*
245411022STom.Erickson@Sun.COM 		 * Only check this in the non-received case. We want to allow
245511022STom.Erickson@Sun.COM 		 * 'inherit -S' to revert non-inheritable properties like quota
245611022STom.Erickson@Sun.COM 		 * and reservation to the received or default values even though
245711022STom.Erickson@Sun.COM 		 * they are not considered inheritable.
245811022STom.Erickson@Sun.COM 		 */
245911022STom.Erickson@Sun.COM 		if (prop != ZPROP_INVAL && !zfs_prop_inheritable(prop))
246011022STom.Erickson@Sun.COM 			return (EINVAL);
246111022STom.Erickson@Sun.COM 	}
246211022STom.Erickson@Sun.COM 
24634849Sahrens 	/* the property name has been validated by zfs_secpolicy_inherit() */
246411022STom.Erickson@Sun.COM 	return (dsl_prop_set(zc->zc_name, zc->zc_value, source, 0, 0, NULL));
24654849Sahrens }
24664849Sahrens 
24674849Sahrens static int
24684098Slling zfs_ioc_pool_set_props(zfs_cmd_t *zc)
24693912Slling {
24705094Slling 	nvlist_t *props;
24713912Slling 	spa_t *spa;
24725094Slling 	int error;
247311022STom.Erickson@Sun.COM 	nvpair_t *pair;
247411022STom.Erickson@Sun.COM 
247511022STom.Erickson@Sun.COM 	if (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
247611022STom.Erickson@Sun.COM 	    zc->zc_iflags, &props))
24773912Slling 		return (error);
24783912Slling 
24798525SEric.Schrock@Sun.COM 	/*
24808525SEric.Schrock@Sun.COM 	 * If the only property is the configfile, then just do a spa_lookup()
24818525SEric.Schrock@Sun.COM 	 * to handle the faulted case.
24828525SEric.Schrock@Sun.COM 	 */
248311022STom.Erickson@Sun.COM 	pair = nvlist_next_nvpair(props, NULL);
248411022STom.Erickson@Sun.COM 	if (pair != NULL && strcmp(nvpair_name(pair),
24858525SEric.Schrock@Sun.COM 	    zpool_prop_to_name(ZPOOL_PROP_CACHEFILE)) == 0 &&
248611022STom.Erickson@Sun.COM 	    nvlist_next_nvpair(props, pair) == NULL) {
24878525SEric.Schrock@Sun.COM 		mutex_enter(&spa_namespace_lock);
24888525SEric.Schrock@Sun.COM 		if ((spa = spa_lookup(zc->zc_name)) != NULL) {
24898525SEric.Schrock@Sun.COM 			spa_configfile_set(spa, props, B_FALSE);
24908525SEric.Schrock@Sun.COM 			spa_config_sync(spa, B_FALSE, B_TRUE);
24918525SEric.Schrock@Sun.COM 		}
24928525SEric.Schrock@Sun.COM 		mutex_exit(&spa_namespace_lock);
249310672SEric.Schrock@Sun.COM 		if (spa != NULL) {
249410672SEric.Schrock@Sun.COM 			nvlist_free(props);
24958525SEric.Schrock@Sun.COM 			return (0);
249610672SEric.Schrock@Sun.COM 		}
24978525SEric.Schrock@Sun.COM 	}
24988525SEric.Schrock@Sun.COM 
24993912Slling 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) {
25005094Slling 		nvlist_free(props);
25013912Slling 		return (error);
25023912Slling 	}
25033912Slling 
25045094Slling 	error = spa_prop_set(spa, props);
25053912Slling 
25065094Slling 	nvlist_free(props);
25073912Slling 	spa_close(spa, FTAG);
25083912Slling 
25093912Slling 	return (error);
25103912Slling }
25113912Slling 
25123912Slling static int
25134098Slling zfs_ioc_pool_get_props(zfs_cmd_t *zc)
25143912Slling {
25153912Slling 	spa_t *spa;
25163912Slling 	int error;
25173912Slling 	nvlist_t *nvp = NULL;
25183912Slling 
25198525SEric.Schrock@Sun.COM 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) {
25208525SEric.Schrock@Sun.COM 		/*
25218525SEric.Schrock@Sun.COM 		 * If the pool is faulted, there may be properties we can still
25228525SEric.Schrock@Sun.COM 		 * get (such as altroot and cachefile), so attempt to get them
25238525SEric.Schrock@Sun.COM 		 * anyway.
25248525SEric.Schrock@Sun.COM 		 */
25258525SEric.Schrock@Sun.COM 		mutex_enter(&spa_namespace_lock);
25268525SEric.Schrock@Sun.COM 		if ((spa = spa_lookup(zc->zc_name)) != NULL)
25278525SEric.Schrock@Sun.COM 			error = spa_prop_get(spa, &nvp);
25288525SEric.Schrock@Sun.COM 		mutex_exit(&spa_namespace_lock);
25298525SEric.Schrock@Sun.COM 	} else {
25308525SEric.Schrock@Sun.COM 		error = spa_prop_get(spa, &nvp);
25318525SEric.Schrock@Sun.COM 		spa_close(spa, FTAG);
25328525SEric.Schrock@Sun.COM 	}
25333912Slling 
25343912Slling 	if (error == 0 && zc->zc_nvlist_dst != NULL)
25353912Slling 		error = put_nvlist(zc, nvp);
25363912Slling 	else
25373912Slling 		error = EFAULT;
25383912Slling 
25398525SEric.Schrock@Sun.COM 	nvlist_free(nvp);
25403912Slling 	return (error);
25413912Slling }
25423912Slling 
25435367Sahrens /*
25445367Sahrens  * inputs:
25455367Sahrens  * zc_name		name of filesystem
25465367Sahrens  * zc_nvlist_src{_size}	nvlist of delegated permissions
25475367Sahrens  * zc_perm_action	allow/unallow flag
25485367Sahrens  *
25495367Sahrens  * outputs:		none
25505367Sahrens  */
25514543Smarks static int
25524543Smarks zfs_ioc_set_fsacl(zfs_cmd_t *zc)
25534543Smarks {
25544543Smarks 	int error;
25554543Smarks 	nvlist_t *fsaclnv = NULL;
25564543Smarks 
25575094Slling 	if ((error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
25589643SEric.Taylor@Sun.COM 	    zc->zc_iflags, &fsaclnv)) != 0)
25594543Smarks 		return (error);
25604543Smarks 
25614543Smarks 	/*
25624543Smarks 	 * Verify nvlist is constructed correctly
25634543Smarks 	 */
25644543Smarks 	if ((error = zfs_deleg_verify_nvlist(fsaclnv)) != 0) {
25654543Smarks 		nvlist_free(fsaclnv);
25664543Smarks 		return (EINVAL);
25674543Smarks 	}
25684543Smarks 
25694543Smarks 	/*
25704543Smarks 	 * If we don't have PRIV_SYS_MOUNT, then validate
25714543Smarks 	 * that user is allowed to hand out each permission in
25724543Smarks 	 * the nvlist(s)
25734543Smarks 	 */
25744543Smarks 
25754787Sahrens 	error = secpolicy_zfs(CRED());
25764543Smarks 	if (error) {
25774787Sahrens 		if (zc->zc_perm_action == B_FALSE) {
25784787Sahrens 			error = dsl_deleg_can_allow(zc->zc_name,
25794787Sahrens 			    fsaclnv, CRED());
25804787Sahrens 		} else {
25814787Sahrens 			error = dsl_deleg_can_unallow(zc->zc_name,
25824787Sahrens 			    fsaclnv, CRED());
25834787Sahrens 		}
25844543Smarks 	}
25854543Smarks 
25864543Smarks 	if (error == 0)
25874543Smarks 		error = dsl_deleg_set(zc->zc_name, fsaclnv, zc->zc_perm_action);
25884543Smarks 
25894543Smarks 	nvlist_free(fsaclnv);
25904543Smarks 	return (error);
25914543Smarks }
25924543Smarks 
25935367Sahrens /*
25945367Sahrens  * inputs:
25955367Sahrens  * zc_name		name of filesystem
25965367Sahrens  *
25975367Sahrens  * outputs:
25985367Sahrens  * zc_nvlist_src{_size}	nvlist of delegated permissions
25995367Sahrens  */
26004543Smarks static int
26014543Smarks zfs_ioc_get_fsacl(zfs_cmd_t *zc)
26024543Smarks {
26034543Smarks 	nvlist_t *nvp;
26044543Smarks 	int error;
26054543Smarks 
26064543Smarks 	if ((error = dsl_deleg_get(zc->zc_name, &nvp)) == 0) {
26074543Smarks 		error = put_nvlist(zc, nvp);
26084543Smarks 		nvlist_free(nvp);
26094543Smarks 	}
26104543Smarks 
26114543Smarks 	return (error);
26124543Smarks }
26134543Smarks 
26145367Sahrens /*
2615789Sahrens  * Search the vfs list for a specified resource.  Returns a pointer to it
2616789Sahrens  * or NULL if no suitable entry is found. The caller of this routine
2617789Sahrens  * is responsible for releasing the returned vfs pointer.
2618789Sahrens  */
2619789Sahrens static vfs_t *
2620789Sahrens zfs_get_vfs(const char *resource)
2621789Sahrens {
2622789Sahrens 	struct vfs *vfsp;
2623789Sahrens 	struct vfs *vfs_found = NULL;
2624789Sahrens 
2625789Sahrens 	vfs_list_read_lock();
2626789Sahrens 	vfsp = rootvfs;
2627789Sahrens 	do {
2628789Sahrens 		if (strcmp(refstr_value(vfsp->vfs_resource), resource) == 0) {
2629789Sahrens 			VFS_HOLD(vfsp);
2630789Sahrens 			vfs_found = vfsp;
2631789Sahrens 			break;
2632789Sahrens 		}
2633789Sahrens 		vfsp = vfsp->vfs_next;
2634789Sahrens 	} while (vfsp != rootvfs);
2635789Sahrens 	vfs_list_unlock();
2636789Sahrens 	return (vfs_found);
2637789Sahrens }
2638789Sahrens 
26394543Smarks /* ARGSUSED */
2640789Sahrens static void
26414543Smarks zfs_create_cb(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx)
2642789Sahrens {
26435331Samw 	zfs_creat_t *zct = arg;
26445498Stimh 
26455498Stimh 	zfs_create_fs(os, cr, zct->zct_zplprops, tx);
26465331Samw }
26475331Samw 
26485498Stimh #define	ZFS_PROP_UNDEFINED	((uint64_t)-1)
26495498Stimh 
26505331Samw /*
26515498Stimh  * inputs:
26527184Stimh  * createprops		list of properties requested by creator
26537184Stimh  * default_zplver	zpl version to use if unspecified in createprops
26547184Stimh  * fuids_ok		fuids allowed in this version of the spa?
26557184Stimh  * os			parent objset pointer (NULL if root fs)
26565331Samw  *
26575498Stimh  * outputs:
26585498Stimh  * zplprops	values for the zplprops we attach to the master node object
26597184Stimh  * is_ci	true if requested file system will be purely case-insensitive
26605331Samw  *
26615498Stimh  * Determine the settings for utf8only, normalization and
26625498Stimh  * casesensitivity.  Specific values may have been requested by the
26635498Stimh  * creator and/or we can inherit values from the parent dataset.  If
26645498Stimh  * the file system is of too early a vintage, a creator can not
26655498Stimh  * request settings for these properties, even if the requested
26665498Stimh  * setting is the default value.  We don't actually want to create dsl
26675498Stimh  * properties for these, so remove them from the source nvlist after
26685498Stimh  * processing.
26695331Samw  */
26705331Samw static int
26719396SMatthew.Ahrens@Sun.COM zfs_fill_zplprops_impl(objset_t *os, uint64_t zplver,
267211935SMark.Shellenbaum@Sun.COM     boolean_t fuids_ok, boolean_t sa_ok, nvlist_t *createprops,
267311935SMark.Shellenbaum@Sun.COM     nvlist_t *zplprops, boolean_t *is_ci)
26745331Samw {
26755498Stimh 	uint64_t sense = ZFS_PROP_UNDEFINED;
26765498Stimh 	uint64_t norm = ZFS_PROP_UNDEFINED;
26775498Stimh 	uint64_t u8 = ZFS_PROP_UNDEFINED;
26785498Stimh 
26795498Stimh 	ASSERT(zplprops != NULL);
26805498Stimh 
26815375Stimh 	/*
26825498Stimh 	 * Pull out creator prop choices, if any.
26835375Stimh 	 */
26845498Stimh 	if (createprops) {
26855498Stimh 		(void) nvlist_lookup_uint64(createprops,
26867184Stimh 		    zfs_prop_to_name(ZFS_PROP_VERSION), &zplver);
26877184Stimh 		(void) nvlist_lookup_uint64(createprops,
26885498Stimh 		    zfs_prop_to_name(ZFS_PROP_NORMALIZE), &norm);
26895498Stimh 		(void) nvlist_remove_all(createprops,
26905498Stimh 		    zfs_prop_to_name(ZFS_PROP_NORMALIZE));
26915498Stimh 		(void) nvlist_lookup_uint64(createprops,
26925498Stimh 		    zfs_prop_to_name(ZFS_PROP_UTF8ONLY), &u8);
26935498Stimh 		(void) nvlist_remove_all(createprops,
26945498Stimh 		    zfs_prop_to_name(ZFS_PROP_UTF8ONLY));
26955498Stimh 		(void) nvlist_lookup_uint64(createprops,
26965498Stimh 		    zfs_prop_to_name(ZFS_PROP_CASE), &sense);
26975498Stimh 		(void) nvlist_remove_all(createprops,
26985498Stimh 		    zfs_prop_to_name(ZFS_PROP_CASE));
26995331Samw 	}
27005331Samw 
27015375Stimh 	/*
27027184Stimh 	 * If the zpl version requested is whacky or the file system
27037184Stimh 	 * or pool is version is too "young" to support normalization
27047184Stimh 	 * and the creator tried to set a value for one of the props,
27057184Stimh 	 * error out.
27065498Stimh 	 */
27077184Stimh 	if ((zplver < ZPL_VERSION_INITIAL || zplver > ZPL_VERSION) ||
27087184Stimh 	    (zplver >= ZPL_VERSION_FUID && !fuids_ok) ||
270911935SMark.Shellenbaum@Sun.COM 	    (zplver >= ZPL_VERSION_SA && !sa_ok) ||
27107184Stimh 	    (zplver < ZPL_VERSION_NORMALIZATION &&
27115498Stimh 	    (norm != ZFS_PROP_UNDEFINED || u8 != ZFS_PROP_UNDEFINED ||
27127184Stimh 	    sense != ZFS_PROP_UNDEFINED)))
27135498Stimh 		return (ENOTSUP);
27145498Stimh 
27155498Stimh 	/*
27165498Stimh 	 * Put the version in the zplprops
27175498Stimh 	 */
27185498Stimh 	VERIFY(nvlist_add_uint64(zplprops,
27195498Stimh 	    zfs_prop_to_name(ZFS_PROP_VERSION), zplver) == 0);
27205498Stimh 
27215498Stimh 	if (norm == ZFS_PROP_UNDEFINED)
27225498Stimh 		VERIFY(zfs_get_zplprop(os, ZFS_PROP_NORMALIZE, &norm) == 0);
27235498Stimh 	VERIFY(nvlist_add_uint64(zplprops,
27245498Stimh 	    zfs_prop_to_name(ZFS_PROP_NORMALIZE), norm) == 0);
27255498Stimh 
27265498Stimh 	/*
27275498Stimh 	 * If we're normalizing, names must always be valid UTF-8 strings.
27285498Stimh 	 */
27295498Stimh 	if (norm)
27305498Stimh 		u8 = 1;
27315498Stimh 	if (u8 == ZFS_PROP_UNDEFINED)
27325498Stimh 		VERIFY(zfs_get_zplprop(os, ZFS_PROP_UTF8ONLY, &u8) == 0);
27335498Stimh 	VERIFY(nvlist_add_uint64(zplprops,
27345498Stimh 	    zfs_prop_to_name(ZFS_PROP_UTF8ONLY), u8) == 0);
27355498Stimh 
27365498Stimh 	if (sense == ZFS_PROP_UNDEFINED)
27375498Stimh 		VERIFY(zfs_get_zplprop(os, ZFS_PROP_CASE, &sense) == 0);
27385498Stimh 	VERIFY(nvlist_add_uint64(zplprops,
27395498Stimh 	    zfs_prop_to_name(ZFS_PROP_CASE), sense) == 0);
27405498Stimh 
27416492Stimh 	if (is_ci)
27426492Stimh 		*is_ci = (sense == ZFS_CASE_INSENSITIVE);
27436492Stimh 
27447184Stimh 	return (0);
27457184Stimh }
27467184Stimh 
27477184Stimh static int
27487184Stimh zfs_fill_zplprops(const char *dataset, nvlist_t *createprops,
27497184Stimh     nvlist_t *zplprops, boolean_t *is_ci)
27507184Stimh {
275111935SMark.Shellenbaum@Sun.COM 	boolean_t fuids_ok, sa_ok;
27527184Stimh 	uint64_t zplver = ZPL_VERSION;
27537184Stimh 	objset_t *os = NULL;
27547184Stimh 	char parentname[MAXNAMELEN];
27557184Stimh 	char *cp;
275611935SMark.Shellenbaum@Sun.COM 	spa_t *spa;
275711935SMark.Shellenbaum@Sun.COM 	uint64_t spa_vers;
27587184Stimh 	int error;
27597184Stimh 
27607184Stimh 	(void) strlcpy(parentname, dataset, sizeof (parentname));
27617184Stimh 	cp = strrchr(parentname, '/');
27627184Stimh 	ASSERT(cp != NULL);
27637184Stimh 	cp[0] = '\0';
27647184Stimh 
276511935SMark.Shellenbaum@Sun.COM 	if ((error = spa_open(dataset, &spa, FTAG)) != 0)
276611935SMark.Shellenbaum@Sun.COM 		return (error);
276711935SMark.Shellenbaum@Sun.COM 
276811935SMark.Shellenbaum@Sun.COM 	spa_vers = spa_version(spa);
276911935SMark.Shellenbaum@Sun.COM 	spa_close(spa, FTAG);
277011935SMark.Shellenbaum@Sun.COM 
277111935SMark.Shellenbaum@Sun.COM 	zplver = zfs_zpl_version_map(spa_vers);
277211935SMark.Shellenbaum@Sun.COM 	fuids_ok = (zplver >= ZPL_VERSION_FUID);
277311935SMark.Shellenbaum@Sun.COM 	sa_ok = (zplver >= ZPL_VERSION_SA);
27747184Stimh 
27757184Stimh 	/*
27767184Stimh 	 * Open parent object set so we can inherit zplprop values.
27777184Stimh 	 */
277810298SMatthew.Ahrens@Sun.COM 	if ((error = dmu_objset_hold(parentname, FTAG, &os)) != 0)
27797184Stimh 		return (error);
27807184Stimh 
278111935SMark.Shellenbaum@Sun.COM 	error = zfs_fill_zplprops_impl(os, zplver, fuids_ok, sa_ok, createprops,
27827184Stimh 	    zplprops, is_ci);
278310298SMatthew.Ahrens@Sun.COM 	dmu_objset_rele(os, FTAG);
27847184Stimh 	return (error);
27857184Stimh }
27867184Stimh 
27877184Stimh static int
27887184Stimh zfs_fill_zplprops_root(uint64_t spa_vers, nvlist_t *createprops,
27897184Stimh     nvlist_t *zplprops, boolean_t *is_ci)
27907184Stimh {
279111935SMark.Shellenbaum@Sun.COM 	boolean_t fuids_ok;
279211935SMark.Shellenbaum@Sun.COM 	boolean_t sa_ok;
27937184Stimh 	uint64_t zplver = ZPL_VERSION;
27947184Stimh 	int error;
27957184Stimh 
279611935SMark.Shellenbaum@Sun.COM 	zplver = zfs_zpl_version_map(spa_vers);
279711935SMark.Shellenbaum@Sun.COM 	fuids_ok = (zplver >= ZPL_VERSION_FUID);
279811935SMark.Shellenbaum@Sun.COM 	sa_ok = (zplver >= ZPL_VERSION_SA);
279911935SMark.Shellenbaum@Sun.COM 
280011935SMark.Shellenbaum@Sun.COM 	error = zfs_fill_zplprops_impl(NULL, zplver, fuids_ok, sa_ok,
280111935SMark.Shellenbaum@Sun.COM 	    createprops, zplprops, is_ci);
28027184Stimh 	return (error);
2803789Sahrens }
2804789Sahrens 
28055367Sahrens /*
28065367Sahrens  * inputs:
28075367Sahrens  * zc_objset_type	type of objset to create (fs vs zvol)
28085367Sahrens  * zc_name		name of new objset
28095367Sahrens  * zc_value		name of snapshot to clone from (may be empty)
28105367Sahrens  * zc_nvlist_src{_size}	nvlist of properties to apply
28115367Sahrens  *
28125498Stimh  * outputs: none
28135367Sahrens  */
2814789Sahrens static int
2815789Sahrens zfs_ioc_create(zfs_cmd_t *zc)
2816789Sahrens {
2817789Sahrens 	objset_t *clone;
2818789Sahrens 	int error = 0;
28195331Samw 	zfs_creat_t zct;
28204543Smarks 	nvlist_t *nvprops = NULL;
28214543Smarks 	void (*cbfunc)(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx);
2822789Sahrens 	dmu_objset_type_t type = zc->zc_objset_type;
2823789Sahrens 
2824789Sahrens 	switch (type) {
2825789Sahrens 
2826789Sahrens 	case DMU_OST_ZFS:
2827789Sahrens 		cbfunc = zfs_create_cb;
2828789Sahrens 		break;
2829789Sahrens 
2830789Sahrens 	case DMU_OST_ZVOL:
2831789Sahrens 		cbfunc = zvol_create_cb;
2832789Sahrens 		break;
2833789Sahrens 
2834789Sahrens 	default:
28352199Sahrens 		cbfunc = NULL;
28366423Sgw25295 		break;
28372199Sahrens 	}
28385326Sek110237 	if (strchr(zc->zc_name, '@') ||
28395326Sek110237 	    strchr(zc->zc_name, '%'))
2840789Sahrens 		return (EINVAL);
2841789Sahrens 
28422676Seschrock 	if (zc->zc_nvlist_src != NULL &&
28435094Slling 	    (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
28449643SEric.Taylor@Sun.COM 	    zc->zc_iflags, &nvprops)) != 0)
28452676Seschrock 		return (error);
28462676Seschrock 
28475498Stimh 	zct.zct_zplprops = NULL;
28485331Samw 	zct.zct_props = nvprops;
28495331Samw 
28502676Seschrock 	if (zc->zc_value[0] != '\0') {
2851789Sahrens 		/*
2852789Sahrens 		 * We're creating a clone of an existing snapshot.
2853789Sahrens 		 */
28542676Seschrock 		zc->zc_value[sizeof (zc->zc_value) - 1] = '\0';
28552676Seschrock 		if (dataset_namecheck(zc->zc_value, NULL, NULL) != 0) {
28564543Smarks 			nvlist_free(nvprops);
2857789Sahrens 			return (EINVAL);
28582676Seschrock 		}
2859789Sahrens 
286010298SMatthew.Ahrens@Sun.COM 		error = dmu_objset_hold(zc->zc_value, FTAG, &clone);
28612676Seschrock 		if (error) {
28624543Smarks 			nvlist_free(nvprops);
2863789Sahrens 			return (error);
28642676Seschrock 		}
28656492Stimh 
286610272SMatthew.Ahrens@Sun.COM 		error = dmu_objset_clone(zc->zc_name, dmu_objset_ds(clone), 0);
286710298SMatthew.Ahrens@Sun.COM 		dmu_objset_rele(clone, FTAG);
28685331Samw 		if (error) {
28695331Samw 			nvlist_free(nvprops);
28705331Samw 			return (error);
28715331Samw 		}
2872789Sahrens 	} else {
28736492Stimh 		boolean_t is_insensitive = B_FALSE;
28746492Stimh 
28752676Seschrock 		if (cbfunc == NULL) {
28764543Smarks 			nvlist_free(nvprops);
28772199Sahrens 			return (EINVAL);
28782676Seschrock 		}
28792676Seschrock 
2880789Sahrens 		if (type == DMU_OST_ZVOL) {
28812676Seschrock 			uint64_t volsize, volblocksize;
28822676Seschrock 
28834543Smarks 			if (nvprops == NULL ||
28844543Smarks 			    nvlist_lookup_uint64(nvprops,
28852676Seschrock 			    zfs_prop_to_name(ZFS_PROP_VOLSIZE),
28862676Seschrock 			    &volsize) != 0) {
28874543Smarks 				nvlist_free(nvprops);
28882676Seschrock 				return (EINVAL);
28892676Seschrock 			}
28902676Seschrock 
28914543Smarks 			if ((error = nvlist_lookup_uint64(nvprops,
28922676Seschrock 			    zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE),
28932676Seschrock 			    &volblocksize)) != 0 && error != ENOENT) {
28944543Smarks 				nvlist_free(nvprops);
28952676Seschrock 				return (EINVAL);
28962676Seschrock 			}
28971133Seschrock 
28982676Seschrock 			if (error != 0)
28992676Seschrock 				volblocksize = zfs_prop_default_numeric(
29002676Seschrock 				    ZFS_PROP_VOLBLOCKSIZE);
29012676Seschrock 
29022676Seschrock 			if ((error = zvol_check_volblocksize(
29032676Seschrock 			    volblocksize)) != 0 ||
29042676Seschrock 			    (error = zvol_check_volsize(volsize,
29052676Seschrock 			    volblocksize)) != 0) {
29064543Smarks 				nvlist_free(nvprops);
2907789Sahrens 				return (error);
29082676Seschrock 			}
29094577Sahrens 		} else if (type == DMU_OST_ZFS) {
29105331Samw 			int error;
29115331Samw 
29125498Stimh 			/*
29135331Samw 			 * We have to have normalization and
29145331Samw 			 * case-folding flags correct when we do the
29155331Samw 			 * file system creation, so go figure them out
29165498Stimh 			 * now.
29175331Samw 			 */
29185498Stimh 			VERIFY(nvlist_alloc(&zct.zct_zplprops,
29195498Stimh 			    NV_UNIQUE_NAME, KM_SLEEP) == 0);
29205498Stimh 			error = zfs_fill_zplprops(zc->zc_name, nvprops,
29217184Stimh 			    zct.zct_zplprops, &is_insensitive);
29225331Samw 			if (error != 0) {
29235331Samw 				nvlist_free(nvprops);
29245498Stimh 				nvlist_free(zct.zct_zplprops);
29255331Samw 				return (error);
29264577Sahrens 			}
29272676Seschrock 		}
292810272SMatthew.Ahrens@Sun.COM 		error = dmu_objset_create(zc->zc_name, type,
29296492Stimh 		    is_insensitive ? DS_FLAG_CI_DATASET : 0, cbfunc, &zct);
29305498Stimh 		nvlist_free(zct.zct_zplprops);
2931789Sahrens 	}
29322676Seschrock 
29332676Seschrock 	/*
29342676Seschrock 	 * It would be nice to do this atomically.
29352676Seschrock 	 */
29362676Seschrock 	if (error == 0) {
293711022STom.Erickson@Sun.COM 		error = zfs_set_prop_nvlist(zc->zc_name, ZPROP_SRC_LOCAL,
293811022STom.Erickson@Sun.COM 		    nvprops, NULL);
293911022STom.Erickson@Sun.COM 		if (error != 0)
294010242Schris.kirby@sun.com 			(void) dmu_objset_destroy(zc->zc_name, B_FALSE);
29412676Seschrock 	}
29424543Smarks 	nvlist_free(nvprops);
2943789Sahrens 	return (error);
2944789Sahrens }
2945789Sahrens 
29465367Sahrens /*
29475367Sahrens  * inputs:
29485367Sahrens  * zc_name	name of filesystem
29495367Sahrens  * zc_value	short name of snapshot
29505367Sahrens  * zc_cookie	recursive flag
29519396SMatthew.Ahrens@Sun.COM  * zc_nvlist_src[_size] property list
29525367Sahrens  *
295310588SEric.Taylor@Sun.COM  * outputs:
295410588SEric.Taylor@Sun.COM  * zc_value	short snapname (i.e. part after the '@')
29555367Sahrens  */
2956789Sahrens static int
29572199Sahrens zfs_ioc_snapshot(zfs_cmd_t *zc)
29582199Sahrens {
29597265Sahrens 	nvlist_t *nvprops = NULL;
29607265Sahrens 	int error;
29617265Sahrens 	boolean_t recursive = zc->zc_cookie;
29627265Sahrens 
29632676Seschrock 	if (snapshot_namecheck(zc->zc_value, NULL, NULL) != 0)
29642199Sahrens 		return (EINVAL);
29657265Sahrens 
29667265Sahrens 	if (zc->zc_nvlist_src != NULL &&
29677265Sahrens 	    (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
29689643SEric.Taylor@Sun.COM 	    zc->zc_iflags, &nvprops)) != 0)
29697265Sahrens 		return (error);
29707265Sahrens 
29719355SMatthew.Ahrens@Sun.COM 	error = zfs_check_userprops(zc->zc_name, nvprops);
29729355SMatthew.Ahrens@Sun.COM 	if (error)
29739355SMatthew.Ahrens@Sun.COM 		goto out;
29749355SMatthew.Ahrens@Sun.COM 
297511022STom.Erickson@Sun.COM 	if (!nvlist_empty(nvprops) &&
29769355SMatthew.Ahrens@Sun.COM 	    zfs_earlier_version(zc->zc_name, SPA_VERSION_SNAP_PROPS)) {
29779355SMatthew.Ahrens@Sun.COM 		error = ENOTSUP;
29789355SMatthew.Ahrens@Sun.COM 		goto out;
29797265Sahrens 	}
29809355SMatthew.Ahrens@Sun.COM 
29819355SMatthew.Ahrens@Sun.COM 	error = dmu_objset_snapshot(zc->zc_name, zc->zc_value,
29829355SMatthew.Ahrens@Sun.COM 	    nvprops, recursive);
29839355SMatthew.Ahrens@Sun.COM 
29849355SMatthew.Ahrens@Sun.COM out:
29857265Sahrens 	nvlist_free(nvprops);
29867265Sahrens 	return (error);
29872199Sahrens }
29882199Sahrens 
29894007Smmusante int
299011209SMatthew.Ahrens@Sun.COM zfs_unmount_snap(const char *name, void *arg)
2991789Sahrens {
29922417Sahrens 	vfs_t *vfsp = NULL;
29932199Sahrens 
29946689Smaybee 	if (arg) {
29956689Smaybee 		char *snapname = arg;
299611209SMatthew.Ahrens@Sun.COM 		char *fullname = kmem_asprintf("%s@%s", name, snapname);
299711209SMatthew.Ahrens@Sun.COM 		vfsp = zfs_get_vfs(fullname);
299811209SMatthew.Ahrens@Sun.COM 		strfree(fullname);
29992417Sahrens 	} else if (strchr(name, '@')) {
30002199Sahrens 		vfsp = zfs_get_vfs(name);
30012199Sahrens 	}
30022199Sahrens 
30032199Sahrens 	if (vfsp) {
30042199Sahrens 		/*
30052199Sahrens 		 * Always force the unmount for snapshots.
30062199Sahrens 		 */
30072199Sahrens 		int flag = MS_FORCE;
3008789Sahrens 		int err;
3009789Sahrens 
30102199Sahrens 		if ((err = vn_vfswlock(vfsp->vfs_vnodecovered)) != 0) {
30112199Sahrens 			VFS_RELE(vfsp);
30122199Sahrens 			return (err);
30132199Sahrens 		}
30142199Sahrens 		VFS_RELE(vfsp);
30152199Sahrens 		if ((err = dounmount(vfsp, flag, kcred)) != 0)
30162199Sahrens 			return (err);
30172199Sahrens 	}
30182199Sahrens 	return (0);
30192199Sahrens }
30202199Sahrens 
30215367Sahrens /*
30225367Sahrens  * inputs:
302310242Schris.kirby@sun.com  * zc_name		name of filesystem
302410242Schris.kirby@sun.com  * zc_value		short name of snapshot
302510242Schris.kirby@sun.com  * zc_defer_destroy	mark for deferred destroy
30265367Sahrens  *
30275367Sahrens  * outputs:	none
30285367Sahrens  */
30292199Sahrens static int
30302199Sahrens zfs_ioc_destroy_snaps(zfs_cmd_t *zc)
30312199Sahrens {
30322199Sahrens 	int err;
3033789Sahrens 
30342676Seschrock 	if (snapshot_namecheck(zc->zc_value, NULL, NULL) != 0)
30352199Sahrens 		return (EINVAL);
30362199Sahrens 	err = dmu_objset_find(zc->zc_name,
30372676Seschrock 	    zfs_unmount_snap, zc->zc_value, DS_FIND_CHILDREN);
30382199Sahrens 	if (err)
30392199Sahrens 		return (err);
304010242Schris.kirby@sun.com 	return (dmu_snapshots_destroy(zc->zc_name, zc->zc_value,
304110242Schris.kirby@sun.com 	    zc->zc_defer_destroy));
30422199Sahrens }
30432199Sahrens 
30445367Sahrens /*
30455367Sahrens  * inputs:
30465367Sahrens  * zc_name		name of dataset to destroy
30475367Sahrens  * zc_objset_type	type of objset
304810242Schris.kirby@sun.com  * zc_defer_destroy	mark for deferred destroy
30495367Sahrens  *
30505367Sahrens  * outputs:		none
30515367Sahrens  */
30522199Sahrens static int
30532199Sahrens zfs_ioc_destroy(zfs_cmd_t *zc)
30542199Sahrens {
305510588SEric.Taylor@Sun.COM 	int err;
30562199Sahrens 	if (strchr(zc->zc_name, '@') && zc->zc_objset_type == DMU_OST_ZFS) {
305710588SEric.Taylor@Sun.COM 		err = zfs_unmount_snap(zc->zc_name, NULL);
30582199Sahrens 		if (err)
30592199Sahrens 			return (err);
3060789Sahrens 	}
3061789Sahrens 
306210588SEric.Taylor@Sun.COM 	err = dmu_objset_destroy(zc->zc_name, zc->zc_defer_destroy);
306310588SEric.Taylor@Sun.COM 	if (zc->zc_objset_type == DMU_OST_ZVOL && err == 0)
306410693Schris.kirby@sun.com 		(void) zvol_remove_minor(zc->zc_name);
306510588SEric.Taylor@Sun.COM 	return (err);
3066789Sahrens }
3067789Sahrens 
30685367Sahrens /*
30695367Sahrens  * inputs:
30705446Sahrens  * zc_name	name of dataset to rollback (to most recent snapshot)
30715367Sahrens  *
30725367Sahrens  * outputs:	none
30735367Sahrens  */
3074789Sahrens static int
3075789Sahrens zfs_ioc_rollback(zfs_cmd_t *zc)
3076789Sahrens {
307710272SMatthew.Ahrens@Sun.COM 	dsl_dataset_t *ds, *clone;
30785446Sahrens 	int error;
307910272SMatthew.Ahrens@Sun.COM 	zfsvfs_t *zfsvfs;
308010272SMatthew.Ahrens@Sun.COM 	char *clone_name;
308110272SMatthew.Ahrens@Sun.COM 
308210272SMatthew.Ahrens@Sun.COM 	error = dsl_dataset_hold(zc->zc_name, FTAG, &ds);
308310272SMatthew.Ahrens@Sun.COM 	if (error)
308410272SMatthew.Ahrens@Sun.COM 		return (error);
308510272SMatthew.Ahrens@Sun.COM 
308610272SMatthew.Ahrens@Sun.COM 	/* must not be a snapshot */
308710272SMatthew.Ahrens@Sun.COM 	if (dsl_dataset_is_snapshot(ds)) {
308810272SMatthew.Ahrens@Sun.COM 		dsl_dataset_rele(ds, FTAG);
308910272SMatthew.Ahrens@Sun.COM 		return (EINVAL);
309010272SMatthew.Ahrens@Sun.COM 	}
309110272SMatthew.Ahrens@Sun.COM 
309210272SMatthew.Ahrens@Sun.COM 	/* must have a most recent snapshot */
309310272SMatthew.Ahrens@Sun.COM 	if (ds->ds_phys->ds_prev_snap_txg < TXG_INITIAL) {
309410272SMatthew.Ahrens@Sun.COM 		dsl_dataset_rele(ds, FTAG);
309510272SMatthew.Ahrens@Sun.COM 		return (EINVAL);
309610272SMatthew.Ahrens@Sun.COM 	}
30975446Sahrens 
30985446Sahrens 	/*
309910272SMatthew.Ahrens@Sun.COM 	 * Create clone of most recent snapshot.
31005446Sahrens 	 */
310110272SMatthew.Ahrens@Sun.COM 	clone_name = kmem_asprintf("%s/%%rollback", zc->zc_name);
310210272SMatthew.Ahrens@Sun.COM 	error = dmu_objset_clone(clone_name, ds->ds_prev, DS_FLAG_INCONSISTENT);
31035446Sahrens 	if (error)
310410272SMatthew.Ahrens@Sun.COM 		goto out;
310510272SMatthew.Ahrens@Sun.COM 
310610298SMatthew.Ahrens@Sun.COM 	error = dsl_dataset_own(clone_name, B_TRUE, FTAG, &clone);
310710272SMatthew.Ahrens@Sun.COM 	if (error)
310810272SMatthew.Ahrens@Sun.COM 		goto out;
310910272SMatthew.Ahrens@Sun.COM 
311010272SMatthew.Ahrens@Sun.COM 	/*
311110272SMatthew.Ahrens@Sun.COM 	 * Do clone swap.
311210272SMatthew.Ahrens@Sun.COM 	 */
31139396SMatthew.Ahrens@Sun.COM 	if (getzfsvfs(zc->zc_name, &zfsvfs) == 0) {
311410298SMatthew.Ahrens@Sun.COM 		error = zfs_suspend_fs(zfsvfs);
31156083Sek110237 		if (error == 0) {
31166083Sek110237 			int resume_err;
31176083Sek110237 
311810272SMatthew.Ahrens@Sun.COM 			if (dsl_dataset_tryown(ds, B_FALSE, FTAG)) {
311910272SMatthew.Ahrens@Sun.COM 				error = dsl_dataset_clone_swap(clone, ds,
312010272SMatthew.Ahrens@Sun.COM 				    B_TRUE);
312110272SMatthew.Ahrens@Sun.COM 				dsl_dataset_disown(ds, FTAG);
312210272SMatthew.Ahrens@Sun.COM 				ds = NULL;
312310272SMatthew.Ahrens@Sun.COM 			} else {
312410272SMatthew.Ahrens@Sun.COM 				error = EBUSY;
312510272SMatthew.Ahrens@Sun.COM 			}
312610298SMatthew.Ahrens@Sun.COM 			resume_err = zfs_resume_fs(zfsvfs, zc->zc_name);
31276083Sek110237 			error = error ? error : resume_err;
31286083Sek110237 		}
31295446Sahrens 		VFS_RELE(zfsvfs->z_vfs);
31305446Sahrens 	} else {
313110272SMatthew.Ahrens@Sun.COM 		if (dsl_dataset_tryown(ds, B_FALSE, FTAG)) {
313210272SMatthew.Ahrens@Sun.COM 			error = dsl_dataset_clone_swap(clone, ds, B_TRUE);
313310272SMatthew.Ahrens@Sun.COM 			dsl_dataset_disown(ds, FTAG);
313410272SMatthew.Ahrens@Sun.COM 			ds = NULL;
313510272SMatthew.Ahrens@Sun.COM 		} else {
313610272SMatthew.Ahrens@Sun.COM 			error = EBUSY;
313710272SMatthew.Ahrens@Sun.COM 		}
31385446Sahrens 	}
313910272SMatthew.Ahrens@Sun.COM 
314010272SMatthew.Ahrens@Sun.COM 	/*
314110272SMatthew.Ahrens@Sun.COM 	 * Destroy clone (which also closes it).
314210272SMatthew.Ahrens@Sun.COM 	 */
314310272SMatthew.Ahrens@Sun.COM 	(void) dsl_dataset_destroy(clone, FTAG, B_FALSE);
314410272SMatthew.Ahrens@Sun.COM 
314510272SMatthew.Ahrens@Sun.COM out:
314610272SMatthew.Ahrens@Sun.COM 	strfree(clone_name);
314710272SMatthew.Ahrens@Sun.COM 	if (ds)
314810272SMatthew.Ahrens@Sun.COM 		dsl_dataset_rele(ds, FTAG);
31495446Sahrens 	return (error);
3150789Sahrens }
3151789Sahrens 
31525367Sahrens /*
31535367Sahrens  * inputs:
31545367Sahrens  * zc_name	old name of dataset
31555367Sahrens  * zc_value	new name of dataset
31565367Sahrens  * zc_cookie	recursive flag (only valid for snapshots)
31575367Sahrens  *
31585367Sahrens  * outputs:	none
31595367Sahrens  */
3160789Sahrens static int
3161789Sahrens zfs_ioc_rename(zfs_cmd_t *zc)
3162789Sahrens {
31634490Svb160487 	boolean_t recursive = zc->zc_cookie & 1;
31644007Smmusante 
31652676Seschrock 	zc->zc_value[sizeof (zc->zc_value) - 1] = '\0';
31665326Sek110237 	if (dataset_namecheck(zc->zc_value, NULL, NULL) != 0 ||
31675326Sek110237 	    strchr(zc->zc_value, '%'))
3168789Sahrens 		return (EINVAL);
3169789Sahrens 
31704007Smmusante 	/*
31714007Smmusante 	 * Unmount snapshot unless we're doing a recursive rename,
31724007Smmusante 	 * in which case the dataset code figures out which snapshots
31734007Smmusante 	 * to unmount.
31744007Smmusante 	 */
31754007Smmusante 	if (!recursive && strchr(zc->zc_name, '@') != NULL &&
3176789Sahrens 	    zc->zc_objset_type == DMU_OST_ZFS) {
31772199Sahrens 		int err = zfs_unmount_snap(zc->zc_name, NULL);
31782199Sahrens 		if (err)
31792199Sahrens 			return (err);
3180789Sahrens 	}
318110588SEric.Taylor@Sun.COM 	if (zc->zc_objset_type == DMU_OST_ZVOL)
318210588SEric.Taylor@Sun.COM 		(void) zvol_remove_minor(zc->zc_name);
31834007Smmusante 	return (dmu_objset_rename(zc->zc_name, zc->zc_value, recursive));
3184789Sahrens }
3185789Sahrens 
318611022STom.Erickson@Sun.COM static int
318711022STom.Erickson@Sun.COM zfs_check_settable(const char *dsname, nvpair_t *pair, cred_t *cr)
318811022STom.Erickson@Sun.COM {
318911022STom.Erickson@Sun.COM 	const char *propname = nvpair_name(pair);
319011022STom.Erickson@Sun.COM 	boolean_t issnap = (strchr(dsname, '@') != NULL);
319111022STom.Erickson@Sun.COM 	zfs_prop_t prop = zfs_name_to_prop(propname);
319211022STom.Erickson@Sun.COM 	uint64_t intval;
319311022STom.Erickson@Sun.COM 	int err;
319411022STom.Erickson@Sun.COM 
319511022STom.Erickson@Sun.COM 	if (prop == ZPROP_INVAL) {
319611022STom.Erickson@Sun.COM 		if (zfs_prop_user(propname)) {
319711022STom.Erickson@Sun.COM 			if (err = zfs_secpolicy_write_perms(dsname,
319811022STom.Erickson@Sun.COM 			    ZFS_DELEG_PERM_USERPROP, cr))
319911022STom.Erickson@Sun.COM 				return (err);
320011022STom.Erickson@Sun.COM 			return (0);
320111022STom.Erickson@Sun.COM 		}
320211022STom.Erickson@Sun.COM 
320311022STom.Erickson@Sun.COM 		if (!issnap && zfs_prop_userquota(propname)) {
320411022STom.Erickson@Sun.COM 			const char *perm = NULL;
320511022STom.Erickson@Sun.COM 			const char *uq_prefix =
320611022STom.Erickson@Sun.COM 			    zfs_userquota_prop_prefixes[ZFS_PROP_USERQUOTA];
320711022STom.Erickson@Sun.COM 			const char *gq_prefix =
320811022STom.Erickson@Sun.COM 			    zfs_userquota_prop_prefixes[ZFS_PROP_GROUPQUOTA];
320911022STom.Erickson@Sun.COM 
321011022STom.Erickson@Sun.COM 			if (strncmp(propname, uq_prefix,
321111022STom.Erickson@Sun.COM 			    strlen(uq_prefix)) == 0) {
321211022STom.Erickson@Sun.COM 				perm = ZFS_DELEG_PERM_USERQUOTA;
321311022STom.Erickson@Sun.COM 			} else if (strncmp(propname, gq_prefix,
321411022STom.Erickson@Sun.COM 			    strlen(gq_prefix)) == 0) {
321511022STom.Erickson@Sun.COM 				perm = ZFS_DELEG_PERM_GROUPQUOTA;
321611022STom.Erickson@Sun.COM 			} else {
321711022STom.Erickson@Sun.COM 				/* USERUSED and GROUPUSED are read-only */
321811022STom.Erickson@Sun.COM 				return (EINVAL);
321911022STom.Erickson@Sun.COM 			}
322011022STom.Erickson@Sun.COM 
322111022STom.Erickson@Sun.COM 			if (err = zfs_secpolicy_write_perms(dsname, perm, cr))
322211022STom.Erickson@Sun.COM 				return (err);
322311022STom.Erickson@Sun.COM 			return (0);
322411022STom.Erickson@Sun.COM 		}
322511022STom.Erickson@Sun.COM 
322611022STom.Erickson@Sun.COM 		return (EINVAL);
322711022STom.Erickson@Sun.COM 	}
322811022STom.Erickson@Sun.COM 
322911022STom.Erickson@Sun.COM 	if (issnap)
323011022STom.Erickson@Sun.COM 		return (EINVAL);
323111022STom.Erickson@Sun.COM 
323211022STom.Erickson@Sun.COM 	if (nvpair_type(pair) == DATA_TYPE_NVLIST) {
323311022STom.Erickson@Sun.COM 		/*
323411022STom.Erickson@Sun.COM 		 * dsl_prop_get_all_impl() returns properties in this
323511022STom.Erickson@Sun.COM 		 * format.
323611022STom.Erickson@Sun.COM 		 */
323711022STom.Erickson@Sun.COM 		nvlist_t *attrs;
323811022STom.Erickson@Sun.COM 		VERIFY(nvpair_value_nvlist(pair, &attrs) == 0);
323911022STom.Erickson@Sun.COM 		VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE,
324011022STom.Erickson@Sun.COM 		    &pair) == 0);
324111022STom.Erickson@Sun.COM 	}
324211022STom.Erickson@Sun.COM 
324311022STom.Erickson@Sun.COM 	/*
324411022STom.Erickson@Sun.COM 	 * Check that this value is valid for this pool version
324511022STom.Erickson@Sun.COM 	 */
324611022STom.Erickson@Sun.COM 	switch (prop) {
324711022STom.Erickson@Sun.COM 	case ZFS_PROP_COMPRESSION:
324811022STom.Erickson@Sun.COM 		/*
324911022STom.Erickson@Sun.COM 		 * If the user specified gzip compression, make sure
325011022STom.Erickson@Sun.COM 		 * the SPA supports it. We ignore any errors here since
325111022STom.Erickson@Sun.COM 		 * we'll catch them later.
325211022STom.Erickson@Sun.COM 		 */
325311022STom.Erickson@Sun.COM 		if (nvpair_type(pair) == DATA_TYPE_UINT64 &&
325411022STom.Erickson@Sun.COM 		    nvpair_value_uint64(pair, &intval) == 0) {
325511022STom.Erickson@Sun.COM 			if (intval >= ZIO_COMPRESS_GZIP_1 &&
325611022STom.Erickson@Sun.COM 			    intval <= ZIO_COMPRESS_GZIP_9 &&
325711022STom.Erickson@Sun.COM 			    zfs_earlier_version(dsname,
325811022STom.Erickson@Sun.COM 			    SPA_VERSION_GZIP_COMPRESSION)) {
325911022STom.Erickson@Sun.COM 				return (ENOTSUP);
326011022STom.Erickson@Sun.COM 			}
326111022STom.Erickson@Sun.COM 
326211022STom.Erickson@Sun.COM 			if (intval == ZIO_COMPRESS_ZLE &&
326311022STom.Erickson@Sun.COM 			    zfs_earlier_version(dsname,
326411022STom.Erickson@Sun.COM 			    SPA_VERSION_ZLE_COMPRESSION))
326511022STom.Erickson@Sun.COM 				return (ENOTSUP);
326611022STom.Erickson@Sun.COM 
326711022STom.Erickson@Sun.COM 			/*
326811022STom.Erickson@Sun.COM 			 * If this is a bootable dataset then
326911022STom.Erickson@Sun.COM 			 * verify that the compression algorithm
327011022STom.Erickson@Sun.COM 			 * is supported for booting. We must return
327111022STom.Erickson@Sun.COM 			 * something other than ENOTSUP since it
327211022STom.Erickson@Sun.COM 			 * implies a downrev pool version.
327311022STom.Erickson@Sun.COM 			 */
327411022STom.Erickson@Sun.COM 			if (zfs_is_bootfs(dsname) &&
327511022STom.Erickson@Sun.COM 			    !BOOTFS_COMPRESS_VALID(intval)) {
327611022STom.Erickson@Sun.COM 				return (ERANGE);
327711022STom.Erickson@Sun.COM 			}
327811022STom.Erickson@Sun.COM 		}
327911022STom.Erickson@Sun.COM 		break;
328011022STom.Erickson@Sun.COM 
328111022STom.Erickson@Sun.COM 	case ZFS_PROP_COPIES:
328211022STom.Erickson@Sun.COM 		if (zfs_earlier_version(dsname, SPA_VERSION_DITTO_BLOCKS))
328311022STom.Erickson@Sun.COM 			return (ENOTSUP);
328411022STom.Erickson@Sun.COM 		break;
328511022STom.Erickson@Sun.COM 
328611022STom.Erickson@Sun.COM 	case ZFS_PROP_DEDUP:
328711022STom.Erickson@Sun.COM 		if (zfs_earlier_version(dsname, SPA_VERSION_DEDUP))
328811022STom.Erickson@Sun.COM 			return (ENOTSUP);
328911022STom.Erickson@Sun.COM 		break;
329011022STom.Erickson@Sun.COM 
329111022STom.Erickson@Sun.COM 	case ZFS_PROP_SHARESMB:
329211022STom.Erickson@Sun.COM 		if (zpl_earlier_version(dsname, ZPL_VERSION_FUID))
329311022STom.Erickson@Sun.COM 			return (ENOTSUP);
329411022STom.Erickson@Sun.COM 		break;
329511022STom.Erickson@Sun.COM 
329611022STom.Erickson@Sun.COM 	case ZFS_PROP_ACLINHERIT:
329711022STom.Erickson@Sun.COM 		if (nvpair_type(pair) == DATA_TYPE_UINT64 &&
329811022STom.Erickson@Sun.COM 		    nvpair_value_uint64(pair, &intval) == 0) {
329911022STom.Erickson@Sun.COM 			if (intval == ZFS_ACL_PASSTHROUGH_X &&
330011022STom.Erickson@Sun.COM 			    zfs_earlier_version(dsname,
330111022STom.Erickson@Sun.COM 			    SPA_VERSION_PASSTHROUGH_X))
330211022STom.Erickson@Sun.COM 				return (ENOTSUP);
330311022STom.Erickson@Sun.COM 		}
330411022STom.Erickson@Sun.COM 		break;
330511022STom.Erickson@Sun.COM 	}
330611022STom.Erickson@Sun.COM 
330711022STom.Erickson@Sun.COM 	return (zfs_secpolicy_setprop(dsname, prop, pair, CRED()));
330811022STom.Erickson@Sun.COM }
330911022STom.Erickson@Sun.COM 
331011022STom.Erickson@Sun.COM /*
331111022STom.Erickson@Sun.COM  * Removes properties from the given props list that fail permission checks
331211022STom.Erickson@Sun.COM  * needed to clear them and to restore them in case of a receive error. For each
331311022STom.Erickson@Sun.COM  * property, make sure we have both set and inherit permissions.
331411022STom.Erickson@Sun.COM  *
331511022STom.Erickson@Sun.COM  * Returns the first error encountered if any permission checks fail. If the
331611022STom.Erickson@Sun.COM  * caller provides a non-NULL errlist, it also gives the complete list of names
331711022STom.Erickson@Sun.COM  * of all the properties that failed a permission check along with the
331811022STom.Erickson@Sun.COM  * corresponding error numbers. The caller is responsible for freeing the
331911022STom.Erickson@Sun.COM  * returned errlist.
332011022STom.Erickson@Sun.COM  *
332111022STom.Erickson@Sun.COM  * If every property checks out successfully, zero is returned and the list
332211022STom.Erickson@Sun.COM  * pointed at by errlist is NULL.
332311022STom.Erickson@Sun.COM  */
332411022STom.Erickson@Sun.COM static int
332511022STom.Erickson@Sun.COM zfs_check_clearable(char *dataset, nvlist_t *props, nvlist_t **errlist)
33266689Smaybee {
33276689Smaybee 	zfs_cmd_t *zc;
332811022STom.Erickson@Sun.COM 	nvpair_t *pair, *next_pair;
332911022STom.Erickson@Sun.COM 	nvlist_t *errors;
333011022STom.Erickson@Sun.COM 	int err, rv = 0;
33316689Smaybee 
33326689Smaybee 	if (props == NULL)
333311022STom.Erickson@Sun.COM 		return (0);
333411022STom.Erickson@Sun.COM 
333511022STom.Erickson@Sun.COM 	VERIFY(nvlist_alloc(&errors, NV_UNIQUE_NAME, KM_SLEEP) == 0);
333611022STom.Erickson@Sun.COM 
33376689Smaybee 	zc = kmem_alloc(sizeof (zfs_cmd_t), KM_SLEEP);
33386689Smaybee 	(void) strcpy(zc->zc_name, dataset);
333911022STom.Erickson@Sun.COM 	pair = nvlist_next_nvpair(props, NULL);
334011022STom.Erickson@Sun.COM 	while (pair != NULL) {
334111022STom.Erickson@Sun.COM 		next_pair = nvlist_next_nvpair(props, pair);
334211022STom.Erickson@Sun.COM 
334311022STom.Erickson@Sun.COM 		(void) strcpy(zc->zc_value, nvpair_name(pair));
334411022STom.Erickson@Sun.COM 		if ((err = zfs_check_settable(dataset, pair, CRED())) != 0 ||
334511022STom.Erickson@Sun.COM 		    (err = zfs_secpolicy_inherit(zc, CRED())) != 0) {
334611022STom.Erickson@Sun.COM 			VERIFY(nvlist_remove_nvpair(props, pair) == 0);
334711022STom.Erickson@Sun.COM 			VERIFY(nvlist_add_int32(errors,
334811022STom.Erickson@Sun.COM 			    zc->zc_value, err) == 0);
334911022STom.Erickson@Sun.COM 		}
335011022STom.Erickson@Sun.COM 		pair = next_pair;
33516689Smaybee 	}
33526689Smaybee 	kmem_free(zc, sizeof (zfs_cmd_t));
335311022STom.Erickson@Sun.COM 
335411022STom.Erickson@Sun.COM 	if ((pair = nvlist_next_nvpair(errors, NULL)) == NULL) {
335511022STom.Erickson@Sun.COM 		nvlist_free(errors);
335611022STom.Erickson@Sun.COM 		errors = NULL;
335711022STom.Erickson@Sun.COM 	} else {
335811022STom.Erickson@Sun.COM 		VERIFY(nvpair_value_int32(pair, &rv) == 0);
335911022STom.Erickson@Sun.COM 	}
336011022STom.Erickson@Sun.COM 
336111022STom.Erickson@Sun.COM 	if (errlist == NULL)
336211022STom.Erickson@Sun.COM 		nvlist_free(errors);
336311022STom.Erickson@Sun.COM 	else
336411022STom.Erickson@Sun.COM 		*errlist = errors;
336511022STom.Erickson@Sun.COM 
336611022STom.Erickson@Sun.COM 	return (rv);
33676689Smaybee }
33686689Smaybee 
336911022STom.Erickson@Sun.COM static boolean_t
337011022STom.Erickson@Sun.COM propval_equals(nvpair_t *p1, nvpair_t *p2)
337111022STom.Erickson@Sun.COM {
337211022STom.Erickson@Sun.COM 	if (nvpair_type(p1) == DATA_TYPE_NVLIST) {
337311022STom.Erickson@Sun.COM 		/* dsl_prop_get_all_impl() format */
337411022STom.Erickson@Sun.COM 		nvlist_t *attrs;
337511022STom.Erickson@Sun.COM 		VERIFY(nvpair_value_nvlist(p1, &attrs) == 0);
337611022STom.Erickson@Sun.COM 		VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE,
337711022STom.Erickson@Sun.COM 		    &p1) == 0);
337811022STom.Erickson@Sun.COM 	}
337911022STom.Erickson@Sun.COM 
338011022STom.Erickson@Sun.COM 	if (nvpair_type(p2) == DATA_TYPE_NVLIST) {
338111022STom.Erickson@Sun.COM 		nvlist_t *attrs;
338211022STom.Erickson@Sun.COM 		VERIFY(nvpair_value_nvlist(p2, &attrs) == 0);
338311022STom.Erickson@Sun.COM 		VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE,
338411022STom.Erickson@Sun.COM 		    &p2) == 0);
338511022STom.Erickson@Sun.COM 	}
338611022STom.Erickson@Sun.COM 
338711022STom.Erickson@Sun.COM 	if (nvpair_type(p1) != nvpair_type(p2))
338811022STom.Erickson@Sun.COM 		return (B_FALSE);
338911022STom.Erickson@Sun.COM 
339011022STom.Erickson@Sun.COM 	if (nvpair_type(p1) == DATA_TYPE_STRING) {
339111022STom.Erickson@Sun.COM 		char *valstr1, *valstr2;
339211022STom.Erickson@Sun.COM 
339311022STom.Erickson@Sun.COM 		VERIFY(nvpair_value_string(p1, (char **)&valstr1) == 0);
339411022STom.Erickson@Sun.COM 		VERIFY(nvpair_value_string(p2, (char **)&valstr2) == 0);
339511022STom.Erickson@Sun.COM 		return (strcmp(valstr1, valstr2) == 0);
339611022STom.Erickson@Sun.COM 	} else {
339711022STom.Erickson@Sun.COM 		uint64_t intval1, intval2;
339811022STom.Erickson@Sun.COM 
339911022STom.Erickson@Sun.COM 		VERIFY(nvpair_value_uint64(p1, &intval1) == 0);
340011022STom.Erickson@Sun.COM 		VERIFY(nvpair_value_uint64(p2, &intval2) == 0);
340111022STom.Erickson@Sun.COM 		return (intval1 == intval2);
340211022STom.Erickson@Sun.COM 	}
340311022STom.Erickson@Sun.COM }
340411022STom.Erickson@Sun.COM 
340511022STom.Erickson@Sun.COM /*
340611022STom.Erickson@Sun.COM  * Remove properties from props if they are not going to change (as determined
340711022STom.Erickson@Sun.COM  * by comparison with origprops). Remove them from origprops as well, since we
340811022STom.Erickson@Sun.COM  * do not need to clear or restore properties that won't change.
340911022STom.Erickson@Sun.COM  */
341011022STom.Erickson@Sun.COM static void
341111022STom.Erickson@Sun.COM props_reduce(nvlist_t *props, nvlist_t *origprops)
341211022STom.Erickson@Sun.COM {
341311022STom.Erickson@Sun.COM 	nvpair_t *pair, *next_pair;
341411022STom.Erickson@Sun.COM 
341511022STom.Erickson@Sun.COM 	if (origprops == NULL)
341611022STom.Erickson@Sun.COM 		return; /* all props need to be received */
341711022STom.Erickson@Sun.COM 
341811022STom.Erickson@Sun.COM 	pair = nvlist_next_nvpair(props, NULL);
341911022STom.Erickson@Sun.COM 	while (pair != NULL) {
342011022STom.Erickson@Sun.COM 		const char *propname = nvpair_name(pair);
342111022STom.Erickson@Sun.COM 		nvpair_t *match;
342211022STom.Erickson@Sun.COM 
342311022STom.Erickson@Sun.COM 		next_pair = nvlist_next_nvpair(props, pair);
342411022STom.Erickson@Sun.COM 
342511022STom.Erickson@Sun.COM 		if ((nvlist_lookup_nvpair(origprops, propname,
342611022STom.Erickson@Sun.COM 		    &match) != 0) || !propval_equals(pair, match))
342711022STom.Erickson@Sun.COM 			goto next; /* need to set received value */
342811022STom.Erickson@Sun.COM 
342911022STom.Erickson@Sun.COM 		/* don't clear the existing received value */
343011022STom.Erickson@Sun.COM 		(void) nvlist_remove_nvpair(origprops, match);
343111022STom.Erickson@Sun.COM 		/* don't bother receiving the property */
343211022STom.Erickson@Sun.COM 		(void) nvlist_remove_nvpair(props, pair);
343311022STom.Erickson@Sun.COM next:
343411022STom.Erickson@Sun.COM 		pair = next_pair;
343511022STom.Erickson@Sun.COM 	}
343611022STom.Erickson@Sun.COM }
343711022STom.Erickson@Sun.COM 
343811022STom.Erickson@Sun.COM #ifdef	DEBUG
343911022STom.Erickson@Sun.COM static boolean_t zfs_ioc_recv_inject_err;
344011022STom.Erickson@Sun.COM #endif
344111022STom.Erickson@Sun.COM 
34425367Sahrens /*
34435367Sahrens  * inputs:
34445367Sahrens  * zc_name		name of containing filesystem
34455367Sahrens  * zc_nvlist_src{_size}	nvlist of properties to apply
34465367Sahrens  * zc_value		name of snapshot to create
34475367Sahrens  * zc_string		name of clone origin (if DRR_FLAG_CLONE)
34485367Sahrens  * zc_cookie		file descriptor to recv from
34495367Sahrens  * zc_begin_record	the BEGIN record of the stream (not byteswapped)
34505367Sahrens  * zc_guid		force flag
345112527SChris.Kirby@oracle.com  * zc_cleanup_fd	cleanup-on-exit file descriptor
345212527SChris.Kirby@oracle.com  * zc_action_handle	handle for this guid/ds mapping (or zero on first call)
34535367Sahrens  *
34545367Sahrens  * outputs:
34555367Sahrens  * zc_cookie		number of bytes read
345611022STom.Erickson@Sun.COM  * zc_nvlist_dst{_size} error for each unapplied received property
345711022STom.Erickson@Sun.COM  * zc_obj		zprop_errflags_t
345812527SChris.Kirby@oracle.com  * zc_action_handle	handle for this guid/ds mapping
34595367Sahrens  */
3460789Sahrens static int
34615367Sahrens zfs_ioc_recv(zfs_cmd_t *zc)
3462789Sahrens {
3463789Sahrens 	file_t *fp;
34645326Sek110237 	objset_t *os;
34655367Sahrens 	dmu_recv_cookie_t drc;
34665326Sek110237 	boolean_t force = (boolean_t)zc->zc_guid;
346711022STom.Erickson@Sun.COM 	int fd;
346811022STom.Erickson@Sun.COM 	int error = 0;
346911022STom.Erickson@Sun.COM 	int props_error = 0;
347011022STom.Erickson@Sun.COM 	nvlist_t *errors;
34715367Sahrens 	offset_t off;
347211022STom.Erickson@Sun.COM 	nvlist_t *props = NULL; /* sent properties */
347311022STom.Erickson@Sun.COM 	nvlist_t *origprops = NULL; /* existing properties */
34745367Sahrens 	objset_t *origin = NULL;
34755367Sahrens 	char *tosnap;
34765367Sahrens 	char tofs[ZFS_MAXNAMELEN];
347711022STom.Erickson@Sun.COM 	boolean_t first_recvd_props = B_FALSE;
3478789Sahrens 
34793265Sahrens 	if (dataset_namecheck(zc->zc_value, NULL, NULL) != 0 ||
34805326Sek110237 	    strchr(zc->zc_value, '@') == NULL ||
34815326Sek110237 	    strchr(zc->zc_value, '%'))
34823265Sahrens 		return (EINVAL);
34833265Sahrens 
34845367Sahrens 	(void) strcpy(tofs, zc->zc_value);
34855367Sahrens 	tosnap = strchr(tofs, '@');
348611022STom.Erickson@Sun.COM 	*tosnap++ = '\0';
34875367Sahrens 
34885367Sahrens 	if (zc->zc_nvlist_src != NULL &&
34895367Sahrens 	    (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
34909643SEric.Taylor@Sun.COM 	    zc->zc_iflags, &props)) != 0)
34915367Sahrens 		return (error);
34925367Sahrens 
3493789Sahrens 	fd = zc->zc_cookie;
3494789Sahrens 	fp = getf(fd);
34955367Sahrens 	if (fp == NULL) {
34965367Sahrens 		nvlist_free(props);
3497789Sahrens 		return (EBADF);
34985367Sahrens 	}
34995326Sek110237 
350011022STom.Erickson@Sun.COM 	VERIFY(nvlist_alloc(&errors, NV_UNIQUE_NAME, KM_SLEEP) == 0);
350111022STom.Erickson@Sun.COM 
350210298SMatthew.Ahrens@Sun.COM 	if (props && dmu_objset_hold(tofs, FTAG, &os) == 0) {
350311022STom.Erickson@Sun.COM 		if ((spa_version(os->os_spa) >= SPA_VERSION_RECVD_PROPS) &&
350411022STom.Erickson@Sun.COM 		    !dsl_prop_get_hasrecvd(os)) {
350511022STom.Erickson@Sun.COM 			first_recvd_props = B_TRUE;
350611022STom.Erickson@Sun.COM 		}
350711022STom.Erickson@Sun.COM 
35086689Smaybee 		/*
350911022STom.Erickson@Sun.COM 		 * If new received properties are supplied, they are to
351011022STom.Erickson@Sun.COM 		 * completely replace the existing received properties, so stash
351111022STom.Erickson@Sun.COM 		 * away the existing ones.
35126689Smaybee 		 */
351311022STom.Erickson@Sun.COM 		if (dsl_prop_get_received(os, &origprops) == 0) {
351411022STom.Erickson@Sun.COM 			nvlist_t *errlist = NULL;
351511022STom.Erickson@Sun.COM 			/*
351611022STom.Erickson@Sun.COM 			 * Don't bother writing a property if its value won't
351711022STom.Erickson@Sun.COM 			 * change (and avoid the unnecessary security checks).
351811022STom.Erickson@Sun.COM 			 *
351911022STom.Erickson@Sun.COM 			 * The first receive after SPA_VERSION_RECVD_PROPS is a
352011022STom.Erickson@Sun.COM 			 * special case where we blow away all local properties
352111022STom.Erickson@Sun.COM 			 * regardless.
352211022STom.Erickson@Sun.COM 			 */
352311022STom.Erickson@Sun.COM 			if (!first_recvd_props)
352411022STom.Erickson@Sun.COM 				props_reduce(props, origprops);
352511022STom.Erickson@Sun.COM 			if (zfs_check_clearable(tofs, origprops,
352611022STom.Erickson@Sun.COM 			    &errlist) != 0)
352711022STom.Erickson@Sun.COM 				(void) nvlist_merge(errors, errlist, 0);
352811022STom.Erickson@Sun.COM 			nvlist_free(errlist);
352911022STom.Erickson@Sun.COM 		}
353010298SMatthew.Ahrens@Sun.COM 
353110298SMatthew.Ahrens@Sun.COM 		dmu_objset_rele(os, FTAG);
35325326Sek110237 	}
35335326Sek110237 
35345367Sahrens 	if (zc->zc_string[0]) {
353510298SMatthew.Ahrens@Sun.COM 		error = dmu_objset_hold(zc->zc_string, FTAG, &origin);
35366689Smaybee 		if (error)
35376689Smaybee 			goto out;
35385367Sahrens 	}
35395367Sahrens 
354011007SLori.Alt@Sun.COM 	error = dmu_recv_begin(tofs, tosnap, zc->zc_top_ds,
354111007SLori.Alt@Sun.COM 	    &zc->zc_begin_record, force, origin, &drc);
35425367Sahrens 	if (origin)
354310298SMatthew.Ahrens@Sun.COM 		dmu_objset_rele(origin, FTAG);
35446689Smaybee 	if (error)
35456689Smaybee 		goto out;
35465326Sek110237 
35475326Sek110237 	/*
354811022STom.Erickson@Sun.COM 	 * Set properties before we receive the stream so that they are applied
354911022STom.Erickson@Sun.COM 	 * to the new data. Note that we must call dmu_recv_stream() if
355011022STom.Erickson@Sun.COM 	 * dmu_recv_begin() succeeds.
35515326Sek110237 	 */
35525367Sahrens 	if (props) {
355311022STom.Erickson@Sun.COM 		nvlist_t *errlist;
355411022STom.Erickson@Sun.COM 
355511022STom.Erickson@Sun.COM 		if (dmu_objset_from_ds(drc.drc_logical_ds, &os) == 0) {
355611022STom.Erickson@Sun.COM 			if (drc.drc_newfs) {
355711022STom.Erickson@Sun.COM 				if (spa_version(os->os_spa) >=
355811022STom.Erickson@Sun.COM 				    SPA_VERSION_RECVD_PROPS)
355911022STom.Erickson@Sun.COM 					first_recvd_props = B_TRUE;
356011022STom.Erickson@Sun.COM 			} else if (origprops != NULL) {
356111022STom.Erickson@Sun.COM 				if (clear_received_props(os, tofs, origprops,
356211022STom.Erickson@Sun.COM 				    first_recvd_props ? NULL : props) != 0)
356311022STom.Erickson@Sun.COM 					zc->zc_obj |= ZPROP_ERR_NOCLEAR;
356411022STom.Erickson@Sun.COM 			} else {
356511022STom.Erickson@Sun.COM 				zc->zc_obj |= ZPROP_ERR_NOCLEAR;
356611022STom.Erickson@Sun.COM 			}
356711022STom.Erickson@Sun.COM 			dsl_prop_set_hasrecvd(os);
356811022STom.Erickson@Sun.COM 		} else if (!drc.drc_newfs) {
356911022STom.Erickson@Sun.COM 			zc->zc_obj |= ZPROP_ERR_NOCLEAR;
357011022STom.Erickson@Sun.COM 		}
357111022STom.Erickson@Sun.COM 
357211022STom.Erickson@Sun.COM 		(void) zfs_set_prop_nvlist(tofs, ZPROP_SRC_RECEIVED,
357311022STom.Erickson@Sun.COM 		    props, &errlist);
357411022STom.Erickson@Sun.COM 		(void) nvlist_merge(errors, errlist, 0);
357511022STom.Erickson@Sun.COM 		nvlist_free(errlist);
357611022STom.Erickson@Sun.COM 	}
357711022STom.Erickson@Sun.COM 
357811022STom.Erickson@Sun.COM 	if (fit_error_list(zc, &errors) != 0 || put_nvlist(zc, errors) != 0) {
35796689Smaybee 		/*
358011022STom.Erickson@Sun.COM 		 * Caller made zc->zc_nvlist_dst less than the minimum expected
358111022STom.Erickson@Sun.COM 		 * size or supplied an invalid address.
35826689Smaybee 		 */
358311022STom.Erickson@Sun.COM 		props_error = EINVAL;
35845367Sahrens 	}
35855367Sahrens 
35865367Sahrens 	off = fp->f_offset;
358712527SChris.Kirby@oracle.com 	error = dmu_recv_stream(&drc, fp->f_vnode, &off, zc->zc_cleanup_fd,
358812527SChris.Kirby@oracle.com 	    &zc->zc_action_handle);
35895367Sahrens 
359010204SMatthew.Ahrens@Sun.COM 	if (error == 0) {
359110204SMatthew.Ahrens@Sun.COM 		zfsvfs_t *zfsvfs = NULL;
359210204SMatthew.Ahrens@Sun.COM 
359310204SMatthew.Ahrens@Sun.COM 		if (getzfsvfs(tofs, &zfsvfs) == 0) {
359410204SMatthew.Ahrens@Sun.COM 			/* online recv */
359510204SMatthew.Ahrens@Sun.COM 			int end_err;
359610298SMatthew.Ahrens@Sun.COM 
359710298SMatthew.Ahrens@Sun.COM 			error = zfs_suspend_fs(zfsvfs);
359810204SMatthew.Ahrens@Sun.COM 			/*
359910204SMatthew.Ahrens@Sun.COM 			 * If the suspend fails, then the recv_end will
360010204SMatthew.Ahrens@Sun.COM 			 * likely also fail, and clean up after itself.
360110204SMatthew.Ahrens@Sun.COM 			 */
360210204SMatthew.Ahrens@Sun.COM 			end_err = dmu_recv_end(&drc);
360311812SGeorge.Wilson@Sun.COM 			if (error == 0)
360411812SGeorge.Wilson@Sun.COM 				error = zfs_resume_fs(zfsvfs, tofs);
360510204SMatthew.Ahrens@Sun.COM 			error = error ? error : end_err;
360610204SMatthew.Ahrens@Sun.COM 			VFS_RELE(zfsvfs->z_vfs);
360710204SMatthew.Ahrens@Sun.COM 		} else {
36086689Smaybee 			error = dmu_recv_end(&drc);
36095367Sahrens 		}
36106083Sek110237 	}
36115367Sahrens 
36125367Sahrens 	zc->zc_cookie = off - fp->f_offset;
36135367Sahrens 	if (VOP_SEEK(fp->f_vnode, fp->f_offset, &off, NULL) == 0)
36145367Sahrens 		fp->f_offset = off;
36152885Sahrens 
361611022STom.Erickson@Sun.COM #ifdef	DEBUG
361711022STom.Erickson@Sun.COM 	if (zfs_ioc_recv_inject_err) {
361811022STom.Erickson@Sun.COM 		zfs_ioc_recv_inject_err = B_FALSE;
361911022STom.Erickson@Sun.COM 		error = 1;
362011022STom.Erickson@Sun.COM 	}
362111022STom.Erickson@Sun.COM #endif
36226689Smaybee 	/*
36236689Smaybee 	 * On error, restore the original props.
36246689Smaybee 	 */
36256689Smaybee 	if (error && props) {
362611022STom.Erickson@Sun.COM 		if (dmu_objset_hold(tofs, FTAG, &os) == 0) {
362711022STom.Erickson@Sun.COM 			if (clear_received_props(os, tofs, props, NULL) != 0) {
362811022STom.Erickson@Sun.COM 				/*
362911022STom.Erickson@Sun.COM 				 * We failed to clear the received properties.
363011022STom.Erickson@Sun.COM 				 * Since we may have left a $recvd value on the
363111022STom.Erickson@Sun.COM 				 * system, we can't clear the $hasrecvd flag.
363211022STom.Erickson@Sun.COM 				 */
363311022STom.Erickson@Sun.COM 				zc->zc_obj |= ZPROP_ERR_NORESTORE;
363411022STom.Erickson@Sun.COM 			} else if (first_recvd_props) {
363511022STom.Erickson@Sun.COM 				dsl_prop_unset_hasrecvd(os);
363611022STom.Erickson@Sun.COM 			}
363711022STom.Erickson@Sun.COM 			dmu_objset_rele(os, FTAG);
363811022STom.Erickson@Sun.COM 		} else if (!drc.drc_newfs) {
363911022STom.Erickson@Sun.COM 			/* We failed to clear the received properties. */
364011022STom.Erickson@Sun.COM 			zc->zc_obj |= ZPROP_ERR_NORESTORE;
364111022STom.Erickson@Sun.COM 		}
364211022STom.Erickson@Sun.COM 
364311022STom.Erickson@Sun.COM 		if (origprops == NULL && !drc.drc_newfs) {
364411022STom.Erickson@Sun.COM 			/* We failed to stash the original properties. */
364511022STom.Erickson@Sun.COM 			zc->zc_obj |= ZPROP_ERR_NORESTORE;
364611022STom.Erickson@Sun.COM 		}
364711022STom.Erickson@Sun.COM 
364811022STom.Erickson@Sun.COM 		/*
364911022STom.Erickson@Sun.COM 		 * dsl_props_set() will not convert RECEIVED to LOCAL on or
365011022STom.Erickson@Sun.COM 		 * after SPA_VERSION_RECVD_PROPS, so we need to specify LOCAL
365111022STom.Erickson@Sun.COM 		 * explictly if we're restoring local properties cleared in the
365211022STom.Erickson@Sun.COM 		 * first new-style receive.
365311022STom.Erickson@Sun.COM 		 */
365411022STom.Erickson@Sun.COM 		if (origprops != NULL &&
365511022STom.Erickson@Sun.COM 		    zfs_set_prop_nvlist(tofs, (first_recvd_props ?
365611022STom.Erickson@Sun.COM 		    ZPROP_SRC_LOCAL : ZPROP_SRC_RECEIVED),
365711022STom.Erickson@Sun.COM 		    origprops, NULL) != 0) {
365811022STom.Erickson@Sun.COM 			/*
365911022STom.Erickson@Sun.COM 			 * We stashed the original properties but failed to
366011022STom.Erickson@Sun.COM 			 * restore them.
366111022STom.Erickson@Sun.COM 			 */
366211022STom.Erickson@Sun.COM 			zc->zc_obj |= ZPROP_ERR_NORESTORE;
366311022STom.Erickson@Sun.COM 		}
36646689Smaybee 	}
36656689Smaybee out:
36666689Smaybee 	nvlist_free(props);
36676689Smaybee 	nvlist_free(origprops);
366811022STom.Erickson@Sun.COM 	nvlist_free(errors);
3669789Sahrens 	releasef(fd);
367011022STom.Erickson@Sun.COM 
367111022STom.Erickson@Sun.COM 	if (error == 0)
367211022STom.Erickson@Sun.COM 		error = props_error;
367311022STom.Erickson@Sun.COM 
3674789Sahrens 	return (error);
3675789Sahrens }
3676789Sahrens 
36775367Sahrens /*
36785367Sahrens  * inputs:
36795367Sahrens  * zc_name	name of snapshot to send
36805367Sahrens  * zc_cookie	file descriptor to send stream to
368112786SChris.Kirby@oracle.com  * zc_obj	fromorigin flag (mutually exclusive with zc_fromobj)
368212786SChris.Kirby@oracle.com  * zc_sendobj	objsetid of snapshot to send
368312786SChris.Kirby@oracle.com  * zc_fromobj	objsetid of incremental fromsnap (may be zero)
36845367Sahrens  *
36855367Sahrens  * outputs: none
36865367Sahrens  */
3687789Sahrens static int
36885367Sahrens zfs_ioc_send(zfs_cmd_t *zc)
3689789Sahrens {
3690789Sahrens 	objset_t *fromsnap = NULL;
3691789Sahrens 	objset_t *tosnap;
3692789Sahrens 	file_t *fp;
3693789Sahrens 	int error;
36945367Sahrens 	offset_t off;
369512786SChris.Kirby@oracle.com 	dsl_dataset_t *ds;
369612786SChris.Kirby@oracle.com 	dsl_dataset_t *dsfrom = NULL;
369712786SChris.Kirby@oracle.com 	spa_t *spa;
369812786SChris.Kirby@oracle.com 	dsl_pool_t *dp;
369912786SChris.Kirby@oracle.com 
370012786SChris.Kirby@oracle.com 	error = spa_open(zc->zc_name, &spa, FTAG);
3701789Sahrens 	if (error)
3702789Sahrens 		return (error);
3703789Sahrens 
370412786SChris.Kirby@oracle.com 	dp = spa_get_dsl(spa);
370512786SChris.Kirby@oracle.com 	rw_enter(&dp->dp_config_rwlock, RW_READER);
370612786SChris.Kirby@oracle.com 	error = dsl_dataset_hold_obj(dp, zc->zc_sendobj, FTAG, &ds);
370712786SChris.Kirby@oracle.com 	rw_exit(&dp->dp_config_rwlock);
370812786SChris.Kirby@oracle.com 	if (error) {
370912786SChris.Kirby@oracle.com 		spa_close(spa, FTAG);
371012786SChris.Kirby@oracle.com 		return (error);
371112786SChris.Kirby@oracle.com 	}
371212786SChris.Kirby@oracle.com 
371312786SChris.Kirby@oracle.com 	error = dmu_objset_from_ds(ds, &tosnap);
371412786SChris.Kirby@oracle.com 	if (error) {
371512786SChris.Kirby@oracle.com 		dsl_dataset_rele(ds, FTAG);
371612786SChris.Kirby@oracle.com 		spa_close(spa, FTAG);
371712786SChris.Kirby@oracle.com 		return (error);
371812786SChris.Kirby@oracle.com 	}
371912786SChris.Kirby@oracle.com 
372012786SChris.Kirby@oracle.com 	if (zc->zc_fromobj != 0) {
372112786SChris.Kirby@oracle.com 		rw_enter(&dp->dp_config_rwlock, RW_READER);
372212786SChris.Kirby@oracle.com 		error = dsl_dataset_hold_obj(dp, zc->zc_fromobj, FTAG, &dsfrom);
372312786SChris.Kirby@oracle.com 		rw_exit(&dp->dp_config_rwlock);
372412786SChris.Kirby@oracle.com 		spa_close(spa, FTAG);
3725789Sahrens 		if (error) {
372612786SChris.Kirby@oracle.com 			dsl_dataset_rele(ds, FTAG);
3727789Sahrens 			return (error);
3728789Sahrens 		}
372912786SChris.Kirby@oracle.com 		error = dmu_objset_from_ds(dsfrom, &fromsnap);
373012786SChris.Kirby@oracle.com 		if (error) {
373112786SChris.Kirby@oracle.com 			dsl_dataset_rele(dsfrom, FTAG);
373212786SChris.Kirby@oracle.com 			dsl_dataset_rele(ds, FTAG);
373312786SChris.Kirby@oracle.com 			return (error);
373412786SChris.Kirby@oracle.com 		}
373512786SChris.Kirby@oracle.com 	} else {
373612786SChris.Kirby@oracle.com 		spa_close(spa, FTAG);
3737789Sahrens 	}
3738789Sahrens 
3739789Sahrens 	fp = getf(zc->zc_cookie);
3740789Sahrens 	if (fp == NULL) {
374112786SChris.Kirby@oracle.com 		dsl_dataset_rele(ds, FTAG);
374212786SChris.Kirby@oracle.com 		if (dsfrom)
374312786SChris.Kirby@oracle.com 			dsl_dataset_rele(dsfrom, FTAG);
3744789Sahrens 		return (EBADF);
3745789Sahrens 	}
3746789Sahrens 
37475367Sahrens 	off = fp->f_offset;
37485367Sahrens 	error = dmu_sendbackup(tosnap, fromsnap, zc->zc_obj, fp->f_vnode, &off);
37495367Sahrens 
37505367Sahrens 	if (VOP_SEEK(fp->f_vnode, fp->f_offset, &off, NULL) == 0)
37515367Sahrens 		fp->f_offset = off;
3752789Sahrens 	releasef(zc->zc_cookie);
375312786SChris.Kirby@oracle.com 	if (dsfrom)
375412786SChris.Kirby@oracle.com 		dsl_dataset_rele(dsfrom, FTAG);
375512786SChris.Kirby@oracle.com 	dsl_dataset_rele(ds, FTAG);
3756789Sahrens 	return (error);
3757789Sahrens }
3758789Sahrens 
37591544Seschrock static int
37601544Seschrock zfs_ioc_inject_fault(zfs_cmd_t *zc)
37611544Seschrock {
37621544Seschrock 	int id, error;
37631544Seschrock 
37641544Seschrock 	error = zio_inject_fault(zc->zc_name, (int)zc->zc_guid, &id,
37651544Seschrock 	    &zc->zc_inject_record);
37661544Seschrock 
37671544Seschrock 	if (error == 0)
37681544Seschrock 		zc->zc_guid = (uint64_t)id;
37691544Seschrock 
37701544Seschrock 	return (error);
37711544Seschrock }
37721544Seschrock 
37731544Seschrock static int
37741544Seschrock zfs_ioc_clear_fault(zfs_cmd_t *zc)
37751544Seschrock {
37761544Seschrock 	return (zio_clear_fault((int)zc->zc_guid));
37771544Seschrock }
37781544Seschrock 
37791544Seschrock static int
37801544Seschrock zfs_ioc_inject_list_next(zfs_cmd_t *zc)
37811544Seschrock {
37821544Seschrock 	int id = (int)zc->zc_guid;
37831544Seschrock 	int error;
37841544Seschrock 
37851544Seschrock 	error = zio_inject_list_next(&id, zc->zc_name, sizeof (zc->zc_name),
37861544Seschrock 	    &zc->zc_inject_record);
37871544Seschrock 
37881544Seschrock 	zc->zc_guid = id;
37891544Seschrock 
37901544Seschrock 	return (error);
37911544Seschrock }
37921544Seschrock 
37931544Seschrock static int
37941544Seschrock zfs_ioc_error_log(zfs_cmd_t *zc)
37951544Seschrock {
37961544Seschrock 	spa_t *spa;
37971544Seschrock 	int error;
37982676Seschrock 	size_t count = (size_t)zc->zc_nvlist_dst_size;
37991544Seschrock 
38001544Seschrock 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
38011544Seschrock 		return (error);
38021544Seschrock 
38032676Seschrock 	error = spa_get_errlog(spa, (void *)(uintptr_t)zc->zc_nvlist_dst,
38041544Seschrock 	    &count);
38051544Seschrock 	if (error == 0)
38062676Seschrock 		zc->zc_nvlist_dst_size = count;
38071544Seschrock 	else
38082676Seschrock 		zc->zc_nvlist_dst_size = spa_get_errlog_size(spa);
38091544Seschrock 
38101544Seschrock 	spa_close(spa, FTAG);
38111544Seschrock 
38121544Seschrock 	return (error);
38131544Seschrock }
38141544Seschrock 
38151544Seschrock static int
38161544Seschrock zfs_ioc_clear(zfs_cmd_t *zc)
38171544Seschrock {
38181544Seschrock 	spa_t *spa;
38191544Seschrock 	vdev_t *vd;
38201544Seschrock 	int error;
38211544Seschrock 
38227294Sperrin 	/*
38237294Sperrin 	 * On zpool clear we also fix up missing slogs
38247294Sperrin 	 */
38257294Sperrin 	mutex_enter(&spa_namespace_lock);
38267294Sperrin 	spa = spa_lookup(zc->zc_name);
38277294Sperrin 	if (spa == NULL) {
38287294Sperrin 		mutex_exit(&spa_namespace_lock);
38297294Sperrin 		return (EIO);
38307294Sperrin 	}
383110922SJeff.Bonwick@Sun.COM 	if (spa_get_log_state(spa) == SPA_LOG_MISSING) {
38327294Sperrin 		/* we need to let spa_open/spa_load clear the chains */
383310922SJeff.Bonwick@Sun.COM 		spa_set_log_state(spa, SPA_LOG_CLEAR);
38347294Sperrin 	}
383510921STim.Haley@Sun.COM 	spa->spa_last_open_failed = 0;
38367294Sperrin 	mutex_exit(&spa_namespace_lock);
38377294Sperrin 
383811727SVictor.Latushkin@Sun.COM 	if (zc->zc_cookie & ZPOOL_NO_REWIND) {
383910921STim.Haley@Sun.COM 		error = spa_open(zc->zc_name, &spa, FTAG);
384010921STim.Haley@Sun.COM 	} else {
384110921STim.Haley@Sun.COM 		nvlist_t *policy;
384210921STim.Haley@Sun.COM 		nvlist_t *config = NULL;
384310921STim.Haley@Sun.COM 
384410921STim.Haley@Sun.COM 		if (zc->zc_nvlist_src == NULL)
384510921STim.Haley@Sun.COM 			return (EINVAL);
384610921STim.Haley@Sun.COM 
384710921STim.Haley@Sun.COM 		if ((error = get_nvlist(zc->zc_nvlist_src,
384810921STim.Haley@Sun.COM 		    zc->zc_nvlist_src_size, zc->zc_iflags, &policy)) == 0) {
384910921STim.Haley@Sun.COM 			error = spa_open_rewind(zc->zc_name, &spa, FTAG,
385010921STim.Haley@Sun.COM 			    policy, &config);
385110921STim.Haley@Sun.COM 			if (config != NULL) {
3852*12949SGeorge.Wilson@Sun.COM 				int err;
3853*12949SGeorge.Wilson@Sun.COM 
3854*12949SGeorge.Wilson@Sun.COM 				if ((err = put_nvlist(zc, config)) != 0)
3855*12949SGeorge.Wilson@Sun.COM 					error = err;
385610921STim.Haley@Sun.COM 				nvlist_free(config);
385710921STim.Haley@Sun.COM 			}
385810921STim.Haley@Sun.COM 			nvlist_free(policy);
385910921STim.Haley@Sun.COM 		}
386010921STim.Haley@Sun.COM 	}
386110921STim.Haley@Sun.COM 
386210921STim.Haley@Sun.COM 	if (error)
38631544Seschrock 		return (error);
38641544Seschrock 
386510685SGeorge.Wilson@Sun.COM 	spa_vdev_state_enter(spa, SCL_NONE);
38661544Seschrock 
38672676Seschrock 	if (zc->zc_guid == 0) {
38681544Seschrock 		vd = NULL;
38696643Seschrock 	} else {
38706643Seschrock 		vd = spa_lookup_by_guid(spa, zc->zc_guid, B_TRUE);
38715450Sbrendan 		if (vd == NULL) {
38727754SJeff.Bonwick@Sun.COM 			(void) spa_vdev_state_exit(spa, NULL, ENODEV);
38735450Sbrendan 			spa_close(spa, FTAG);
38745450Sbrendan 			return (ENODEV);
38755450Sbrendan 		}
38761544Seschrock 	}
38771544Seschrock 
38787754SJeff.Bonwick@Sun.COM 	vdev_clear(spa, vd);
38797754SJeff.Bonwick@Sun.COM 
38807754SJeff.Bonwick@Sun.COM 	(void) spa_vdev_state_exit(spa, NULL, 0);
38817754SJeff.Bonwick@Sun.COM 
38827754SJeff.Bonwick@Sun.COM 	/*
38837754SJeff.Bonwick@Sun.COM 	 * Resume any suspended I/Os.
38847754SJeff.Bonwick@Sun.COM 	 */
38859234SGeorge.Wilson@Sun.COM 	if (zio_resume(spa) != 0)
38869234SGeorge.Wilson@Sun.COM 		error = EIO;
38871544Seschrock 
38881544Seschrock 	spa_close(spa, FTAG);
38891544Seschrock 
38909234SGeorge.Wilson@Sun.COM 	return (error);
38911544Seschrock }
38921544Seschrock 
38935367Sahrens /*
38945367Sahrens  * inputs:
38955367Sahrens  * zc_name	name of filesystem
38965367Sahrens  * zc_value	name of origin snapshot
38975367Sahrens  *
389810588SEric.Taylor@Sun.COM  * outputs:
389910588SEric.Taylor@Sun.COM  * zc_string	name of conflicting snapshot, if there is one
39005367Sahrens  */
39011544Seschrock static int
39022082Seschrock zfs_ioc_promote(zfs_cmd_t *zc)
39032082Seschrock {
39042417Sahrens 	char *cp;
39052417Sahrens 
39062417Sahrens 	/*
39072417Sahrens 	 * We don't need to unmount *all* the origin fs's snapshots, but
39082417Sahrens 	 * it's easier.
39092417Sahrens 	 */
39102676Seschrock 	cp = strchr(zc->zc_value, '@');
39112417Sahrens 	if (cp)
39122417Sahrens 		*cp = '\0';
39132676Seschrock 	(void) dmu_objset_find(zc->zc_value,
39142417Sahrens 	    zfs_unmount_snap, NULL, DS_FIND_SNAPSHOTS);
391510588SEric.Taylor@Sun.COM 	return (dsl_dataset_promote(zc->zc_name, zc->zc_string));
39162082Seschrock }
39172082Seschrock 
39184543Smarks /*
39199396SMatthew.Ahrens@Sun.COM  * Retrieve a single {user|group}{used|quota}@... property.
39209396SMatthew.Ahrens@Sun.COM  *
39219396SMatthew.Ahrens@Sun.COM  * inputs:
39229396SMatthew.Ahrens@Sun.COM  * zc_name	name of filesystem
39239396SMatthew.Ahrens@Sun.COM  * zc_objset_type zfs_userquota_prop_t
39249396SMatthew.Ahrens@Sun.COM  * zc_value	domain name (eg. "S-1-234-567-89")
39259396SMatthew.Ahrens@Sun.COM  * zc_guid	RID/UID/GID
39269396SMatthew.Ahrens@Sun.COM  *
39279396SMatthew.Ahrens@Sun.COM  * outputs:
39289396SMatthew.Ahrens@Sun.COM  * zc_cookie	property value
39299396SMatthew.Ahrens@Sun.COM  */
39309396SMatthew.Ahrens@Sun.COM static int
39319396SMatthew.Ahrens@Sun.COM zfs_ioc_userspace_one(zfs_cmd_t *zc)
39329396SMatthew.Ahrens@Sun.COM {
39339396SMatthew.Ahrens@Sun.COM 	zfsvfs_t *zfsvfs;
39349396SMatthew.Ahrens@Sun.COM 	int error;
39359396SMatthew.Ahrens@Sun.COM 
39369396SMatthew.Ahrens@Sun.COM 	if (zc->zc_objset_type >= ZFS_NUM_USERQUOTA_PROPS)
39379396SMatthew.Ahrens@Sun.COM 		return (EINVAL);
39389396SMatthew.Ahrens@Sun.COM 
393912620SMark.Shellenbaum@Oracle.COM 	error = zfsvfs_hold(zc->zc_name, FTAG, &zfsvfs, B_FALSE);
39409396SMatthew.Ahrens@Sun.COM 	if (error)
39419396SMatthew.Ahrens@Sun.COM 		return (error);
39429396SMatthew.Ahrens@Sun.COM 
39439396SMatthew.Ahrens@Sun.COM 	error = zfs_userspace_one(zfsvfs,
39449396SMatthew.Ahrens@Sun.COM 	    zc->zc_objset_type, zc->zc_value, zc->zc_guid, &zc->zc_cookie);
39459396SMatthew.Ahrens@Sun.COM 	zfsvfs_rele(zfsvfs, FTAG);
39469396SMatthew.Ahrens@Sun.COM 
39479396SMatthew.Ahrens@Sun.COM 	return (error);
39489396SMatthew.Ahrens@Sun.COM }
39499396SMatthew.Ahrens@Sun.COM 
39509396SMatthew.Ahrens@Sun.COM /*
39519396SMatthew.Ahrens@Sun.COM  * inputs:
39529396SMatthew.Ahrens@Sun.COM  * zc_name		name of filesystem
39539396SMatthew.Ahrens@Sun.COM  * zc_cookie		zap cursor
39549396SMatthew.Ahrens@Sun.COM  * zc_objset_type	zfs_userquota_prop_t
39559396SMatthew.Ahrens@Sun.COM  * zc_nvlist_dst[_size] buffer to fill (not really an nvlist)
39569396SMatthew.Ahrens@Sun.COM  *
39579396SMatthew.Ahrens@Sun.COM  * outputs:
39589396SMatthew.Ahrens@Sun.COM  * zc_nvlist_dst[_size]	data buffer (array of zfs_useracct_t)
39599396SMatthew.Ahrens@Sun.COM  * zc_cookie	zap cursor
39609396SMatthew.Ahrens@Sun.COM  */
39619396SMatthew.Ahrens@Sun.COM static int
39629396SMatthew.Ahrens@Sun.COM zfs_ioc_userspace_many(zfs_cmd_t *zc)
39639396SMatthew.Ahrens@Sun.COM {
39649396SMatthew.Ahrens@Sun.COM 	zfsvfs_t *zfsvfs;
396511933STim.Haley@Sun.COM 	int bufsize = zc->zc_nvlist_dst_size;
396611933STim.Haley@Sun.COM 
396711933STim.Haley@Sun.COM 	if (bufsize <= 0)
396811933STim.Haley@Sun.COM 		return (ENOMEM);
396911933STim.Haley@Sun.COM 
397012620SMark.Shellenbaum@Oracle.COM 	int error = zfsvfs_hold(zc->zc_name, FTAG, &zfsvfs, B_FALSE);
39719396SMatthew.Ahrens@Sun.COM 	if (error)
39729396SMatthew.Ahrens@Sun.COM 		return (error);
39739396SMatthew.Ahrens@Sun.COM 
39749396SMatthew.Ahrens@Sun.COM 	void *buf = kmem_alloc(bufsize, KM_SLEEP);
39759396SMatthew.Ahrens@Sun.COM 
39769396SMatthew.Ahrens@Sun.COM 	error = zfs_userspace_many(zfsvfs, zc->zc_objset_type, &zc->zc_cookie,
39779396SMatthew.Ahrens@Sun.COM 	    buf, &zc->zc_nvlist_dst_size);
39789396SMatthew.Ahrens@Sun.COM 
39799396SMatthew.Ahrens@Sun.COM 	if (error == 0) {
39809396SMatthew.Ahrens@Sun.COM 		error = xcopyout(buf,
39819396SMatthew.Ahrens@Sun.COM 		    (void *)(uintptr_t)zc->zc_nvlist_dst,
39829396SMatthew.Ahrens@Sun.COM 		    zc->zc_nvlist_dst_size);
39839396SMatthew.Ahrens@Sun.COM 	}
39849396SMatthew.Ahrens@Sun.COM 	kmem_free(buf, bufsize);
39859396SMatthew.Ahrens@Sun.COM 	zfsvfs_rele(zfsvfs, FTAG);
39869396SMatthew.Ahrens@Sun.COM 
39879396SMatthew.Ahrens@Sun.COM 	return (error);
39889396SMatthew.Ahrens@Sun.COM }
39899396SMatthew.Ahrens@Sun.COM 
39909396SMatthew.Ahrens@Sun.COM /*
39919396SMatthew.Ahrens@Sun.COM  * inputs:
39929396SMatthew.Ahrens@Sun.COM  * zc_name		name of filesystem
39939396SMatthew.Ahrens@Sun.COM  *
39949396SMatthew.Ahrens@Sun.COM  * outputs:
39959396SMatthew.Ahrens@Sun.COM  * none
39969396SMatthew.Ahrens@Sun.COM  */
39979396SMatthew.Ahrens@Sun.COM static int
39989396SMatthew.Ahrens@Sun.COM zfs_ioc_userspace_upgrade(zfs_cmd_t *zc)
39999396SMatthew.Ahrens@Sun.COM {
40009396SMatthew.Ahrens@Sun.COM 	objset_t *os;
400111422SMark.Musante@Sun.COM 	int error = 0;
40029396SMatthew.Ahrens@Sun.COM 	zfsvfs_t *zfsvfs;
40039396SMatthew.Ahrens@Sun.COM 
40049396SMatthew.Ahrens@Sun.COM 	if (getzfsvfs(zc->zc_name, &zfsvfs) == 0) {
400510298SMatthew.Ahrens@Sun.COM 		if (!dmu_objset_userused_enabled(zfsvfs->z_os)) {
40069396SMatthew.Ahrens@Sun.COM 			/*
40079396SMatthew.Ahrens@Sun.COM 			 * If userused is not enabled, it may be because the
40089396SMatthew.Ahrens@Sun.COM 			 * objset needs to be closed & reopened (to grow the
40099396SMatthew.Ahrens@Sun.COM 			 * objset_phys_t).  Suspend/resume the fs will do that.
40109396SMatthew.Ahrens@Sun.COM 			 */
401110298SMatthew.Ahrens@Sun.COM 			error = zfs_suspend_fs(zfsvfs);
401210298SMatthew.Ahrens@Sun.COM 			if (error == 0)
401310298SMatthew.Ahrens@Sun.COM 				error = zfs_resume_fs(zfsvfs, zc->zc_name);
40149396SMatthew.Ahrens@Sun.COM 		}
40159396SMatthew.Ahrens@Sun.COM 		if (error == 0)
40169396SMatthew.Ahrens@Sun.COM 			error = dmu_objset_userspace_upgrade(zfsvfs->z_os);
40179396SMatthew.Ahrens@Sun.COM 		VFS_RELE(zfsvfs->z_vfs);
40189396SMatthew.Ahrens@Sun.COM 	} else {
401910298SMatthew.Ahrens@Sun.COM 		/* XXX kind of reading contents without owning */
402010298SMatthew.Ahrens@Sun.COM 		error = dmu_objset_hold(zc->zc_name, FTAG, &os);
40219396SMatthew.Ahrens@Sun.COM 		if (error)
40229396SMatthew.Ahrens@Sun.COM 			return (error);
40239396SMatthew.Ahrens@Sun.COM 
40249396SMatthew.Ahrens@Sun.COM 		error = dmu_objset_userspace_upgrade(os);
402510298SMatthew.Ahrens@Sun.COM 		dmu_objset_rele(os, FTAG);
40269396SMatthew.Ahrens@Sun.COM 	}
40279396SMatthew.Ahrens@Sun.COM 
40289396SMatthew.Ahrens@Sun.COM 	return (error);
40299396SMatthew.Ahrens@Sun.COM }
40309396SMatthew.Ahrens@Sun.COM 
40319396SMatthew.Ahrens@Sun.COM /*
40324543Smarks  * We don't want to have a hard dependency
40334543Smarks  * against some special symbols in sharefs
40345331Samw  * nfs, and smbsrv.  Determine them if needed when
40354543Smarks  * the first file system is shared.
40365331Samw  * Neither sharefs, nfs or smbsrv are unloadable modules.
40374543Smarks  */
40385331Samw int (*znfsexport_fs)(void *arg);
40394543Smarks int (*zshare_fs)(enum sharefs_sys_op, share_t *, uint32_t);
40405331Samw int (*zsmbexport_fs)(void *arg, boolean_t add_share);
40415331Samw 
40425331Samw int zfs_nfsshare_inited;
40435331Samw int zfs_smbshare_inited;
40445331Samw 
40454543Smarks ddi_modhandle_t nfs_mod;
40464543Smarks ddi_modhandle_t sharefs_mod;
40475331Samw ddi_modhandle_t smbsrv_mod;
40484543Smarks kmutex_t zfs_share_lock;
40494543Smarks 
40504543Smarks static int
40515331Samw zfs_init_sharefs()
40525331Samw {
40535331Samw 	int error;
40545331Samw 
40555331Samw 	ASSERT(MUTEX_HELD(&zfs_share_lock));
40565331Samw 	/* Both NFS and SMB shares also require sharetab support. */
40575331Samw 	if (sharefs_mod == NULL && ((sharefs_mod =
40585331Samw 	    ddi_modopen("fs/sharefs",
40595331Samw 	    KRTLD_MODE_FIRST, &error)) == NULL)) {
40605331Samw 		return (ENOSYS);
40615331Samw 	}
40625331Samw 	if (zshare_fs == NULL && ((zshare_fs =
40635331Samw 	    (int (*)(enum sharefs_sys_op, share_t *, uint32_t))
40645331Samw 	    ddi_modsym(sharefs_mod, "sharefs_impl", &error)) == NULL)) {
40655331Samw 		return (ENOSYS);
40665331Samw 	}
40675331Samw 	return (0);
40685331Samw }
40695331Samw 
40705331Samw static int
40714543Smarks zfs_ioc_share(zfs_cmd_t *zc)
40724543Smarks {
40734543Smarks 	int error;
40744543Smarks 	int opcode;
40754543Smarks 
40765331Samw 	switch (zc->zc_share.z_sharetype) {
40775331Samw 	case ZFS_SHARE_NFS:
40785331Samw 	case ZFS_UNSHARE_NFS:
40795331Samw 		if (zfs_nfsshare_inited == 0) {
40805331Samw 			mutex_enter(&zfs_share_lock);
40815331Samw 			if (nfs_mod == NULL && ((nfs_mod = ddi_modopen("fs/nfs",
40825331Samw 			    KRTLD_MODE_FIRST, &error)) == NULL)) {
40835331Samw 				mutex_exit(&zfs_share_lock);
40845331Samw 				return (ENOSYS);
40855331Samw 			}
40865331Samw 			if (znfsexport_fs == NULL &&
40875331Samw 			    ((znfsexport_fs = (int (*)(void *))
40885331Samw 			    ddi_modsym(nfs_mod,
40895331Samw 			    "nfs_export", &error)) == NULL)) {
40905331Samw 				mutex_exit(&zfs_share_lock);
40915331Samw 				return (ENOSYS);
40925331Samw 			}
40935331Samw 			error = zfs_init_sharefs();
40945331Samw 			if (error) {
40955331Samw 				mutex_exit(&zfs_share_lock);
40965331Samw 				return (ENOSYS);
40975331Samw 			}
40985331Samw 			zfs_nfsshare_inited = 1;
40994543Smarks 			mutex_exit(&zfs_share_lock);
41004543Smarks 		}
41015331Samw 		break;
41025331Samw 	case ZFS_SHARE_SMB:
41035331Samw 	case ZFS_UNSHARE_SMB:
41045331Samw 		if (zfs_smbshare_inited == 0) {
41055331Samw 			mutex_enter(&zfs_share_lock);
41065331Samw 			if (smbsrv_mod == NULL && ((smbsrv_mod =
41075331Samw 			    ddi_modopen("drv/smbsrv",
41085331Samw 			    KRTLD_MODE_FIRST, &error)) == NULL)) {
41095331Samw 				mutex_exit(&zfs_share_lock);
41105331Samw 				return (ENOSYS);
41115331Samw 			}
41125331Samw 			if (zsmbexport_fs == NULL && ((zsmbexport_fs =
41135331Samw 			    (int (*)(void *, boolean_t))ddi_modsym(smbsrv_mod,
41146139Sjb150015 			    "smb_server_share", &error)) == NULL)) {
41155331Samw 				mutex_exit(&zfs_share_lock);
41165331Samw 				return (ENOSYS);
41175331Samw 			}
41185331Samw 			error = zfs_init_sharefs();
41195331Samw 			if (error) {
41205331Samw 				mutex_exit(&zfs_share_lock);
41215331Samw 				return (ENOSYS);
41225331Samw 			}
41235331Samw 			zfs_smbshare_inited = 1;
41244543Smarks 			mutex_exit(&zfs_share_lock);
41254543Smarks 		}
41265331Samw 		break;
41275331Samw 	default:
41285331Samw 		return (EINVAL);
41294543Smarks 	}
41304543Smarks 
41315331Samw 	switch (zc->zc_share.z_sharetype) {
41325331Samw 	case ZFS_SHARE_NFS:
41335331Samw 	case ZFS_UNSHARE_NFS:
41345331Samw 		if (error =
41355331Samw 		    znfsexport_fs((void *)
41365331Samw 		    (uintptr_t)zc->zc_share.z_exportdata))
41375331Samw 			return (error);
41385331Samw 		break;
41395331Samw 	case ZFS_SHARE_SMB:
41405331Samw 	case ZFS_UNSHARE_SMB:
41415331Samw 		if (error = zsmbexport_fs((void *)
41425331Samw 		    (uintptr_t)zc->zc_share.z_exportdata,
41435331Samw 		    zc->zc_share.z_sharetype == ZFS_SHARE_SMB ?
41448845Samw@Sun.COM 		    B_TRUE: B_FALSE)) {
41455331Samw 			return (error);
41465331Samw 		}
41475331Samw 		break;
41485331Samw 	}
41495331Samw 
41505331Samw 	opcode = (zc->zc_share.z_sharetype == ZFS_SHARE_NFS ||
41515331Samw 	    zc->zc_share.z_sharetype == ZFS_SHARE_SMB) ?
41524543Smarks 	    SHAREFS_ADD : SHAREFS_REMOVE;
41534543Smarks 
41545331Samw 	/*
41555331Samw 	 * Add or remove share from sharetab
41565331Samw 	 */
41574543Smarks 	error = zshare_fs(opcode,
41584543Smarks 	    (void *)(uintptr_t)zc->zc_share.z_sharedata,
41594543Smarks 	    zc->zc_share.z_sharemax);
41604543Smarks 
41614543Smarks 	return (error);
41624543Smarks 
41634543Smarks }
41644543Smarks 
41658845Samw@Sun.COM ace_t full_access[] = {
41668845Samw@Sun.COM 	{(uid_t)-1, ACE_ALL_PERMS, ACE_EVERYONE, 0}
41678845Samw@Sun.COM };
41688845Samw@Sun.COM 
41698845Samw@Sun.COM /*
41708845Samw@Sun.COM  * Remove all ACL files in shares dir
41718845Samw@Sun.COM  */
41728845Samw@Sun.COM static int
41738845Samw@Sun.COM zfs_smb_acl_purge(znode_t *dzp)
41748845Samw@Sun.COM {
41758845Samw@Sun.COM 	zap_cursor_t	zc;
41768845Samw@Sun.COM 	zap_attribute_t	zap;
41778845Samw@Sun.COM 	zfsvfs_t *zfsvfs = dzp->z_zfsvfs;
41788845Samw@Sun.COM 	int error;
41798845Samw@Sun.COM 
41808845Samw@Sun.COM 	for (zap_cursor_init(&zc, zfsvfs->z_os, dzp->z_id);
41818845Samw@Sun.COM 	    (error = zap_cursor_retrieve(&zc, &zap)) == 0;
41828845Samw@Sun.COM 	    zap_cursor_advance(&zc)) {
41838845Samw@Sun.COM 		if ((error = VOP_REMOVE(ZTOV(dzp), zap.za_name, kcred,
41848845Samw@Sun.COM 		    NULL, 0)) != 0)
41858845Samw@Sun.COM 			break;
41868845Samw@Sun.COM 	}
41878845Samw@Sun.COM 	zap_cursor_fini(&zc);
41888845Samw@Sun.COM 	return (error);
41898845Samw@Sun.COM }
41908845Samw@Sun.COM 
41918845Samw@Sun.COM static int
41928845Samw@Sun.COM zfs_ioc_smb_acl(zfs_cmd_t *zc)
41938845Samw@Sun.COM {
41948845Samw@Sun.COM 	vnode_t *vp;
41958845Samw@Sun.COM 	znode_t *dzp;
41968845Samw@Sun.COM 	vnode_t *resourcevp = NULL;
41978845Samw@Sun.COM 	znode_t *sharedir;
41988845Samw@Sun.COM 	zfsvfs_t *zfsvfs;
41998845Samw@Sun.COM 	nvlist_t *nvlist;
42008845Samw@Sun.COM 	char *src, *target;
42018845Samw@Sun.COM 	vattr_t vattr;
42028845Samw@Sun.COM 	vsecattr_t vsec;
42038845Samw@Sun.COM 	int error = 0;
42048845Samw@Sun.COM 
42058845Samw@Sun.COM 	if ((error = lookupname(zc->zc_value, UIO_SYSSPACE,
42068845Samw@Sun.COM 	    NO_FOLLOW, NULL, &vp)) != 0)
42078845Samw@Sun.COM 		return (error);
42088845Samw@Sun.COM 
42098845Samw@Sun.COM 	/* Now make sure mntpnt and dataset are ZFS */
42108845Samw@Sun.COM 
42118845Samw@Sun.COM 	if (vp->v_vfsp->vfs_fstype != zfsfstype ||
42128845Samw@Sun.COM 	    (strcmp((char *)refstr_value(vp->v_vfsp->vfs_resource),
42138845Samw@Sun.COM 	    zc->zc_name) != 0)) {
42148845Samw@Sun.COM 		VN_RELE(vp);
42158845Samw@Sun.COM 		return (EINVAL);
42168845Samw@Sun.COM 	}
42178845Samw@Sun.COM 
42188845Samw@Sun.COM 	dzp = VTOZ(vp);
42198845Samw@Sun.COM 	zfsvfs = dzp->z_zfsvfs;
42208845Samw@Sun.COM 	ZFS_ENTER(zfsvfs);
42218845Samw@Sun.COM 
42229030SMark.Shellenbaum@Sun.COM 	/*
42239030SMark.Shellenbaum@Sun.COM 	 * Create share dir if its missing.
42249030SMark.Shellenbaum@Sun.COM 	 */
42259030SMark.Shellenbaum@Sun.COM 	mutex_enter(&zfsvfs->z_lock);
42269030SMark.Shellenbaum@Sun.COM 	if (zfsvfs->z_shares_dir == 0) {
42279030SMark.Shellenbaum@Sun.COM 		dmu_tx_t *tx;
42289030SMark.Shellenbaum@Sun.COM 
42299030SMark.Shellenbaum@Sun.COM 		tx = dmu_tx_create(zfsvfs->z_os);
42309030SMark.Shellenbaum@Sun.COM 		dmu_tx_hold_zap(tx, MASTER_NODE_OBJ, TRUE,
42319030SMark.Shellenbaum@Sun.COM 		    ZFS_SHARES_DIR);
42329030SMark.Shellenbaum@Sun.COM 		dmu_tx_hold_zap(tx, DMU_NEW_OBJECT, FALSE, NULL);
42339030SMark.Shellenbaum@Sun.COM 		error = dmu_tx_assign(tx, TXG_WAIT);
42349030SMark.Shellenbaum@Sun.COM 		if (error) {
42359030SMark.Shellenbaum@Sun.COM 			dmu_tx_abort(tx);
42369030SMark.Shellenbaum@Sun.COM 		} else {
42379030SMark.Shellenbaum@Sun.COM 			error = zfs_create_share_dir(zfsvfs, tx);
42389030SMark.Shellenbaum@Sun.COM 			dmu_tx_commit(tx);
42399030SMark.Shellenbaum@Sun.COM 		}
42409030SMark.Shellenbaum@Sun.COM 		if (error) {
42419030SMark.Shellenbaum@Sun.COM 			mutex_exit(&zfsvfs->z_lock);
42429030SMark.Shellenbaum@Sun.COM 			VN_RELE(vp);
42439030SMark.Shellenbaum@Sun.COM 			ZFS_EXIT(zfsvfs);
42449030SMark.Shellenbaum@Sun.COM 			return (error);
42459030SMark.Shellenbaum@Sun.COM 		}
42469030SMark.Shellenbaum@Sun.COM 	}
42479030SMark.Shellenbaum@Sun.COM 	mutex_exit(&zfsvfs->z_lock);
42489030SMark.Shellenbaum@Sun.COM 
42499030SMark.Shellenbaum@Sun.COM 	ASSERT(zfsvfs->z_shares_dir);
42508845Samw@Sun.COM 	if ((error = zfs_zget(zfsvfs, zfsvfs->z_shares_dir, &sharedir)) != 0) {
42519030SMark.Shellenbaum@Sun.COM 		VN_RELE(vp);
42528845Samw@Sun.COM 		ZFS_EXIT(zfsvfs);
42538845Samw@Sun.COM 		return (error);
42548845Samw@Sun.COM 	}
42558845Samw@Sun.COM 
42568845Samw@Sun.COM 	switch (zc->zc_cookie) {
42578845Samw@Sun.COM 	case ZFS_SMB_ACL_ADD:
42588845Samw@Sun.COM 		vattr.va_mask = AT_MODE|AT_UID|AT_GID|AT_TYPE;
42598845Samw@Sun.COM 		vattr.va_type = VREG;
42608845Samw@Sun.COM 		vattr.va_mode = S_IFREG|0777;
42618845Samw@Sun.COM 		vattr.va_uid = 0;
42628845Samw@Sun.COM 		vattr.va_gid = 0;
42638845Samw@Sun.COM 
42648845Samw@Sun.COM 		vsec.vsa_mask = VSA_ACE;
42658845Samw@Sun.COM 		vsec.vsa_aclentp = &full_access;
42668845Samw@Sun.COM 		vsec.vsa_aclentsz = sizeof (full_access);
42678845Samw@Sun.COM 		vsec.vsa_aclcnt = 1;
42688845Samw@Sun.COM 
42698845Samw@Sun.COM 		error = VOP_CREATE(ZTOV(sharedir), zc->zc_string,
42708845Samw@Sun.COM 		    &vattr, EXCL, 0, &resourcevp, kcred, 0, NULL, &vsec);
42718845Samw@Sun.COM 		if (resourcevp)
42728845Samw@Sun.COM 			VN_RELE(resourcevp);
42738845Samw@Sun.COM 		break;
42748845Samw@Sun.COM 
42758845Samw@Sun.COM 	case ZFS_SMB_ACL_REMOVE:
42768845Samw@Sun.COM 		error = VOP_REMOVE(ZTOV(sharedir), zc->zc_string, kcred,
42778845Samw@Sun.COM 		    NULL, 0);
42788845Samw@Sun.COM 		break;
42798845Samw@Sun.COM 
42808845Samw@Sun.COM 	case ZFS_SMB_ACL_RENAME:
42818845Samw@Sun.COM 		if ((error = get_nvlist(zc->zc_nvlist_src,
42829643SEric.Taylor@Sun.COM 		    zc->zc_nvlist_src_size, zc->zc_iflags, &nvlist)) != 0) {
42838845Samw@Sun.COM 			VN_RELE(vp);
42848845Samw@Sun.COM 			ZFS_EXIT(zfsvfs);
42858845Samw@Sun.COM 			return (error);
42868845Samw@Sun.COM 		}
42878845Samw@Sun.COM 		if (nvlist_lookup_string(nvlist, ZFS_SMB_ACL_SRC, &src) ||
42888845Samw@Sun.COM 		    nvlist_lookup_string(nvlist, ZFS_SMB_ACL_TARGET,
42898845Samw@Sun.COM 		    &target)) {
42908845Samw@Sun.COM 			VN_RELE(vp);
42919179SMark.Shellenbaum@Sun.COM 			VN_RELE(ZTOV(sharedir));
42928845Samw@Sun.COM 			ZFS_EXIT(zfsvfs);
429311422SMark.Musante@Sun.COM 			nvlist_free(nvlist);
42948845Samw@Sun.COM 			return (error);
42958845Samw@Sun.COM 		}
42968845Samw@Sun.COM 		error = VOP_RENAME(ZTOV(sharedir), src, ZTOV(sharedir), target,
42978845Samw@Sun.COM 		    kcred, NULL, 0);
42988845Samw@Sun.COM 		nvlist_free(nvlist);
42998845Samw@Sun.COM 		break;
43008845Samw@Sun.COM 
43018845Samw@Sun.COM 	case ZFS_SMB_ACL_PURGE:
43028845Samw@Sun.COM 		error = zfs_smb_acl_purge(sharedir);
43038845Samw@Sun.COM 		break;
43048845Samw@Sun.COM 
43058845Samw@Sun.COM 	default:
43068845Samw@Sun.COM 		error = EINVAL;
43078845Samw@Sun.COM 		break;
43088845Samw@Sun.COM 	}
43098845Samw@Sun.COM 
43108845Samw@Sun.COM 	VN_RELE(vp);
43118845Samw@Sun.COM 	VN_RELE(ZTOV(sharedir));
43128845Samw@Sun.COM 
43138845Samw@Sun.COM 	ZFS_EXIT(zfsvfs);
43148845Samw@Sun.COM 
43158845Samw@Sun.COM 	return (error);
43168845Samw@Sun.COM }
43178845Samw@Sun.COM 
43184543Smarks /*
431910242Schris.kirby@sun.com  * inputs:
432012527SChris.Kirby@oracle.com  * zc_name		name of filesystem
432112527SChris.Kirby@oracle.com  * zc_value		short name of snap
432212527SChris.Kirby@oracle.com  * zc_string		user-supplied tag for this hold
432312527SChris.Kirby@oracle.com  * zc_cookie		recursive flag
432412527SChris.Kirby@oracle.com  * zc_temphold		set if hold is temporary
432512527SChris.Kirby@oracle.com  * zc_cleanup_fd	cleanup-on-exit file descriptor for calling process
432612786SChris.Kirby@oracle.com  * zc_sendobj		if non-zero, the objid for zc_name@zc_value
432712786SChris.Kirby@oracle.com  * zc_createtxg		if zc_sendobj is non-zero, snap must have zc_createtxg
432810242Schris.kirby@sun.com  *
432910242Schris.kirby@sun.com  * outputs:		none
433010242Schris.kirby@sun.com  */
433110242Schris.kirby@sun.com static int
433210242Schris.kirby@sun.com zfs_ioc_hold(zfs_cmd_t *zc)
433310242Schris.kirby@sun.com {
433410242Schris.kirby@sun.com 	boolean_t recursive = zc->zc_cookie;
433512786SChris.Kirby@oracle.com 	spa_t *spa;
433612786SChris.Kirby@oracle.com 	dsl_pool_t *dp;
433712786SChris.Kirby@oracle.com 	dsl_dataset_t *ds;
433812786SChris.Kirby@oracle.com 	int error;
433912786SChris.Kirby@oracle.com 	minor_t minor = 0;
434010242Schris.kirby@sun.com 
434110242Schris.kirby@sun.com 	if (snapshot_namecheck(zc->zc_value, NULL, NULL) != 0)
434210242Schris.kirby@sun.com 		return (EINVAL);
434310242Schris.kirby@sun.com 
434412786SChris.Kirby@oracle.com 	if (zc->zc_sendobj == 0) {
434512786SChris.Kirby@oracle.com 		return (dsl_dataset_user_hold(zc->zc_name, zc->zc_value,
434612786SChris.Kirby@oracle.com 		    zc->zc_string, recursive, zc->zc_temphold,
434712786SChris.Kirby@oracle.com 		    zc->zc_cleanup_fd));
434812786SChris.Kirby@oracle.com 	}
434912786SChris.Kirby@oracle.com 
435012786SChris.Kirby@oracle.com 	if (recursive)
435112786SChris.Kirby@oracle.com 		return (EINVAL);
435212786SChris.Kirby@oracle.com 
435312786SChris.Kirby@oracle.com 	error = spa_open(zc->zc_name, &spa, FTAG);
435412786SChris.Kirby@oracle.com 	if (error)
435512786SChris.Kirby@oracle.com 		return (error);
435612786SChris.Kirby@oracle.com 
435712786SChris.Kirby@oracle.com 	dp = spa_get_dsl(spa);
435812786SChris.Kirby@oracle.com 	rw_enter(&dp->dp_config_rwlock, RW_READER);
435912786SChris.Kirby@oracle.com 	error = dsl_dataset_hold_obj(dp, zc->zc_sendobj, FTAG, &ds);
436012786SChris.Kirby@oracle.com 	rw_exit(&dp->dp_config_rwlock);
436112786SChris.Kirby@oracle.com 	spa_close(spa, FTAG);
436212786SChris.Kirby@oracle.com 	if (error)
436312786SChris.Kirby@oracle.com 		return (error);
436412786SChris.Kirby@oracle.com 
436512786SChris.Kirby@oracle.com 	/*
436612786SChris.Kirby@oracle.com 	 * Until we have a hold on this snapshot, it's possible that
436712786SChris.Kirby@oracle.com 	 * zc_sendobj could've been destroyed and reused as part
436812786SChris.Kirby@oracle.com 	 * of a later txg.  Make sure we're looking at the right object.
436912786SChris.Kirby@oracle.com 	 */
437012786SChris.Kirby@oracle.com 	if (zc->zc_createtxg != ds->ds_phys->ds_creation_txg) {
437112786SChris.Kirby@oracle.com 		dsl_dataset_rele(ds, FTAG);
437212786SChris.Kirby@oracle.com 		return (ENOENT);
437312786SChris.Kirby@oracle.com 	}
437412786SChris.Kirby@oracle.com 
437512786SChris.Kirby@oracle.com 	if (zc->zc_cleanup_fd != -1 && zc->zc_temphold) {
437612786SChris.Kirby@oracle.com 		error = zfs_onexit_fd_hold(zc->zc_cleanup_fd, &minor);
437712786SChris.Kirby@oracle.com 		if (error) {
437812786SChris.Kirby@oracle.com 			dsl_dataset_rele(ds, FTAG);
437912786SChris.Kirby@oracle.com 			return (error);
438012786SChris.Kirby@oracle.com 		}
438112786SChris.Kirby@oracle.com 	}
438212786SChris.Kirby@oracle.com 
438312786SChris.Kirby@oracle.com 	error = dsl_dataset_user_hold_for_send(ds, zc->zc_string,
438412786SChris.Kirby@oracle.com 	    zc->zc_temphold);
438512786SChris.Kirby@oracle.com 	if (minor != 0) {
438612786SChris.Kirby@oracle.com 		if (error == 0) {
438712786SChris.Kirby@oracle.com 			dsl_register_onexit_hold_cleanup(ds, zc->zc_string,
438812786SChris.Kirby@oracle.com 			    minor);
438912786SChris.Kirby@oracle.com 		}
439012786SChris.Kirby@oracle.com 		zfs_onexit_fd_rele(zc->zc_cleanup_fd);
439112786SChris.Kirby@oracle.com 	}
439212786SChris.Kirby@oracle.com 	dsl_dataset_rele(ds, FTAG);
439312786SChris.Kirby@oracle.com 
439412786SChris.Kirby@oracle.com 	return (error);
439510242Schris.kirby@sun.com }
439610242Schris.kirby@sun.com 
439710242Schris.kirby@sun.com /*
439810242Schris.kirby@sun.com  * inputs:
439912527SChris.Kirby@oracle.com  * zc_name	name of dataset from which we're releasing a user hold
440010242Schris.kirby@sun.com  * zc_value	short name of snap
440112527SChris.Kirby@oracle.com  * zc_string	user-supplied tag for this hold
440210242Schris.kirby@sun.com  * zc_cookie	recursive flag
440310242Schris.kirby@sun.com  *
440412527SChris.Kirby@oracle.com  * outputs:	none
440510242Schris.kirby@sun.com  */
440610242Schris.kirby@sun.com static int
440710242Schris.kirby@sun.com zfs_ioc_release(zfs_cmd_t *zc)
440810242Schris.kirby@sun.com {
440910242Schris.kirby@sun.com 	boolean_t recursive = zc->zc_cookie;
441010242Schris.kirby@sun.com 
441110242Schris.kirby@sun.com 	if (snapshot_namecheck(zc->zc_value, NULL, NULL) != 0)
441210242Schris.kirby@sun.com 		return (EINVAL);
441310242Schris.kirby@sun.com 
441410242Schris.kirby@sun.com 	return (dsl_dataset_user_release(zc->zc_name, zc->zc_value,
441510242Schris.kirby@sun.com 	    zc->zc_string, recursive));
441610242Schris.kirby@sun.com }
441710242Schris.kirby@sun.com 
441810242Schris.kirby@sun.com /*
441910242Schris.kirby@sun.com  * inputs:
442010242Schris.kirby@sun.com  * zc_name		name of filesystem
442110242Schris.kirby@sun.com  *
442210242Schris.kirby@sun.com  * outputs:
442310242Schris.kirby@sun.com  * zc_nvlist_src{_size}	nvlist of snapshot holds
442410242Schris.kirby@sun.com  */
442510242Schris.kirby@sun.com static int
442610242Schris.kirby@sun.com zfs_ioc_get_holds(zfs_cmd_t *zc)
442710242Schris.kirby@sun.com {
442810242Schris.kirby@sun.com 	nvlist_t *nvp;
442910242Schris.kirby@sun.com 	int error;
443010242Schris.kirby@sun.com 
443110242Schris.kirby@sun.com 	if ((error = dsl_dataset_get_holds(zc->zc_name, &nvp)) == 0) {
443210242Schris.kirby@sun.com 		error = put_nvlist(zc, nvp);
443310242Schris.kirby@sun.com 		nvlist_free(nvp);
443410242Schris.kirby@sun.com 	}
443510242Schris.kirby@sun.com 
443610242Schris.kirby@sun.com 	return (error);
443710242Schris.kirby@sun.com }
443810242Schris.kirby@sun.com 
443910242Schris.kirby@sun.com /*
44404988Sek110237  * pool create, destroy, and export don't log the history as part of
44414988Sek110237  * zfsdev_ioctl, but rather zfs_ioc_pool_create, and zfs_ioc_pool_export
44424988Sek110237  * do the logging of those commands.
44434543Smarks  */
4444789Sahrens static zfs_ioc_vec_t zfs_ioc_vec[] = {
44459234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_pool_create, zfs_secpolicy_config, POOL_NAME, B_FALSE,
44469234SGeorge.Wilson@Sun.COM 	    B_FALSE },
44479234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_pool_destroy,	zfs_secpolicy_config, POOL_NAME, B_FALSE,
44489234SGeorge.Wilson@Sun.COM 	    B_FALSE },
44499234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_pool_import, zfs_secpolicy_config, POOL_NAME, B_TRUE,
44509234SGeorge.Wilson@Sun.COM 	    B_FALSE },
44519234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_pool_export, zfs_secpolicy_config, POOL_NAME, B_FALSE,
44529234SGeorge.Wilson@Sun.COM 	    B_FALSE },
44539234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_pool_configs,	zfs_secpolicy_none, NO_NAME, B_FALSE,
44549234SGeorge.Wilson@Sun.COM 	    B_FALSE },
44559234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_pool_stats, zfs_secpolicy_read, POOL_NAME, B_FALSE,
44569234SGeorge.Wilson@Sun.COM 	    B_FALSE },
44579234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_pool_tryimport, zfs_secpolicy_config, NO_NAME, B_FALSE,
44589234SGeorge.Wilson@Sun.COM 	    B_FALSE },
445912296SLin.Ling@Sun.COM 	{ zfs_ioc_pool_scan, zfs_secpolicy_config, POOL_NAME, B_TRUE,
44609234SGeorge.Wilson@Sun.COM 	    B_TRUE },
44619234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_pool_freeze, zfs_secpolicy_config, NO_NAME, B_FALSE,
44629234SGeorge.Wilson@Sun.COM 	    B_FALSE },
44639234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_pool_upgrade,	zfs_secpolicy_config, POOL_NAME, B_TRUE,
44649234SGeorge.Wilson@Sun.COM 	    B_TRUE },
44659234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_pool_get_history, zfs_secpolicy_config, POOL_NAME, B_FALSE,
44669234SGeorge.Wilson@Sun.COM 	    B_FALSE },
44679234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_vdev_add, zfs_secpolicy_config, POOL_NAME, B_TRUE,
44689234SGeorge.Wilson@Sun.COM 	    B_TRUE },
44699234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_vdev_remove, zfs_secpolicy_config, POOL_NAME, B_TRUE,
44709234SGeorge.Wilson@Sun.COM 	    B_TRUE },
44719234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_vdev_set_state, zfs_secpolicy_config,	POOL_NAME, B_TRUE,
44729234SGeorge.Wilson@Sun.COM 	    B_FALSE },
44739234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_vdev_attach, zfs_secpolicy_config, POOL_NAME, B_TRUE,
44749234SGeorge.Wilson@Sun.COM 	    B_TRUE },
44759234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_vdev_detach, zfs_secpolicy_config, POOL_NAME, B_TRUE,
44769234SGeorge.Wilson@Sun.COM 	    B_TRUE },
44779234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_vdev_setpath,	zfs_secpolicy_config, POOL_NAME, B_FALSE,
44789234SGeorge.Wilson@Sun.COM 	    B_TRUE },
44799425SEric.Schrock@Sun.COM 	{ zfs_ioc_vdev_setfru,	zfs_secpolicy_config, POOL_NAME, B_FALSE,
44809425SEric.Schrock@Sun.COM 	    B_TRUE },
44819234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_objset_stats,	zfs_secpolicy_read, DATASET_NAME, B_FALSE,
448211454SSanjeev.Bagewadi@Sun.COM 	    B_TRUE },
44839234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_objset_zplprops, zfs_secpolicy_read, DATASET_NAME, B_FALSE,
44849234SGeorge.Wilson@Sun.COM 	    B_FALSE },
44859234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_dataset_list_next, zfs_secpolicy_read, DATASET_NAME, B_FALSE,
448611454SSanjeev.Bagewadi@Sun.COM 	    B_TRUE },
44879234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_snapshot_list_next, zfs_secpolicy_read, DATASET_NAME, B_FALSE,
448811454SSanjeev.Bagewadi@Sun.COM 	    B_TRUE },
44899234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_set_prop, zfs_secpolicy_none, DATASET_NAME, B_TRUE, B_TRUE },
44909234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_create, zfs_secpolicy_create, DATASET_NAME, B_TRUE, B_TRUE },
44919234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_destroy, zfs_secpolicy_destroy, DATASET_NAME, B_TRUE,
44929234SGeorge.Wilson@Sun.COM 	    B_TRUE},
44939234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_rollback, zfs_secpolicy_rollback, DATASET_NAME, B_TRUE,
44949234SGeorge.Wilson@Sun.COM 	    B_TRUE },
44959234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_rename, zfs_secpolicy_rename,	DATASET_NAME, B_TRUE, B_TRUE },
44969234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_recv, zfs_secpolicy_receive, DATASET_NAME, B_TRUE, B_TRUE },
44979234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_send, zfs_secpolicy_send, DATASET_NAME, B_TRUE, B_FALSE },
44989234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_inject_fault,	zfs_secpolicy_inject, NO_NAME, B_FALSE,
44999234SGeorge.Wilson@Sun.COM 	    B_FALSE },
45009234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_clear_fault, zfs_secpolicy_inject, NO_NAME, B_FALSE,
45019234SGeorge.Wilson@Sun.COM 	    B_FALSE },
45029234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_inject_list_next, zfs_secpolicy_inject, NO_NAME, B_FALSE,
45039234SGeorge.Wilson@Sun.COM 	    B_FALSE },
45049234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_error_log, zfs_secpolicy_inject, POOL_NAME, B_FALSE,
45059234SGeorge.Wilson@Sun.COM 	    B_FALSE },
45069234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_clear, zfs_secpolicy_config, POOL_NAME, B_TRUE, B_FALSE },
45079234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_promote, zfs_secpolicy_promote, DATASET_NAME, B_TRUE,
45089234SGeorge.Wilson@Sun.COM 	    B_TRUE },
450911314Swilliam.gorrell@sun.com 	{ zfs_ioc_destroy_snaps, zfs_secpolicy_destroy_snaps, DATASET_NAME,
451011314Swilliam.gorrell@sun.com 	    B_TRUE, B_TRUE },
45119234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_snapshot, zfs_secpolicy_snapshot, DATASET_NAME, B_TRUE,
45129234SGeorge.Wilson@Sun.COM 	    B_TRUE },
45139234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_dsobj_to_dsname, zfs_secpolicy_config, POOL_NAME, B_FALSE,
45149234SGeorge.Wilson@Sun.COM 	    B_FALSE },
451510233SGeorge.Wilson@Sun.COM 	{ zfs_ioc_obj_to_path, zfs_secpolicy_config, DATASET_NAME, B_FALSE,
451610233SGeorge.Wilson@Sun.COM 	    B_TRUE },
45179234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_pool_set_props, zfs_secpolicy_config,	POOL_NAME, B_TRUE,
45189234SGeorge.Wilson@Sun.COM 	    B_TRUE },
45199234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_pool_get_props, zfs_secpolicy_read, POOL_NAME, B_FALSE,
45209234SGeorge.Wilson@Sun.COM 	    B_FALSE },
45219234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_set_fsacl, zfs_secpolicy_fsacl, DATASET_NAME, B_TRUE,
45229234SGeorge.Wilson@Sun.COM 	    B_TRUE },
45239234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_get_fsacl, zfs_secpolicy_read, DATASET_NAME, B_FALSE,
45249234SGeorge.Wilson@Sun.COM 	    B_FALSE },
45259234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_share, zfs_secpolicy_share, DATASET_NAME, B_FALSE, B_FALSE },
45269234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_inherit_prop, zfs_secpolicy_inherit, DATASET_NAME, B_TRUE,
45279234SGeorge.Wilson@Sun.COM 	    B_TRUE },
45289234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_smb_acl, zfs_secpolicy_smb_acl, DATASET_NAME, B_FALSE,
45299396SMatthew.Ahrens@Sun.COM 	    B_FALSE },
45309396SMatthew.Ahrens@Sun.COM 	{ zfs_ioc_userspace_one, zfs_secpolicy_userspace_one,
45319396SMatthew.Ahrens@Sun.COM 	    DATASET_NAME, B_FALSE, B_FALSE },
45329396SMatthew.Ahrens@Sun.COM 	{ zfs_ioc_userspace_many, zfs_secpolicy_userspace_many,
45339396SMatthew.Ahrens@Sun.COM 	    DATASET_NAME, B_FALSE, B_FALSE },
45349396SMatthew.Ahrens@Sun.COM 	{ zfs_ioc_userspace_upgrade, zfs_secpolicy_userspace_upgrade,
45359396SMatthew.Ahrens@Sun.COM 	    DATASET_NAME, B_FALSE, B_TRUE },
453610242Schris.kirby@sun.com 	{ zfs_ioc_hold, zfs_secpolicy_hold, DATASET_NAME, B_TRUE, B_TRUE },
453710242Schris.kirby@sun.com 	{ zfs_ioc_release, zfs_secpolicy_release, DATASET_NAME, B_TRUE,
453810242Schris.kirby@sun.com 	    B_TRUE },
453910242Schris.kirby@sun.com 	{ zfs_ioc_get_holds, zfs_secpolicy_read, DATASET_NAME, B_FALSE,
454011022STom.Erickson@Sun.COM 	    B_TRUE },
454111022STom.Erickson@Sun.COM 	{ zfs_ioc_objset_recvd_props, zfs_secpolicy_read, DATASET_NAME, B_FALSE,
454211422SMark.Musante@Sun.COM 	    B_FALSE },
454311422SMark.Musante@Sun.COM 	{ zfs_ioc_vdev_split, zfs_secpolicy_config, POOL_NAME, B_TRUE,
454411422SMark.Musante@Sun.COM 	    B_TRUE }
4545789Sahrens };
4546789Sahrens 
45479234SGeorge.Wilson@Sun.COM int
45489234SGeorge.Wilson@Sun.COM pool_status_check(const char *name, zfs_ioc_namecheck_t type)
45499234SGeorge.Wilson@Sun.COM {
45509234SGeorge.Wilson@Sun.COM 	spa_t *spa;
45519234SGeorge.Wilson@Sun.COM 	int error;
45529234SGeorge.Wilson@Sun.COM 
45539234SGeorge.Wilson@Sun.COM 	ASSERT(type == POOL_NAME || type == DATASET_NAME);
45549234SGeorge.Wilson@Sun.COM 
45559396SMatthew.Ahrens@Sun.COM 	error = spa_open(name, &spa, FTAG);
45569234SGeorge.Wilson@Sun.COM 	if (error == 0) {
45579234SGeorge.Wilson@Sun.COM 		if (spa_suspended(spa))
45589234SGeorge.Wilson@Sun.COM 			error = EAGAIN;
45599234SGeorge.Wilson@Sun.COM 		spa_close(spa, FTAG);
45609234SGeorge.Wilson@Sun.COM 	}
45619234SGeorge.Wilson@Sun.COM 	return (error);
45629234SGeorge.Wilson@Sun.COM }
45639234SGeorge.Wilson@Sun.COM 
456412527SChris.Kirby@oracle.com /*
456512527SChris.Kirby@oracle.com  * Find a free minor number.
456612527SChris.Kirby@oracle.com  */
456712527SChris.Kirby@oracle.com minor_t
456812527SChris.Kirby@oracle.com zfsdev_minor_alloc(void)
456912527SChris.Kirby@oracle.com {
457012527SChris.Kirby@oracle.com 	static minor_t last_minor;
457112527SChris.Kirby@oracle.com 	minor_t m;
457212527SChris.Kirby@oracle.com 
457312527SChris.Kirby@oracle.com 	ASSERT(MUTEX_HELD(&zfsdev_state_lock));
457412527SChris.Kirby@oracle.com 
457512527SChris.Kirby@oracle.com 	for (m = last_minor + 1; m != last_minor; m++) {
457612527SChris.Kirby@oracle.com 		if (m > ZFSDEV_MAX_MINOR)
457712527SChris.Kirby@oracle.com 			m = 1;
457812527SChris.Kirby@oracle.com 		if (ddi_get_soft_state(zfsdev_state, m) == NULL) {
457912527SChris.Kirby@oracle.com 			last_minor = m;
458012527SChris.Kirby@oracle.com 			return (m);
458112527SChris.Kirby@oracle.com 		}
458212527SChris.Kirby@oracle.com 	}
458312527SChris.Kirby@oracle.com 
458412527SChris.Kirby@oracle.com 	return (0);
458512527SChris.Kirby@oracle.com }
458612527SChris.Kirby@oracle.com 
458712527SChris.Kirby@oracle.com static int
458812527SChris.Kirby@oracle.com zfs_ctldev_init(dev_t *devp)
458912527SChris.Kirby@oracle.com {
459012527SChris.Kirby@oracle.com 	minor_t minor;
459112527SChris.Kirby@oracle.com 	zfs_soft_state_t *zs;
459212527SChris.Kirby@oracle.com 
459312527SChris.Kirby@oracle.com 	ASSERT(MUTEX_HELD(&zfsdev_state_lock));
459412527SChris.Kirby@oracle.com 	ASSERT(getminor(*devp) == 0);
459512527SChris.Kirby@oracle.com 
459612527SChris.Kirby@oracle.com 	minor = zfsdev_minor_alloc();
459712527SChris.Kirby@oracle.com 	if (minor == 0)
459812527SChris.Kirby@oracle.com 		return (ENXIO);
459912527SChris.Kirby@oracle.com 
460012527SChris.Kirby@oracle.com 	if (ddi_soft_state_zalloc(zfsdev_state, minor) != DDI_SUCCESS)
460112527SChris.Kirby@oracle.com 		return (EAGAIN);
460212527SChris.Kirby@oracle.com 
460312527SChris.Kirby@oracle.com 	*devp = makedevice(getemajor(*devp), minor);
460412527SChris.Kirby@oracle.com 
460512527SChris.Kirby@oracle.com 	zs = ddi_get_soft_state(zfsdev_state, minor);
460612527SChris.Kirby@oracle.com 	zs->zss_type = ZSST_CTLDEV;
460712527SChris.Kirby@oracle.com 	zfs_onexit_init((zfs_onexit_t **)&zs->zss_data);
460812527SChris.Kirby@oracle.com 
460912527SChris.Kirby@oracle.com 	return (0);
461012527SChris.Kirby@oracle.com }
461112527SChris.Kirby@oracle.com 
461212527SChris.Kirby@oracle.com static void
461312527SChris.Kirby@oracle.com zfs_ctldev_destroy(zfs_onexit_t *zo, minor_t minor)
461412527SChris.Kirby@oracle.com {
461512527SChris.Kirby@oracle.com 	ASSERT(MUTEX_HELD(&zfsdev_state_lock));
461612527SChris.Kirby@oracle.com 
461712527SChris.Kirby@oracle.com 	zfs_onexit_destroy(zo);
461812527SChris.Kirby@oracle.com 	ddi_soft_state_free(zfsdev_state, minor);
461912527SChris.Kirby@oracle.com }
462012527SChris.Kirby@oracle.com 
462112527SChris.Kirby@oracle.com void *
462212527SChris.Kirby@oracle.com zfsdev_get_soft_state(minor_t minor, enum zfs_soft_state_type which)
462312527SChris.Kirby@oracle.com {
462412527SChris.Kirby@oracle.com 	zfs_soft_state_t *zp;
462512527SChris.Kirby@oracle.com 
462612527SChris.Kirby@oracle.com 	zp = ddi_get_soft_state(zfsdev_state, minor);
462712527SChris.Kirby@oracle.com 	if (zp == NULL || zp->zss_type != which)
462812527SChris.Kirby@oracle.com 		return (NULL);
462912527SChris.Kirby@oracle.com 
463012527SChris.Kirby@oracle.com 	return (zp->zss_data);
463112527SChris.Kirby@oracle.com }
463212527SChris.Kirby@oracle.com 
463312527SChris.Kirby@oracle.com static int
463412527SChris.Kirby@oracle.com zfsdev_open(dev_t *devp, int flag, int otyp, cred_t *cr)
463512527SChris.Kirby@oracle.com {
463612527SChris.Kirby@oracle.com 	int error = 0;
463712527SChris.Kirby@oracle.com 
463812527SChris.Kirby@oracle.com 	if (getminor(*devp) != 0)
463912527SChris.Kirby@oracle.com 		return (zvol_open(devp, flag, otyp, cr));
464012527SChris.Kirby@oracle.com 
464112527SChris.Kirby@oracle.com 	/* This is the control device. Allocate a new minor if requested. */
464212527SChris.Kirby@oracle.com 	if (flag & FEXCL) {
464312527SChris.Kirby@oracle.com 		mutex_enter(&zfsdev_state_lock);
464412527SChris.Kirby@oracle.com 		error = zfs_ctldev_init(devp);
464512527SChris.Kirby@oracle.com 		mutex_exit(&zfsdev_state_lock);
464612527SChris.Kirby@oracle.com 	}
464712527SChris.Kirby@oracle.com 
464812527SChris.Kirby@oracle.com 	return (error);
464912527SChris.Kirby@oracle.com }
465012527SChris.Kirby@oracle.com 
465112527SChris.Kirby@oracle.com static int
465212527SChris.Kirby@oracle.com zfsdev_close(dev_t dev, int flag, int otyp, cred_t *cr)
465312527SChris.Kirby@oracle.com {
465412527SChris.Kirby@oracle.com 	zfs_onexit_t *zo;
465512527SChris.Kirby@oracle.com 	minor_t minor = getminor(dev);
465612527SChris.Kirby@oracle.com 
465712527SChris.Kirby@oracle.com 	if (minor == 0)
465812527SChris.Kirby@oracle.com 		return (0);
465912527SChris.Kirby@oracle.com 
466012527SChris.Kirby@oracle.com 	mutex_enter(&zfsdev_state_lock);
466112527SChris.Kirby@oracle.com 	zo = zfsdev_get_soft_state(minor, ZSST_CTLDEV);
466212527SChris.Kirby@oracle.com 	if (zo == NULL) {
466312527SChris.Kirby@oracle.com 		mutex_exit(&zfsdev_state_lock);
466412527SChris.Kirby@oracle.com 		return (zvol_close(dev, flag, otyp, cr));
466512527SChris.Kirby@oracle.com 	}
466612527SChris.Kirby@oracle.com 	zfs_ctldev_destroy(zo, minor);
466712527SChris.Kirby@oracle.com 	mutex_exit(&zfsdev_state_lock);
466812527SChris.Kirby@oracle.com 
466912527SChris.Kirby@oracle.com 	return (0);
467012527SChris.Kirby@oracle.com }
467112527SChris.Kirby@oracle.com 
4672789Sahrens static int
4673789Sahrens zfsdev_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *cr, int *rvalp)
4674789Sahrens {
4675789Sahrens 	zfs_cmd_t *zc;
4676789Sahrens 	uint_t vec;
46772199Sahrens 	int error, rc;
467812527SChris.Kirby@oracle.com 	minor_t minor = getminor(dev);
467912527SChris.Kirby@oracle.com 
468012527SChris.Kirby@oracle.com 	if (minor != 0 &&
468112527SChris.Kirby@oracle.com 	    zfsdev_get_soft_state(minor, ZSST_CTLDEV) == NULL)
4682789Sahrens 		return (zvol_ioctl(dev, cmd, arg, flag, cr, rvalp));
4683789Sahrens 
4684789Sahrens 	vec = cmd - ZFS_IOC;
46854787Sahrens 	ASSERT3U(getmajor(dev), ==, ddi_driver_major(zfs_dip));
4686789Sahrens 
4687789Sahrens 	if (vec >= sizeof (zfs_ioc_vec) / sizeof (zfs_ioc_vec[0]))
4688789Sahrens 		return (EINVAL);
4689789Sahrens 
4690789Sahrens 	zc = kmem_zalloc(sizeof (zfs_cmd_t), KM_SLEEP);
4691789Sahrens 
46929643SEric.Taylor@Sun.COM 	error = ddi_copyin((void *)arg, zc, sizeof (zfs_cmd_t), flag);
469311807SSam.Falkner@Sun.COM 	if (error != 0)
469411807SSam.Falkner@Sun.COM 		error = EFAULT;
4695789Sahrens 
469610588SEric.Taylor@Sun.COM 	if ((error == 0) && !(flag & FKIOCTL))
46974543Smarks 		error = zfs_ioc_vec[vec].zvec_secpolicy(zc, cr);
4698789Sahrens 
4699789Sahrens 	/*
4700789Sahrens 	 * Ensure that all pool/dataset names are valid before we pass down to
4701789Sahrens 	 * the lower layers.
4702789Sahrens 	 */
4703789Sahrens 	if (error == 0) {
4704789Sahrens 		zc->zc_name[sizeof (zc->zc_name) - 1] = '\0';
47059643SEric.Taylor@Sun.COM 		zc->zc_iflags = flag & FKIOCTL;
4706789Sahrens 		switch (zfs_ioc_vec[vec].zvec_namecheck) {
47074577Sahrens 		case POOL_NAME:
4708789Sahrens 			if (pool_namecheck(zc->zc_name, NULL, NULL) != 0)
4709789Sahrens 				error = EINVAL;
47109234SGeorge.Wilson@Sun.COM 			if (zfs_ioc_vec[vec].zvec_pool_check)
47119234SGeorge.Wilson@Sun.COM 				error = pool_status_check(zc->zc_name,
47129234SGeorge.Wilson@Sun.COM 				    zfs_ioc_vec[vec].zvec_namecheck);
4713789Sahrens 			break;
4714789Sahrens 
47154577Sahrens 		case DATASET_NAME:
4716789Sahrens 			if (dataset_namecheck(zc->zc_name, NULL, NULL) != 0)
4717789Sahrens 				error = EINVAL;
47189234SGeorge.Wilson@Sun.COM 			if (zfs_ioc_vec[vec].zvec_pool_check)
47199234SGeorge.Wilson@Sun.COM 				error = pool_status_check(zc->zc_name,
47209234SGeorge.Wilson@Sun.COM 				    zfs_ioc_vec[vec].zvec_namecheck);
4721789Sahrens 			break;
47222856Snd150628 
47234577Sahrens 		case NO_NAME:
47242856Snd150628 			break;
4725789Sahrens 		}
4726789Sahrens 	}
4727789Sahrens 
4728789Sahrens 	if (error == 0)
4729789Sahrens 		error = zfs_ioc_vec[vec].zvec_func(zc);
4730789Sahrens 
47319643SEric.Taylor@Sun.COM 	rc = ddi_copyout(zc, (void *)arg, sizeof (zfs_cmd_t), flag);
47324543Smarks 	if (error == 0) {
473311807SSam.Falkner@Sun.COM 		if (rc != 0)
473411807SSam.Falkner@Sun.COM 			error = EFAULT;
47359396SMatthew.Ahrens@Sun.COM 		if (zfs_ioc_vec[vec].zvec_his_log)
47364543Smarks 			zfs_log_history(zc);
47374543Smarks 	}
4738789Sahrens 
4739789Sahrens 	kmem_free(zc, sizeof (zfs_cmd_t));
4740789Sahrens 	return (error);
4741789Sahrens }
4742789Sahrens 
4743789Sahrens static int
4744789Sahrens zfs_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
4745789Sahrens {
4746789Sahrens 	if (cmd != DDI_ATTACH)
4747789Sahrens 		return (DDI_FAILURE);
4748789Sahrens 
4749789Sahrens 	if (ddi_create_minor_node(dip, "zfs", S_IFCHR, 0,
4750789Sahrens 	    DDI_PSEUDO, 0) == DDI_FAILURE)
4751789Sahrens 		return (DDI_FAILURE);
4752789Sahrens 
4753789Sahrens 	zfs_dip = dip;
4754789Sahrens 
4755789Sahrens 	ddi_report_dev(dip);
4756789Sahrens 
4757789Sahrens 	return (DDI_SUCCESS);
4758789Sahrens }
4759789Sahrens 
4760789Sahrens static int
4761789Sahrens zfs_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
4762789Sahrens {
4763789Sahrens 	if (spa_busy() || zfs_busy() || zvol_busy())
4764789Sahrens 		return (DDI_FAILURE);
4765789Sahrens 
4766789Sahrens 	if (cmd != DDI_DETACH)
4767789Sahrens 		return (DDI_FAILURE);
4768789Sahrens 
4769789Sahrens 	zfs_dip = NULL;
4770789Sahrens 
4771789Sahrens 	ddi_prop_remove_all(dip);
4772789Sahrens 	ddi_remove_minor_node(dip, NULL);
4773789Sahrens 
4774789Sahrens 	return (DDI_SUCCESS);
4775789Sahrens }
4776789Sahrens 
4777789Sahrens /*ARGSUSED*/
4778789Sahrens static int
4779789Sahrens zfs_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
4780789Sahrens {
4781789Sahrens 	switch (infocmd) {
4782789Sahrens 	case DDI_INFO_DEVT2DEVINFO:
4783789Sahrens 		*result = zfs_dip;
4784789Sahrens 		return (DDI_SUCCESS);
4785789Sahrens 
4786789Sahrens 	case DDI_INFO_DEVT2INSTANCE:
4787849Sbonwick 		*result = (void *)0;
4788789Sahrens 		return (DDI_SUCCESS);
4789789Sahrens 	}
4790789Sahrens 
4791789Sahrens 	return (DDI_FAILURE);
4792789Sahrens }
4793789Sahrens 
4794789Sahrens /*
4795789Sahrens  * OK, so this is a little weird.
4796789Sahrens  *
4797789Sahrens  * /dev/zfs is the control node, i.e. minor 0.
4798789Sahrens  * /dev/zvol/[r]dsk/pool/dataset are the zvols, minor > 0.
4799789Sahrens  *
4800789Sahrens  * /dev/zfs has basically nothing to do except serve up ioctls,
4801789Sahrens  * so most of the standard driver entry points are in zvol.c.
4802789Sahrens  */
4803789Sahrens static struct cb_ops zfs_cb_ops = {
480412527SChris.Kirby@oracle.com 	zfsdev_open,	/* open */
480512527SChris.Kirby@oracle.com 	zfsdev_close,	/* close */
4806789Sahrens 	zvol_strategy,	/* strategy */
4807789Sahrens 	nodev,		/* print */
48086423Sgw25295 	zvol_dump,	/* dump */
4809789Sahrens 	zvol_read,	/* read */
4810789Sahrens 	zvol_write,	/* write */
4811789Sahrens 	zfsdev_ioctl,	/* ioctl */
4812789Sahrens 	nodev,		/* devmap */
4813789Sahrens 	nodev,		/* mmap */
4814789Sahrens 	nodev,		/* segmap */
4815789Sahrens 	nochpoll,	/* poll */
4816789Sahrens 	ddi_prop_op,	/* prop_op */
4817789Sahrens 	NULL,		/* streamtab */
4818789Sahrens 	D_NEW | D_MP | D_64BIT,		/* Driver compatibility flag */
4819789Sahrens 	CB_REV,		/* version */
48203638Sbillm 	nodev,		/* async read */
48213638Sbillm 	nodev,		/* async write */
4822789Sahrens };
4823789Sahrens 
4824789Sahrens static struct dev_ops zfs_dev_ops = {
4825789Sahrens 	DEVO_REV,	/* version */
4826789Sahrens 	0,		/* refcnt */
4827789Sahrens 	zfs_info,	/* info */
4828789Sahrens 	nulldev,	/* identify */
4829789Sahrens 	nulldev,	/* probe */
4830789Sahrens 	zfs_attach,	/* attach */
4831789Sahrens 	zfs_detach,	/* detach */
4832789Sahrens 	nodev,		/* reset */
4833789Sahrens 	&zfs_cb_ops,	/* driver operations */
48347656SSherry.Moore@Sun.COM 	NULL,		/* no bus operations */
48357656SSherry.Moore@Sun.COM 	NULL,		/* power */
48367656SSherry.Moore@Sun.COM 	ddi_quiesce_not_needed,	/* quiesce */
4837789Sahrens };
4838789Sahrens 
4839789Sahrens static struct modldrv zfs_modldrv = {
48407656SSherry.Moore@Sun.COM 	&mod_driverops,
48417656SSherry.Moore@Sun.COM 	"ZFS storage pool",
48427656SSherry.Moore@Sun.COM 	&zfs_dev_ops
4843789Sahrens };
4844789Sahrens 
4845789Sahrens static struct modlinkage modlinkage = {
4846789Sahrens 	MODREV_1,
4847789Sahrens 	(void *)&zfs_modlfs,
4848789Sahrens 	(void *)&zfs_modldrv,
4849789Sahrens 	NULL
4850789Sahrens };
4851789Sahrens 
48524720Sfr157268 
48534720Sfr157268 uint_t zfs_fsyncer_key;
48545326Sek110237 extern uint_t rrw_tsd_key;
48554720Sfr157268 
4856789Sahrens int
4857789Sahrens _init(void)
4858789Sahrens {
4859789Sahrens 	int error;
4860789Sahrens 
4861849Sbonwick 	spa_init(FREAD | FWRITE);
4862849Sbonwick 	zfs_init();
4863849Sbonwick 	zvol_init();
4864849Sbonwick 
4865849Sbonwick 	if ((error = mod_install(&modlinkage)) != 0) {
4866849Sbonwick 		zvol_fini();
4867849Sbonwick 		zfs_fini();
4868849Sbonwick 		spa_fini();
4869789Sahrens 		return (error);
4870849Sbonwick 	}
4871789Sahrens 
48724720Sfr157268 	tsd_create(&zfs_fsyncer_key, NULL);
48735326Sek110237 	tsd_create(&rrw_tsd_key, NULL);
48744720Sfr157268 
4875789Sahrens 	error = ldi_ident_from_mod(&modlinkage, &zfs_li);
4876789Sahrens 	ASSERT(error == 0);
48774543Smarks 	mutex_init(&zfs_share_lock, NULL, MUTEX_DEFAULT, NULL);
4878789Sahrens 
4879789Sahrens 	return (0);
4880789Sahrens }
4881789Sahrens 
4882789Sahrens int
4883789Sahrens _fini(void)
4884789Sahrens {
4885789Sahrens 	int error;
4886789Sahrens 
48871544Seschrock 	if (spa_busy() || zfs_busy() || zvol_busy() || zio_injection_enabled)
4888789Sahrens 		return (EBUSY);
4889789Sahrens 
4890789Sahrens 	if ((error = mod_remove(&modlinkage)) != 0)
4891789Sahrens 		return (error);
4892789Sahrens 
4893789Sahrens 	zvol_fini();
4894789Sahrens 	zfs_fini();
4895789Sahrens 	spa_fini();
48965331Samw 	if (zfs_nfsshare_inited)
48974543Smarks 		(void) ddi_modclose(nfs_mod);
48985331Samw 	if (zfs_smbshare_inited)
48995331Samw 		(void) ddi_modclose(smbsrv_mod);
49005331Samw 	if (zfs_nfsshare_inited || zfs_smbshare_inited)
49014543Smarks 		(void) ddi_modclose(sharefs_mod);
4902789Sahrens 
49034720Sfr157268 	tsd_destroy(&zfs_fsyncer_key);
4904789Sahrens 	ldi_ident_release(zfs_li);
4905789Sahrens 	zfs_li = NULL;
49064543Smarks 	mutex_destroy(&zfs_share_lock);
4907789Sahrens 
4908789Sahrens 	return (error);
4909789Sahrens }
4910789Sahrens 
4911789Sahrens int
4912789Sahrens _info(struct modinfo *modinfop)
4913789Sahrens {
4914789Sahrens 	return (mod_info(&modlinkage, modinfop));
4915789Sahrens }
4916