xref: /onnv-gate/usr/src/uts/common/fs/zfs/zfs_ioctl.c (revision 11181:daeb45eeb9fb)
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 /*
228525SEric.Schrock@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23789Sahrens  * Use is subject to license terms.
24789Sahrens  */
25789Sahrens 
26789Sahrens #include <sys/types.h>
27789Sahrens #include <sys/param.h>
28789Sahrens #include <sys/errno.h>
29789Sahrens #include <sys/uio.h>
30789Sahrens #include <sys/buf.h>
31789Sahrens #include <sys/modctl.h>
32789Sahrens #include <sys/open.h>
33789Sahrens #include <sys/file.h>
34789Sahrens #include <sys/kmem.h>
35789Sahrens #include <sys/conf.h>
36789Sahrens #include <sys/cmn_err.h>
37789Sahrens #include <sys/stat.h>
38789Sahrens #include <sys/zfs_ioctl.h>
3910972SRic.Aleshire@Sun.COM #include <sys/zfs_vfsops.h>
405331Samw #include <sys/zfs_znode.h>
41789Sahrens #include <sys/zap.h>
42789Sahrens #include <sys/spa.h>
433912Slling #include <sys/spa_impl.h>
44789Sahrens #include <sys/vdev.h>
4510972SRic.Aleshire@Sun.COM #include <sys/priv_impl.h>
46789Sahrens #include <sys/dmu.h>
47789Sahrens #include <sys/dsl_dir.h>
48789Sahrens #include <sys/dsl_dataset.h>
49789Sahrens #include <sys/dsl_prop.h>
504543Smarks #include <sys/dsl_deleg.h>
514543Smarks #include <sys/dmu_objset.h>
52789Sahrens #include <sys/ddi.h>
53789Sahrens #include <sys/sunddi.h>
54789Sahrens #include <sys/sunldi.h>
55789Sahrens #include <sys/policy.h>
56789Sahrens #include <sys/zone.h>
57789Sahrens #include <sys/nvpair.h>
58789Sahrens #include <sys/pathname.h>
59789Sahrens #include <sys/mount.h>
60789Sahrens #include <sys/sdt.h>
61789Sahrens #include <sys/fs/zfs.h>
62789Sahrens #include <sys/zfs_ctldir.h>
635331Samw #include <sys/zfs_dir.h>
642885Sahrens #include <sys/zvol.h>
654543Smarks #include <sharefs/share.h>
665326Sek110237 #include <sys/dmu_objset.h>
67789Sahrens 
68789Sahrens #include "zfs_namecheck.h"
692676Seschrock #include "zfs_prop.h"
704543Smarks #include "zfs_deleg.h"
71789Sahrens 
72789Sahrens extern struct modlfs zfs_modlfs;
73789Sahrens 
74789Sahrens extern void zfs_init(void);
75789Sahrens extern void zfs_fini(void);
76789Sahrens 
77789Sahrens ldi_ident_t zfs_li = NULL;
78789Sahrens dev_info_t *zfs_dip;
79789Sahrens 
80789Sahrens typedef int zfs_ioc_func_t(zfs_cmd_t *);
814543Smarks typedef int zfs_secpolicy_func_t(zfs_cmd_t *, cred_t *);
82789Sahrens 
839234SGeorge.Wilson@Sun.COM typedef enum {
849234SGeorge.Wilson@Sun.COM 	NO_NAME,
859234SGeorge.Wilson@Sun.COM 	POOL_NAME,
869234SGeorge.Wilson@Sun.COM 	DATASET_NAME
879234SGeorge.Wilson@Sun.COM } zfs_ioc_namecheck_t;
889234SGeorge.Wilson@Sun.COM 
89789Sahrens typedef struct zfs_ioc_vec {
90789Sahrens 	zfs_ioc_func_t		*zvec_func;
91789Sahrens 	zfs_secpolicy_func_t	*zvec_secpolicy;
929234SGeorge.Wilson@Sun.COM 	zfs_ioc_namecheck_t	zvec_namecheck;
934543Smarks 	boolean_t		zvec_his_log;
949234SGeorge.Wilson@Sun.COM 	boolean_t		zvec_pool_check;
95789Sahrens } zfs_ioc_vec_t;
96789Sahrens 
979396SMatthew.Ahrens@Sun.COM /* This array is indexed by zfs_userquota_prop_t */
989396SMatthew.Ahrens@Sun.COM static const char *userquota_perms[] = {
999396SMatthew.Ahrens@Sun.COM 	ZFS_DELEG_PERM_USERUSED,
1009396SMatthew.Ahrens@Sun.COM 	ZFS_DELEG_PERM_USERQUOTA,
1019396SMatthew.Ahrens@Sun.COM 	ZFS_DELEG_PERM_GROUPUSED,
1029396SMatthew.Ahrens@Sun.COM 	ZFS_DELEG_PERM_GROUPQUOTA,
1039396SMatthew.Ahrens@Sun.COM };
1049396SMatthew.Ahrens@Sun.COM 
1059396SMatthew.Ahrens@Sun.COM static int zfs_ioc_userspace_upgrade(zfs_cmd_t *zc);
10611022STom.Erickson@Sun.COM static int zfs_check_settable(const char *name, nvpair_t *property,
10711022STom.Erickson@Sun.COM     cred_t *cr);
10811022STom.Erickson@Sun.COM static int zfs_check_clearable(char *dataset, nvlist_t *props,
10911022STom.Erickson@Sun.COM     nvlist_t **errors);
1107184Stimh static int zfs_fill_zplprops_root(uint64_t, nvlist_t *, nvlist_t *,
1117184Stimh     boolean_t *);
11211022STom.Erickson@Sun.COM int zfs_set_prop_nvlist(const char *, zprop_source_t, nvlist_t *, nvlist_t **);
1137184Stimh 
114789Sahrens /* _NOTE(PRINTFLIKE(4)) - this is printf-like, but lint is too whiney */
115789Sahrens void
116789Sahrens __dprintf(const char *file, const char *func, int line, const char *fmt, ...)
117789Sahrens {
118789Sahrens 	const char *newfile;
119789Sahrens 	char buf[256];
120789Sahrens 	va_list adx;
121789Sahrens 
122789Sahrens 	/*
123789Sahrens 	 * Get rid of annoying "../common/" prefix to filename.
124789Sahrens 	 */
125789Sahrens 	newfile = strrchr(file, '/');
126789Sahrens 	if (newfile != NULL) {
127789Sahrens 		newfile = newfile + 1; /* Get rid of leading / */
128789Sahrens 	} else {
129789Sahrens 		newfile = file;
130789Sahrens 	}
131789Sahrens 
132789Sahrens 	va_start(adx, fmt);
133789Sahrens 	(void) vsnprintf(buf, sizeof (buf), fmt, adx);
134789Sahrens 	va_end(adx);
135789Sahrens 
136789Sahrens 	/*
137789Sahrens 	 * To get this data, use the zfs-dprintf probe as so:
138789Sahrens 	 * dtrace -q -n 'zfs-dprintf \
139789Sahrens 	 *	/stringof(arg0) == "dbuf.c"/ \
140789Sahrens 	 *	{printf("%s: %s", stringof(arg1), stringof(arg3))}'
141789Sahrens 	 * arg0 = file name
142789Sahrens 	 * arg1 = function name
143789Sahrens 	 * arg2 = line number
144789Sahrens 	 * arg3 = message
145789Sahrens 	 */
146789Sahrens 	DTRACE_PROBE4(zfs__dprintf,
147789Sahrens 	    char *, newfile, char *, func, int, line, char *, buf);
148789Sahrens }
149789Sahrens 
1504543Smarks static void
1514715Sek110237 history_str_free(char *buf)
1524715Sek110237 {
1534715Sek110237 	kmem_free(buf, HIS_MAX_RECORD_LEN);
1544715Sek110237 }
1554715Sek110237 
1564715Sek110237 static char *
1574715Sek110237 history_str_get(zfs_cmd_t *zc)
1584715Sek110237 {
1594715Sek110237 	char *buf;
1604715Sek110237 
1614715Sek110237 	if (zc->zc_history == NULL)
1624715Sek110237 		return (NULL);
1634715Sek110237 
1644715Sek110237 	buf = kmem_alloc(HIS_MAX_RECORD_LEN, KM_SLEEP);
1654715Sek110237 	if (copyinstr((void *)(uintptr_t)zc->zc_history,
1664715Sek110237 	    buf, HIS_MAX_RECORD_LEN, NULL) != 0) {
1674715Sek110237 		history_str_free(buf);
1684715Sek110237 		return (NULL);
1694715Sek110237 	}
1704715Sek110237 
1714715Sek110237 	buf[HIS_MAX_RECORD_LEN -1] = '\0';
1724715Sek110237 
1734715Sek110237 	return (buf);
1744715Sek110237 }
1754715Sek110237 
1765375Stimh /*
1777042Sgw25295  * Check to see if the named dataset is currently defined as bootable
1787042Sgw25295  */
1797042Sgw25295 static boolean_t
1807042Sgw25295 zfs_is_bootfs(const char *name)
1817042Sgw25295 {
18210298SMatthew.Ahrens@Sun.COM 	objset_t *os;
18310298SMatthew.Ahrens@Sun.COM 
18410298SMatthew.Ahrens@Sun.COM 	if (dmu_objset_hold(name, FTAG, &os) == 0) {
18510298SMatthew.Ahrens@Sun.COM 		boolean_t ret;
18610922SJeff.Bonwick@Sun.COM 		ret = (dmu_objset_id(os) == spa_bootfs(dmu_objset_spa(os)));
18710298SMatthew.Ahrens@Sun.COM 		dmu_objset_rele(os, FTAG);
18810298SMatthew.Ahrens@Sun.COM 		return (ret);
1897042Sgw25295 	}
19010298SMatthew.Ahrens@Sun.COM 	return (B_FALSE);
1917042Sgw25295 }
1927042Sgw25295 
1937042Sgw25295 /*
1947184Stimh  * zfs_earlier_version
1955375Stimh  *
1965375Stimh  *	Return non-zero if the spa version is less than requested version.
1975375Stimh  */
1985331Samw static int
1997184Stimh zfs_earlier_version(const char *name, int version)
2005331Samw {
2015331Samw 	spa_t *spa;
2025331Samw 
2035331Samw 	if (spa_open(name, &spa, FTAG) == 0) {
2045331Samw 		if (spa_version(spa) < version) {
2055331Samw 			spa_close(spa, FTAG);
2065331Samw 			return (1);
2075331Samw 		}
2085331Samw 		spa_close(spa, FTAG);
2095331Samw 	}
2105331Samw 	return (0);
2115331Samw }
2125331Samw 
2135977Smarks /*
2146689Smaybee  * zpl_earlier_version
2155977Smarks  *
2166689Smaybee  * Return TRUE if the ZPL version is less than requested version.
2175977Smarks  */
2186689Smaybee static boolean_t
2196689Smaybee zpl_earlier_version(const char *name, int version)
2205977Smarks {
2215977Smarks 	objset_t *os;
2226689Smaybee 	boolean_t rc = B_TRUE;
2235977Smarks 
22410298SMatthew.Ahrens@Sun.COM 	if (dmu_objset_hold(name, FTAG, &os) == 0) {
2256689Smaybee 		uint64_t zplversion;
2266689Smaybee 
22710298SMatthew.Ahrens@Sun.COM 		if (dmu_objset_type(os) != DMU_OST_ZFS) {
22810298SMatthew.Ahrens@Sun.COM 			dmu_objset_rele(os, FTAG);
22910298SMatthew.Ahrens@Sun.COM 			return (B_TRUE);
23010298SMatthew.Ahrens@Sun.COM 		}
23110298SMatthew.Ahrens@Sun.COM 		/* XXX reading from non-owned objset */
2326689Smaybee 		if (zfs_get_zplprop(os, ZFS_PROP_VERSION, &zplversion) == 0)
2336689Smaybee 			rc = zplversion < version;
23410298SMatthew.Ahrens@Sun.COM 		dmu_objset_rele(os, FTAG);
2355977Smarks 	}
2365977Smarks 	return (rc);
2375977Smarks }
2385977Smarks 
2394715Sek110237 static void
2404543Smarks zfs_log_history(zfs_cmd_t *zc)
2414543Smarks {
2424543Smarks 	spa_t *spa;
2434603Sahrens 	char *buf;
2444543Smarks 
2454715Sek110237 	if ((buf = history_str_get(zc)) == NULL)
2464577Sahrens 		return;
2474577Sahrens 
2484715Sek110237 	if (spa_open(zc->zc_name, &spa, FTAG) == 0) {
2494715Sek110237 		if (spa_version(spa) >= SPA_VERSION_ZPOOL_HISTORY)
2504715Sek110237 			(void) spa_history_log(spa, buf, LOG_CMD_NORMAL);
2514715Sek110237 		spa_close(spa, FTAG);
2524543Smarks 	}
2534715Sek110237 	history_str_free(buf);
2544543Smarks }
2554543Smarks 
256789Sahrens /*
257789Sahrens  * Policy for top-level read operations (list pools).  Requires no privileges,
258789Sahrens  * and can be used in the local zone, as there is no associated dataset.
259789Sahrens  */
260789Sahrens /* ARGSUSED */
261789Sahrens static int
2624543Smarks zfs_secpolicy_none(zfs_cmd_t *zc, cred_t *cr)
263789Sahrens {
264789Sahrens 	return (0);
265789Sahrens }
266789Sahrens 
267789Sahrens /*
268789Sahrens  * Policy for dataset read operations (list children, get statistics).  Requires
269789Sahrens  * no privileges, but must be visible in the local zone.
270789Sahrens  */
271789Sahrens /* ARGSUSED */
272789Sahrens static int
2734543Smarks zfs_secpolicy_read(zfs_cmd_t *zc, cred_t *cr)
274789Sahrens {
275789Sahrens 	if (INGLOBALZONE(curproc) ||
2764543Smarks 	    zone_dataset_visible(zc->zc_name, NULL))
277789Sahrens 		return (0);
278789Sahrens 
279789Sahrens 	return (ENOENT);
280789Sahrens }
281789Sahrens 
282789Sahrens static int
283789Sahrens zfs_dozonecheck(const char *dataset, cred_t *cr)
284789Sahrens {
285789Sahrens 	uint64_t zoned;
286789Sahrens 	int writable = 1;
287789Sahrens 
288789Sahrens 	/*
289789Sahrens 	 * The dataset must be visible by this zone -- check this first
290789Sahrens 	 * so they don't see EPERM on something they shouldn't know about.
291789Sahrens 	 */
292789Sahrens 	if (!INGLOBALZONE(curproc) &&
293789Sahrens 	    !zone_dataset_visible(dataset, &writable))
294789Sahrens 		return (ENOENT);
295789Sahrens 
296789Sahrens 	if (dsl_prop_get_integer(dataset, "zoned", &zoned, NULL))
297789Sahrens 		return (ENOENT);
298789Sahrens 
299789Sahrens 	if (INGLOBALZONE(curproc)) {
300789Sahrens 		/*
301789Sahrens 		 * If the fs is zoned, only root can access it from the
302789Sahrens 		 * global zone.
303789Sahrens 		 */
304789Sahrens 		if (secpolicy_zfs(cr) && zoned)
305789Sahrens 			return (EPERM);
306789Sahrens 	} else {
307789Sahrens 		/*
308789Sahrens 		 * If we are in a local zone, the 'zoned' property must be set.
309789Sahrens 		 */
310789Sahrens 		if (!zoned)
311789Sahrens 			return (EPERM);
312789Sahrens 
313789Sahrens 		/* must be writable by this zone */
314789Sahrens 		if (!writable)
315789Sahrens 			return (EPERM);
316789Sahrens 	}
317789Sahrens 	return (0);
318789Sahrens }
319789Sahrens 
320789Sahrens int
3214543Smarks zfs_secpolicy_write_perms(const char *name, const char *perm, cred_t *cr)
322789Sahrens {
323789Sahrens 	int error;
324789Sahrens 
3254543Smarks 	error = zfs_dozonecheck(name, cr);
3264543Smarks 	if (error == 0) {
3274543Smarks 		error = secpolicy_zfs(cr);
3284670Sahrens 		if (error)
3294543Smarks 			error = dsl_deleg_access(name, perm, cr);
3304543Smarks 	}
3314543Smarks 	return (error);
3324543Smarks }
3334543Smarks 
33410972SRic.Aleshire@Sun.COM /*
33510972SRic.Aleshire@Sun.COM  * Policy for setting the security label property.
33610972SRic.Aleshire@Sun.COM  *
33710972SRic.Aleshire@Sun.COM  * Returns 0 for success, non-zero for access and other errors.
33810972SRic.Aleshire@Sun.COM  */
33910972SRic.Aleshire@Sun.COM static int
34011022STom.Erickson@Sun.COM zfs_set_slabel_policy(const char *name, char *strval, cred_t *cr)
34110972SRic.Aleshire@Sun.COM {
34210972SRic.Aleshire@Sun.COM 	char		ds_hexsl[MAXNAMELEN];
34310972SRic.Aleshire@Sun.COM 	bslabel_t	ds_sl, new_sl;
34410972SRic.Aleshire@Sun.COM 	boolean_t	new_default = FALSE;
34510972SRic.Aleshire@Sun.COM 	uint64_t	zoned;
34610972SRic.Aleshire@Sun.COM 	int		needed_priv = -1;
34710972SRic.Aleshire@Sun.COM 	int		error;
34810972SRic.Aleshire@Sun.COM 
34910972SRic.Aleshire@Sun.COM 	/* First get the existing dataset label. */
35010972SRic.Aleshire@Sun.COM 	error = dsl_prop_get(name, zfs_prop_to_name(ZFS_PROP_MLSLABEL),
35110972SRic.Aleshire@Sun.COM 	    1, sizeof (ds_hexsl), &ds_hexsl, NULL);
35210972SRic.Aleshire@Sun.COM 	if (error)
35310972SRic.Aleshire@Sun.COM 		return (EPERM);
35410972SRic.Aleshire@Sun.COM 
35510972SRic.Aleshire@Sun.COM 	if (strcasecmp(strval, ZFS_MLSLABEL_DEFAULT) == 0)
35610972SRic.Aleshire@Sun.COM 		new_default = TRUE;
35710972SRic.Aleshire@Sun.COM 
35810972SRic.Aleshire@Sun.COM 	/* The label must be translatable */
35910972SRic.Aleshire@Sun.COM 	if (!new_default && (hexstr_to_label(strval, &new_sl) != 0))
36010972SRic.Aleshire@Sun.COM 		return (EINVAL);
36110972SRic.Aleshire@Sun.COM 
36210972SRic.Aleshire@Sun.COM 	/*
36310972SRic.Aleshire@Sun.COM 	 * In a non-global zone, disallow attempts to set a label that
36410972SRic.Aleshire@Sun.COM 	 * doesn't match that of the zone; otherwise no other checks
36510972SRic.Aleshire@Sun.COM 	 * are needed.
36610972SRic.Aleshire@Sun.COM 	 */
36710972SRic.Aleshire@Sun.COM 	if (!INGLOBALZONE(curproc)) {
36810972SRic.Aleshire@Sun.COM 		if (new_default || !blequal(&new_sl, CR_SL(CRED())))
36910972SRic.Aleshire@Sun.COM 			return (EPERM);
37010972SRic.Aleshire@Sun.COM 		return (0);
37110972SRic.Aleshire@Sun.COM 	}
37210972SRic.Aleshire@Sun.COM 
37310972SRic.Aleshire@Sun.COM 	/*
37410972SRic.Aleshire@Sun.COM 	 * For global-zone datasets (i.e., those whose zoned property is
37510972SRic.Aleshire@Sun.COM 	 * "off", verify that the specified new label is valid for the
37610972SRic.Aleshire@Sun.COM 	 * global zone.
37710972SRic.Aleshire@Sun.COM 	 */
37810972SRic.Aleshire@Sun.COM 	if (dsl_prop_get_integer(name,
37910972SRic.Aleshire@Sun.COM 	    zfs_prop_to_name(ZFS_PROP_ZONED), &zoned, NULL))
38010972SRic.Aleshire@Sun.COM 		return (EPERM);
38110972SRic.Aleshire@Sun.COM 	if (!zoned) {
38210972SRic.Aleshire@Sun.COM 		if (zfs_check_global_label(name, strval) != 0)
38310972SRic.Aleshire@Sun.COM 			return (EPERM);
38410972SRic.Aleshire@Sun.COM 	}
38510972SRic.Aleshire@Sun.COM 
38610972SRic.Aleshire@Sun.COM 	/*
38710972SRic.Aleshire@Sun.COM 	 * If the existing dataset label is nondefault, check if the
38810972SRic.Aleshire@Sun.COM 	 * dataset is mounted (label cannot be changed while mounted).
38910972SRic.Aleshire@Sun.COM 	 * Get the zfsvfs; if there isn't one, then the dataset isn't
39010972SRic.Aleshire@Sun.COM 	 * mounted (or isn't a dataset, doesn't exist, ...).
39110972SRic.Aleshire@Sun.COM 	 */
39210972SRic.Aleshire@Sun.COM 	if (strcasecmp(ds_hexsl, ZFS_MLSLABEL_DEFAULT) != 0) {
39311022STom.Erickson@Sun.COM 		objset_t *os;
39411022STom.Erickson@Sun.COM 		static char *setsl_tag = "setsl_tag";
39511022STom.Erickson@Sun.COM 
39610972SRic.Aleshire@Sun.COM 		/*
39710972SRic.Aleshire@Sun.COM 		 * Try to own the dataset; abort if there is any error,
39810972SRic.Aleshire@Sun.COM 		 * (e.g., already mounted, in use, or other error).
39910972SRic.Aleshire@Sun.COM 		 */
40010972SRic.Aleshire@Sun.COM 		error = dmu_objset_own(name, DMU_OST_ZFS, B_TRUE,
40111022STom.Erickson@Sun.COM 		    setsl_tag, &os);
40210972SRic.Aleshire@Sun.COM 		if (error)
40310972SRic.Aleshire@Sun.COM 			return (EPERM);
40410972SRic.Aleshire@Sun.COM 
40511022STom.Erickson@Sun.COM 		dmu_objset_disown(os, setsl_tag);
40611022STom.Erickson@Sun.COM 
40710972SRic.Aleshire@Sun.COM 		if (new_default) {
40810972SRic.Aleshire@Sun.COM 			needed_priv = PRIV_FILE_DOWNGRADE_SL;
40910972SRic.Aleshire@Sun.COM 			goto out_check;
41010972SRic.Aleshire@Sun.COM 		}
41110972SRic.Aleshire@Sun.COM 
41210972SRic.Aleshire@Sun.COM 		if (hexstr_to_label(strval, &new_sl) != 0)
41310972SRic.Aleshire@Sun.COM 			return (EPERM);
41410972SRic.Aleshire@Sun.COM 
41510972SRic.Aleshire@Sun.COM 		if (blstrictdom(&ds_sl, &new_sl))
41610972SRic.Aleshire@Sun.COM 			needed_priv = PRIV_FILE_DOWNGRADE_SL;
41710972SRic.Aleshire@Sun.COM 		else if (blstrictdom(&new_sl, &ds_sl))
41810972SRic.Aleshire@Sun.COM 			needed_priv = PRIV_FILE_UPGRADE_SL;
41910972SRic.Aleshire@Sun.COM 	} else {
42010972SRic.Aleshire@Sun.COM 		/* dataset currently has a default label */
42110972SRic.Aleshire@Sun.COM 		if (!new_default)
42210972SRic.Aleshire@Sun.COM 			needed_priv = PRIV_FILE_UPGRADE_SL;
42310972SRic.Aleshire@Sun.COM 	}
42410972SRic.Aleshire@Sun.COM 
42510972SRic.Aleshire@Sun.COM out_check:
42610972SRic.Aleshire@Sun.COM 	if (needed_priv != -1)
42710972SRic.Aleshire@Sun.COM 		return (PRIV_POLICY(cr, needed_priv, B_FALSE, EPERM, NULL));
42810972SRic.Aleshire@Sun.COM 	return (0);
42910972SRic.Aleshire@Sun.COM }
43010972SRic.Aleshire@Sun.COM 
4314543Smarks static int
43211022STom.Erickson@Sun.COM zfs_secpolicy_setprop(const char *dsname, zfs_prop_t prop, nvpair_t *propval,
43311022STom.Erickson@Sun.COM     cred_t *cr)
4344543Smarks {
43511022STom.Erickson@Sun.COM 	char *strval;
43611022STom.Erickson@Sun.COM 
4374543Smarks 	/*
4384543Smarks 	 * Check permissions for special properties.
4394543Smarks 	 */
4404543Smarks 	switch (prop) {
4414543Smarks 	case ZFS_PROP_ZONED:
4424543Smarks 		/*
4434543Smarks 		 * Disallow setting of 'zoned' from within a local zone.
4444543Smarks 		 */
4454543Smarks 		if (!INGLOBALZONE(curproc))
4464543Smarks 			return (EPERM);
4474543Smarks 		break;
448789Sahrens 
4494543Smarks 	case ZFS_PROP_QUOTA:
4504543Smarks 		if (!INGLOBALZONE(curproc)) {
4514543Smarks 			uint64_t zoned;
4524543Smarks 			char setpoint[MAXNAMELEN];
4534543Smarks 			/*
4544543Smarks 			 * Unprivileged users are allowed to modify the
4554543Smarks 			 * quota on things *under* (ie. contained by)
4564543Smarks 			 * the thing they own.
4574543Smarks 			 */
45811022STom.Erickson@Sun.COM 			if (dsl_prop_get_integer(dsname, "zoned", &zoned,
4594543Smarks 			    setpoint))
4604543Smarks 				return (EPERM);
46111022STom.Erickson@Sun.COM 			if (!zoned || strlen(dsname) <= strlen(setpoint))
4624543Smarks 				return (EPERM);
4634543Smarks 		}
4644670Sahrens 		break;
46510972SRic.Aleshire@Sun.COM 
46610972SRic.Aleshire@Sun.COM 	case ZFS_PROP_MLSLABEL:
46710972SRic.Aleshire@Sun.COM 		if (!is_system_labeled())
46810972SRic.Aleshire@Sun.COM 			return (EPERM);
46911022STom.Erickson@Sun.COM 
47011022STom.Erickson@Sun.COM 		if (nvpair_value_string(propval, &strval) == 0) {
47111022STom.Erickson@Sun.COM 			int err;
47211022STom.Erickson@Sun.COM 
47311022STom.Erickson@Sun.COM 			err = zfs_set_slabel_policy(dsname, strval, CRED());
47411022STom.Erickson@Sun.COM 			if (err != 0)
47511022STom.Erickson@Sun.COM 				return (err);
47611022STom.Erickson@Sun.COM 		}
47710972SRic.Aleshire@Sun.COM 		break;
4784543Smarks 	}
4794543Smarks 
48011022STom.Erickson@Sun.COM 	return (zfs_secpolicy_write_perms(dsname, zfs_prop_to_name(prop), cr));
481789Sahrens }
482789Sahrens 
4834543Smarks int
4844543Smarks zfs_secpolicy_fsacl(zfs_cmd_t *zc, cred_t *cr)
4854543Smarks {
4864543Smarks 	int error;
4874543Smarks 
4884543Smarks 	error = zfs_dozonecheck(zc->zc_name, cr);
4894543Smarks 	if (error)
4904543Smarks 		return (error);
4914543Smarks 
4924543Smarks 	/*
4934543Smarks 	 * permission to set permissions will be evaluated later in
4944543Smarks 	 * dsl_deleg_can_allow()
4954543Smarks 	 */
4964543Smarks 	return (0);
4974543Smarks }
4984543Smarks 
4994543Smarks int
5004543Smarks zfs_secpolicy_rollback(zfs_cmd_t *zc, cred_t *cr)
5014543Smarks {
50210588SEric.Taylor@Sun.COM 	return (zfs_secpolicy_write_perms(zc->zc_name,
50310588SEric.Taylor@Sun.COM 	    ZFS_DELEG_PERM_ROLLBACK, cr));
5044543Smarks }
5054543Smarks 
5064543Smarks int
5074543Smarks zfs_secpolicy_send(zfs_cmd_t *zc, cred_t *cr)
5084543Smarks {
5094543Smarks 	return (zfs_secpolicy_write_perms(zc->zc_name,
5104543Smarks 	    ZFS_DELEG_PERM_SEND, cr));
5114543Smarks }
5124543Smarks 
5138845Samw@Sun.COM static int
5148845Samw@Sun.COM zfs_secpolicy_deleg_share(zfs_cmd_t *zc, cred_t *cr)
5158845Samw@Sun.COM {
5168845Samw@Sun.COM 	vnode_t *vp;
5178845Samw@Sun.COM 	int error;
5188845Samw@Sun.COM 
5198845Samw@Sun.COM 	if ((error = lookupname(zc->zc_value, UIO_SYSSPACE,
5208845Samw@Sun.COM 	    NO_FOLLOW, NULL, &vp)) != 0)
5218845Samw@Sun.COM 		return (error);
5228845Samw@Sun.COM 
5238845Samw@Sun.COM 	/* Now make sure mntpnt and dataset are ZFS */
5248845Samw@Sun.COM 
5258845Samw@Sun.COM 	if (vp->v_vfsp->vfs_fstype != zfsfstype ||
5268845Samw@Sun.COM 	    (strcmp((char *)refstr_value(vp->v_vfsp->vfs_resource),
5278845Samw@Sun.COM 	    zc->zc_name) != 0)) {
5288845Samw@Sun.COM 		VN_RELE(vp);
5298845Samw@Sun.COM 		return (EPERM);
5308845Samw@Sun.COM 	}
5318845Samw@Sun.COM 
5328845Samw@Sun.COM 	VN_RELE(vp);
5338845Samw@Sun.COM 	return (dsl_deleg_access(zc->zc_name,
5348845Samw@Sun.COM 	    ZFS_DELEG_PERM_SHARE, cr));
5358845Samw@Sun.COM }
5368845Samw@Sun.COM 
5374543Smarks int
5384543Smarks zfs_secpolicy_share(zfs_cmd_t *zc, cred_t *cr)
5394543Smarks {
5404543Smarks 	if (!INGLOBALZONE(curproc))
5414543Smarks 		return (EPERM);
5424543Smarks 
5435367Sahrens 	if (secpolicy_nfs(cr) == 0) {
5444543Smarks 		return (0);
5454543Smarks 	} else {
5468845Samw@Sun.COM 		return (zfs_secpolicy_deleg_share(zc, cr));
5478845Samw@Sun.COM 	}
5488845Samw@Sun.COM }
5498845Samw@Sun.COM 
5508845Samw@Sun.COM int
5518845Samw@Sun.COM zfs_secpolicy_smb_acl(zfs_cmd_t *zc, cred_t *cr)
5528845Samw@Sun.COM {
5538845Samw@Sun.COM 	if (!INGLOBALZONE(curproc))
5548845Samw@Sun.COM 		return (EPERM);
5558845Samw@Sun.COM 
5568845Samw@Sun.COM 	if (secpolicy_smb(cr) == 0) {
5578845Samw@Sun.COM 		return (0);
5588845Samw@Sun.COM 	} else {
5598845Samw@Sun.COM 		return (zfs_secpolicy_deleg_share(zc, cr));
5604543Smarks 	}
5614543Smarks }
5624543Smarks 
563789Sahrens static int
5644543Smarks zfs_get_parent(const char *datasetname, char *parent, int parentsize)
565789Sahrens {
566789Sahrens 	char *cp;
567789Sahrens 
568789Sahrens 	/*
569789Sahrens 	 * Remove the @bla or /bla from the end of the name to get the parent.
570789Sahrens 	 */
5714543Smarks 	(void) strncpy(parent, datasetname, parentsize);
5724543Smarks 	cp = strrchr(parent, '@');
573789Sahrens 	if (cp != NULL) {
574789Sahrens 		cp[0] = '\0';
575789Sahrens 	} else {
5764543Smarks 		cp = strrchr(parent, '/');
577789Sahrens 		if (cp == NULL)
578789Sahrens 			return (ENOENT);
579789Sahrens 		cp[0] = '\0';
580789Sahrens 	}
581789Sahrens 
5824543Smarks 	return (0);
5834543Smarks }
5844543Smarks 
5854543Smarks int
5864543Smarks zfs_secpolicy_destroy_perms(const char *name, cred_t *cr)
5874543Smarks {
5884543Smarks 	int error;
5894543Smarks 
5904543Smarks 	if ((error = zfs_secpolicy_write_perms(name,
5914543Smarks 	    ZFS_DELEG_PERM_MOUNT, cr)) != 0)
5924543Smarks 		return (error);
5934543Smarks 
5944543Smarks 	return (zfs_secpolicy_write_perms(name, ZFS_DELEG_PERM_DESTROY, cr));
5954543Smarks }
5964543Smarks 
5974543Smarks static int
5984543Smarks zfs_secpolicy_destroy(zfs_cmd_t *zc, cred_t *cr)
5994543Smarks {
6004543Smarks 	return (zfs_secpolicy_destroy_perms(zc->zc_name, cr));
6014543Smarks }
6024543Smarks 
6034543Smarks /*
6044543Smarks  * Must have sys_config privilege to check the iscsi permission
6054543Smarks  */
6064543Smarks /* ARGSUSED */
6074543Smarks static int
6084543Smarks zfs_secpolicy_iscsi(zfs_cmd_t *zc, cred_t *cr)
6094543Smarks {
6104543Smarks 	return (secpolicy_zfs(cr));
6114543Smarks }
6124543Smarks 
6134543Smarks int
6144543Smarks zfs_secpolicy_rename_perms(const char *from, const char *to, cred_t *cr)
6154543Smarks {
61611022STom.Erickson@Sun.COM 	char	parentname[MAXNAMELEN];
6174543Smarks 	int	error;
6184543Smarks 
6194543Smarks 	if ((error = zfs_secpolicy_write_perms(from,
6204543Smarks 	    ZFS_DELEG_PERM_RENAME, cr)) != 0)
6214543Smarks 		return (error);
6224543Smarks 
6234543Smarks 	if ((error = zfs_secpolicy_write_perms(from,
6244543Smarks 	    ZFS_DELEG_PERM_MOUNT, cr)) != 0)
6254543Smarks 		return (error);
6264543Smarks 
6274543Smarks 	if ((error = zfs_get_parent(to, parentname,
6284543Smarks 	    sizeof (parentname))) != 0)
6294543Smarks 		return (error);
6304543Smarks 
6314543Smarks 	if ((error = zfs_secpolicy_write_perms(parentname,
6324543Smarks 	    ZFS_DELEG_PERM_CREATE, cr)) != 0)
6334543Smarks 		return (error);
6344543Smarks 
6354543Smarks 	if ((error = zfs_secpolicy_write_perms(parentname,
6364543Smarks 	    ZFS_DELEG_PERM_MOUNT, cr)) != 0)
6374543Smarks 		return (error);
6384543Smarks 
6394543Smarks 	return (error);
6404543Smarks }
6414543Smarks 
6424543Smarks static int
6434543Smarks zfs_secpolicy_rename(zfs_cmd_t *zc, cred_t *cr)
6444543Smarks {
6454543Smarks 	return (zfs_secpolicy_rename_perms(zc->zc_name, zc->zc_value, cr));
6464543Smarks }
6474543Smarks 
6484543Smarks static int
6494543Smarks zfs_secpolicy_promote(zfs_cmd_t *zc, cred_t *cr)
6504543Smarks {
65111022STom.Erickson@Sun.COM 	char	parentname[MAXNAMELEN];
6524543Smarks 	objset_t *clone;
6534543Smarks 	int error;
6544543Smarks 
6554543Smarks 	error = zfs_secpolicy_write_perms(zc->zc_name,
6564543Smarks 	    ZFS_DELEG_PERM_PROMOTE, cr);
6574543Smarks 	if (error)
6584543Smarks 		return (error);
6594543Smarks 
66010298SMatthew.Ahrens@Sun.COM 	error = dmu_objset_hold(zc->zc_name, FTAG, &clone);
6614543Smarks 
6624543Smarks 	if (error == 0) {
6634543Smarks 		dsl_dataset_t *pclone = NULL;
6644543Smarks 		dsl_dir_t *dd;
66510298SMatthew.Ahrens@Sun.COM 		dd = clone->os_dsl_dataset->ds_dir;
6664543Smarks 
6674543Smarks 		rw_enter(&dd->dd_pool->dp_config_rwlock, RW_READER);
6686689Smaybee 		error = dsl_dataset_hold_obj(dd->dd_pool,
6696689Smaybee 		    dd->dd_phys->dd_origin_obj, FTAG, &pclone);
6704543Smarks 		rw_exit(&dd->dd_pool->dp_config_rwlock);
6714543Smarks 		if (error) {
67210298SMatthew.Ahrens@Sun.COM 			dmu_objset_rele(clone, FTAG);
6734543Smarks 			return (error);
6744543Smarks 		}
6754543Smarks 
6764543Smarks 		error = zfs_secpolicy_write_perms(zc->zc_name,
6774543Smarks 		    ZFS_DELEG_PERM_MOUNT, cr);
6784543Smarks 
6794543Smarks 		dsl_dataset_name(pclone, parentname);
68010298SMatthew.Ahrens@Sun.COM 		dmu_objset_rele(clone, FTAG);
6816689Smaybee 		dsl_dataset_rele(pclone, FTAG);
6824543Smarks 		if (error == 0)
6834543Smarks 			error = zfs_secpolicy_write_perms(parentname,
6844543Smarks 			    ZFS_DELEG_PERM_PROMOTE, cr);
6854543Smarks 	}
6864543Smarks 	return (error);
6874543Smarks }
6884543Smarks 
6894543Smarks static int
6904543Smarks zfs_secpolicy_receive(zfs_cmd_t *zc, cred_t *cr)
6914543Smarks {
6924543Smarks 	int error;
6934543Smarks 
6944543Smarks 	if ((error = zfs_secpolicy_write_perms(zc->zc_name,
6954543Smarks 	    ZFS_DELEG_PERM_RECEIVE, cr)) != 0)
6964543Smarks 		return (error);
6974543Smarks 
6984543Smarks 	if ((error = zfs_secpolicy_write_perms(zc->zc_name,
6994543Smarks 	    ZFS_DELEG_PERM_MOUNT, cr)) != 0)
7004543Smarks 		return (error);
7014543Smarks 
7024543Smarks 	return (zfs_secpolicy_write_perms(zc->zc_name,
7034543Smarks 	    ZFS_DELEG_PERM_CREATE, cr));
7044543Smarks }
7054543Smarks 
7064543Smarks int
7074543Smarks zfs_secpolicy_snapshot_perms(const char *name, cred_t *cr)
7084543Smarks {
70910588SEric.Taylor@Sun.COM 	return (zfs_secpolicy_write_perms(name,
71010588SEric.Taylor@Sun.COM 	    ZFS_DELEG_PERM_SNAPSHOT, cr));
7114543Smarks }
7124543Smarks 
7134543Smarks static int
7144543Smarks zfs_secpolicy_snapshot(zfs_cmd_t *zc, cred_t *cr)
7154543Smarks {
7164543Smarks 
7174543Smarks 	return (zfs_secpolicy_snapshot_perms(zc->zc_name, cr));
7184543Smarks }
7194543Smarks 
7204543Smarks static int
7214543Smarks zfs_secpolicy_create(zfs_cmd_t *zc, cred_t *cr)
7224543Smarks {
72311022STom.Erickson@Sun.COM 	char	parentname[MAXNAMELEN];
72411022STom.Erickson@Sun.COM 	int	error;
7254543Smarks 
7264543Smarks 	if ((error = zfs_get_parent(zc->zc_name, parentname,
7274543Smarks 	    sizeof (parentname))) != 0)
7284543Smarks 		return (error);
7294543Smarks 
7304543Smarks 	if (zc->zc_value[0] != '\0') {
7314543Smarks 		if ((error = zfs_secpolicy_write_perms(zc->zc_value,
7324543Smarks 		    ZFS_DELEG_PERM_CLONE, cr)) != 0)
7334543Smarks 			return (error);
7344543Smarks 	}
7354543Smarks 
7364543Smarks 	if ((error = zfs_secpolicy_write_perms(parentname,
7374543Smarks 	    ZFS_DELEG_PERM_CREATE, cr)) != 0)
7384543Smarks 		return (error);
7394543Smarks 
7404543Smarks 	error = zfs_secpolicy_write_perms(parentname,
7414543Smarks 	    ZFS_DELEG_PERM_MOUNT, cr);
7424543Smarks 
7434543Smarks 	return (error);
7444543Smarks }
7454543Smarks 
7464543Smarks static int
7474543Smarks zfs_secpolicy_umount(zfs_cmd_t *zc, cred_t *cr)
7484543Smarks {
7494543Smarks 	int error;
7504543Smarks 
7514543Smarks 	error = secpolicy_fs_unmount(cr, NULL);
7524543Smarks 	if (error) {
7534543Smarks 		error = dsl_deleg_access(zc->zc_name, ZFS_DELEG_PERM_MOUNT, cr);
7544543Smarks 	}
7554543Smarks 	return (error);
756789Sahrens }
757789Sahrens 
758789Sahrens /*
759789Sahrens  * Policy for pool operations - create/destroy pools, add vdevs, etc.  Requires
760789Sahrens  * SYS_CONFIG privilege, which is not available in a local zone.
761789Sahrens  */
762789Sahrens /* ARGSUSED */
763789Sahrens static int
7644543Smarks zfs_secpolicy_config(zfs_cmd_t *zc, cred_t *cr)
765789Sahrens {
766789Sahrens 	if (secpolicy_sys_config(cr, B_FALSE) != 0)
767789Sahrens 		return (EPERM);
768789Sahrens 
769789Sahrens 	return (0);
770789Sahrens }
771789Sahrens 
772789Sahrens /*
7731544Seschrock  * Policy for fault injection.  Requires all privileges.
7741544Seschrock  */
7751544Seschrock /* ARGSUSED */
7761544Seschrock static int
7774543Smarks zfs_secpolicy_inject(zfs_cmd_t *zc, cred_t *cr)
7781544Seschrock {
7791544Seschrock 	return (secpolicy_zinject(cr));
7801544Seschrock }
7811544Seschrock 
7824849Sahrens static int
7834849Sahrens zfs_secpolicy_inherit(zfs_cmd_t *zc, cred_t *cr)
7844849Sahrens {
7854849Sahrens 	zfs_prop_t prop = zfs_name_to_prop(zc->zc_value);
7864849Sahrens 
7875094Slling 	if (prop == ZPROP_INVAL) {
7884849Sahrens 		if (!zfs_prop_user(zc->zc_value))
7894849Sahrens 			return (EINVAL);
7904849Sahrens 		return (zfs_secpolicy_write_perms(zc->zc_name,
7914849Sahrens 		    ZFS_DELEG_PERM_USERPROP, cr));
7924849Sahrens 	} else {
79311022STom.Erickson@Sun.COM 		return (zfs_secpolicy_setprop(zc->zc_name, prop,
79411022STom.Erickson@Sun.COM 		    NULL, cr));
7954849Sahrens 	}
7964849Sahrens }
7974849Sahrens 
7989396SMatthew.Ahrens@Sun.COM static int
7999396SMatthew.Ahrens@Sun.COM zfs_secpolicy_userspace_one(zfs_cmd_t *zc, cred_t *cr)
8009396SMatthew.Ahrens@Sun.COM {
8019396SMatthew.Ahrens@Sun.COM 	int err = zfs_secpolicy_read(zc, cr);
8029396SMatthew.Ahrens@Sun.COM 	if (err)
8039396SMatthew.Ahrens@Sun.COM 		return (err);
8049396SMatthew.Ahrens@Sun.COM 
8059396SMatthew.Ahrens@Sun.COM 	if (zc->zc_objset_type >= ZFS_NUM_USERQUOTA_PROPS)
8069396SMatthew.Ahrens@Sun.COM 		return (EINVAL);
8079396SMatthew.Ahrens@Sun.COM 
8089396SMatthew.Ahrens@Sun.COM 	if (zc->zc_value[0] == 0) {
8099396SMatthew.Ahrens@Sun.COM 		/*
8109396SMatthew.Ahrens@Sun.COM 		 * They are asking about a posix uid/gid.  If it's
8119396SMatthew.Ahrens@Sun.COM 		 * themself, allow it.
8129396SMatthew.Ahrens@Sun.COM 		 */
8139396SMatthew.Ahrens@Sun.COM 		if (zc->zc_objset_type == ZFS_PROP_USERUSED ||
8149396SMatthew.Ahrens@Sun.COM 		    zc->zc_objset_type == ZFS_PROP_USERQUOTA) {
8159396SMatthew.Ahrens@Sun.COM 			if (zc->zc_guid == crgetuid(cr))
8169396SMatthew.Ahrens@Sun.COM 				return (0);
8179396SMatthew.Ahrens@Sun.COM 		} else {
8189396SMatthew.Ahrens@Sun.COM 			if (groupmember(zc->zc_guid, cr))
8199396SMatthew.Ahrens@Sun.COM 				return (0);
8209396SMatthew.Ahrens@Sun.COM 		}
8219396SMatthew.Ahrens@Sun.COM 	}
8229396SMatthew.Ahrens@Sun.COM 
8239396SMatthew.Ahrens@Sun.COM 	return (zfs_secpolicy_write_perms(zc->zc_name,
8249396SMatthew.Ahrens@Sun.COM 	    userquota_perms[zc->zc_objset_type], cr));
8259396SMatthew.Ahrens@Sun.COM }
8269396SMatthew.Ahrens@Sun.COM 
8279396SMatthew.Ahrens@Sun.COM static int
8289396SMatthew.Ahrens@Sun.COM zfs_secpolicy_userspace_many(zfs_cmd_t *zc, cred_t *cr)
8299396SMatthew.Ahrens@Sun.COM {
8309396SMatthew.Ahrens@Sun.COM 	int err = zfs_secpolicy_read(zc, cr);
8319396SMatthew.Ahrens@Sun.COM 	if (err)
8329396SMatthew.Ahrens@Sun.COM 		return (err);
8339396SMatthew.Ahrens@Sun.COM 
8349396SMatthew.Ahrens@Sun.COM 	if (zc->zc_objset_type >= ZFS_NUM_USERQUOTA_PROPS)
8359396SMatthew.Ahrens@Sun.COM 		return (EINVAL);
8369396SMatthew.Ahrens@Sun.COM 
8379396SMatthew.Ahrens@Sun.COM 	return (zfs_secpolicy_write_perms(zc->zc_name,
8389396SMatthew.Ahrens@Sun.COM 	    userquota_perms[zc->zc_objset_type], cr));
8399396SMatthew.Ahrens@Sun.COM }
8409396SMatthew.Ahrens@Sun.COM 
8419396SMatthew.Ahrens@Sun.COM static int
8429396SMatthew.Ahrens@Sun.COM zfs_secpolicy_userspace_upgrade(zfs_cmd_t *zc, cred_t *cr)
8439396SMatthew.Ahrens@Sun.COM {
84411022STom.Erickson@Sun.COM 	return (zfs_secpolicy_setprop(zc->zc_name, ZFS_PROP_VERSION,
84511022STom.Erickson@Sun.COM 	    NULL, cr));
8469396SMatthew.Ahrens@Sun.COM }
8479396SMatthew.Ahrens@Sun.COM 
84810242Schris.kirby@sun.com static int
84910242Schris.kirby@sun.com zfs_secpolicy_hold(zfs_cmd_t *zc, cred_t *cr)
85010242Schris.kirby@sun.com {
85110242Schris.kirby@sun.com 	return (zfs_secpolicy_write_perms(zc->zc_name,
85210242Schris.kirby@sun.com 	    ZFS_DELEG_PERM_HOLD, cr));
85310242Schris.kirby@sun.com }
85410242Schris.kirby@sun.com 
85510242Schris.kirby@sun.com static int
85610242Schris.kirby@sun.com zfs_secpolicy_release(zfs_cmd_t *zc, cred_t *cr)
85710242Schris.kirby@sun.com {
85810242Schris.kirby@sun.com 	return (zfs_secpolicy_write_perms(zc->zc_name,
85910242Schris.kirby@sun.com 	    ZFS_DELEG_PERM_RELEASE, cr));
86010242Schris.kirby@sun.com }
86110242Schris.kirby@sun.com 
8621544Seschrock /*
863789Sahrens  * Returns the nvlist as specified by the user in the zfs_cmd_t.
864789Sahrens  */
865789Sahrens static int
8669643SEric.Taylor@Sun.COM get_nvlist(uint64_t nvl, uint64_t size, int iflag, nvlist_t **nvp)
867789Sahrens {
868789Sahrens 	char *packed;
869789Sahrens 	int error;
8705094Slling 	nvlist_t *list = NULL;
871789Sahrens 
872789Sahrens 	/*
8732676Seschrock 	 * Read in and unpack the user-supplied nvlist.
874789Sahrens 	 */
8755094Slling 	if (size == 0)
876789Sahrens 		return (EINVAL);
877789Sahrens 
878789Sahrens 	packed = kmem_alloc(size, KM_SLEEP);
879789Sahrens 
8809643SEric.Taylor@Sun.COM 	if ((error = ddi_copyin((void *)(uintptr_t)nvl, packed, size,
8819643SEric.Taylor@Sun.COM 	    iflag)) != 0) {
882789Sahrens 		kmem_free(packed, size);
883789Sahrens 		return (error);
884789Sahrens 	}
885789Sahrens 
8865094Slling 	if ((error = nvlist_unpack(packed, size, &list, 0)) != 0) {
887789Sahrens 		kmem_free(packed, size);
888789Sahrens 		return (error);
889789Sahrens 	}
890789Sahrens 
891789Sahrens 	kmem_free(packed, size);
892789Sahrens 
8935094Slling 	*nvp = list;
894789Sahrens 	return (0);
895789Sahrens }
896789Sahrens 
897789Sahrens static int
89811022STom.Erickson@Sun.COM fit_error_list(zfs_cmd_t *zc, nvlist_t **errors)
89911022STom.Erickson@Sun.COM {
90011022STom.Erickson@Sun.COM 	size_t size;
90111022STom.Erickson@Sun.COM 
90211022STom.Erickson@Sun.COM 	VERIFY(nvlist_size(*errors, &size, NV_ENCODE_NATIVE) == 0);
90311022STom.Erickson@Sun.COM 
90411022STom.Erickson@Sun.COM 	if (size > zc->zc_nvlist_dst_size) {
90511022STom.Erickson@Sun.COM 		nvpair_t *more_errors;
90611022STom.Erickson@Sun.COM 		int n = 0;
90711022STom.Erickson@Sun.COM 
90811022STom.Erickson@Sun.COM 		if (zc->zc_nvlist_dst_size < 1024)
90911022STom.Erickson@Sun.COM 			return (ENOMEM);
91011022STom.Erickson@Sun.COM 
91111022STom.Erickson@Sun.COM 		VERIFY(nvlist_add_int32(*errors, ZPROP_N_MORE_ERRORS, 0) == 0);
91211022STom.Erickson@Sun.COM 		more_errors = nvlist_prev_nvpair(*errors, NULL);
91311022STom.Erickson@Sun.COM 
91411022STom.Erickson@Sun.COM 		do {
91511022STom.Erickson@Sun.COM 			nvpair_t *pair = nvlist_prev_nvpair(*errors,
91611022STom.Erickson@Sun.COM 			    more_errors);
91711022STom.Erickson@Sun.COM 			VERIFY(nvlist_remove_nvpair(*errors, pair) == 0);
91811022STom.Erickson@Sun.COM 			n++;
91911022STom.Erickson@Sun.COM 			VERIFY(nvlist_size(*errors, &size,
92011022STom.Erickson@Sun.COM 			    NV_ENCODE_NATIVE) == 0);
92111022STom.Erickson@Sun.COM 		} while (size > zc->zc_nvlist_dst_size);
92211022STom.Erickson@Sun.COM 
92311022STom.Erickson@Sun.COM 		VERIFY(nvlist_remove_nvpair(*errors, more_errors) == 0);
92411022STom.Erickson@Sun.COM 		VERIFY(nvlist_add_int32(*errors, ZPROP_N_MORE_ERRORS, n) == 0);
92511022STom.Erickson@Sun.COM 		ASSERT(nvlist_size(*errors, &size, NV_ENCODE_NATIVE) == 0);
92611022STom.Erickson@Sun.COM 		ASSERT(size <= zc->zc_nvlist_dst_size);
92711022STom.Erickson@Sun.COM 	}
92811022STom.Erickson@Sun.COM 
92911022STom.Erickson@Sun.COM 	return (0);
93011022STom.Erickson@Sun.COM }
93111022STom.Erickson@Sun.COM 
93211022STom.Erickson@Sun.COM static int
9332676Seschrock put_nvlist(zfs_cmd_t *zc, nvlist_t *nvl)
9342676Seschrock {
9352676Seschrock 	char *packed = NULL;
9362676Seschrock 	size_t size;
9372676Seschrock 	int error;
9382676Seschrock 
9392676Seschrock 	VERIFY(nvlist_size(nvl, &size, NV_ENCODE_NATIVE) == 0);
9402676Seschrock 
9412676Seschrock 	if (size > zc->zc_nvlist_dst_size) {
9422676Seschrock 		error = ENOMEM;
9432676Seschrock 	} else {
9444611Smarks 		packed = kmem_alloc(size, KM_SLEEP);
9452676Seschrock 		VERIFY(nvlist_pack(nvl, &packed, &size, NV_ENCODE_NATIVE,
9462676Seschrock 		    KM_SLEEP) == 0);
9479643SEric.Taylor@Sun.COM 		error = ddi_copyout(packed,
9489643SEric.Taylor@Sun.COM 		    (void *)(uintptr_t)zc->zc_nvlist_dst, size, zc->zc_iflags);
9492676Seschrock 		kmem_free(packed, size);
9502676Seschrock 	}
9512676Seschrock 
9522676Seschrock 	zc->zc_nvlist_dst_size = size;
9532676Seschrock 	return (error);
9542676Seschrock }
9552676Seschrock 
9562676Seschrock static int
9579396SMatthew.Ahrens@Sun.COM getzfsvfs(const char *dsname, zfsvfs_t **zvp)
9589396SMatthew.Ahrens@Sun.COM {
9599396SMatthew.Ahrens@Sun.COM 	objset_t *os;
9609396SMatthew.Ahrens@Sun.COM 	int error;
9619396SMatthew.Ahrens@Sun.COM 
96210298SMatthew.Ahrens@Sun.COM 	error = dmu_objset_hold(dsname, FTAG, &os);
9639396SMatthew.Ahrens@Sun.COM 	if (error)
9649396SMatthew.Ahrens@Sun.COM 		return (error);
96510298SMatthew.Ahrens@Sun.COM 	if (dmu_objset_type(os) != DMU_OST_ZFS) {
96610298SMatthew.Ahrens@Sun.COM 		dmu_objset_rele(os, FTAG);
96710298SMatthew.Ahrens@Sun.COM 		return (EINVAL);
96810298SMatthew.Ahrens@Sun.COM 	}
96910298SMatthew.Ahrens@Sun.COM 
97010298SMatthew.Ahrens@Sun.COM 	mutex_enter(&os->os_user_ptr_lock);
9719396SMatthew.Ahrens@Sun.COM 	*zvp = dmu_objset_get_user(os);
9729396SMatthew.Ahrens@Sun.COM 	if (*zvp) {
9739396SMatthew.Ahrens@Sun.COM 		VFS_HOLD((*zvp)->z_vfs);
9749396SMatthew.Ahrens@Sun.COM 	} else {
9759396SMatthew.Ahrens@Sun.COM 		error = ESRCH;
9769396SMatthew.Ahrens@Sun.COM 	}
97710298SMatthew.Ahrens@Sun.COM 	mutex_exit(&os->os_user_ptr_lock);
97810298SMatthew.Ahrens@Sun.COM 	dmu_objset_rele(os, FTAG);
9799396SMatthew.Ahrens@Sun.COM 	return (error);
9809396SMatthew.Ahrens@Sun.COM }
9819396SMatthew.Ahrens@Sun.COM 
9829396SMatthew.Ahrens@Sun.COM /*
9839396SMatthew.Ahrens@Sun.COM  * Find a zfsvfs_t for a mounted filesystem, or create our own, in which
9849396SMatthew.Ahrens@Sun.COM  * case its z_vfs will be NULL, and it will be opened as the owner.
9859396SMatthew.Ahrens@Sun.COM  */
9869396SMatthew.Ahrens@Sun.COM static int
98710298SMatthew.Ahrens@Sun.COM zfsvfs_hold(const char *name, void *tag, zfsvfs_t **zvp)
9889396SMatthew.Ahrens@Sun.COM {
9899396SMatthew.Ahrens@Sun.COM 	int error = 0;
9909396SMatthew.Ahrens@Sun.COM 
9919396SMatthew.Ahrens@Sun.COM 	if (getzfsvfs(name, zvp) != 0)
99210298SMatthew.Ahrens@Sun.COM 		error = zfsvfs_create(name, zvp);
9939396SMatthew.Ahrens@Sun.COM 	if (error == 0) {
9949396SMatthew.Ahrens@Sun.COM 		rrw_enter(&(*zvp)->z_teardown_lock, RW_READER, tag);
9959396SMatthew.Ahrens@Sun.COM 		if ((*zvp)->z_unmounted) {
9969396SMatthew.Ahrens@Sun.COM 			/*
9979396SMatthew.Ahrens@Sun.COM 			 * XXX we could probably try again, since the unmounting
9989396SMatthew.Ahrens@Sun.COM 			 * thread should be just about to disassociate the
9999396SMatthew.Ahrens@Sun.COM 			 * objset from the zfsvfs.
10009396SMatthew.Ahrens@Sun.COM 			 */
10019396SMatthew.Ahrens@Sun.COM 			rrw_exit(&(*zvp)->z_teardown_lock, tag);
10029396SMatthew.Ahrens@Sun.COM 			return (EBUSY);
10039396SMatthew.Ahrens@Sun.COM 		}
10049396SMatthew.Ahrens@Sun.COM 	}
10059396SMatthew.Ahrens@Sun.COM 	return (error);
10069396SMatthew.Ahrens@Sun.COM }
10079396SMatthew.Ahrens@Sun.COM 
10089396SMatthew.Ahrens@Sun.COM static void
10099396SMatthew.Ahrens@Sun.COM zfsvfs_rele(zfsvfs_t *zfsvfs, void *tag)
10109396SMatthew.Ahrens@Sun.COM {
10119396SMatthew.Ahrens@Sun.COM 	rrw_exit(&zfsvfs->z_teardown_lock, tag);
10129396SMatthew.Ahrens@Sun.COM 
10139396SMatthew.Ahrens@Sun.COM 	if (zfsvfs->z_vfs) {
10149396SMatthew.Ahrens@Sun.COM 		VFS_RELE(zfsvfs->z_vfs);
10159396SMatthew.Ahrens@Sun.COM 	} else {
101610298SMatthew.Ahrens@Sun.COM 		dmu_objset_disown(zfsvfs->z_os, zfsvfs);
10179396SMatthew.Ahrens@Sun.COM 		zfsvfs_free(zfsvfs);
10189396SMatthew.Ahrens@Sun.COM 	}
10199396SMatthew.Ahrens@Sun.COM }
10209396SMatthew.Ahrens@Sun.COM 
10219396SMatthew.Ahrens@Sun.COM static int
1022789Sahrens zfs_ioc_pool_create(zfs_cmd_t *zc)
1023789Sahrens {
1024789Sahrens 	int error;
10255094Slling 	nvlist_t *config, *props = NULL;
10267184Stimh 	nvlist_t *rootprops = NULL;
10277184Stimh 	nvlist_t *zplprops = NULL;
10284715Sek110237 	char *buf;
1029789Sahrens 
10305094Slling 	if (error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size,
10319643SEric.Taylor@Sun.COM 	    zc->zc_iflags, &config))
10324988Sek110237 		return (error);
10334715Sek110237 
10345094Slling 	if (zc->zc_nvlist_src_size != 0 && (error =
10359643SEric.Taylor@Sun.COM 	    get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
10369643SEric.Taylor@Sun.COM 	    zc->zc_iflags, &props))) {
10375094Slling 		nvlist_free(config);
10385094Slling 		return (error);
10395094Slling 	}
10405094Slling 
10417184Stimh 	if (props) {
10427184Stimh 		nvlist_t *nvl = NULL;
10437184Stimh 		uint64_t version = SPA_VERSION;
10447184Stimh 
10457184Stimh 		(void) nvlist_lookup_uint64(props,
10467184Stimh 		    zpool_prop_to_name(ZPOOL_PROP_VERSION), &version);
10477184Stimh 		if (version < SPA_VERSION_INITIAL || version > SPA_VERSION) {
10487184Stimh 			error = EINVAL;
10497184Stimh 			goto pool_props_bad;
10507184Stimh 		}
10517184Stimh 		(void) nvlist_lookup_nvlist(props, ZPOOL_ROOTFS_PROPS, &nvl);
10527184Stimh 		if (nvl) {
10537184Stimh 			error = nvlist_dup(nvl, &rootprops, KM_SLEEP);
10547184Stimh 			if (error != 0) {
10557184Stimh 				nvlist_free(config);
10567184Stimh 				nvlist_free(props);
10577184Stimh 				return (error);
10587184Stimh 			}
10597184Stimh 			(void) nvlist_remove_all(props, ZPOOL_ROOTFS_PROPS);
10607184Stimh 		}
10617184Stimh 		VERIFY(nvlist_alloc(&zplprops, NV_UNIQUE_NAME, KM_SLEEP) == 0);
10627184Stimh 		error = zfs_fill_zplprops_root(version, rootprops,
10637184Stimh 		    zplprops, NULL);
10647184Stimh 		if (error)
10657184Stimh 			goto pool_props_bad;
10667184Stimh 	}
10677184Stimh 
10684988Sek110237 	buf = history_str_get(zc);
1069789Sahrens 
10707184Stimh 	error = spa_create(zc->zc_name, config, props, buf, zplprops);
10717184Stimh 
10727184Stimh 	/*
10737184Stimh 	 * Set the remaining root properties
10747184Stimh 	 */
107511022STom.Erickson@Sun.COM 	if (!error && (error = zfs_set_prop_nvlist(zc->zc_name,
107611022STom.Erickson@Sun.COM 	    ZPROP_SRC_LOCAL, rootprops, NULL)) != 0)
10777184Stimh 		(void) spa_destroy(zc->zc_name);
1078789Sahrens 
10794988Sek110237 	if (buf != NULL)
10804988Sek110237 		history_str_free(buf);
10815094Slling 
10827184Stimh pool_props_bad:
10837184Stimh 	nvlist_free(rootprops);
10847184Stimh 	nvlist_free(zplprops);
1085789Sahrens 	nvlist_free(config);
10867184Stimh 	nvlist_free(props);
10875094Slling 
1088789Sahrens 	return (error);
1089789Sahrens }
1090789Sahrens 
1091789Sahrens static int
1092789Sahrens zfs_ioc_pool_destroy(zfs_cmd_t *zc)
1093789Sahrens {
10944543Smarks 	int error;
10954543Smarks 	zfs_log_history(zc);
10964543Smarks 	error = spa_destroy(zc->zc_name);
109710588SEric.Taylor@Sun.COM 	if (error == 0)
109810588SEric.Taylor@Sun.COM 		zvol_remove_minors(zc->zc_name);
10994543Smarks 	return (error);
1100789Sahrens }
1101789Sahrens 
1102789Sahrens static int
1103789Sahrens zfs_ioc_pool_import(zfs_cmd_t *zc)
1104789Sahrens {
11055094Slling 	nvlist_t *config, *props = NULL;
1106789Sahrens 	uint64_t guid;
110710921STim.Haley@Sun.COM 	int error;
1108789Sahrens 
11095094Slling 	if ((error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size,
11109643SEric.Taylor@Sun.COM 	    zc->zc_iflags, &config)) != 0)
1111789Sahrens 		return (error);
1112789Sahrens 
11135094Slling 	if (zc->zc_nvlist_src_size != 0 && (error =
11149643SEric.Taylor@Sun.COM 	    get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
11159643SEric.Taylor@Sun.COM 	    zc->zc_iflags, &props))) {
11165094Slling 		nvlist_free(config);
11175094Slling 		return (error);
11185094Slling 	}
11195094Slling 
1120789Sahrens 	if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID, &guid) != 0 ||
11211544Seschrock 	    guid != zc->zc_guid)
1122789Sahrens 		error = EINVAL;
11236643Seschrock 	else if (zc->zc_cookie)
112410921STim.Haley@Sun.COM 		error = spa_import_verbatim(zc->zc_name, config, props);
1125789Sahrens 	else
11265094Slling 		error = spa_import(zc->zc_name, config, props);
1127789Sahrens 
112810921STim.Haley@Sun.COM 	if (zc->zc_nvlist_dst != 0)
112910921STim.Haley@Sun.COM 		(void) put_nvlist(zc, config);
113010921STim.Haley@Sun.COM 
1131789Sahrens 	nvlist_free(config);
1132789Sahrens 
11335094Slling 	if (props)
11345094Slling 		nvlist_free(props);
11355094Slling 
1136789Sahrens 	return (error);
1137789Sahrens }
1138789Sahrens 
1139789Sahrens static int
1140789Sahrens zfs_ioc_pool_export(zfs_cmd_t *zc)
1141789Sahrens {
11424543Smarks 	int error;
11437214Slling 	boolean_t force = (boolean_t)zc->zc_cookie;
11448211SGeorge.Wilson@Sun.COM 	boolean_t hardforce = (boolean_t)zc->zc_guid;
11457214Slling 
11464543Smarks 	zfs_log_history(zc);
11478211SGeorge.Wilson@Sun.COM 	error = spa_export(zc->zc_name, NULL, force, hardforce);
114810588SEric.Taylor@Sun.COM 	if (error == 0)
114910588SEric.Taylor@Sun.COM 		zvol_remove_minors(zc->zc_name);
11504543Smarks 	return (error);
1151789Sahrens }
1152789Sahrens 
1153789Sahrens static int
1154789Sahrens zfs_ioc_pool_configs(zfs_cmd_t *zc)
1155789Sahrens {
1156789Sahrens 	nvlist_t *configs;
1157789Sahrens 	int error;
1158789Sahrens 
1159789Sahrens 	if ((configs = spa_all_configs(&zc->zc_cookie)) == NULL)
1160789Sahrens 		return (EEXIST);
1161789Sahrens 
11622676Seschrock 	error = put_nvlist(zc, configs);
1163789Sahrens 
1164789Sahrens 	nvlist_free(configs);
1165789Sahrens 
1166789Sahrens 	return (error);
1167789Sahrens }
1168789Sahrens 
1169789Sahrens static int
1170789Sahrens zfs_ioc_pool_stats(zfs_cmd_t *zc)
1171789Sahrens {
1172789Sahrens 	nvlist_t *config;
1173789Sahrens 	int error;
11741544Seschrock 	int ret = 0;
1175789Sahrens 
11762676Seschrock 	error = spa_get_stats(zc->zc_name, &config, zc->zc_value,
11772676Seschrock 	    sizeof (zc->zc_value));
1178789Sahrens 
1179789Sahrens 	if (config != NULL) {
11802676Seschrock 		ret = put_nvlist(zc, config);
1181789Sahrens 		nvlist_free(config);
11821544Seschrock 
11831544Seschrock 		/*
11841544Seschrock 		 * The config may be present even if 'error' is non-zero.
11851544Seschrock 		 * In this case we return success, and preserve the real errno
11861544Seschrock 		 * in 'zc_cookie'.
11871544Seschrock 		 */
11881544Seschrock 		zc->zc_cookie = error;
1189789Sahrens 	} else {
11901544Seschrock 		ret = error;
1191789Sahrens 	}
1192789Sahrens 
11931544Seschrock 	return (ret);
1194789Sahrens }
1195789Sahrens 
1196789Sahrens /*
1197789Sahrens  * Try to import the given pool, returning pool stats as appropriate so that
1198789Sahrens  * user land knows which devices are available and overall pool health.
1199789Sahrens  */
1200789Sahrens static int
1201789Sahrens zfs_ioc_pool_tryimport(zfs_cmd_t *zc)
1202789Sahrens {
1203789Sahrens 	nvlist_t *tryconfig, *config;
1204789Sahrens 	int error;
1205789Sahrens 
12065094Slling 	if ((error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size,
12079643SEric.Taylor@Sun.COM 	    zc->zc_iflags, &tryconfig)) != 0)
1208789Sahrens 		return (error);
1209789Sahrens 
1210789Sahrens 	config = spa_tryimport(tryconfig);
1211789Sahrens 
1212789Sahrens 	nvlist_free(tryconfig);
1213789Sahrens 
1214789Sahrens 	if (config == NULL)
1215789Sahrens 		return (EINVAL);
1216789Sahrens 
12172676Seschrock 	error = put_nvlist(zc, config);
1218789Sahrens 	nvlist_free(config);
1219789Sahrens 
1220789Sahrens 	return (error);
1221789Sahrens }
1222789Sahrens 
1223789Sahrens static int
1224789Sahrens zfs_ioc_pool_scrub(zfs_cmd_t *zc)
1225789Sahrens {
1226789Sahrens 	spa_t *spa;
1227789Sahrens 	int error;
1228789Sahrens 
12292926Sek110237 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
12302926Sek110237 		return (error);
12312926Sek110237 
12327046Sahrens 	error = spa_scrub(spa, zc->zc_cookie);
12332926Sek110237 
12342926Sek110237 	spa_close(spa, FTAG);
12352926Sek110237 
1236789Sahrens 	return (error);
1237789Sahrens }
1238789Sahrens 
1239789Sahrens static int
1240789Sahrens zfs_ioc_pool_freeze(zfs_cmd_t *zc)
1241789Sahrens {
1242789Sahrens 	spa_t *spa;
1243789Sahrens 	int error;
1244789Sahrens 
1245789Sahrens 	error = spa_open(zc->zc_name, &spa, FTAG);
1246789Sahrens 	if (error == 0) {
1247789Sahrens 		spa_freeze(spa);
1248789Sahrens 		spa_close(spa, FTAG);
1249789Sahrens 	}
1250789Sahrens 	return (error);
1251789Sahrens }
1252789Sahrens 
1253789Sahrens static int
12541760Seschrock zfs_ioc_pool_upgrade(zfs_cmd_t *zc)
12551760Seschrock {
12561760Seschrock 	spa_t *spa;
12571760Seschrock 	int error;
12581760Seschrock 
12592926Sek110237 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
12602926Sek110237 		return (error);
12612926Sek110237 
12625118Slling 	if (zc->zc_cookie < spa_version(spa) || zc->zc_cookie > SPA_VERSION) {
12635118Slling 		spa_close(spa, FTAG);
12645118Slling 		return (EINVAL);
12655118Slling 	}
12665118Slling 
12675094Slling 	spa_upgrade(spa, zc->zc_cookie);
12682926Sek110237 	spa_close(spa, FTAG);
12692926Sek110237 
12702926Sek110237 	return (error);
12712926Sek110237 }
12722926Sek110237 
12732926Sek110237 static int
12742926Sek110237 zfs_ioc_pool_get_history(zfs_cmd_t *zc)
12752926Sek110237 {
12762926Sek110237 	spa_t *spa;
12772926Sek110237 	char *hist_buf;
12782926Sek110237 	uint64_t size;
12792926Sek110237 	int error;
12802926Sek110237 
12812926Sek110237 	if ((size = zc->zc_history_len) == 0)
12822926Sek110237 		return (EINVAL);
12832926Sek110237 
12842926Sek110237 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
12852926Sek110237 		return (error);
12862926Sek110237 
12874577Sahrens 	if (spa_version(spa) < SPA_VERSION_ZPOOL_HISTORY) {
12883863Sek110237 		spa_close(spa, FTAG);
12893863Sek110237 		return (ENOTSUP);
12903863Sek110237 	}
12913863Sek110237 
12922926Sek110237 	hist_buf = kmem_alloc(size, KM_SLEEP);
12932926Sek110237 	if ((error = spa_history_get(spa, &zc->zc_history_offset,
12942926Sek110237 	    &zc->zc_history_len, hist_buf)) == 0) {
12959643SEric.Taylor@Sun.COM 		error = ddi_copyout(hist_buf,
12969643SEric.Taylor@Sun.COM 		    (void *)(uintptr_t)zc->zc_history,
12979643SEric.Taylor@Sun.COM 		    zc->zc_history_len, zc->zc_iflags);
12982926Sek110237 	}
12992926Sek110237 
13002926Sek110237 	spa_close(spa, FTAG);
13012926Sek110237 	kmem_free(hist_buf, size);
13022926Sek110237 	return (error);
13032926Sek110237 }
13042926Sek110237 
13052926Sek110237 static int
13063444Sek110237 zfs_ioc_dsobj_to_dsname(zfs_cmd_t *zc)
13073444Sek110237 {
13083444Sek110237 	int error;
13093444Sek110237 
13103912Slling 	if (error = dsl_dsobj_to_dsname(zc->zc_name, zc->zc_obj, zc->zc_value))
13113444Sek110237 		return (error);
13123444Sek110237 
13133444Sek110237 	return (0);
13143444Sek110237 }
13153444Sek110237 
131610298SMatthew.Ahrens@Sun.COM /*
131710298SMatthew.Ahrens@Sun.COM  * inputs:
131810298SMatthew.Ahrens@Sun.COM  * zc_name		name of filesystem
131910298SMatthew.Ahrens@Sun.COM  * zc_obj		object to find
132010298SMatthew.Ahrens@Sun.COM  *
132110298SMatthew.Ahrens@Sun.COM  * outputs:
132210298SMatthew.Ahrens@Sun.COM  * zc_value		name of object
132310298SMatthew.Ahrens@Sun.COM  */
13243444Sek110237 static int
13253444Sek110237 zfs_ioc_obj_to_path(zfs_cmd_t *zc)
13263444Sek110237 {
132710298SMatthew.Ahrens@Sun.COM 	objset_t *os;
13283444Sek110237 	int error;
13293444Sek110237 
133010298SMatthew.Ahrens@Sun.COM 	/* XXX reading from objset not owned */
133110298SMatthew.Ahrens@Sun.COM 	if ((error = dmu_objset_hold(zc->zc_name, FTAG, &os)) != 0)
13323444Sek110237 		return (error);
133310298SMatthew.Ahrens@Sun.COM 	if (dmu_objset_type(os) != DMU_OST_ZFS) {
133410298SMatthew.Ahrens@Sun.COM 		dmu_objset_rele(os, FTAG);
133510298SMatthew.Ahrens@Sun.COM 		return (EINVAL);
133610298SMatthew.Ahrens@Sun.COM 	}
133710298SMatthew.Ahrens@Sun.COM 	error = zfs_obj_to_path(os, zc->zc_obj, zc->zc_value,
13383444Sek110237 	    sizeof (zc->zc_value));
133910298SMatthew.Ahrens@Sun.COM 	dmu_objset_rele(os, FTAG);
13403444Sek110237 
13413444Sek110237 	return (error);
13423444Sek110237 }
13433444Sek110237 
13443444Sek110237 static int
1345789Sahrens zfs_ioc_vdev_add(zfs_cmd_t *zc)
1346789Sahrens {
1347789Sahrens 	spa_t *spa;
1348789Sahrens 	int error;
13496423Sgw25295 	nvlist_t *config, **l2cache, **spares;
13506423Sgw25295 	uint_t nl2cache = 0, nspares = 0;
1351789Sahrens 
1352789Sahrens 	error = spa_open(zc->zc_name, &spa, FTAG);
1353789Sahrens 	if (error != 0)
1354789Sahrens 		return (error);
1355789Sahrens 
13565450Sbrendan 	error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size,
13579643SEric.Taylor@Sun.COM 	    zc->zc_iflags, &config);
13585450Sbrendan 	(void) nvlist_lookup_nvlist_array(config, ZPOOL_CONFIG_L2CACHE,
13595450Sbrendan 	    &l2cache, &nl2cache);
13605450Sbrendan 
13616423Sgw25295 	(void) nvlist_lookup_nvlist_array(config, ZPOOL_CONFIG_SPARES,
13626423Sgw25295 	    &spares, &nspares);
13636423Sgw25295 
13643912Slling 	/*
13653912Slling 	 * A root pool with concatenated devices is not supported.
13666423Sgw25295 	 * Thus, can not add a device to a root pool.
13676423Sgw25295 	 *
13686423Sgw25295 	 * Intent log device can not be added to a rootpool because
13696423Sgw25295 	 * during mountroot, zil is replayed, a seperated log device
13706423Sgw25295 	 * can not be accessed during the mountroot time.
13716423Sgw25295 	 *
13726423Sgw25295 	 * l2cache and spare devices are ok to be added to a rootpool.
13733912Slling 	 */
137410922SJeff.Bonwick@Sun.COM 	if (spa_bootfs(spa) != 0 && nl2cache == 0 && nspares == 0) {
13753912Slling 		spa_close(spa, FTAG);
13763912Slling 		return (EDOM);
13773912Slling 	}
13783912Slling 
13795450Sbrendan 	if (error == 0) {
1380789Sahrens 		error = spa_vdev_add(spa, config);
1381789Sahrens 		nvlist_free(config);
1382789Sahrens 	}
1383789Sahrens 	spa_close(spa, FTAG);
1384789Sahrens 	return (error);
1385789Sahrens }
1386789Sahrens 
1387789Sahrens static int
1388789Sahrens zfs_ioc_vdev_remove(zfs_cmd_t *zc)
1389789Sahrens {
13902082Seschrock 	spa_t *spa;
13912082Seschrock 	int error;
13922082Seschrock 
13932082Seschrock 	error = spa_open(zc->zc_name, &spa, FTAG);
13942082Seschrock 	if (error != 0)
13952082Seschrock 		return (error);
13962082Seschrock 	error = spa_vdev_remove(spa, zc->zc_guid, B_FALSE);
13972082Seschrock 	spa_close(spa, FTAG);
13982082Seschrock 	return (error);
1399789Sahrens }
1400789Sahrens 
1401789Sahrens static int
14024451Seschrock zfs_ioc_vdev_set_state(zfs_cmd_t *zc)
1403789Sahrens {
1404789Sahrens 	spa_t *spa;
1405789Sahrens 	int error;
14064451Seschrock 	vdev_state_t newstate = VDEV_STATE_UNKNOWN;
1407789Sahrens 
14082926Sek110237 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
1409789Sahrens 		return (error);
14104451Seschrock 	switch (zc->zc_cookie) {
14114451Seschrock 	case VDEV_STATE_ONLINE:
14124451Seschrock 		error = vdev_online(spa, zc->zc_guid, zc->zc_obj, &newstate);
14134451Seschrock 		break;
14144451Seschrock 
14154451Seschrock 	case VDEV_STATE_OFFLINE:
14164451Seschrock 		error = vdev_offline(spa, zc->zc_guid, zc->zc_obj);
14174451Seschrock 		break;
1418789Sahrens 
14194451Seschrock 	case VDEV_STATE_FAULTED:
142010817SEric.Schrock@Sun.COM 		if (zc->zc_obj != VDEV_AUX_ERR_EXCEEDED &&
142110817SEric.Schrock@Sun.COM 		    zc->zc_obj != VDEV_AUX_EXTERNAL)
142210817SEric.Schrock@Sun.COM 			zc->zc_obj = VDEV_AUX_ERR_EXCEEDED;
142310817SEric.Schrock@Sun.COM 
142410817SEric.Schrock@Sun.COM 		error = vdev_fault(spa, zc->zc_guid, zc->zc_obj);
14254451Seschrock 		break;
1426789Sahrens 
14274451Seschrock 	case VDEV_STATE_DEGRADED:
142810817SEric.Schrock@Sun.COM 		if (zc->zc_obj != VDEV_AUX_ERR_EXCEEDED &&
142910817SEric.Schrock@Sun.COM 		    zc->zc_obj != VDEV_AUX_EXTERNAL)
143010817SEric.Schrock@Sun.COM 			zc->zc_obj = VDEV_AUX_ERR_EXCEEDED;
143110817SEric.Schrock@Sun.COM 
143210817SEric.Schrock@Sun.COM 		error = vdev_degrade(spa, zc->zc_guid, zc->zc_obj);
14334451Seschrock 		break;
14344451Seschrock 
14354451Seschrock 	default:
14364451Seschrock 		error = EINVAL;
14374451Seschrock 	}
14384451Seschrock 	zc->zc_cookie = newstate;
1439789Sahrens 	spa_close(spa, FTAG);
1440789Sahrens 	return (error);
1441789Sahrens }
1442789Sahrens 
1443789Sahrens static int
1444789Sahrens zfs_ioc_vdev_attach(zfs_cmd_t *zc)
1445789Sahrens {
1446789Sahrens 	spa_t *spa;
1447789Sahrens 	int replacing = zc->zc_cookie;
1448789Sahrens 	nvlist_t *config;
1449789Sahrens 	int error;
1450789Sahrens 
14512926Sek110237 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
1452789Sahrens 		return (error);
1453789Sahrens 
14545094Slling 	if ((error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size,
14559643SEric.Taylor@Sun.COM 	    zc->zc_iflags, &config)) == 0) {
14561544Seschrock 		error = spa_vdev_attach(spa, zc->zc_guid, config, replacing);
1457789Sahrens 		nvlist_free(config);
1458789Sahrens 	}
1459789Sahrens 
1460789Sahrens 	spa_close(spa, FTAG);
1461789Sahrens 	return (error);
1462789Sahrens }
1463789Sahrens 
1464789Sahrens static int
1465789Sahrens zfs_ioc_vdev_detach(zfs_cmd_t *zc)
1466789Sahrens {
1467789Sahrens 	spa_t *spa;
1468789Sahrens 	int error;
1469789Sahrens 
14702926Sek110237 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
1471789Sahrens 		return (error);
1472789Sahrens 
14738241SJeff.Bonwick@Sun.COM 	error = spa_vdev_detach(spa, zc->zc_guid, 0, B_FALSE);
1474789Sahrens 
1475789Sahrens 	spa_close(spa, FTAG);
1476789Sahrens 	return (error);
1477789Sahrens }
1478789Sahrens 
1479789Sahrens static int
14801354Seschrock zfs_ioc_vdev_setpath(zfs_cmd_t *zc)
14811354Seschrock {
14821354Seschrock 	spa_t *spa;
14832676Seschrock 	char *path = zc->zc_value;
14841544Seschrock 	uint64_t guid = zc->zc_guid;
14851354Seschrock 	int error;
14861354Seschrock 
14871354Seschrock 	error = spa_open(zc->zc_name, &spa, FTAG);
14881354Seschrock 	if (error != 0)
14891354Seschrock 		return (error);
14901354Seschrock 
14911354Seschrock 	error = spa_vdev_setpath(spa, guid, path);
14921354Seschrock 	spa_close(spa, FTAG);
14931354Seschrock 	return (error);
14941354Seschrock }
14951354Seschrock 
14969425SEric.Schrock@Sun.COM static int
14979425SEric.Schrock@Sun.COM zfs_ioc_vdev_setfru(zfs_cmd_t *zc)
14989425SEric.Schrock@Sun.COM {
14999425SEric.Schrock@Sun.COM 	spa_t *spa;
15009425SEric.Schrock@Sun.COM 	char *fru = zc->zc_value;
15019425SEric.Schrock@Sun.COM 	uint64_t guid = zc->zc_guid;
15029425SEric.Schrock@Sun.COM 	int error;
15039425SEric.Schrock@Sun.COM 
15049425SEric.Schrock@Sun.COM 	error = spa_open(zc->zc_name, &spa, FTAG);
15059425SEric.Schrock@Sun.COM 	if (error != 0)
15069425SEric.Schrock@Sun.COM 		return (error);
15079425SEric.Schrock@Sun.COM 
15089425SEric.Schrock@Sun.COM 	error = spa_vdev_setfru(spa, guid, fru);
15099425SEric.Schrock@Sun.COM 	spa_close(spa, FTAG);
15109425SEric.Schrock@Sun.COM 	return (error);
15119425SEric.Schrock@Sun.COM }
15129425SEric.Schrock@Sun.COM 
15135367Sahrens /*
15145367Sahrens  * inputs:
15155367Sahrens  * zc_name		name of filesystem
15165367Sahrens  * zc_nvlist_dst_size	size of buffer for property nvlist
15175367Sahrens  *
15185367Sahrens  * outputs:
15195367Sahrens  * zc_objset_stats	stats
15205367Sahrens  * zc_nvlist_dst	property nvlist
15215367Sahrens  * zc_nvlist_dst_size	size of property nvlist
15225367Sahrens  */
15231354Seschrock static int
1524789Sahrens zfs_ioc_objset_stats(zfs_cmd_t *zc)
1525789Sahrens {
1526789Sahrens 	objset_t *os = NULL;
1527789Sahrens 	int error;
15281356Seschrock 	nvlist_t *nv;
1529789Sahrens 
153010298SMatthew.Ahrens@Sun.COM 	if (error = dmu_objset_hold(zc->zc_name, FTAG, &os))
1531789Sahrens 		return (error);
1532789Sahrens 
15332885Sahrens 	dmu_objset_fast_stat(os, &zc->zc_objset_stats);
1534789Sahrens 
15352856Snd150628 	if (zc->zc_nvlist_dst != 0 &&
153611022STom.Erickson@Sun.COM 	    (error = dsl_prop_get_all(os, &nv)) == 0) {
15372885Sahrens 		dmu_objset_stats(os, nv);
15383087Sahrens 		/*
15395147Srm160521 		 * NB: zvol_get_stats() will read the objset contents,
15403087Sahrens 		 * which we aren't supposed to do with a
15416689Smaybee 		 * DS_MODE_USER hold, because it could be
15423087Sahrens 		 * inconsistent.  So this is a bit of a workaround...
154310298SMatthew.Ahrens@Sun.COM 		 * XXX reading with out owning
15443087Sahrens 		 */
15454577Sahrens 		if (!zc->zc_objset_stats.dds_inconsistent) {
15464577Sahrens 			if (dmu_objset_type(os) == DMU_OST_ZVOL)
15474577Sahrens 				VERIFY(zvol_get_stats(os, nv) == 0);
15484577Sahrens 		}
15492676Seschrock 		error = put_nvlist(zc, nv);
15501356Seschrock 		nvlist_free(nv);
15511356Seschrock 	}
1552789Sahrens 
155310298SMatthew.Ahrens@Sun.COM 	dmu_objset_rele(os, FTAG);
1554789Sahrens 	return (error);
1555789Sahrens }
1556789Sahrens 
155711022STom.Erickson@Sun.COM /*
155811022STom.Erickson@Sun.COM  * inputs:
155911022STom.Erickson@Sun.COM  * zc_name		name of filesystem
156011022STom.Erickson@Sun.COM  * zc_nvlist_dst_size	size of buffer for property nvlist
156111022STom.Erickson@Sun.COM  *
156211022STom.Erickson@Sun.COM  * outputs:
156311022STom.Erickson@Sun.COM  * zc_nvlist_dst	received property nvlist
156411022STom.Erickson@Sun.COM  * zc_nvlist_dst_size	size of received property nvlist
156511022STom.Erickson@Sun.COM  *
156611022STom.Erickson@Sun.COM  * Gets received properties (distinct from local properties on or after
156711022STom.Erickson@Sun.COM  * SPA_VERSION_RECVD_PROPS) for callers who want to differentiate received from
156811022STom.Erickson@Sun.COM  * local property values.
156911022STom.Erickson@Sun.COM  */
157011022STom.Erickson@Sun.COM static int
157111022STom.Erickson@Sun.COM zfs_ioc_objset_recvd_props(zfs_cmd_t *zc)
157211022STom.Erickson@Sun.COM {
157311022STom.Erickson@Sun.COM 	objset_t *os = NULL;
157411022STom.Erickson@Sun.COM 	int error;
157511022STom.Erickson@Sun.COM 	nvlist_t *nv;
157611022STom.Erickson@Sun.COM 
157711022STom.Erickson@Sun.COM 	if (error = dmu_objset_hold(zc->zc_name, FTAG, &os))
157811022STom.Erickson@Sun.COM 		return (error);
157911022STom.Erickson@Sun.COM 
158011022STom.Erickson@Sun.COM 	/*
158111022STom.Erickson@Sun.COM 	 * Without this check, we would return local property values if the
158211022STom.Erickson@Sun.COM 	 * caller has not already received properties on or after
158311022STom.Erickson@Sun.COM 	 * SPA_VERSION_RECVD_PROPS.
158411022STom.Erickson@Sun.COM 	 */
158511022STom.Erickson@Sun.COM 	if (!dsl_prop_get_hasrecvd(os)) {
158611022STom.Erickson@Sun.COM 		dmu_objset_rele(os, FTAG);
158711022STom.Erickson@Sun.COM 		return (ENOTSUP);
158811022STom.Erickson@Sun.COM 	}
158911022STom.Erickson@Sun.COM 
159011022STom.Erickson@Sun.COM 	if (zc->zc_nvlist_dst != 0 &&
159111022STom.Erickson@Sun.COM 	    (error = dsl_prop_get_received(os, &nv)) == 0) {
159211022STom.Erickson@Sun.COM 		error = put_nvlist(zc, nv);
159311022STom.Erickson@Sun.COM 		nvlist_free(nv);
159411022STom.Erickson@Sun.COM 	}
159511022STom.Erickson@Sun.COM 
159611022STom.Erickson@Sun.COM 	dmu_objset_rele(os, FTAG);
159711022STom.Erickson@Sun.COM 	return (error);
159811022STom.Erickson@Sun.COM }
159911022STom.Erickson@Sun.COM 
16005498Stimh static int
16015498Stimh nvl_add_zplprop(objset_t *os, nvlist_t *props, zfs_prop_t prop)
16025498Stimh {
16035498Stimh 	uint64_t value;
16045498Stimh 	int error;
16055498Stimh 
16065498Stimh 	/*
16075498Stimh 	 * zfs_get_zplprop() will either find a value or give us
16085498Stimh 	 * the default value (if there is one).
16095498Stimh 	 */
16105498Stimh 	if ((error = zfs_get_zplprop(os, prop, &value)) != 0)
16115498Stimh 		return (error);
16125498Stimh 	VERIFY(nvlist_add_uint64(props, zfs_prop_to_name(prop), value) == 0);
16135498Stimh 	return (0);
16145498Stimh }
16155498Stimh 
16165498Stimh /*
16175498Stimh  * inputs:
16185498Stimh  * zc_name		name of filesystem
16195498Stimh  * zc_nvlist_dst_size	size of buffer for zpl property nvlist
16205498Stimh  *
16215498Stimh  * outputs:
16225498Stimh  * zc_nvlist_dst	zpl property nvlist
16235498Stimh  * zc_nvlist_dst_size	size of zpl property nvlist
16245498Stimh  */
16255498Stimh static int
16265498Stimh zfs_ioc_objset_zplprops(zfs_cmd_t *zc)
16275498Stimh {
16285498Stimh 	objset_t *os;
16295498Stimh 	int err;
16305498Stimh 
163110298SMatthew.Ahrens@Sun.COM 	/* XXX reading without owning */
163210298SMatthew.Ahrens@Sun.COM 	if (err = dmu_objset_hold(zc->zc_name, FTAG, &os))
16335498Stimh 		return (err);
16345498Stimh 
16355498Stimh 	dmu_objset_fast_stat(os, &zc->zc_objset_stats);
16365498Stimh 
16375498Stimh 	/*
16385498Stimh 	 * NB: nvl_add_zplprop() will read the objset contents,
16396689Smaybee 	 * which we aren't supposed to do with a DS_MODE_USER
16406689Smaybee 	 * hold, because it could be inconsistent.
16415498Stimh 	 */
16425498Stimh 	if (zc->zc_nvlist_dst != NULL &&
16435498Stimh 	    !zc->zc_objset_stats.dds_inconsistent &&
16445498Stimh 	    dmu_objset_type(os) == DMU_OST_ZFS) {
16455498Stimh 		nvlist_t *nv;
16465498Stimh 
16475498Stimh 		VERIFY(nvlist_alloc(&nv, NV_UNIQUE_NAME, KM_SLEEP) == 0);
16485498Stimh 		if ((err = nvl_add_zplprop(os, nv, ZFS_PROP_VERSION)) == 0 &&
16495498Stimh 		    (err = nvl_add_zplprop(os, nv, ZFS_PROP_NORMALIZE)) == 0 &&
16505498Stimh 		    (err = nvl_add_zplprop(os, nv, ZFS_PROP_UTF8ONLY)) == 0 &&
16515498Stimh 		    (err = nvl_add_zplprop(os, nv, ZFS_PROP_CASE)) == 0)
16525498Stimh 			err = put_nvlist(zc, nv);
16535498Stimh 		nvlist_free(nv);
16545498Stimh 	} else {
16555498Stimh 		err = ENOENT;
16565498Stimh 	}
165710298SMatthew.Ahrens@Sun.COM 	dmu_objset_rele(os, FTAG);
16585498Stimh 	return (err);
16595498Stimh }
16605498Stimh 
16619396SMatthew.Ahrens@Sun.COM static boolean_t
16629396SMatthew.Ahrens@Sun.COM dataset_name_hidden(const char *name)
16639396SMatthew.Ahrens@Sun.COM {
16649396SMatthew.Ahrens@Sun.COM 	/*
16659396SMatthew.Ahrens@Sun.COM 	 * Skip over datasets that are not visible in this zone,
16669396SMatthew.Ahrens@Sun.COM 	 * internal datasets (which have a $ in their name), and
16679396SMatthew.Ahrens@Sun.COM 	 * temporary datasets (which have a % in their name).
16689396SMatthew.Ahrens@Sun.COM 	 */
16699396SMatthew.Ahrens@Sun.COM 	if (strchr(name, '$') != NULL)
16709396SMatthew.Ahrens@Sun.COM 		return (B_TRUE);
16719396SMatthew.Ahrens@Sun.COM 	if (strchr(name, '%') != NULL)
16729396SMatthew.Ahrens@Sun.COM 		return (B_TRUE);
16739396SMatthew.Ahrens@Sun.COM 	if (!INGLOBALZONE(curproc) && !zone_dataset_visible(name, NULL))
16749396SMatthew.Ahrens@Sun.COM 		return (B_TRUE);
16759396SMatthew.Ahrens@Sun.COM 	return (B_FALSE);
16769396SMatthew.Ahrens@Sun.COM }
16779396SMatthew.Ahrens@Sun.COM 
16785367Sahrens /*
16795367Sahrens  * inputs:
16805367Sahrens  * zc_name		name of filesystem
16815367Sahrens  * zc_cookie		zap cursor
16825367Sahrens  * zc_nvlist_dst_size	size of buffer for property nvlist
16835367Sahrens  *
16845367Sahrens  * outputs:
16855367Sahrens  * zc_name		name of next filesystem
16869396SMatthew.Ahrens@Sun.COM  * zc_cookie		zap cursor
16875367Sahrens  * zc_objset_stats	stats
16885367Sahrens  * zc_nvlist_dst	property nvlist
16895367Sahrens  * zc_nvlist_dst_size	size of property nvlist
16905367Sahrens  */
1691789Sahrens static int
1692789Sahrens zfs_ioc_dataset_list_next(zfs_cmd_t *zc)
1693789Sahrens {
1694885Sahrens 	objset_t *os;
1695789Sahrens 	int error;
1696789Sahrens 	char *p;
1697789Sahrens 
169810298SMatthew.Ahrens@Sun.COM 	if (error = dmu_objset_hold(zc->zc_name, FTAG, &os)) {
1699885Sahrens 		if (error == ENOENT)
1700885Sahrens 			error = ESRCH;
1701885Sahrens 		return (error);
1702789Sahrens 	}
1703789Sahrens 
1704789Sahrens 	p = strrchr(zc->zc_name, '/');
1705789Sahrens 	if (p == NULL || p[1] != '\0')
1706789Sahrens 		(void) strlcat(zc->zc_name, "/", sizeof (zc->zc_name));
1707789Sahrens 	p = zc->zc_name + strlen(zc->zc_name);
1708789Sahrens 
17098697SRichard.Morris@Sun.COM 	/*
17108697SRichard.Morris@Sun.COM 	 * Pre-fetch the datasets.  dmu_objset_prefetch() always returns 0
17118697SRichard.Morris@Sun.COM 	 * but is not declared void because its called by dmu_objset_find().
17128697SRichard.Morris@Sun.COM 	 */
17138415SRichard.Morris@Sun.COM 	if (zc->zc_cookie == 0) {
17148415SRichard.Morris@Sun.COM 		uint64_t cookie = 0;
17158415SRichard.Morris@Sun.COM 		int len = sizeof (zc->zc_name) - (p - zc->zc_name);
17168415SRichard.Morris@Sun.COM 
17178415SRichard.Morris@Sun.COM 		while (dmu_dir_list_next(os, len, p, NULL, &cookie) == 0)
17188697SRichard.Morris@Sun.COM 			(void) dmu_objset_prefetch(p, NULL);
17198415SRichard.Morris@Sun.COM 	}
17208415SRichard.Morris@Sun.COM 
1721789Sahrens 	do {
1722885Sahrens 		error = dmu_dir_list_next(os,
1723885Sahrens 		    sizeof (zc->zc_name) - (p - zc->zc_name), p,
1724885Sahrens 		    NULL, &zc->zc_cookie);
1725789Sahrens 		if (error == ENOENT)
1726789Sahrens 			error = ESRCH;
172710588SEric.Taylor@Sun.COM 	} while (error == 0 && dataset_name_hidden(zc->zc_name) &&
172810588SEric.Taylor@Sun.COM 	    !(zc->zc_iflags & FKIOCTL));
172910298SMatthew.Ahrens@Sun.COM 	dmu_objset_rele(os, FTAG);
1730789Sahrens 
173110588SEric.Taylor@Sun.COM 	/*
173210588SEric.Taylor@Sun.COM 	 * If it's an internal dataset (ie. with a '$' in its name),
173310588SEric.Taylor@Sun.COM 	 * don't try to get stats for it, otherwise we'll return ENOENT.
173410588SEric.Taylor@Sun.COM 	 */
173510588SEric.Taylor@Sun.COM 	if (error == 0 && strchr(zc->zc_name, '$') == NULL)
1736885Sahrens 		error = zfs_ioc_objset_stats(zc); /* fill in the stats */
1737789Sahrens 	return (error);
1738789Sahrens }
1739789Sahrens 
17405367Sahrens /*
17415367Sahrens  * inputs:
17425367Sahrens  * zc_name		name of filesystem
17435367Sahrens  * zc_cookie		zap cursor
17445367Sahrens  * zc_nvlist_dst_size	size of buffer for property nvlist
17455367Sahrens  *
17465367Sahrens  * outputs:
17475367Sahrens  * zc_name		name of next snapshot
17485367Sahrens  * zc_objset_stats	stats
17495367Sahrens  * zc_nvlist_dst	property nvlist
17505367Sahrens  * zc_nvlist_dst_size	size of property nvlist
17515367Sahrens  */
1752789Sahrens static int
1753789Sahrens zfs_ioc_snapshot_list_next(zfs_cmd_t *zc)
1754789Sahrens {
1755885Sahrens 	objset_t *os;
1756789Sahrens 	int error;
1757789Sahrens 
175810474SRichard.Morris@Sun.COM 	if (zc->zc_cookie == 0)
175910474SRichard.Morris@Sun.COM 		(void) dmu_objset_find(zc->zc_name, dmu_objset_prefetch,
176010474SRichard.Morris@Sun.COM 		    NULL, DS_FIND_SNAPSHOTS);
176110474SRichard.Morris@Sun.COM 
176210298SMatthew.Ahrens@Sun.COM 	error = dmu_objset_hold(zc->zc_name, FTAG, &os);
17636689Smaybee 	if (error)
17646689Smaybee 		return (error == ENOENT ? ESRCH : error);
1765789Sahrens 
17661003Slling 	/*
17671003Slling 	 * A dataset name of maximum length cannot have any snapshots,
17681003Slling 	 * so exit immediately.
17691003Slling 	 */
17701003Slling 	if (strlcat(zc->zc_name, "@", sizeof (zc->zc_name)) >= MAXNAMELEN) {
177110298SMatthew.Ahrens@Sun.COM 		dmu_objset_rele(os, FTAG);
17721003Slling 		return (ESRCH);
1773789Sahrens 	}
1774789Sahrens 
1775885Sahrens 	error = dmu_snapshot_list_next(os,
1776885Sahrens 	    sizeof (zc->zc_name) - strlen(zc->zc_name),
17775663Sck153898 	    zc->zc_name + strlen(zc->zc_name), NULL, &zc->zc_cookie, NULL);
177810298SMatthew.Ahrens@Sun.COM 	dmu_objset_rele(os, FTAG);
1779885Sahrens 	if (error == 0)
1780885Sahrens 		error = zfs_ioc_objset_stats(zc); /* fill in the stats */
17816689Smaybee 	else if (error == ENOENT)
17826689Smaybee 		error = ESRCH;
1783789Sahrens 
17845367Sahrens 	/* if we failed, undo the @ that we tacked on to zc_name */
17856689Smaybee 	if (error)
17865367Sahrens 		*strchr(zc->zc_name, '@') = '\0';
1787789Sahrens 	return (error);
1788789Sahrens }
1789789Sahrens 
179011022STom.Erickson@Sun.COM static int
179111022STom.Erickson@Sun.COM zfs_prop_set_userquota(const char *dsname, nvpair_t *pair)
179211022STom.Erickson@Sun.COM {
179311022STom.Erickson@Sun.COM 	const char *propname = nvpair_name(pair);
179411022STom.Erickson@Sun.COM 	uint64_t *valary;
179511022STom.Erickson@Sun.COM 	unsigned int vallen;
179611022STom.Erickson@Sun.COM 	const char *domain;
179711022STom.Erickson@Sun.COM 	zfs_userquota_prop_t type;
179811022STom.Erickson@Sun.COM 	uint64_t rid;
179911022STom.Erickson@Sun.COM 	uint64_t quota;
180011022STom.Erickson@Sun.COM 	zfsvfs_t *zfsvfs;
180111022STom.Erickson@Sun.COM 	int err;
180211022STom.Erickson@Sun.COM 
180311022STom.Erickson@Sun.COM 	if (nvpair_type(pair) == DATA_TYPE_NVLIST) {
180411022STom.Erickson@Sun.COM 		nvlist_t *attrs;
180511022STom.Erickson@Sun.COM 		VERIFY(nvpair_value_nvlist(pair, &attrs) == 0);
180611022STom.Erickson@Sun.COM 		VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE,
180711022STom.Erickson@Sun.COM 		    &pair) == 0);
180811022STom.Erickson@Sun.COM 	}
180911022STom.Erickson@Sun.COM 
181011022STom.Erickson@Sun.COM 	VERIFY(nvpair_value_uint64_array(pair, &valary, &vallen) == 0);
181111022STom.Erickson@Sun.COM 	VERIFY(vallen == 3);
181211022STom.Erickson@Sun.COM 	type = valary[0];
181311022STom.Erickson@Sun.COM 	rid = valary[1];
181411022STom.Erickson@Sun.COM 	quota = valary[2];
181511022STom.Erickson@Sun.COM 	/*
181611022STom.Erickson@Sun.COM 	 * The propname is encoded as
181711022STom.Erickson@Sun.COM 	 * userquota@<rid>-<domain>.
181811022STom.Erickson@Sun.COM 	 */
181911022STom.Erickson@Sun.COM 	domain = strchr(propname, '-') + 1;
182011022STom.Erickson@Sun.COM 
182111022STom.Erickson@Sun.COM 	err = zfsvfs_hold(dsname, FTAG, &zfsvfs);
182211022STom.Erickson@Sun.COM 	if (err == 0) {
182311022STom.Erickson@Sun.COM 		err = zfs_set_userquota(zfsvfs, type, domain, rid, quota);
182411022STom.Erickson@Sun.COM 		zfsvfs_rele(zfsvfs, FTAG);
182511022STom.Erickson@Sun.COM 	}
182611022STom.Erickson@Sun.COM 
182711022STom.Erickson@Sun.COM 	return (err);
182811022STom.Erickson@Sun.COM }
182911022STom.Erickson@Sun.COM 
183011022STom.Erickson@Sun.COM /*
183111022STom.Erickson@Sun.COM  * If the named property is one that has a special function to set its value,
183211022STom.Erickson@Sun.COM  * return 0 on success and a positive error code on failure; otherwise if it is
183311022STom.Erickson@Sun.COM  * not one of the special properties handled by this function, return -1.
183411022STom.Erickson@Sun.COM  *
183511022STom.Erickson@Sun.COM  * XXX: It would be better for callers of the properety interface if we handled
183611022STom.Erickson@Sun.COM  * these special cases in dsl_prop.c (in the dsl layer).
183711022STom.Erickson@Sun.COM  */
183811022STom.Erickson@Sun.COM static int
183911022STom.Erickson@Sun.COM zfs_prop_set_special(const char *dsname, zprop_source_t source,
184011022STom.Erickson@Sun.COM     nvpair_t *pair)
1841789Sahrens {
184211022STom.Erickson@Sun.COM 	const char *propname = nvpair_name(pair);
184311022STom.Erickson@Sun.COM 	zfs_prop_t prop = zfs_name_to_prop(propname);
184411022STom.Erickson@Sun.COM 	uint64_t intval;
184511022STom.Erickson@Sun.COM 	int err;
184611022STom.Erickson@Sun.COM 
184711022STom.Erickson@Sun.COM 	if (prop == ZPROP_INVAL) {
184811022STom.Erickson@Sun.COM 		if (zfs_prop_userquota(propname))
184911022STom.Erickson@Sun.COM 			return (zfs_prop_set_userquota(dsname, pair));
185011022STom.Erickson@Sun.COM 		return (-1);
185111022STom.Erickson@Sun.COM 	}
185211022STom.Erickson@Sun.COM 
185311022STom.Erickson@Sun.COM 	if (nvpair_type(pair) == DATA_TYPE_NVLIST) {
185411022STom.Erickson@Sun.COM 		nvlist_t *attrs;
185511022STom.Erickson@Sun.COM 		VERIFY(nvpair_value_nvlist(pair, &attrs) == 0);
185611022STom.Erickson@Sun.COM 		VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE,
185711022STom.Erickson@Sun.COM 		    &pair) == 0);
185811022STom.Erickson@Sun.COM 	}
185911022STom.Erickson@Sun.COM 
186011022STom.Erickson@Sun.COM 	if (zfs_prop_get_type(prop) == PROP_TYPE_STRING)
186111022STom.Erickson@Sun.COM 		return (-1);
186211022STom.Erickson@Sun.COM 
186311022STom.Erickson@Sun.COM 	VERIFY(0 == nvpair_value_uint64(pair, &intval));
186411022STom.Erickson@Sun.COM 
186511022STom.Erickson@Sun.COM 	switch (prop) {
186611022STom.Erickson@Sun.COM 	case ZFS_PROP_QUOTA:
186711022STom.Erickson@Sun.COM 		err = dsl_dir_set_quota(dsname, source, intval);
186811022STom.Erickson@Sun.COM 		break;
186911022STom.Erickson@Sun.COM 	case ZFS_PROP_REFQUOTA:
187011022STom.Erickson@Sun.COM 		err = dsl_dataset_set_quota(dsname, source, intval);
187111022STom.Erickson@Sun.COM 		break;
187211022STom.Erickson@Sun.COM 	case ZFS_PROP_RESERVATION:
187311022STom.Erickson@Sun.COM 		err = dsl_dir_set_reservation(dsname, source, intval);
187411022STom.Erickson@Sun.COM 		break;
187511022STom.Erickson@Sun.COM 	case ZFS_PROP_REFRESERVATION:
187611022STom.Erickson@Sun.COM 		err = dsl_dataset_set_reservation(dsname, source, intval);
187711022STom.Erickson@Sun.COM 		break;
187811022STom.Erickson@Sun.COM 	case ZFS_PROP_VOLSIZE:
187911022STom.Erickson@Sun.COM 		err = zvol_set_volsize(dsname, ddi_driver_major(zfs_dip),
188011022STom.Erickson@Sun.COM 		    intval);
188111022STom.Erickson@Sun.COM 		break;
188211022STom.Erickson@Sun.COM 	case ZFS_PROP_VERSION:
188311022STom.Erickson@Sun.COM 	{
188411022STom.Erickson@Sun.COM 		zfsvfs_t *zfsvfs;
188511022STom.Erickson@Sun.COM 
188611022STom.Erickson@Sun.COM 		if ((err = zfsvfs_hold(dsname, FTAG, &zfsvfs)) != 0)
188711022STom.Erickson@Sun.COM 			break;
188811022STom.Erickson@Sun.COM 
188911022STom.Erickson@Sun.COM 		err = zfs_set_version(zfsvfs, intval);
189011022STom.Erickson@Sun.COM 		zfsvfs_rele(zfsvfs, FTAG);
189111022STom.Erickson@Sun.COM 
189211022STom.Erickson@Sun.COM 		if (err == 0 && intval >= ZPL_VERSION_USERSPACE) {
189311147SGeorge.Wilson@Sun.COM 			zfs_cmd_t *zc;
189411147SGeorge.Wilson@Sun.COM 
189511147SGeorge.Wilson@Sun.COM 			zc = kmem_zalloc(sizeof (zfs_cmd_t), KM_SLEEP);
189611147SGeorge.Wilson@Sun.COM 			(void) strcpy(zc->zc_name, dsname);
189711147SGeorge.Wilson@Sun.COM 			(void) zfs_ioc_userspace_upgrade(zc);
189811147SGeorge.Wilson@Sun.COM 			kmem_free(zc, sizeof (zfs_cmd_t));
189911022STom.Erickson@Sun.COM 		}
190011022STom.Erickson@Sun.COM 		break;
190111022STom.Erickson@Sun.COM 	}
190211022STom.Erickson@Sun.COM 
190311022STom.Erickson@Sun.COM 	default:
190411022STom.Erickson@Sun.COM 		err = -1;
190511022STom.Erickson@Sun.COM 	}
190611022STom.Erickson@Sun.COM 
190711022STom.Erickson@Sun.COM 	return (err);
190811022STom.Erickson@Sun.COM }
190911022STom.Erickson@Sun.COM 
191011022STom.Erickson@Sun.COM /*
191111022STom.Erickson@Sun.COM  * This function is best effort. If it fails to set any of the given properties,
191211022STom.Erickson@Sun.COM  * it continues to set as many as it can and returns the first error
191311022STom.Erickson@Sun.COM  * encountered. If the caller provides a non-NULL errlist, it also gives the
191411022STom.Erickson@Sun.COM  * complete list of names of all the properties it failed to set along with the
191511022STom.Erickson@Sun.COM  * corresponding error numbers. The caller is responsible for freeing the
191611022STom.Erickson@Sun.COM  * returned errlist.
191711022STom.Erickson@Sun.COM  *
191811022STom.Erickson@Sun.COM  * If every property is set successfully, zero is returned and the list pointed
191911022STom.Erickson@Sun.COM  * at by errlist is NULL.
192011022STom.Erickson@Sun.COM  */
192111022STom.Erickson@Sun.COM int
192211022STom.Erickson@Sun.COM zfs_set_prop_nvlist(const char *dsname, zprop_source_t source, nvlist_t *nvl,
192311022STom.Erickson@Sun.COM     nvlist_t **errlist)
192411022STom.Erickson@Sun.COM {
192511022STom.Erickson@Sun.COM 	nvpair_t *pair;
192611022STom.Erickson@Sun.COM 	nvpair_t *propval;
192711045STom.Erickson@Sun.COM 	int rv = 0;
19282676Seschrock 	uint64_t intval;
19292676Seschrock 	char *strval;
19308697SRichard.Morris@Sun.COM 	nvlist_t *genericnvl;
193111022STom.Erickson@Sun.COM 	nvlist_t *errors;
193211022STom.Erickson@Sun.COM 	nvlist_t *retrynvl;
19334543Smarks 
19348697SRichard.Morris@Sun.COM 	VERIFY(nvlist_alloc(&genericnvl, NV_UNIQUE_NAME, KM_SLEEP) == 0);
193511022STom.Erickson@Sun.COM 	VERIFY(nvlist_alloc(&errors, NV_UNIQUE_NAME, KM_SLEEP) == 0);
193611022STom.Erickson@Sun.COM 	VERIFY(nvlist_alloc(&retrynvl, NV_UNIQUE_NAME, KM_SLEEP) == 0);
193711022STom.Erickson@Sun.COM 
193811022STom.Erickson@Sun.COM retry:
193911022STom.Erickson@Sun.COM 	pair = NULL;
194011022STom.Erickson@Sun.COM 	while ((pair = nvlist_next_nvpair(nvl, pair)) != NULL) {
194111022STom.Erickson@Sun.COM 		const char *propname = nvpair_name(pair);
19424670Sahrens 		zfs_prop_t prop = zfs_name_to_prop(propname);
1943*11181STom.Erickson@Sun.COM 		int err = 0;
19444543Smarks 
194511022STom.Erickson@Sun.COM 		/* decode the property value */
194611022STom.Erickson@Sun.COM 		propval = pair;
194711022STom.Erickson@Sun.COM 		if (nvpair_type(pair) == DATA_TYPE_NVLIST) {
194811022STom.Erickson@Sun.COM 			nvlist_t *attrs;
194911022STom.Erickson@Sun.COM 			VERIFY(nvpair_value_nvlist(pair, &attrs) == 0);
195011022STom.Erickson@Sun.COM 			VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE,
195111022STom.Erickson@Sun.COM 			    &propval) == 0);
19524543Smarks 		}
19532676Seschrock 
195411022STom.Erickson@Sun.COM 		/* Validate value type */
195511022STom.Erickson@Sun.COM 		if (prop == ZPROP_INVAL) {
195611022STom.Erickson@Sun.COM 			if (zfs_prop_user(propname)) {
195711022STom.Erickson@Sun.COM 				if (nvpair_type(propval) != DATA_TYPE_STRING)
195811022STom.Erickson@Sun.COM 					err = EINVAL;
195911022STom.Erickson@Sun.COM 			} else if (zfs_prop_userquota(propname)) {
196011022STom.Erickson@Sun.COM 				if (nvpair_type(propval) !=
196111022STom.Erickson@Sun.COM 				    DATA_TYPE_UINT64_ARRAY)
196211022STom.Erickson@Sun.COM 					err = EINVAL;
19639396SMatthew.Ahrens@Sun.COM 			}
196411022STom.Erickson@Sun.COM 		} else {
196511022STom.Erickson@Sun.COM 			if (nvpair_type(propval) == DATA_TYPE_STRING) {
196611022STom.Erickson@Sun.COM 				if (zfs_prop_get_type(prop) != PROP_TYPE_STRING)
196711022STom.Erickson@Sun.COM 					err = EINVAL;
196811022STom.Erickson@Sun.COM 			} else if (nvpair_type(propval) == DATA_TYPE_UINT64) {
19692885Sahrens 				const char *unused;
19702885Sahrens 
197111022STom.Erickson@Sun.COM 				VERIFY(nvpair_value_uint64(propval,
197211022STom.Erickson@Sun.COM 				    &intval) == 0);
19732676Seschrock 
19742676Seschrock 				switch (zfs_prop_get_type(prop)) {
19754787Sahrens 				case PROP_TYPE_NUMBER:
19762676Seschrock 					break;
19774787Sahrens 				case PROP_TYPE_STRING:
197811022STom.Erickson@Sun.COM 					err = EINVAL;
197911022STom.Erickson@Sun.COM 					break;
19804787Sahrens 				case PROP_TYPE_INDEX:
19812717Seschrock 					if (zfs_prop_index_to_string(prop,
198211022STom.Erickson@Sun.COM 					    intval, &unused) != 0)
198311022STom.Erickson@Sun.COM 						err = EINVAL;
19842676Seschrock 					break;
19852676Seschrock 				default:
19864577Sahrens 					cmn_err(CE_PANIC,
19874577Sahrens 					    "unknown property type");
19882676Seschrock 				}
19892676Seschrock 			} else {
199011022STom.Erickson@Sun.COM 				err = EINVAL;
199111022STom.Erickson@Sun.COM 			}
199211022STom.Erickson@Sun.COM 		}
199311022STom.Erickson@Sun.COM 
199411022STom.Erickson@Sun.COM 		/* Validate permissions */
199511022STom.Erickson@Sun.COM 		if (err == 0)
199611022STom.Erickson@Sun.COM 			err = zfs_check_settable(dsname, pair, CRED());
199711022STom.Erickson@Sun.COM 
199811022STom.Erickson@Sun.COM 		if (err == 0) {
199911022STom.Erickson@Sun.COM 			err = zfs_prop_set_special(dsname, source, pair);
200011022STom.Erickson@Sun.COM 			if (err == -1) {
200111022STom.Erickson@Sun.COM 				/*
200211022STom.Erickson@Sun.COM 				 * For better performance we build up a list of
200311022STom.Erickson@Sun.COM 				 * properties to set in a single transaction.
200411022STom.Erickson@Sun.COM 				 */
200511022STom.Erickson@Sun.COM 				err = nvlist_add_nvpair(genericnvl, pair);
200611022STom.Erickson@Sun.COM 			} else if (err != 0 && nvl != retrynvl) {
200711022STom.Erickson@Sun.COM 				/*
200811022STom.Erickson@Sun.COM 				 * This may be a spurious error caused by
200911022STom.Erickson@Sun.COM 				 * receiving quota and reservation out of order.
201011022STom.Erickson@Sun.COM 				 * Try again in a second pass.
201111022STom.Erickson@Sun.COM 				 */
201211022STom.Erickson@Sun.COM 				err = nvlist_add_nvpair(retrynvl, pair);
20132676Seschrock 			}
201411022STom.Erickson@Sun.COM 		}
201511022STom.Erickson@Sun.COM 
201611022STom.Erickson@Sun.COM 		if (err != 0)
201711022STom.Erickson@Sun.COM 			VERIFY(nvlist_add_int32(errors, propname, err) == 0);
201811022STom.Erickson@Sun.COM 	}
201911022STom.Erickson@Sun.COM 
202011022STom.Erickson@Sun.COM 	if (nvl != retrynvl && !nvlist_empty(retrynvl)) {
202111022STom.Erickson@Sun.COM 		nvl = retrynvl;
202211022STom.Erickson@Sun.COM 		goto retry;
202311022STom.Erickson@Sun.COM 	}
202411022STom.Erickson@Sun.COM 
202511022STom.Erickson@Sun.COM 	if (!nvlist_empty(genericnvl) &&
202611022STom.Erickson@Sun.COM 	    dsl_props_set(dsname, source, genericnvl) != 0) {
202711022STom.Erickson@Sun.COM 		/*
202811022STom.Erickson@Sun.COM 		 * If this fails, we still want to set as many properties as we
202911022STom.Erickson@Sun.COM 		 * can, so try setting them individually.
203011022STom.Erickson@Sun.COM 		 */
203111022STom.Erickson@Sun.COM 		pair = NULL;
203211022STom.Erickson@Sun.COM 		while ((pair = nvlist_next_nvpair(genericnvl, pair)) != NULL) {
203311022STom.Erickson@Sun.COM 			const char *propname = nvpair_name(pair);
2034*11181STom.Erickson@Sun.COM 			int err = 0;
203511022STom.Erickson@Sun.COM 
203611022STom.Erickson@Sun.COM 			propval = pair;
203711022STom.Erickson@Sun.COM 			if (nvpair_type(pair) == DATA_TYPE_NVLIST) {
203811022STom.Erickson@Sun.COM 				nvlist_t *attrs;
203911022STom.Erickson@Sun.COM 				VERIFY(nvpair_value_nvlist(pair, &attrs) == 0);
204011022STom.Erickson@Sun.COM 				VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE,
204111022STom.Erickson@Sun.COM 				    &propval) == 0);
204211022STom.Erickson@Sun.COM 			}
204311022STom.Erickson@Sun.COM 
204411022STom.Erickson@Sun.COM 			if (nvpair_type(propval) == DATA_TYPE_STRING) {
204511022STom.Erickson@Sun.COM 				VERIFY(nvpair_value_string(propval,
204611022STom.Erickson@Sun.COM 				    &strval) == 0);
204711022STom.Erickson@Sun.COM 				err = dsl_prop_set(dsname, propname, source, 1,
204811022STom.Erickson@Sun.COM 				    strlen(strval) + 1, strval);
204911022STom.Erickson@Sun.COM 			} else {
205011022STom.Erickson@Sun.COM 				VERIFY(nvpair_value_uint64(propval,
205111022STom.Erickson@Sun.COM 				    &intval) == 0);
205211022STom.Erickson@Sun.COM 				err = dsl_prop_set(dsname, propname, source, 8,
205311022STom.Erickson@Sun.COM 				    1, &intval);
205411022STom.Erickson@Sun.COM 			}
205511022STom.Erickson@Sun.COM 
205611022STom.Erickson@Sun.COM 			if (err != 0) {
205711022STom.Erickson@Sun.COM 				VERIFY(nvlist_add_int32(errors, propname,
205811022STom.Erickson@Sun.COM 				    err) == 0);
205911022STom.Erickson@Sun.COM 			}
20602676Seschrock 		}
20612676Seschrock 	}
206211022STom.Erickson@Sun.COM 	nvlist_free(genericnvl);
206311022STom.Erickson@Sun.COM 	nvlist_free(retrynvl);
206411022STom.Erickson@Sun.COM 
206511022STom.Erickson@Sun.COM 	if ((pair = nvlist_next_nvpair(errors, NULL)) == NULL) {
206611022STom.Erickson@Sun.COM 		nvlist_free(errors);
206711022STom.Erickson@Sun.COM 		errors = NULL;
206811022STom.Erickson@Sun.COM 	} else {
206911022STom.Erickson@Sun.COM 		VERIFY(nvpair_value_int32(pair, &rv) == 0);
20708697SRichard.Morris@Sun.COM 	}
207111022STom.Erickson@Sun.COM 
207211022STom.Erickson@Sun.COM 	if (errlist == NULL)
207311022STom.Erickson@Sun.COM 		nvlist_free(errors);
207411022STom.Erickson@Sun.COM 	else
207511022STom.Erickson@Sun.COM 		*errlist = errors;
207611022STom.Erickson@Sun.COM 
207711022STom.Erickson@Sun.COM 	return (rv);
2078789Sahrens }
2079789Sahrens 
20805367Sahrens /*
20819355SMatthew.Ahrens@Sun.COM  * Check that all the properties are valid user properties.
20829355SMatthew.Ahrens@Sun.COM  */
20839355SMatthew.Ahrens@Sun.COM static int
20849355SMatthew.Ahrens@Sun.COM zfs_check_userprops(char *fsname, nvlist_t *nvl)
20859355SMatthew.Ahrens@Sun.COM {
208611022STom.Erickson@Sun.COM 	nvpair_t *pair = NULL;
20879355SMatthew.Ahrens@Sun.COM 	int error = 0;
20889355SMatthew.Ahrens@Sun.COM 
208911022STom.Erickson@Sun.COM 	while ((pair = nvlist_next_nvpair(nvl, pair)) != NULL) {
209011022STom.Erickson@Sun.COM 		const char *propname = nvpair_name(pair);
20919355SMatthew.Ahrens@Sun.COM 		char *valstr;
20929355SMatthew.Ahrens@Sun.COM 
20939355SMatthew.Ahrens@Sun.COM 		if (!zfs_prop_user(propname) ||
209411022STom.Erickson@Sun.COM 		    nvpair_type(pair) != DATA_TYPE_STRING)
20959355SMatthew.Ahrens@Sun.COM 			return (EINVAL);
20969355SMatthew.Ahrens@Sun.COM 
20979355SMatthew.Ahrens@Sun.COM 		if (error = zfs_secpolicy_write_perms(fsname,
20989355SMatthew.Ahrens@Sun.COM 		    ZFS_DELEG_PERM_USERPROP, CRED()))
20999355SMatthew.Ahrens@Sun.COM 			return (error);
21009355SMatthew.Ahrens@Sun.COM 
21019355SMatthew.Ahrens@Sun.COM 		if (strlen(propname) >= ZAP_MAXNAMELEN)
21029355SMatthew.Ahrens@Sun.COM 			return (ENAMETOOLONG);
21039355SMatthew.Ahrens@Sun.COM 
210411022STom.Erickson@Sun.COM 		VERIFY(nvpair_value_string(pair, &valstr) == 0);
21059355SMatthew.Ahrens@Sun.COM 		if (strlen(valstr) >= ZAP_MAXVALUELEN)
21069355SMatthew.Ahrens@Sun.COM 			return (E2BIG);
21079355SMatthew.Ahrens@Sun.COM 	}
21089355SMatthew.Ahrens@Sun.COM 	return (0);
21099355SMatthew.Ahrens@Sun.COM }
21109355SMatthew.Ahrens@Sun.COM 
211111022STom.Erickson@Sun.COM static void
211211022STom.Erickson@Sun.COM props_skip(nvlist_t *props, nvlist_t *skipped, nvlist_t **newprops)
211311022STom.Erickson@Sun.COM {
211411022STom.Erickson@Sun.COM 	nvpair_t *pair;
211511022STom.Erickson@Sun.COM 
211611022STom.Erickson@Sun.COM 	VERIFY(nvlist_alloc(newprops, NV_UNIQUE_NAME, KM_SLEEP) == 0);
211711022STom.Erickson@Sun.COM 
211811022STom.Erickson@Sun.COM 	pair = NULL;
211911022STom.Erickson@Sun.COM 	while ((pair = nvlist_next_nvpair(props, pair)) != NULL) {
212011022STom.Erickson@Sun.COM 		if (nvlist_exists(skipped, nvpair_name(pair)))
212111022STom.Erickson@Sun.COM 			continue;
212211022STom.Erickson@Sun.COM 
212311022STom.Erickson@Sun.COM 		VERIFY(nvlist_add_nvpair(*newprops, pair) == 0);
212411022STom.Erickson@Sun.COM 	}
212511022STom.Erickson@Sun.COM }
212611022STom.Erickson@Sun.COM 
212711022STom.Erickson@Sun.COM static int
212811022STom.Erickson@Sun.COM clear_received_props(objset_t *os, const char *fs, nvlist_t *props,
212911022STom.Erickson@Sun.COM     nvlist_t *skipped)
213011022STom.Erickson@Sun.COM {
213111022STom.Erickson@Sun.COM 	int err = 0;
213211022STom.Erickson@Sun.COM 	nvlist_t *cleared_props = NULL;
213311022STom.Erickson@Sun.COM 	props_skip(props, skipped, &cleared_props);
213411022STom.Erickson@Sun.COM 	if (!nvlist_empty(cleared_props)) {
213511022STom.Erickson@Sun.COM 		/*
213611022STom.Erickson@Sun.COM 		 * Acts on local properties until the dataset has received
213711022STom.Erickson@Sun.COM 		 * properties at least once on or after SPA_VERSION_RECVD_PROPS.
213811022STom.Erickson@Sun.COM 		 */
213911022STom.Erickson@Sun.COM 		zprop_source_t flags = (ZPROP_SRC_NONE |
214011022STom.Erickson@Sun.COM 		    (dsl_prop_get_hasrecvd(os) ? ZPROP_SRC_RECEIVED : 0));
214111022STom.Erickson@Sun.COM 		err = zfs_set_prop_nvlist(fs, flags, cleared_props, NULL);
214211022STom.Erickson@Sun.COM 	}
214311022STom.Erickson@Sun.COM 	nvlist_free(cleared_props);
214411022STom.Erickson@Sun.COM 	return (err);
214511022STom.Erickson@Sun.COM }
214611022STom.Erickson@Sun.COM 
21479355SMatthew.Ahrens@Sun.COM /*
21485367Sahrens  * inputs:
21495367Sahrens  * zc_name		name of filesystem
21508697SRichard.Morris@Sun.COM  * zc_value		name of property to set
21515367Sahrens  * zc_nvlist_src{_size}	nvlist of properties to apply
215211022STom.Erickson@Sun.COM  * zc_cookie		received properties flag
21535367Sahrens  *
215411022STom.Erickson@Sun.COM  * outputs:
215511022STom.Erickson@Sun.COM  * zc_nvlist_dst{_size} error for each unapplied received property
21565367Sahrens  */
2157789Sahrens static int
21582676Seschrock zfs_ioc_set_prop(zfs_cmd_t *zc)
2159789Sahrens {
21602676Seschrock 	nvlist_t *nvl;
216111022STom.Erickson@Sun.COM 	boolean_t received = zc->zc_cookie;
216211022STom.Erickson@Sun.COM 	zprop_source_t source = (received ? ZPROP_SRC_RECEIVED :
216311022STom.Erickson@Sun.COM 	    ZPROP_SRC_LOCAL);
216411022STom.Erickson@Sun.COM 	nvlist_t *errors = NULL;
21652676Seschrock 	int error;
2166789Sahrens 
21675094Slling 	if ((error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
21689643SEric.Taylor@Sun.COM 	    zc->zc_iflags, &nvl)) != 0)
21692676Seschrock 		return (error);
21702676Seschrock 
217111022STom.Erickson@Sun.COM 	if (received) {
21727265Sahrens 		nvlist_t *origprops;
21737265Sahrens 		objset_t *os;
21747265Sahrens 
217510298SMatthew.Ahrens@Sun.COM 		if (dmu_objset_hold(zc->zc_name, FTAG, &os) == 0) {
217611022STom.Erickson@Sun.COM 			if (dsl_prop_get_received(os, &origprops) == 0) {
217711022STom.Erickson@Sun.COM 				(void) clear_received_props(os,
217811022STom.Erickson@Sun.COM 				    zc->zc_name, origprops, nvl);
21797265Sahrens 				nvlist_free(origprops);
21807265Sahrens 			}
218111022STom.Erickson@Sun.COM 
218211022STom.Erickson@Sun.COM 			dsl_prop_set_hasrecvd(os);
218310298SMatthew.Ahrens@Sun.COM 			dmu_objset_rele(os, FTAG);
21847265Sahrens 		}
21857265Sahrens 	}
21867265Sahrens 
218711022STom.Erickson@Sun.COM 	error = zfs_set_prop_nvlist(zc->zc_name, source, nvl, &errors);
218811022STom.Erickson@Sun.COM 
218911022STom.Erickson@Sun.COM 	if (zc->zc_nvlist_dst != NULL && errors != NULL) {
219011022STom.Erickson@Sun.COM 		(void) put_nvlist(zc, errors);
219111022STom.Erickson@Sun.COM 	}
219211022STom.Erickson@Sun.COM 
219311022STom.Erickson@Sun.COM 	nvlist_free(errors);
21942676Seschrock 	nvlist_free(nvl);
21952676Seschrock 	return (error);
2196789Sahrens }
2197789Sahrens 
21985367Sahrens /*
21995367Sahrens  * inputs:
22005367Sahrens  * zc_name		name of filesystem
22015367Sahrens  * zc_value		name of property to inherit
220211022STom.Erickson@Sun.COM  * zc_cookie		revert to received value if TRUE
22035367Sahrens  *
22045367Sahrens  * outputs:		none
22055367Sahrens  */
2206789Sahrens static int
22074849Sahrens zfs_ioc_inherit_prop(zfs_cmd_t *zc)
22084849Sahrens {
220911022STom.Erickson@Sun.COM 	const char *propname = zc->zc_value;
221011022STom.Erickson@Sun.COM 	zfs_prop_t prop = zfs_name_to_prop(propname);
221111022STom.Erickson@Sun.COM 	boolean_t received = zc->zc_cookie;
221211022STom.Erickson@Sun.COM 	zprop_source_t source = (received
221311022STom.Erickson@Sun.COM 	    ? ZPROP_SRC_NONE		/* revert to received value, if any */
221411022STom.Erickson@Sun.COM 	    : ZPROP_SRC_INHERITED);	/* explicitly inherit */
221511022STom.Erickson@Sun.COM 
221611022STom.Erickson@Sun.COM 	if (received) {
221711022STom.Erickson@Sun.COM 		nvlist_t *dummy;
221811022STom.Erickson@Sun.COM 		nvpair_t *pair;
221911022STom.Erickson@Sun.COM 		zprop_type_t type;
222011022STom.Erickson@Sun.COM 		int err;
222111022STom.Erickson@Sun.COM 
222211022STom.Erickson@Sun.COM 		/*
222311022STom.Erickson@Sun.COM 		 * zfs_prop_set_special() expects properties in the form of an
222411022STom.Erickson@Sun.COM 		 * nvpair with type info.
222511022STom.Erickson@Sun.COM 		 */
222611022STom.Erickson@Sun.COM 		if (prop == ZPROP_INVAL) {
222711022STom.Erickson@Sun.COM 			if (!zfs_prop_user(propname))
222811022STom.Erickson@Sun.COM 				return (EINVAL);
222911022STom.Erickson@Sun.COM 
223011022STom.Erickson@Sun.COM 			type = PROP_TYPE_STRING;
223111022STom.Erickson@Sun.COM 		} else {
223211022STom.Erickson@Sun.COM 			type = zfs_prop_get_type(prop);
223311022STom.Erickson@Sun.COM 		}
223411022STom.Erickson@Sun.COM 
223511022STom.Erickson@Sun.COM 		VERIFY(nvlist_alloc(&dummy, NV_UNIQUE_NAME, KM_SLEEP) == 0);
223611022STom.Erickson@Sun.COM 
223711022STom.Erickson@Sun.COM 		switch (type) {
223811022STom.Erickson@Sun.COM 		case PROP_TYPE_STRING:
223911022STom.Erickson@Sun.COM 			VERIFY(0 == nvlist_add_string(dummy, propname, ""));
224011022STom.Erickson@Sun.COM 			break;
224111022STom.Erickson@Sun.COM 		case PROP_TYPE_NUMBER:
224211022STom.Erickson@Sun.COM 		case PROP_TYPE_INDEX:
224311022STom.Erickson@Sun.COM 			VERIFY(0 == nvlist_add_uint64(dummy, propname, 0));
224411022STom.Erickson@Sun.COM 			break;
224511022STom.Erickson@Sun.COM 		default:
224611022STom.Erickson@Sun.COM 			nvlist_free(dummy);
224711022STom.Erickson@Sun.COM 			return (EINVAL);
224811022STom.Erickson@Sun.COM 		}
224911022STom.Erickson@Sun.COM 
225011022STom.Erickson@Sun.COM 		pair = nvlist_next_nvpair(dummy, NULL);
225111022STom.Erickson@Sun.COM 		err = zfs_prop_set_special(zc->zc_name, source, pair);
225211022STom.Erickson@Sun.COM 		nvlist_free(dummy);
225311022STom.Erickson@Sun.COM 		if (err != -1)
225411022STom.Erickson@Sun.COM 			return (err); /* special property already handled */
225511022STom.Erickson@Sun.COM 	} else {
225611022STom.Erickson@Sun.COM 		/*
225711022STom.Erickson@Sun.COM 		 * Only check this in the non-received case. We want to allow
225811022STom.Erickson@Sun.COM 		 * 'inherit -S' to revert non-inheritable properties like quota
225911022STom.Erickson@Sun.COM 		 * and reservation to the received or default values even though
226011022STom.Erickson@Sun.COM 		 * they are not considered inheritable.
226111022STom.Erickson@Sun.COM 		 */
226211022STom.Erickson@Sun.COM 		if (prop != ZPROP_INVAL && !zfs_prop_inheritable(prop))
226311022STom.Erickson@Sun.COM 			return (EINVAL);
226411022STom.Erickson@Sun.COM 	}
226511022STom.Erickson@Sun.COM 
22664849Sahrens 	/* the property name has been validated by zfs_secpolicy_inherit() */
226711022STom.Erickson@Sun.COM 	return (dsl_prop_set(zc->zc_name, zc->zc_value, source, 0, 0, NULL));
22684849Sahrens }
22694849Sahrens 
22704849Sahrens static int
22714098Slling zfs_ioc_pool_set_props(zfs_cmd_t *zc)
22723912Slling {
22735094Slling 	nvlist_t *props;
22743912Slling 	spa_t *spa;
22755094Slling 	int error;
227611022STom.Erickson@Sun.COM 	nvpair_t *pair;
227711022STom.Erickson@Sun.COM 
227811022STom.Erickson@Sun.COM 	if (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
227911022STom.Erickson@Sun.COM 	    zc->zc_iflags, &props))
22803912Slling 		return (error);
22813912Slling 
22828525SEric.Schrock@Sun.COM 	/*
22838525SEric.Schrock@Sun.COM 	 * If the only property is the configfile, then just do a spa_lookup()
22848525SEric.Schrock@Sun.COM 	 * to handle the faulted case.
22858525SEric.Schrock@Sun.COM 	 */
228611022STom.Erickson@Sun.COM 	pair = nvlist_next_nvpair(props, NULL);
228711022STom.Erickson@Sun.COM 	if (pair != NULL && strcmp(nvpair_name(pair),
22888525SEric.Schrock@Sun.COM 	    zpool_prop_to_name(ZPOOL_PROP_CACHEFILE)) == 0 &&
228911022STom.Erickson@Sun.COM 	    nvlist_next_nvpair(props, pair) == NULL) {
22908525SEric.Schrock@Sun.COM 		mutex_enter(&spa_namespace_lock);
22918525SEric.Schrock@Sun.COM 		if ((spa = spa_lookup(zc->zc_name)) != NULL) {
22928525SEric.Schrock@Sun.COM 			spa_configfile_set(spa, props, B_FALSE);
22938525SEric.Schrock@Sun.COM 			spa_config_sync(spa, B_FALSE, B_TRUE);
22948525SEric.Schrock@Sun.COM 		}
22958525SEric.Schrock@Sun.COM 		mutex_exit(&spa_namespace_lock);
229610672SEric.Schrock@Sun.COM 		if (spa != NULL) {
229710672SEric.Schrock@Sun.COM 			nvlist_free(props);
22988525SEric.Schrock@Sun.COM 			return (0);
229910672SEric.Schrock@Sun.COM 		}
23008525SEric.Schrock@Sun.COM 	}
23018525SEric.Schrock@Sun.COM 
23023912Slling 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) {
23035094Slling 		nvlist_free(props);
23043912Slling 		return (error);
23053912Slling 	}
23063912Slling 
23075094Slling 	error = spa_prop_set(spa, props);
23083912Slling 
23095094Slling 	nvlist_free(props);
23103912Slling 	spa_close(spa, FTAG);
23113912Slling 
23123912Slling 	return (error);
23133912Slling }
23143912Slling 
23153912Slling static int
23164098Slling zfs_ioc_pool_get_props(zfs_cmd_t *zc)
23173912Slling {
23183912Slling 	spa_t *spa;
23193912Slling 	int error;
23203912Slling 	nvlist_t *nvp = NULL;
23213912Slling 
23228525SEric.Schrock@Sun.COM 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) {
23238525SEric.Schrock@Sun.COM 		/*
23248525SEric.Schrock@Sun.COM 		 * If the pool is faulted, there may be properties we can still
23258525SEric.Schrock@Sun.COM 		 * get (such as altroot and cachefile), so attempt to get them
23268525SEric.Schrock@Sun.COM 		 * anyway.
23278525SEric.Schrock@Sun.COM 		 */
23288525SEric.Schrock@Sun.COM 		mutex_enter(&spa_namespace_lock);
23298525SEric.Schrock@Sun.COM 		if ((spa = spa_lookup(zc->zc_name)) != NULL)
23308525SEric.Schrock@Sun.COM 			error = spa_prop_get(spa, &nvp);
23318525SEric.Schrock@Sun.COM 		mutex_exit(&spa_namespace_lock);
23328525SEric.Schrock@Sun.COM 	} else {
23338525SEric.Schrock@Sun.COM 		error = spa_prop_get(spa, &nvp);
23348525SEric.Schrock@Sun.COM 		spa_close(spa, FTAG);
23358525SEric.Schrock@Sun.COM 	}
23363912Slling 
23373912Slling 	if (error == 0 && zc->zc_nvlist_dst != NULL)
23383912Slling 		error = put_nvlist(zc, nvp);
23393912Slling 	else
23403912Slling 		error = EFAULT;
23413912Slling 
23428525SEric.Schrock@Sun.COM 	nvlist_free(nvp);
23433912Slling 	return (error);
23443912Slling }
23453912Slling 
23463912Slling static int
23474543Smarks zfs_ioc_iscsi_perm_check(zfs_cmd_t *zc)
23484543Smarks {
23494543Smarks 	nvlist_t *nvp;
23504543Smarks 	int error;
23514543Smarks 	uint32_t uid;
23524543Smarks 	uint32_t gid;
23534543Smarks 	uint32_t *groups;
23544543Smarks 	uint_t group_cnt;
23554543Smarks 	cred_t	*usercred;
23564543Smarks 
23575094Slling 	if ((error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
23589643SEric.Taylor@Sun.COM 	    zc->zc_iflags, &nvp)) != 0) {
23594543Smarks 		return (error);
23604543Smarks 	}
23614543Smarks 
23624543Smarks 	if ((error = nvlist_lookup_uint32(nvp,
23634543Smarks 	    ZFS_DELEG_PERM_UID, &uid)) != 0) {
23644543Smarks 		nvlist_free(nvp);
23654543Smarks 		return (EPERM);
23664543Smarks 	}
23674543Smarks 
23684543Smarks 	if ((error = nvlist_lookup_uint32(nvp,
23694543Smarks 	    ZFS_DELEG_PERM_GID, &gid)) != 0) {
23704543Smarks 		nvlist_free(nvp);
23714543Smarks 		return (EPERM);
23724543Smarks 	}
23734543Smarks 
23744543Smarks 	if ((error = nvlist_lookup_uint32_array(nvp, ZFS_DELEG_PERM_GROUPS,
23754543Smarks 	    &groups, &group_cnt)) != 0) {
23764543Smarks 		nvlist_free(nvp);
23774543Smarks 		return (EPERM);
23784543Smarks 	}
23794543Smarks 	usercred = cralloc();
23804543Smarks 	if ((crsetugid(usercred, uid, gid) != 0) ||
23814543Smarks 	    (crsetgroups(usercred, group_cnt, (gid_t *)groups) != 0)) {
23824543Smarks 		nvlist_free(nvp);
23834543Smarks 		crfree(usercred);
23844543Smarks 		return (EPERM);
23854543Smarks 	}
23864543Smarks 	nvlist_free(nvp);
23874543Smarks 	error = dsl_deleg_access(zc->zc_name,
23884787Sahrens 	    zfs_prop_to_name(ZFS_PROP_SHAREISCSI), usercred);
23894543Smarks 	crfree(usercred);
23904543Smarks 	return (error);
23914543Smarks }
23924543Smarks 
23935367Sahrens /*
23945367Sahrens  * inputs:
23955367Sahrens  * zc_name		name of filesystem
23965367Sahrens  * zc_nvlist_src{_size}	nvlist of delegated permissions
23975367Sahrens  * zc_perm_action	allow/unallow flag
23985367Sahrens  *
23995367Sahrens  * outputs:		none
24005367Sahrens  */
24014543Smarks static int
24024543Smarks zfs_ioc_set_fsacl(zfs_cmd_t *zc)
24034543Smarks {
24044543Smarks 	int error;
24054543Smarks 	nvlist_t *fsaclnv = NULL;
24064543Smarks 
24075094Slling 	if ((error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
24089643SEric.Taylor@Sun.COM 	    zc->zc_iflags, &fsaclnv)) != 0)
24094543Smarks 		return (error);
24104543Smarks 
24114543Smarks 	/*
24124543Smarks 	 * Verify nvlist is constructed correctly
24134543Smarks 	 */
24144543Smarks 	if ((error = zfs_deleg_verify_nvlist(fsaclnv)) != 0) {
24154543Smarks 		nvlist_free(fsaclnv);
24164543Smarks 		return (EINVAL);
24174543Smarks 	}
24184543Smarks 
24194543Smarks 	/*
24204543Smarks 	 * If we don't have PRIV_SYS_MOUNT, then validate
24214543Smarks 	 * that user is allowed to hand out each permission in
24224543Smarks 	 * the nvlist(s)
24234543Smarks 	 */
24244543Smarks 
24254787Sahrens 	error = secpolicy_zfs(CRED());
24264543Smarks 	if (error) {
24274787Sahrens 		if (zc->zc_perm_action == B_FALSE) {
24284787Sahrens 			error = dsl_deleg_can_allow(zc->zc_name,
24294787Sahrens 			    fsaclnv, CRED());
24304787Sahrens 		} else {
24314787Sahrens 			error = dsl_deleg_can_unallow(zc->zc_name,
24324787Sahrens 			    fsaclnv, CRED());
24334787Sahrens 		}
24344543Smarks 	}
24354543Smarks 
24364543Smarks 	if (error == 0)
24374543Smarks 		error = dsl_deleg_set(zc->zc_name, fsaclnv, zc->zc_perm_action);
24384543Smarks 
24394543Smarks 	nvlist_free(fsaclnv);
24404543Smarks 	return (error);
24414543Smarks }
24424543Smarks 
24435367Sahrens /*
24445367Sahrens  * inputs:
24455367Sahrens  * zc_name		name of filesystem
24465367Sahrens  *
24475367Sahrens  * outputs:
24485367Sahrens  * zc_nvlist_src{_size}	nvlist of delegated permissions
24495367Sahrens  */
24504543Smarks static int
24514543Smarks zfs_ioc_get_fsacl(zfs_cmd_t *zc)
24524543Smarks {
24534543Smarks 	nvlist_t *nvp;
24544543Smarks 	int error;
24554543Smarks 
24564543Smarks 	if ((error = dsl_deleg_get(zc->zc_name, &nvp)) == 0) {
24574543Smarks 		error = put_nvlist(zc, nvp);
24584543Smarks 		nvlist_free(nvp);
24594543Smarks 	}
24604543Smarks 
24614543Smarks 	return (error);
24624543Smarks }
24634543Smarks 
24645367Sahrens /*
2465789Sahrens  * Search the vfs list for a specified resource.  Returns a pointer to it
2466789Sahrens  * or NULL if no suitable entry is found. The caller of this routine
2467789Sahrens  * is responsible for releasing the returned vfs pointer.
2468789Sahrens  */
2469789Sahrens static vfs_t *
2470789Sahrens zfs_get_vfs(const char *resource)
2471789Sahrens {
2472789Sahrens 	struct vfs *vfsp;
2473789Sahrens 	struct vfs *vfs_found = NULL;
2474789Sahrens 
2475789Sahrens 	vfs_list_read_lock();
2476789Sahrens 	vfsp = rootvfs;
2477789Sahrens 	do {
2478789Sahrens 		if (strcmp(refstr_value(vfsp->vfs_resource), resource) == 0) {
2479789Sahrens 			VFS_HOLD(vfsp);
2480789Sahrens 			vfs_found = vfsp;
2481789Sahrens 			break;
2482789Sahrens 		}
2483789Sahrens 		vfsp = vfsp->vfs_next;
2484789Sahrens 	} while (vfsp != rootvfs);
2485789Sahrens 	vfs_list_unlock();
2486789Sahrens 	return (vfs_found);
2487789Sahrens }
2488789Sahrens 
24894543Smarks /* ARGSUSED */
2490789Sahrens static void
24914543Smarks zfs_create_cb(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx)
2492789Sahrens {
24935331Samw 	zfs_creat_t *zct = arg;
24945498Stimh 
24955498Stimh 	zfs_create_fs(os, cr, zct->zct_zplprops, tx);
24965331Samw }
24975331Samw 
24985498Stimh #define	ZFS_PROP_UNDEFINED	((uint64_t)-1)
24995498Stimh 
25005331Samw /*
25015498Stimh  * inputs:
25027184Stimh  * createprops		list of properties requested by creator
25037184Stimh  * default_zplver	zpl version to use if unspecified in createprops
25047184Stimh  * fuids_ok		fuids allowed in this version of the spa?
25057184Stimh  * os			parent objset pointer (NULL if root fs)
25065331Samw  *
25075498Stimh  * outputs:
25085498Stimh  * zplprops	values for the zplprops we attach to the master node object
25097184Stimh  * is_ci	true if requested file system will be purely case-insensitive
25105331Samw  *
25115498Stimh  * Determine the settings for utf8only, normalization and
25125498Stimh  * casesensitivity.  Specific values may have been requested by the
25135498Stimh  * creator and/or we can inherit values from the parent dataset.  If
25145498Stimh  * the file system is of too early a vintage, a creator can not
25155498Stimh  * request settings for these properties, even if the requested
25165498Stimh  * setting is the default value.  We don't actually want to create dsl
25175498Stimh  * properties for these, so remove them from the source nvlist after
25185498Stimh  * processing.
25195331Samw  */
25205331Samw static int
25219396SMatthew.Ahrens@Sun.COM zfs_fill_zplprops_impl(objset_t *os, uint64_t zplver,
25227184Stimh     boolean_t fuids_ok, nvlist_t *createprops, nvlist_t *zplprops,
25237184Stimh     boolean_t *is_ci)
25245331Samw {
25255498Stimh 	uint64_t sense = ZFS_PROP_UNDEFINED;
25265498Stimh 	uint64_t norm = ZFS_PROP_UNDEFINED;
25275498Stimh 	uint64_t u8 = ZFS_PROP_UNDEFINED;
25285498Stimh 
25295498Stimh 	ASSERT(zplprops != NULL);
25305498Stimh 
25315375Stimh 	/*
25325498Stimh 	 * Pull out creator prop choices, if any.
25335375Stimh 	 */
25345498Stimh 	if (createprops) {
25355498Stimh 		(void) nvlist_lookup_uint64(createprops,
25367184Stimh 		    zfs_prop_to_name(ZFS_PROP_VERSION), &zplver);
25377184Stimh 		(void) nvlist_lookup_uint64(createprops,
25385498Stimh 		    zfs_prop_to_name(ZFS_PROP_NORMALIZE), &norm);
25395498Stimh 		(void) nvlist_remove_all(createprops,
25405498Stimh 		    zfs_prop_to_name(ZFS_PROP_NORMALIZE));
25415498Stimh 		(void) nvlist_lookup_uint64(createprops,
25425498Stimh 		    zfs_prop_to_name(ZFS_PROP_UTF8ONLY), &u8);
25435498Stimh 		(void) nvlist_remove_all(createprops,
25445498Stimh 		    zfs_prop_to_name(ZFS_PROP_UTF8ONLY));
25455498Stimh 		(void) nvlist_lookup_uint64(createprops,
25465498Stimh 		    zfs_prop_to_name(ZFS_PROP_CASE), &sense);
25475498Stimh 		(void) nvlist_remove_all(createprops,
25485498Stimh 		    zfs_prop_to_name(ZFS_PROP_CASE));
25495331Samw 	}
25505331Samw 
25515375Stimh 	/*
25527184Stimh 	 * If the zpl version requested is whacky or the file system
25537184Stimh 	 * or pool is version is too "young" to support normalization
25547184Stimh 	 * and the creator tried to set a value for one of the props,
25557184Stimh 	 * error out.
25565498Stimh 	 */
25577184Stimh 	if ((zplver < ZPL_VERSION_INITIAL || zplver > ZPL_VERSION) ||
25587184Stimh 	    (zplver >= ZPL_VERSION_FUID && !fuids_ok) ||
25597184Stimh 	    (zplver < ZPL_VERSION_NORMALIZATION &&
25605498Stimh 	    (norm != ZFS_PROP_UNDEFINED || u8 != ZFS_PROP_UNDEFINED ||
25617184Stimh 	    sense != ZFS_PROP_UNDEFINED)))
25625498Stimh 		return (ENOTSUP);
25635498Stimh 
25645498Stimh 	/*
25655498Stimh 	 * Put the version in the zplprops
25665498Stimh 	 */
25675498Stimh 	VERIFY(nvlist_add_uint64(zplprops,
25685498Stimh 	    zfs_prop_to_name(ZFS_PROP_VERSION), zplver) == 0);
25695498Stimh 
25705498Stimh 	if (norm == ZFS_PROP_UNDEFINED)
25715498Stimh 		VERIFY(zfs_get_zplprop(os, ZFS_PROP_NORMALIZE, &norm) == 0);
25725498Stimh 	VERIFY(nvlist_add_uint64(zplprops,
25735498Stimh 	    zfs_prop_to_name(ZFS_PROP_NORMALIZE), norm) == 0);
25745498Stimh 
25755498Stimh 	/*
25765498Stimh 	 * If we're normalizing, names must always be valid UTF-8 strings.
25775498Stimh 	 */
25785498Stimh 	if (norm)
25795498Stimh 		u8 = 1;
25805498Stimh 	if (u8 == ZFS_PROP_UNDEFINED)
25815498Stimh 		VERIFY(zfs_get_zplprop(os, ZFS_PROP_UTF8ONLY, &u8) == 0);
25825498Stimh 	VERIFY(nvlist_add_uint64(zplprops,
25835498Stimh 	    zfs_prop_to_name(ZFS_PROP_UTF8ONLY), u8) == 0);
25845498Stimh 
25855498Stimh 	if (sense == ZFS_PROP_UNDEFINED)
25865498Stimh 		VERIFY(zfs_get_zplprop(os, ZFS_PROP_CASE, &sense) == 0);
25875498Stimh 	VERIFY(nvlist_add_uint64(zplprops,
25885498Stimh 	    zfs_prop_to_name(ZFS_PROP_CASE), sense) == 0);
25895498Stimh 
25906492Stimh 	if (is_ci)
25916492Stimh 		*is_ci = (sense == ZFS_CASE_INSENSITIVE);
25926492Stimh 
25937184Stimh 	return (0);
25947184Stimh }
25957184Stimh 
25967184Stimh static int
25977184Stimh zfs_fill_zplprops(const char *dataset, nvlist_t *createprops,
25987184Stimh     nvlist_t *zplprops, boolean_t *is_ci)
25997184Stimh {
26007184Stimh 	boolean_t fuids_ok = B_TRUE;
26017184Stimh 	uint64_t zplver = ZPL_VERSION;
26027184Stimh 	objset_t *os = NULL;
26037184Stimh 	char parentname[MAXNAMELEN];
26047184Stimh 	char *cp;
26057184Stimh 	int error;
26067184Stimh 
26077184Stimh 	(void) strlcpy(parentname, dataset, sizeof (parentname));
26087184Stimh 	cp = strrchr(parentname, '/');
26097184Stimh 	ASSERT(cp != NULL);
26107184Stimh 	cp[0] = '\0';
26117184Stimh 
26129396SMatthew.Ahrens@Sun.COM 	if (zfs_earlier_version(dataset, SPA_VERSION_USERSPACE))
26139396SMatthew.Ahrens@Sun.COM 		zplver = ZPL_VERSION_USERSPACE - 1;
26147184Stimh 	if (zfs_earlier_version(dataset, SPA_VERSION_FUID)) {
26157184Stimh 		zplver = ZPL_VERSION_FUID - 1;
26167184Stimh 		fuids_ok = B_FALSE;
26177184Stimh 	}
26187184Stimh 
26197184Stimh 	/*
26207184Stimh 	 * Open parent object set so we can inherit zplprop values.
26217184Stimh 	 */
262210298SMatthew.Ahrens@Sun.COM 	if ((error = dmu_objset_hold(parentname, FTAG, &os)) != 0)
26237184Stimh 		return (error);
26247184Stimh 
26257184Stimh 	error = zfs_fill_zplprops_impl(os, zplver, fuids_ok, createprops,
26267184Stimh 	    zplprops, is_ci);
262710298SMatthew.Ahrens@Sun.COM 	dmu_objset_rele(os, FTAG);
26287184Stimh 	return (error);
26297184Stimh }
26307184Stimh 
26317184Stimh static int
26327184Stimh zfs_fill_zplprops_root(uint64_t spa_vers, nvlist_t *createprops,
26337184Stimh     nvlist_t *zplprops, boolean_t *is_ci)
26347184Stimh {
26357184Stimh 	boolean_t fuids_ok = B_TRUE;
26367184Stimh 	uint64_t zplver = ZPL_VERSION;
26377184Stimh 	int error;
26387184Stimh 
26397184Stimh 	if (spa_vers < SPA_VERSION_FUID) {
26407184Stimh 		zplver = ZPL_VERSION_FUID - 1;
26417184Stimh 		fuids_ok = B_FALSE;
26427184Stimh 	}
26437184Stimh 
26447184Stimh 	error = zfs_fill_zplprops_impl(NULL, zplver, fuids_ok, createprops,
26457184Stimh 	    zplprops, is_ci);
26467184Stimh 	return (error);
2647789Sahrens }
2648789Sahrens 
26495367Sahrens /*
26505367Sahrens  * inputs:
26515367Sahrens  * zc_objset_type	type of objset to create (fs vs zvol)
26525367Sahrens  * zc_name		name of new objset
26535367Sahrens  * zc_value		name of snapshot to clone from (may be empty)
26545367Sahrens  * zc_nvlist_src{_size}	nvlist of properties to apply
26555367Sahrens  *
26565498Stimh  * outputs: none
26575367Sahrens  */
2658789Sahrens static int
2659789Sahrens zfs_ioc_create(zfs_cmd_t *zc)
2660789Sahrens {
2661789Sahrens 	objset_t *clone;
2662789Sahrens 	int error = 0;
26635331Samw 	zfs_creat_t zct;
26644543Smarks 	nvlist_t *nvprops = NULL;
26654543Smarks 	void (*cbfunc)(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx);
2666789Sahrens 	dmu_objset_type_t type = zc->zc_objset_type;
2667789Sahrens 
2668789Sahrens 	switch (type) {
2669789Sahrens 
2670789Sahrens 	case DMU_OST_ZFS:
2671789Sahrens 		cbfunc = zfs_create_cb;
2672789Sahrens 		break;
2673789Sahrens 
2674789Sahrens 	case DMU_OST_ZVOL:
2675789Sahrens 		cbfunc = zvol_create_cb;
2676789Sahrens 		break;
2677789Sahrens 
2678789Sahrens 	default:
26792199Sahrens 		cbfunc = NULL;
26806423Sgw25295 		break;
26812199Sahrens 	}
26825326Sek110237 	if (strchr(zc->zc_name, '@') ||
26835326Sek110237 	    strchr(zc->zc_name, '%'))
2684789Sahrens 		return (EINVAL);
2685789Sahrens 
26862676Seschrock 	if (zc->zc_nvlist_src != NULL &&
26875094Slling 	    (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
26889643SEric.Taylor@Sun.COM 	    zc->zc_iflags, &nvprops)) != 0)
26892676Seschrock 		return (error);
26902676Seschrock 
26915498Stimh 	zct.zct_zplprops = NULL;
26925331Samw 	zct.zct_props = nvprops;
26935331Samw 
26942676Seschrock 	if (zc->zc_value[0] != '\0') {
2695789Sahrens 		/*
2696789Sahrens 		 * We're creating a clone of an existing snapshot.
2697789Sahrens 		 */
26982676Seschrock 		zc->zc_value[sizeof (zc->zc_value) - 1] = '\0';
26992676Seschrock 		if (dataset_namecheck(zc->zc_value, NULL, NULL) != 0) {
27004543Smarks 			nvlist_free(nvprops);
2701789Sahrens 			return (EINVAL);
27022676Seschrock 		}
2703789Sahrens 
270410298SMatthew.Ahrens@Sun.COM 		error = dmu_objset_hold(zc->zc_value, FTAG, &clone);
27052676Seschrock 		if (error) {
27064543Smarks 			nvlist_free(nvprops);
2707789Sahrens 			return (error);
27082676Seschrock 		}
27096492Stimh 
271010272SMatthew.Ahrens@Sun.COM 		error = dmu_objset_clone(zc->zc_name, dmu_objset_ds(clone), 0);
271110298SMatthew.Ahrens@Sun.COM 		dmu_objset_rele(clone, FTAG);
27125331Samw 		if (error) {
27135331Samw 			nvlist_free(nvprops);
27145331Samw 			return (error);
27155331Samw 		}
2716789Sahrens 	} else {
27176492Stimh 		boolean_t is_insensitive = B_FALSE;
27186492Stimh 
27192676Seschrock 		if (cbfunc == NULL) {
27204543Smarks 			nvlist_free(nvprops);
27212199Sahrens 			return (EINVAL);
27222676Seschrock 		}
27232676Seschrock 
2724789Sahrens 		if (type == DMU_OST_ZVOL) {
27252676Seschrock 			uint64_t volsize, volblocksize;
27262676Seschrock 
27274543Smarks 			if (nvprops == NULL ||
27284543Smarks 			    nvlist_lookup_uint64(nvprops,
27292676Seschrock 			    zfs_prop_to_name(ZFS_PROP_VOLSIZE),
27302676Seschrock 			    &volsize) != 0) {
27314543Smarks 				nvlist_free(nvprops);
27322676Seschrock 				return (EINVAL);
27332676Seschrock 			}
27342676Seschrock 
27354543Smarks 			if ((error = nvlist_lookup_uint64(nvprops,
27362676Seschrock 			    zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE),
27372676Seschrock 			    &volblocksize)) != 0 && error != ENOENT) {
27384543Smarks 				nvlist_free(nvprops);
27392676Seschrock 				return (EINVAL);
27402676Seschrock 			}
27411133Seschrock 
27422676Seschrock 			if (error != 0)
27432676Seschrock 				volblocksize = zfs_prop_default_numeric(
27442676Seschrock 				    ZFS_PROP_VOLBLOCKSIZE);
27452676Seschrock 
27462676Seschrock 			if ((error = zvol_check_volblocksize(
27472676Seschrock 			    volblocksize)) != 0 ||
27482676Seschrock 			    (error = zvol_check_volsize(volsize,
27492676Seschrock 			    volblocksize)) != 0) {
27504543Smarks 				nvlist_free(nvprops);
2751789Sahrens 				return (error);
27522676Seschrock 			}
27534577Sahrens 		} else if (type == DMU_OST_ZFS) {
27545331Samw 			int error;
27555331Samw 
27565498Stimh 			/*
27575331Samw 			 * We have to have normalization and
27585331Samw 			 * case-folding flags correct when we do the
27595331Samw 			 * file system creation, so go figure them out
27605498Stimh 			 * now.
27615331Samw 			 */
27625498Stimh 			VERIFY(nvlist_alloc(&zct.zct_zplprops,
27635498Stimh 			    NV_UNIQUE_NAME, KM_SLEEP) == 0);
27645498Stimh 			error = zfs_fill_zplprops(zc->zc_name, nvprops,
27657184Stimh 			    zct.zct_zplprops, &is_insensitive);
27665331Samw 			if (error != 0) {
27675331Samw 				nvlist_free(nvprops);
27685498Stimh 				nvlist_free(zct.zct_zplprops);
27695331Samw 				return (error);
27704577Sahrens 			}
27712676Seschrock 		}
277210272SMatthew.Ahrens@Sun.COM 		error = dmu_objset_create(zc->zc_name, type,
27736492Stimh 		    is_insensitive ? DS_FLAG_CI_DATASET : 0, cbfunc, &zct);
27745498Stimh 		nvlist_free(zct.zct_zplprops);
2775789Sahrens 	}
27762676Seschrock 
27772676Seschrock 	/*
27782676Seschrock 	 * It would be nice to do this atomically.
27792676Seschrock 	 */
27802676Seschrock 	if (error == 0) {
278111022STom.Erickson@Sun.COM 		error = zfs_set_prop_nvlist(zc->zc_name, ZPROP_SRC_LOCAL,
278211022STom.Erickson@Sun.COM 		    nvprops, NULL);
278311022STom.Erickson@Sun.COM 		if (error != 0)
278410242Schris.kirby@sun.com 			(void) dmu_objset_destroy(zc->zc_name, B_FALSE);
27852676Seschrock 	}
27864543Smarks 	nvlist_free(nvprops);
2787789Sahrens 	return (error);
2788789Sahrens }
2789789Sahrens 
27905367Sahrens /*
27915367Sahrens  * inputs:
27925367Sahrens  * zc_name	name of filesystem
27935367Sahrens  * zc_value	short name of snapshot
27945367Sahrens  * zc_cookie	recursive flag
27959396SMatthew.Ahrens@Sun.COM  * zc_nvlist_src[_size] property list
27965367Sahrens  *
279710588SEric.Taylor@Sun.COM  * outputs:
279810588SEric.Taylor@Sun.COM  * zc_value	short snapname (i.e. part after the '@')
27995367Sahrens  */
2800789Sahrens static int
28012199Sahrens zfs_ioc_snapshot(zfs_cmd_t *zc)
28022199Sahrens {
28037265Sahrens 	nvlist_t *nvprops = NULL;
28047265Sahrens 	int error;
28057265Sahrens 	boolean_t recursive = zc->zc_cookie;
28067265Sahrens 
28072676Seschrock 	if (snapshot_namecheck(zc->zc_value, NULL, NULL) != 0)
28082199Sahrens 		return (EINVAL);
28097265Sahrens 
28107265Sahrens 	if (zc->zc_nvlist_src != NULL &&
28117265Sahrens 	    (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
28129643SEric.Taylor@Sun.COM 	    zc->zc_iflags, &nvprops)) != 0)
28137265Sahrens 		return (error);
28147265Sahrens 
28159355SMatthew.Ahrens@Sun.COM 	error = zfs_check_userprops(zc->zc_name, nvprops);
28169355SMatthew.Ahrens@Sun.COM 	if (error)
28179355SMatthew.Ahrens@Sun.COM 		goto out;
28189355SMatthew.Ahrens@Sun.COM 
281911022STom.Erickson@Sun.COM 	if (!nvlist_empty(nvprops) &&
28209355SMatthew.Ahrens@Sun.COM 	    zfs_earlier_version(zc->zc_name, SPA_VERSION_SNAP_PROPS)) {
28219355SMatthew.Ahrens@Sun.COM 		error = ENOTSUP;
28229355SMatthew.Ahrens@Sun.COM 		goto out;
28237265Sahrens 	}
28249355SMatthew.Ahrens@Sun.COM 
28259355SMatthew.Ahrens@Sun.COM 	error = dmu_objset_snapshot(zc->zc_name, zc->zc_value,
28269355SMatthew.Ahrens@Sun.COM 	    nvprops, recursive);
28279355SMatthew.Ahrens@Sun.COM 
28289355SMatthew.Ahrens@Sun.COM out:
28297265Sahrens 	nvlist_free(nvprops);
28307265Sahrens 	return (error);
28312199Sahrens }
28322199Sahrens 
28334007Smmusante int
28342199Sahrens zfs_unmount_snap(char *name, void *arg)
2835789Sahrens {
28362417Sahrens 	vfs_t *vfsp = NULL;
28372199Sahrens 
28386689Smaybee 	if (arg) {
28396689Smaybee 		char *snapname = arg;
28406689Smaybee 		int len = strlen(name) + strlen(snapname) + 2;
28416689Smaybee 		char *buf = kmem_alloc(len, KM_SLEEP);
28426689Smaybee 
28436689Smaybee 		(void) strcpy(buf, name);
28446689Smaybee 		(void) strcat(buf, "@");
28456689Smaybee 		(void) strcat(buf, snapname);
28466689Smaybee 		vfsp = zfs_get_vfs(buf);
28476689Smaybee 		kmem_free(buf, len);
28482417Sahrens 	} else if (strchr(name, '@')) {
28492199Sahrens 		vfsp = zfs_get_vfs(name);
28502199Sahrens 	}
28512199Sahrens 
28522199Sahrens 	if (vfsp) {
28532199Sahrens 		/*
28542199Sahrens 		 * Always force the unmount for snapshots.
28552199Sahrens 		 */
28562199Sahrens 		int flag = MS_FORCE;
2857789Sahrens 		int err;
2858789Sahrens 
28592199Sahrens 		if ((err = vn_vfswlock(vfsp->vfs_vnodecovered)) != 0) {
28602199Sahrens 			VFS_RELE(vfsp);
28612199Sahrens 			return (err);
28622199Sahrens 		}
28632199Sahrens 		VFS_RELE(vfsp);
28642199Sahrens 		if ((err = dounmount(vfsp, flag, kcred)) != 0)
28652199Sahrens 			return (err);
28662199Sahrens 	}
28672199Sahrens 	return (0);
28682199Sahrens }
28692199Sahrens 
28705367Sahrens /*
28715367Sahrens  * inputs:
287210242Schris.kirby@sun.com  * zc_name		name of filesystem
287310242Schris.kirby@sun.com  * zc_value		short name of snapshot
287410242Schris.kirby@sun.com  * zc_defer_destroy	mark for deferred destroy
28755367Sahrens  *
28765367Sahrens  * outputs:	none
28775367Sahrens  */
28782199Sahrens static int
28792199Sahrens zfs_ioc_destroy_snaps(zfs_cmd_t *zc)
28802199Sahrens {
28812199Sahrens 	int err;
2882789Sahrens 
28832676Seschrock 	if (snapshot_namecheck(zc->zc_value, NULL, NULL) != 0)
28842199Sahrens 		return (EINVAL);
28852199Sahrens 	err = dmu_objset_find(zc->zc_name,
28862676Seschrock 	    zfs_unmount_snap, zc->zc_value, DS_FIND_CHILDREN);
28872199Sahrens 	if (err)
28882199Sahrens 		return (err);
288910242Schris.kirby@sun.com 	return (dmu_snapshots_destroy(zc->zc_name, zc->zc_value,
289010242Schris.kirby@sun.com 	    zc->zc_defer_destroy));
28912199Sahrens }
28922199Sahrens 
28935367Sahrens /*
28945367Sahrens  * inputs:
28955367Sahrens  * zc_name		name of dataset to destroy
28965367Sahrens  * zc_objset_type	type of objset
289710242Schris.kirby@sun.com  * zc_defer_destroy	mark for deferred destroy
28985367Sahrens  *
28995367Sahrens  * outputs:		none
29005367Sahrens  */
29012199Sahrens static int
29022199Sahrens zfs_ioc_destroy(zfs_cmd_t *zc)
29032199Sahrens {
290410588SEric.Taylor@Sun.COM 	int err;
29052199Sahrens 	if (strchr(zc->zc_name, '@') && zc->zc_objset_type == DMU_OST_ZFS) {
290610588SEric.Taylor@Sun.COM 		err = zfs_unmount_snap(zc->zc_name, NULL);
29072199Sahrens 		if (err)
29082199Sahrens 			return (err);
2909789Sahrens 	}
2910789Sahrens 
291110588SEric.Taylor@Sun.COM 	err = dmu_objset_destroy(zc->zc_name, zc->zc_defer_destroy);
291210588SEric.Taylor@Sun.COM 	if (zc->zc_objset_type == DMU_OST_ZVOL && err == 0)
291310693Schris.kirby@sun.com 		(void) zvol_remove_minor(zc->zc_name);
291410588SEric.Taylor@Sun.COM 	return (err);
2915789Sahrens }
2916789Sahrens 
29175367Sahrens /*
29185367Sahrens  * inputs:
29195446Sahrens  * zc_name	name of dataset to rollback (to most recent snapshot)
29205367Sahrens  *
29215367Sahrens  * outputs:	none
29225367Sahrens  */
2923789Sahrens static int
2924789Sahrens zfs_ioc_rollback(zfs_cmd_t *zc)
2925789Sahrens {
292610272SMatthew.Ahrens@Sun.COM 	dsl_dataset_t *ds, *clone;
29275446Sahrens 	int error;
292810272SMatthew.Ahrens@Sun.COM 	zfsvfs_t *zfsvfs;
292910272SMatthew.Ahrens@Sun.COM 	char *clone_name;
293010272SMatthew.Ahrens@Sun.COM 
293110272SMatthew.Ahrens@Sun.COM 	error = dsl_dataset_hold(zc->zc_name, FTAG, &ds);
293210272SMatthew.Ahrens@Sun.COM 	if (error)
293310272SMatthew.Ahrens@Sun.COM 		return (error);
293410272SMatthew.Ahrens@Sun.COM 
293510272SMatthew.Ahrens@Sun.COM 	/* must not be a snapshot */
293610272SMatthew.Ahrens@Sun.COM 	if (dsl_dataset_is_snapshot(ds)) {
293710272SMatthew.Ahrens@Sun.COM 		dsl_dataset_rele(ds, FTAG);
293810272SMatthew.Ahrens@Sun.COM 		return (EINVAL);
293910272SMatthew.Ahrens@Sun.COM 	}
294010272SMatthew.Ahrens@Sun.COM 
294110272SMatthew.Ahrens@Sun.COM 	/* must have a most recent snapshot */
294210272SMatthew.Ahrens@Sun.COM 	if (ds->ds_phys->ds_prev_snap_txg < TXG_INITIAL) {
294310272SMatthew.Ahrens@Sun.COM 		dsl_dataset_rele(ds, FTAG);
294410272SMatthew.Ahrens@Sun.COM 		return (EINVAL);
294510272SMatthew.Ahrens@Sun.COM 	}
29465446Sahrens 
29475446Sahrens 	/*
294810272SMatthew.Ahrens@Sun.COM 	 * Create clone of most recent snapshot.
29495446Sahrens 	 */
295010272SMatthew.Ahrens@Sun.COM 	clone_name = kmem_asprintf("%s/%%rollback", zc->zc_name);
295110272SMatthew.Ahrens@Sun.COM 	error = dmu_objset_clone(clone_name, ds->ds_prev, DS_FLAG_INCONSISTENT);
29525446Sahrens 	if (error)
295310272SMatthew.Ahrens@Sun.COM 		goto out;
295410272SMatthew.Ahrens@Sun.COM 
295510298SMatthew.Ahrens@Sun.COM 	error = dsl_dataset_own(clone_name, B_TRUE, FTAG, &clone);
295610272SMatthew.Ahrens@Sun.COM 	if (error)
295710272SMatthew.Ahrens@Sun.COM 		goto out;
295810272SMatthew.Ahrens@Sun.COM 
295910272SMatthew.Ahrens@Sun.COM 	/*
296010272SMatthew.Ahrens@Sun.COM 	 * Do clone swap.
296110272SMatthew.Ahrens@Sun.COM 	 */
29629396SMatthew.Ahrens@Sun.COM 	if (getzfsvfs(zc->zc_name, &zfsvfs) == 0) {
296310298SMatthew.Ahrens@Sun.COM 		error = zfs_suspend_fs(zfsvfs);
29646083Sek110237 		if (error == 0) {
29656083Sek110237 			int resume_err;
29666083Sek110237 
296710272SMatthew.Ahrens@Sun.COM 			if (dsl_dataset_tryown(ds, B_FALSE, FTAG)) {
296810272SMatthew.Ahrens@Sun.COM 				error = dsl_dataset_clone_swap(clone, ds,
296910272SMatthew.Ahrens@Sun.COM 				    B_TRUE);
297010272SMatthew.Ahrens@Sun.COM 				dsl_dataset_disown(ds, FTAG);
297110272SMatthew.Ahrens@Sun.COM 				ds = NULL;
297210272SMatthew.Ahrens@Sun.COM 			} else {
297310272SMatthew.Ahrens@Sun.COM 				error = EBUSY;
297410272SMatthew.Ahrens@Sun.COM 			}
297510298SMatthew.Ahrens@Sun.COM 			resume_err = zfs_resume_fs(zfsvfs, zc->zc_name);
29766083Sek110237 			error = error ? error : resume_err;
29776083Sek110237 		}
29785446Sahrens 		VFS_RELE(zfsvfs->z_vfs);
29795446Sahrens 	} else {
298010272SMatthew.Ahrens@Sun.COM 		if (dsl_dataset_tryown(ds, B_FALSE, FTAG)) {
298110272SMatthew.Ahrens@Sun.COM 			error = dsl_dataset_clone_swap(clone, ds, B_TRUE);
298210272SMatthew.Ahrens@Sun.COM 			dsl_dataset_disown(ds, FTAG);
298310272SMatthew.Ahrens@Sun.COM 			ds = NULL;
298410272SMatthew.Ahrens@Sun.COM 		} else {
298510272SMatthew.Ahrens@Sun.COM 			error = EBUSY;
298610272SMatthew.Ahrens@Sun.COM 		}
29875446Sahrens 	}
298810272SMatthew.Ahrens@Sun.COM 
298910272SMatthew.Ahrens@Sun.COM 	/*
299010272SMatthew.Ahrens@Sun.COM 	 * Destroy clone (which also closes it).
299110272SMatthew.Ahrens@Sun.COM 	 */
299210272SMatthew.Ahrens@Sun.COM 	(void) dsl_dataset_destroy(clone, FTAG, B_FALSE);
299310272SMatthew.Ahrens@Sun.COM 
299410272SMatthew.Ahrens@Sun.COM out:
299510272SMatthew.Ahrens@Sun.COM 	strfree(clone_name);
299610272SMatthew.Ahrens@Sun.COM 	if (ds)
299710272SMatthew.Ahrens@Sun.COM 		dsl_dataset_rele(ds, FTAG);
29985446Sahrens 	return (error);
2999789Sahrens }
3000789Sahrens 
30015367Sahrens /*
30025367Sahrens  * inputs:
30035367Sahrens  * zc_name	old name of dataset
30045367Sahrens  * zc_value	new name of dataset
30055367Sahrens  * zc_cookie	recursive flag (only valid for snapshots)
30065367Sahrens  *
30075367Sahrens  * outputs:	none
30085367Sahrens  */
3009789Sahrens static int
3010789Sahrens zfs_ioc_rename(zfs_cmd_t *zc)
3011789Sahrens {
30124490Svb160487 	boolean_t recursive = zc->zc_cookie & 1;
30134007Smmusante 
30142676Seschrock 	zc->zc_value[sizeof (zc->zc_value) - 1] = '\0';
30155326Sek110237 	if (dataset_namecheck(zc->zc_value, NULL, NULL) != 0 ||
30165326Sek110237 	    strchr(zc->zc_value, '%'))
3017789Sahrens 		return (EINVAL);
3018789Sahrens 
30194007Smmusante 	/*
30204007Smmusante 	 * Unmount snapshot unless we're doing a recursive rename,
30214007Smmusante 	 * in which case the dataset code figures out which snapshots
30224007Smmusante 	 * to unmount.
30234007Smmusante 	 */
30244007Smmusante 	if (!recursive && strchr(zc->zc_name, '@') != NULL &&
3025789Sahrens 	    zc->zc_objset_type == DMU_OST_ZFS) {
30262199Sahrens 		int err = zfs_unmount_snap(zc->zc_name, NULL);
30272199Sahrens 		if (err)
30282199Sahrens 			return (err);
3029789Sahrens 	}
303010588SEric.Taylor@Sun.COM 	if (zc->zc_objset_type == DMU_OST_ZVOL)
303110588SEric.Taylor@Sun.COM 		(void) zvol_remove_minor(zc->zc_name);
30324007Smmusante 	return (dmu_objset_rename(zc->zc_name, zc->zc_value, recursive));
3033789Sahrens }
3034789Sahrens 
303511022STom.Erickson@Sun.COM static int
303611022STom.Erickson@Sun.COM zfs_check_settable(const char *dsname, nvpair_t *pair, cred_t *cr)
303711022STom.Erickson@Sun.COM {
303811022STom.Erickson@Sun.COM 	const char *propname = nvpair_name(pair);
303911022STom.Erickson@Sun.COM 	boolean_t issnap = (strchr(dsname, '@') != NULL);
304011022STom.Erickson@Sun.COM 	zfs_prop_t prop = zfs_name_to_prop(propname);
304111022STom.Erickson@Sun.COM 	uint64_t intval;
304211022STom.Erickson@Sun.COM 	int err;
304311022STom.Erickson@Sun.COM 
304411022STom.Erickson@Sun.COM 	if (prop == ZPROP_INVAL) {
304511022STom.Erickson@Sun.COM 		if (zfs_prop_user(propname)) {
304611022STom.Erickson@Sun.COM 			if (err = zfs_secpolicy_write_perms(dsname,
304711022STom.Erickson@Sun.COM 			    ZFS_DELEG_PERM_USERPROP, cr))
304811022STom.Erickson@Sun.COM 				return (err);
304911022STom.Erickson@Sun.COM 			return (0);
305011022STom.Erickson@Sun.COM 		}
305111022STom.Erickson@Sun.COM 
305211022STom.Erickson@Sun.COM 		if (!issnap && zfs_prop_userquota(propname)) {
305311022STom.Erickson@Sun.COM 			const char *perm = NULL;
305411022STom.Erickson@Sun.COM 			const char *uq_prefix =
305511022STom.Erickson@Sun.COM 			    zfs_userquota_prop_prefixes[ZFS_PROP_USERQUOTA];
305611022STom.Erickson@Sun.COM 			const char *gq_prefix =
305711022STom.Erickson@Sun.COM 			    zfs_userquota_prop_prefixes[ZFS_PROP_GROUPQUOTA];
305811022STom.Erickson@Sun.COM 
305911022STom.Erickson@Sun.COM 			if (strncmp(propname, uq_prefix,
306011022STom.Erickson@Sun.COM 			    strlen(uq_prefix)) == 0) {
306111022STom.Erickson@Sun.COM 				perm = ZFS_DELEG_PERM_USERQUOTA;
306211022STom.Erickson@Sun.COM 			} else if (strncmp(propname, gq_prefix,
306311022STom.Erickson@Sun.COM 			    strlen(gq_prefix)) == 0) {
306411022STom.Erickson@Sun.COM 				perm = ZFS_DELEG_PERM_GROUPQUOTA;
306511022STom.Erickson@Sun.COM 			} else {
306611022STom.Erickson@Sun.COM 				/* USERUSED and GROUPUSED are read-only */
306711022STom.Erickson@Sun.COM 				return (EINVAL);
306811022STom.Erickson@Sun.COM 			}
306911022STom.Erickson@Sun.COM 
307011022STom.Erickson@Sun.COM 			if (err = zfs_secpolicy_write_perms(dsname, perm, cr))
307111022STom.Erickson@Sun.COM 				return (err);
307211022STom.Erickson@Sun.COM 			return (0);
307311022STom.Erickson@Sun.COM 		}
307411022STom.Erickson@Sun.COM 
307511022STom.Erickson@Sun.COM 		return (EINVAL);
307611022STom.Erickson@Sun.COM 	}
307711022STom.Erickson@Sun.COM 
307811022STom.Erickson@Sun.COM 	if (issnap)
307911022STom.Erickson@Sun.COM 		return (EINVAL);
308011022STom.Erickson@Sun.COM 
308111022STom.Erickson@Sun.COM 	if (nvpair_type(pair) == DATA_TYPE_NVLIST) {
308211022STom.Erickson@Sun.COM 		/*
308311022STom.Erickson@Sun.COM 		 * dsl_prop_get_all_impl() returns properties in this
308411022STom.Erickson@Sun.COM 		 * format.
308511022STom.Erickson@Sun.COM 		 */
308611022STom.Erickson@Sun.COM 		nvlist_t *attrs;
308711022STom.Erickson@Sun.COM 		VERIFY(nvpair_value_nvlist(pair, &attrs) == 0);
308811022STom.Erickson@Sun.COM 		VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE,
308911022STom.Erickson@Sun.COM 		    &pair) == 0);
309011022STom.Erickson@Sun.COM 	}
309111022STom.Erickson@Sun.COM 
309211022STom.Erickson@Sun.COM 	/*
309311022STom.Erickson@Sun.COM 	 * Check that this value is valid for this pool version
309411022STom.Erickson@Sun.COM 	 */
309511022STom.Erickson@Sun.COM 	switch (prop) {
309611022STom.Erickson@Sun.COM 	case ZFS_PROP_COMPRESSION:
309711022STom.Erickson@Sun.COM 		/*
309811022STom.Erickson@Sun.COM 		 * If the user specified gzip compression, make sure
309911022STom.Erickson@Sun.COM 		 * the SPA supports it. We ignore any errors here since
310011022STom.Erickson@Sun.COM 		 * we'll catch them later.
310111022STom.Erickson@Sun.COM 		 */
310211022STom.Erickson@Sun.COM 		if (nvpair_type(pair) == DATA_TYPE_UINT64 &&
310311022STom.Erickson@Sun.COM 		    nvpair_value_uint64(pair, &intval) == 0) {
310411022STom.Erickson@Sun.COM 			if (intval >= ZIO_COMPRESS_GZIP_1 &&
310511022STom.Erickson@Sun.COM 			    intval <= ZIO_COMPRESS_GZIP_9 &&
310611022STom.Erickson@Sun.COM 			    zfs_earlier_version(dsname,
310711022STom.Erickson@Sun.COM 			    SPA_VERSION_GZIP_COMPRESSION)) {
310811022STom.Erickson@Sun.COM 				return (ENOTSUP);
310911022STom.Erickson@Sun.COM 			}
311011022STom.Erickson@Sun.COM 
311111022STom.Erickson@Sun.COM 			if (intval == ZIO_COMPRESS_ZLE &&
311211022STom.Erickson@Sun.COM 			    zfs_earlier_version(dsname,
311311022STom.Erickson@Sun.COM 			    SPA_VERSION_ZLE_COMPRESSION))
311411022STom.Erickson@Sun.COM 				return (ENOTSUP);
311511022STom.Erickson@Sun.COM 
311611022STom.Erickson@Sun.COM 			/*
311711022STom.Erickson@Sun.COM 			 * If this is a bootable dataset then
311811022STom.Erickson@Sun.COM 			 * verify that the compression algorithm
311911022STom.Erickson@Sun.COM 			 * is supported for booting. We must return
312011022STom.Erickson@Sun.COM 			 * something other than ENOTSUP since it
312111022STom.Erickson@Sun.COM 			 * implies a downrev pool version.
312211022STom.Erickson@Sun.COM 			 */
312311022STom.Erickson@Sun.COM 			if (zfs_is_bootfs(dsname) &&
312411022STom.Erickson@Sun.COM 			    !BOOTFS_COMPRESS_VALID(intval)) {
312511022STom.Erickson@Sun.COM 				return (ERANGE);
312611022STom.Erickson@Sun.COM 			}
312711022STom.Erickson@Sun.COM 		}
312811022STom.Erickson@Sun.COM 		break;
312911022STom.Erickson@Sun.COM 
313011022STom.Erickson@Sun.COM 	case ZFS_PROP_COPIES:
313111022STom.Erickson@Sun.COM 		if (zfs_earlier_version(dsname, SPA_VERSION_DITTO_BLOCKS))
313211022STom.Erickson@Sun.COM 			return (ENOTSUP);
313311022STom.Erickson@Sun.COM 		break;
313411022STom.Erickson@Sun.COM 
313511022STom.Erickson@Sun.COM 	case ZFS_PROP_DEDUP:
313611022STom.Erickson@Sun.COM 		if (zfs_earlier_version(dsname, SPA_VERSION_DEDUP))
313711022STom.Erickson@Sun.COM 			return (ENOTSUP);
313811022STom.Erickson@Sun.COM 		break;
313911022STom.Erickson@Sun.COM 
314011022STom.Erickson@Sun.COM 	case ZFS_PROP_SHARESMB:
314111022STom.Erickson@Sun.COM 		if (zpl_earlier_version(dsname, ZPL_VERSION_FUID))
314211022STom.Erickson@Sun.COM 			return (ENOTSUP);
314311022STom.Erickson@Sun.COM 		break;
314411022STom.Erickson@Sun.COM 
314511022STom.Erickson@Sun.COM 	case ZFS_PROP_ACLINHERIT:
314611022STom.Erickson@Sun.COM 		if (nvpair_type(pair) == DATA_TYPE_UINT64 &&
314711022STom.Erickson@Sun.COM 		    nvpair_value_uint64(pair, &intval) == 0) {
314811022STom.Erickson@Sun.COM 			if (intval == ZFS_ACL_PASSTHROUGH_X &&
314911022STom.Erickson@Sun.COM 			    zfs_earlier_version(dsname,
315011022STom.Erickson@Sun.COM 			    SPA_VERSION_PASSTHROUGH_X))
315111022STom.Erickson@Sun.COM 				return (ENOTSUP);
315211022STom.Erickson@Sun.COM 		}
315311022STom.Erickson@Sun.COM 		break;
315411022STom.Erickson@Sun.COM 	}
315511022STom.Erickson@Sun.COM 
315611022STom.Erickson@Sun.COM 	return (zfs_secpolicy_setprop(dsname, prop, pair, CRED()));
315711022STom.Erickson@Sun.COM }
315811022STom.Erickson@Sun.COM 
315911022STom.Erickson@Sun.COM /*
316011022STom.Erickson@Sun.COM  * Removes properties from the given props list that fail permission checks
316111022STom.Erickson@Sun.COM  * needed to clear them and to restore them in case of a receive error. For each
316211022STom.Erickson@Sun.COM  * property, make sure we have both set and inherit permissions.
316311022STom.Erickson@Sun.COM  *
316411022STom.Erickson@Sun.COM  * Returns the first error encountered if any permission checks fail. If the
316511022STom.Erickson@Sun.COM  * caller provides a non-NULL errlist, it also gives the complete list of names
316611022STom.Erickson@Sun.COM  * of all the properties that failed a permission check along with the
316711022STom.Erickson@Sun.COM  * corresponding error numbers. The caller is responsible for freeing the
316811022STom.Erickson@Sun.COM  * returned errlist.
316911022STom.Erickson@Sun.COM  *
317011022STom.Erickson@Sun.COM  * If every property checks out successfully, zero is returned and the list
317111022STom.Erickson@Sun.COM  * pointed at by errlist is NULL.
317211022STom.Erickson@Sun.COM  */
317311022STom.Erickson@Sun.COM static int
317411022STom.Erickson@Sun.COM zfs_check_clearable(char *dataset, nvlist_t *props, nvlist_t **errlist)
31756689Smaybee {
31766689Smaybee 	zfs_cmd_t *zc;
317711022STom.Erickson@Sun.COM 	nvpair_t *pair, *next_pair;
317811022STom.Erickson@Sun.COM 	nvlist_t *errors;
317911022STom.Erickson@Sun.COM 	int err, rv = 0;
31806689Smaybee 
31816689Smaybee 	if (props == NULL)
318211022STom.Erickson@Sun.COM 		return (0);
318311022STom.Erickson@Sun.COM 
318411022STom.Erickson@Sun.COM 	VERIFY(nvlist_alloc(&errors, NV_UNIQUE_NAME, KM_SLEEP) == 0);
318511022STom.Erickson@Sun.COM 
31866689Smaybee 	zc = kmem_alloc(sizeof (zfs_cmd_t), KM_SLEEP);
31876689Smaybee 	(void) strcpy(zc->zc_name, dataset);
318811022STom.Erickson@Sun.COM 	pair = nvlist_next_nvpair(props, NULL);
318911022STom.Erickson@Sun.COM 	while (pair != NULL) {
319011022STom.Erickson@Sun.COM 		next_pair = nvlist_next_nvpair(props, pair);
319111022STom.Erickson@Sun.COM 
319211022STom.Erickson@Sun.COM 		(void) strcpy(zc->zc_value, nvpair_name(pair));
319311022STom.Erickson@Sun.COM 		if ((err = zfs_check_settable(dataset, pair, CRED())) != 0 ||
319411022STom.Erickson@Sun.COM 		    (err = zfs_secpolicy_inherit(zc, CRED())) != 0) {
319511022STom.Erickson@Sun.COM 			VERIFY(nvlist_remove_nvpair(props, pair) == 0);
319611022STom.Erickson@Sun.COM 			VERIFY(nvlist_add_int32(errors,
319711022STom.Erickson@Sun.COM 			    zc->zc_value, err) == 0);
319811022STom.Erickson@Sun.COM 		}
319911022STom.Erickson@Sun.COM 		pair = next_pair;
32006689Smaybee 	}
32016689Smaybee 	kmem_free(zc, sizeof (zfs_cmd_t));
320211022STom.Erickson@Sun.COM 
320311022STom.Erickson@Sun.COM 	if ((pair = nvlist_next_nvpair(errors, NULL)) == NULL) {
320411022STom.Erickson@Sun.COM 		nvlist_free(errors);
320511022STom.Erickson@Sun.COM 		errors = NULL;
320611022STom.Erickson@Sun.COM 	} else {
320711022STom.Erickson@Sun.COM 		VERIFY(nvpair_value_int32(pair, &rv) == 0);
320811022STom.Erickson@Sun.COM 	}
320911022STom.Erickson@Sun.COM 
321011022STom.Erickson@Sun.COM 	if (errlist == NULL)
321111022STom.Erickson@Sun.COM 		nvlist_free(errors);
321211022STom.Erickson@Sun.COM 	else
321311022STom.Erickson@Sun.COM 		*errlist = errors;
321411022STom.Erickson@Sun.COM 
321511022STom.Erickson@Sun.COM 	return (rv);
32166689Smaybee }
32176689Smaybee 
321811022STom.Erickson@Sun.COM static boolean_t
321911022STom.Erickson@Sun.COM propval_equals(nvpair_t *p1, nvpair_t *p2)
322011022STom.Erickson@Sun.COM {
322111022STom.Erickson@Sun.COM 	if (nvpair_type(p1) == DATA_TYPE_NVLIST) {
322211022STom.Erickson@Sun.COM 		/* dsl_prop_get_all_impl() format */
322311022STom.Erickson@Sun.COM 		nvlist_t *attrs;
322411022STom.Erickson@Sun.COM 		VERIFY(nvpair_value_nvlist(p1, &attrs) == 0);
322511022STom.Erickson@Sun.COM 		VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE,
322611022STom.Erickson@Sun.COM 		    &p1) == 0);
322711022STom.Erickson@Sun.COM 	}
322811022STom.Erickson@Sun.COM 
322911022STom.Erickson@Sun.COM 	if (nvpair_type(p2) == DATA_TYPE_NVLIST) {
323011022STom.Erickson@Sun.COM 		nvlist_t *attrs;
323111022STom.Erickson@Sun.COM 		VERIFY(nvpair_value_nvlist(p2, &attrs) == 0);
323211022STom.Erickson@Sun.COM 		VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE,
323311022STom.Erickson@Sun.COM 		    &p2) == 0);
323411022STom.Erickson@Sun.COM 	}
323511022STom.Erickson@Sun.COM 
323611022STom.Erickson@Sun.COM 	if (nvpair_type(p1) != nvpair_type(p2))
323711022STom.Erickson@Sun.COM 		return (B_FALSE);
323811022STom.Erickson@Sun.COM 
323911022STom.Erickson@Sun.COM 	if (nvpair_type(p1) == DATA_TYPE_STRING) {
324011022STom.Erickson@Sun.COM 		char *valstr1, *valstr2;
324111022STom.Erickson@Sun.COM 
324211022STom.Erickson@Sun.COM 		VERIFY(nvpair_value_string(p1, (char **)&valstr1) == 0);
324311022STom.Erickson@Sun.COM 		VERIFY(nvpair_value_string(p2, (char **)&valstr2) == 0);
324411022STom.Erickson@Sun.COM 		return (strcmp(valstr1, valstr2) == 0);
324511022STom.Erickson@Sun.COM 	} else {
324611022STom.Erickson@Sun.COM 		uint64_t intval1, intval2;
324711022STom.Erickson@Sun.COM 
324811022STom.Erickson@Sun.COM 		VERIFY(nvpair_value_uint64(p1, &intval1) == 0);
324911022STom.Erickson@Sun.COM 		VERIFY(nvpair_value_uint64(p2, &intval2) == 0);
325011022STom.Erickson@Sun.COM 		return (intval1 == intval2);
325111022STom.Erickson@Sun.COM 	}
325211022STom.Erickson@Sun.COM }
325311022STom.Erickson@Sun.COM 
325411022STom.Erickson@Sun.COM /*
325511022STom.Erickson@Sun.COM  * Remove properties from props if they are not going to change (as determined
325611022STom.Erickson@Sun.COM  * by comparison with origprops). Remove them from origprops as well, since we
325711022STom.Erickson@Sun.COM  * do not need to clear or restore properties that won't change.
325811022STom.Erickson@Sun.COM  */
325911022STom.Erickson@Sun.COM static void
326011022STom.Erickson@Sun.COM props_reduce(nvlist_t *props, nvlist_t *origprops)
326111022STom.Erickson@Sun.COM {
326211022STom.Erickson@Sun.COM 	nvpair_t *pair, *next_pair;
326311022STom.Erickson@Sun.COM 
326411022STom.Erickson@Sun.COM 	if (origprops == NULL)
326511022STom.Erickson@Sun.COM 		return; /* all props need to be received */
326611022STom.Erickson@Sun.COM 
326711022STom.Erickson@Sun.COM 	pair = nvlist_next_nvpair(props, NULL);
326811022STom.Erickson@Sun.COM 	while (pair != NULL) {
326911022STom.Erickson@Sun.COM 		const char *propname = nvpair_name(pair);
327011022STom.Erickson@Sun.COM 		nvpair_t *match;
327111022STom.Erickson@Sun.COM 
327211022STom.Erickson@Sun.COM 		next_pair = nvlist_next_nvpair(props, pair);
327311022STom.Erickson@Sun.COM 
327411022STom.Erickson@Sun.COM 		if ((nvlist_lookup_nvpair(origprops, propname,
327511022STom.Erickson@Sun.COM 		    &match) != 0) || !propval_equals(pair, match))
327611022STom.Erickson@Sun.COM 			goto next; /* need to set received value */
327711022STom.Erickson@Sun.COM 
327811022STom.Erickson@Sun.COM 		/* don't clear the existing received value */
327911022STom.Erickson@Sun.COM 		(void) nvlist_remove_nvpair(origprops, match);
328011022STom.Erickson@Sun.COM 		/* don't bother receiving the property */
328111022STom.Erickson@Sun.COM 		(void) nvlist_remove_nvpair(props, pair);
328211022STom.Erickson@Sun.COM next:
328311022STom.Erickson@Sun.COM 		pair = next_pair;
328411022STom.Erickson@Sun.COM 	}
328511022STom.Erickson@Sun.COM }
328611022STom.Erickson@Sun.COM 
328711022STom.Erickson@Sun.COM #ifdef	DEBUG
328811022STom.Erickson@Sun.COM static boolean_t zfs_ioc_recv_inject_err;
328911022STom.Erickson@Sun.COM #endif
329011022STom.Erickson@Sun.COM 
32915367Sahrens /*
32925367Sahrens  * inputs:
32935367Sahrens  * zc_name		name of containing filesystem
32945367Sahrens  * zc_nvlist_src{_size}	nvlist of properties to apply
32955367Sahrens  * zc_value		name of snapshot to create
32965367Sahrens  * zc_string		name of clone origin (if DRR_FLAG_CLONE)
32975367Sahrens  * zc_cookie		file descriptor to recv from
32985367Sahrens  * zc_begin_record	the BEGIN record of the stream (not byteswapped)
32995367Sahrens  * zc_guid		force flag
33005367Sahrens  *
33015367Sahrens  * outputs:
33025367Sahrens  * zc_cookie		number of bytes read
330311022STom.Erickson@Sun.COM  * zc_nvlist_dst{_size} error for each unapplied received property
330411022STom.Erickson@Sun.COM  * zc_obj		zprop_errflags_t
33055367Sahrens  */
3306789Sahrens static int
33075367Sahrens zfs_ioc_recv(zfs_cmd_t *zc)
3308789Sahrens {
3309789Sahrens 	file_t *fp;
33105326Sek110237 	objset_t *os;
33115367Sahrens 	dmu_recv_cookie_t drc;
33125326Sek110237 	boolean_t force = (boolean_t)zc->zc_guid;
331311022STom.Erickson@Sun.COM 	int fd;
331411022STom.Erickson@Sun.COM 	int error = 0;
331511022STom.Erickson@Sun.COM 	int props_error = 0;
331611022STom.Erickson@Sun.COM 	nvlist_t *errors;
33175367Sahrens 	offset_t off;
331811022STom.Erickson@Sun.COM 	nvlist_t *props = NULL; /* sent properties */
331911022STom.Erickson@Sun.COM 	nvlist_t *origprops = NULL; /* existing properties */
33205367Sahrens 	objset_t *origin = NULL;
33215367Sahrens 	char *tosnap;
33225367Sahrens 	char tofs[ZFS_MAXNAMELEN];
332311022STom.Erickson@Sun.COM 	boolean_t first_recvd_props = B_FALSE;
3324789Sahrens 
33253265Sahrens 	if (dataset_namecheck(zc->zc_value, NULL, NULL) != 0 ||
33265326Sek110237 	    strchr(zc->zc_value, '@') == NULL ||
33275326Sek110237 	    strchr(zc->zc_value, '%'))
33283265Sahrens 		return (EINVAL);
33293265Sahrens 
33305367Sahrens 	(void) strcpy(tofs, zc->zc_value);
33315367Sahrens 	tosnap = strchr(tofs, '@');
333211022STom.Erickson@Sun.COM 	*tosnap++ = '\0';
33335367Sahrens 
33345367Sahrens 	if (zc->zc_nvlist_src != NULL &&
33355367Sahrens 	    (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
33369643SEric.Taylor@Sun.COM 	    zc->zc_iflags, &props)) != 0)
33375367Sahrens 		return (error);
33385367Sahrens 
3339789Sahrens 	fd = zc->zc_cookie;
3340789Sahrens 	fp = getf(fd);
33415367Sahrens 	if (fp == NULL) {
33425367Sahrens 		nvlist_free(props);
3343789Sahrens 		return (EBADF);
33445367Sahrens 	}
33455326Sek110237 
334611022STom.Erickson@Sun.COM 	VERIFY(nvlist_alloc(&errors, NV_UNIQUE_NAME, KM_SLEEP) == 0);
334711022STom.Erickson@Sun.COM 
334810298SMatthew.Ahrens@Sun.COM 	if (props && dmu_objset_hold(tofs, FTAG, &os) == 0) {
334911022STom.Erickson@Sun.COM 		if ((spa_version(os->os_spa) >= SPA_VERSION_RECVD_PROPS) &&
335011022STom.Erickson@Sun.COM 		    !dsl_prop_get_hasrecvd(os)) {
335111022STom.Erickson@Sun.COM 			first_recvd_props = B_TRUE;
335211022STom.Erickson@Sun.COM 		}
335311022STom.Erickson@Sun.COM 
33546689Smaybee 		/*
335511022STom.Erickson@Sun.COM 		 * If new received properties are supplied, they are to
335611022STom.Erickson@Sun.COM 		 * completely replace the existing received properties, so stash
335711022STom.Erickson@Sun.COM 		 * away the existing ones.
33586689Smaybee 		 */
335911022STom.Erickson@Sun.COM 		if (dsl_prop_get_received(os, &origprops) == 0) {
336011022STom.Erickson@Sun.COM 			nvlist_t *errlist = NULL;
336111022STom.Erickson@Sun.COM 			/*
336211022STom.Erickson@Sun.COM 			 * Don't bother writing a property if its value won't
336311022STom.Erickson@Sun.COM 			 * change (and avoid the unnecessary security checks).
336411022STom.Erickson@Sun.COM 			 *
336511022STom.Erickson@Sun.COM 			 * The first receive after SPA_VERSION_RECVD_PROPS is a
336611022STom.Erickson@Sun.COM 			 * special case where we blow away all local properties
336711022STom.Erickson@Sun.COM 			 * regardless.
336811022STom.Erickson@Sun.COM 			 */
336911022STom.Erickson@Sun.COM 			if (!first_recvd_props)
337011022STom.Erickson@Sun.COM 				props_reduce(props, origprops);
337111022STom.Erickson@Sun.COM 			if (zfs_check_clearable(tofs, origprops,
337211022STom.Erickson@Sun.COM 			    &errlist) != 0)
337311022STom.Erickson@Sun.COM 				(void) nvlist_merge(errors, errlist, 0);
337411022STom.Erickson@Sun.COM 			nvlist_free(errlist);
337511022STom.Erickson@Sun.COM 		}
337610298SMatthew.Ahrens@Sun.COM 
337710298SMatthew.Ahrens@Sun.COM 		dmu_objset_rele(os, FTAG);
33785326Sek110237 	}
33795326Sek110237 
33805367Sahrens 	if (zc->zc_string[0]) {
338110298SMatthew.Ahrens@Sun.COM 		error = dmu_objset_hold(zc->zc_string, FTAG, &origin);
33826689Smaybee 		if (error)
33836689Smaybee 			goto out;
33845367Sahrens 	}
33855367Sahrens 
338611007SLori.Alt@Sun.COM 	error = dmu_recv_begin(tofs, tosnap, zc->zc_top_ds,
338711007SLori.Alt@Sun.COM 	    &zc->zc_begin_record, force, origin, &drc);
33885367Sahrens 	if (origin)
338910298SMatthew.Ahrens@Sun.COM 		dmu_objset_rele(origin, FTAG);
33906689Smaybee 	if (error)
33916689Smaybee 		goto out;
33925326Sek110237 
33935326Sek110237 	/*
339411022STom.Erickson@Sun.COM 	 * Set properties before we receive the stream so that they are applied
339511022STom.Erickson@Sun.COM 	 * to the new data. Note that we must call dmu_recv_stream() if
339611022STom.Erickson@Sun.COM 	 * dmu_recv_begin() succeeds.
33975326Sek110237 	 */
33985367Sahrens 	if (props) {
339911022STom.Erickson@Sun.COM 		nvlist_t *errlist;
340011022STom.Erickson@Sun.COM 
340111022STom.Erickson@Sun.COM 		if (dmu_objset_from_ds(drc.drc_logical_ds, &os) == 0) {
340211022STom.Erickson@Sun.COM 			if (drc.drc_newfs) {
340311022STom.Erickson@Sun.COM 				if (spa_version(os->os_spa) >=
340411022STom.Erickson@Sun.COM 				    SPA_VERSION_RECVD_PROPS)
340511022STom.Erickson@Sun.COM 					first_recvd_props = B_TRUE;
340611022STom.Erickson@Sun.COM 			} else if (origprops != NULL) {
340711022STom.Erickson@Sun.COM 				if (clear_received_props(os, tofs, origprops,
340811022STom.Erickson@Sun.COM 				    first_recvd_props ? NULL : props) != 0)
340911022STom.Erickson@Sun.COM 					zc->zc_obj |= ZPROP_ERR_NOCLEAR;
341011022STom.Erickson@Sun.COM 			} else {
341111022STom.Erickson@Sun.COM 				zc->zc_obj |= ZPROP_ERR_NOCLEAR;
341211022STom.Erickson@Sun.COM 			}
341311022STom.Erickson@Sun.COM 			dsl_prop_set_hasrecvd(os);
341411022STom.Erickson@Sun.COM 		} else if (!drc.drc_newfs) {
341511022STom.Erickson@Sun.COM 			zc->zc_obj |= ZPROP_ERR_NOCLEAR;
341611022STom.Erickson@Sun.COM 		}
341711022STom.Erickson@Sun.COM 
341811022STom.Erickson@Sun.COM 		(void) zfs_set_prop_nvlist(tofs, ZPROP_SRC_RECEIVED,
341911022STom.Erickson@Sun.COM 		    props, &errlist);
342011022STom.Erickson@Sun.COM 		(void) nvlist_merge(errors, errlist, 0);
342111022STom.Erickson@Sun.COM 		nvlist_free(errlist);
342211022STom.Erickson@Sun.COM 	}
342311022STom.Erickson@Sun.COM 
342411022STom.Erickson@Sun.COM 	if (fit_error_list(zc, &errors) != 0 || put_nvlist(zc, errors) != 0) {
34256689Smaybee 		/*
342611022STom.Erickson@Sun.COM 		 * Caller made zc->zc_nvlist_dst less than the minimum expected
342711022STom.Erickson@Sun.COM 		 * size or supplied an invalid address.
34286689Smaybee 		 */
342911022STom.Erickson@Sun.COM 		props_error = EINVAL;
34305367Sahrens 	}
34315367Sahrens 
34325367Sahrens 	off = fp->f_offset;
34335367Sahrens 	error = dmu_recv_stream(&drc, fp->f_vnode, &off);
34345367Sahrens 
343510204SMatthew.Ahrens@Sun.COM 	if (error == 0) {
343610204SMatthew.Ahrens@Sun.COM 		zfsvfs_t *zfsvfs = NULL;
343710204SMatthew.Ahrens@Sun.COM 
343810204SMatthew.Ahrens@Sun.COM 		if (getzfsvfs(tofs, &zfsvfs) == 0) {
343910204SMatthew.Ahrens@Sun.COM 			/* online recv */
344010204SMatthew.Ahrens@Sun.COM 			int end_err;
344110298SMatthew.Ahrens@Sun.COM 
344210298SMatthew.Ahrens@Sun.COM 			error = zfs_suspend_fs(zfsvfs);
344310204SMatthew.Ahrens@Sun.COM 			/*
344410204SMatthew.Ahrens@Sun.COM 			 * If the suspend fails, then the recv_end will
344510204SMatthew.Ahrens@Sun.COM 			 * likely also fail, and clean up after itself.
344610204SMatthew.Ahrens@Sun.COM 			 */
344710204SMatthew.Ahrens@Sun.COM 			end_err = dmu_recv_end(&drc);
344810204SMatthew.Ahrens@Sun.COM 			if (error == 0) {
344910204SMatthew.Ahrens@Sun.COM 				int resume_err =
345010298SMatthew.Ahrens@Sun.COM 				    zfs_resume_fs(zfsvfs, tofs);
345110204SMatthew.Ahrens@Sun.COM 				error = error ? error : resume_err;
345210204SMatthew.Ahrens@Sun.COM 			}
345310204SMatthew.Ahrens@Sun.COM 			error = error ? error : end_err;
345410204SMatthew.Ahrens@Sun.COM 			VFS_RELE(zfsvfs->z_vfs);
345510204SMatthew.Ahrens@Sun.COM 		} else {
34566689Smaybee 			error = dmu_recv_end(&drc);
34575367Sahrens 		}
34586083Sek110237 	}
34595367Sahrens 
34605367Sahrens 	zc->zc_cookie = off - fp->f_offset;
34615367Sahrens 	if (VOP_SEEK(fp->f_vnode, fp->f_offset, &off, NULL) == 0)
34625367Sahrens 		fp->f_offset = off;
34632885Sahrens 
346411022STom.Erickson@Sun.COM #ifdef	DEBUG
346511022STom.Erickson@Sun.COM 	if (zfs_ioc_recv_inject_err) {
346611022STom.Erickson@Sun.COM 		zfs_ioc_recv_inject_err = B_FALSE;
346711022STom.Erickson@Sun.COM 		error = 1;
346811022STom.Erickson@Sun.COM 	}
346911022STom.Erickson@Sun.COM #endif
34706689Smaybee 	/*
34716689Smaybee 	 * On error, restore the original props.
34726689Smaybee 	 */
34736689Smaybee 	if (error && props) {
347411022STom.Erickson@Sun.COM 		if (dmu_objset_hold(tofs, FTAG, &os) == 0) {
347511022STom.Erickson@Sun.COM 			if (clear_received_props(os, tofs, props, NULL) != 0) {
347611022STom.Erickson@Sun.COM 				/*
347711022STom.Erickson@Sun.COM 				 * We failed to clear the received properties.
347811022STom.Erickson@Sun.COM 				 * Since we may have left a $recvd value on the
347911022STom.Erickson@Sun.COM 				 * system, we can't clear the $hasrecvd flag.
348011022STom.Erickson@Sun.COM 				 */
348111022STom.Erickson@Sun.COM 				zc->zc_obj |= ZPROP_ERR_NORESTORE;
348211022STom.Erickson@Sun.COM 			} else if (first_recvd_props) {
348311022STom.Erickson@Sun.COM 				dsl_prop_unset_hasrecvd(os);
348411022STom.Erickson@Sun.COM 			}
348511022STom.Erickson@Sun.COM 			dmu_objset_rele(os, FTAG);
348611022STom.Erickson@Sun.COM 		} else if (!drc.drc_newfs) {
348711022STom.Erickson@Sun.COM 			/* We failed to clear the received properties. */
348811022STom.Erickson@Sun.COM 			zc->zc_obj |= ZPROP_ERR_NORESTORE;
348911022STom.Erickson@Sun.COM 		}
349011022STom.Erickson@Sun.COM 
349111022STom.Erickson@Sun.COM 		if (origprops == NULL && !drc.drc_newfs) {
349211022STom.Erickson@Sun.COM 			/* We failed to stash the original properties. */
349311022STom.Erickson@Sun.COM 			zc->zc_obj |= ZPROP_ERR_NORESTORE;
349411022STom.Erickson@Sun.COM 		}
349511022STom.Erickson@Sun.COM 
349611022STom.Erickson@Sun.COM 		/*
349711022STom.Erickson@Sun.COM 		 * dsl_props_set() will not convert RECEIVED to LOCAL on or
349811022STom.Erickson@Sun.COM 		 * after SPA_VERSION_RECVD_PROPS, so we need to specify LOCAL
349911022STom.Erickson@Sun.COM 		 * explictly if we're restoring local properties cleared in the
350011022STom.Erickson@Sun.COM 		 * first new-style receive.
350111022STom.Erickson@Sun.COM 		 */
350211022STom.Erickson@Sun.COM 		if (origprops != NULL &&
350311022STom.Erickson@Sun.COM 		    zfs_set_prop_nvlist(tofs, (first_recvd_props ?
350411022STom.Erickson@Sun.COM 		    ZPROP_SRC_LOCAL : ZPROP_SRC_RECEIVED),
350511022STom.Erickson@Sun.COM 		    origprops, NULL) != 0) {
350611022STom.Erickson@Sun.COM 			/*
350711022STom.Erickson@Sun.COM 			 * We stashed the original properties but failed to
350811022STom.Erickson@Sun.COM 			 * restore them.
350911022STom.Erickson@Sun.COM 			 */
351011022STom.Erickson@Sun.COM 			zc->zc_obj |= ZPROP_ERR_NORESTORE;
351111022STom.Erickson@Sun.COM 		}
35126689Smaybee 	}
35136689Smaybee out:
35146689Smaybee 	nvlist_free(props);
35156689Smaybee 	nvlist_free(origprops);
351611022STom.Erickson@Sun.COM 	nvlist_free(errors);
3517789Sahrens 	releasef(fd);
351811022STom.Erickson@Sun.COM 
351911022STom.Erickson@Sun.COM 	if (error == 0)
352011022STom.Erickson@Sun.COM 		error = props_error;
352111022STom.Erickson@Sun.COM 
3522789Sahrens 	return (error);
3523789Sahrens }
3524789Sahrens 
35255367Sahrens /*
35265367Sahrens  * inputs:
35275367Sahrens  * zc_name	name of snapshot to send
35285367Sahrens  * zc_value	short name of incremental fromsnap (may be empty)
35295367Sahrens  * zc_cookie	file descriptor to send stream to
35305367Sahrens  * zc_obj	fromorigin flag (mutually exclusive with zc_value)
35315367Sahrens  *
35325367Sahrens  * outputs: none
35335367Sahrens  */
3534789Sahrens static int
35355367Sahrens zfs_ioc_send(zfs_cmd_t *zc)
3536789Sahrens {
3537789Sahrens 	objset_t *fromsnap = NULL;
3538789Sahrens 	objset_t *tosnap;
3539789Sahrens 	file_t *fp;
3540789Sahrens 	int error;
35415367Sahrens 	offset_t off;
3542789Sahrens 
354310298SMatthew.Ahrens@Sun.COM 	error = dmu_objset_hold(zc->zc_name, FTAG, &tosnap);
3544789Sahrens 	if (error)
3545789Sahrens 		return (error);
3546789Sahrens 
35472676Seschrock 	if (zc->zc_value[0] != '\0') {
35488012SEric.Taylor@Sun.COM 		char *buf;
35492885Sahrens 		char *cp;
35502885Sahrens 
35518012SEric.Taylor@Sun.COM 		buf = kmem_alloc(MAXPATHLEN, KM_SLEEP);
35528012SEric.Taylor@Sun.COM 		(void) strncpy(buf, zc->zc_name, MAXPATHLEN);
35532885Sahrens 		cp = strchr(buf, '@');
35542885Sahrens 		if (cp)
35552885Sahrens 			*(cp+1) = 0;
35568012SEric.Taylor@Sun.COM 		(void) strncat(buf, zc->zc_value, MAXPATHLEN);
355710298SMatthew.Ahrens@Sun.COM 		error = dmu_objset_hold(buf, FTAG, &fromsnap);
35588012SEric.Taylor@Sun.COM 		kmem_free(buf, MAXPATHLEN);
3559789Sahrens 		if (error) {
356010298SMatthew.Ahrens@Sun.COM 			dmu_objset_rele(tosnap, FTAG);
3561789Sahrens 			return (error);
3562789Sahrens 		}
3563789Sahrens 	}
3564789Sahrens 
3565789Sahrens 	fp = getf(zc->zc_cookie);
3566789Sahrens 	if (fp == NULL) {
356710298SMatthew.Ahrens@Sun.COM 		dmu_objset_rele(tosnap, FTAG);
3568789Sahrens 		if (fromsnap)
356910298SMatthew.Ahrens@Sun.COM 			dmu_objset_rele(fromsnap, FTAG);
3570789Sahrens 		return (EBADF);
3571789Sahrens 	}
3572789Sahrens 
35735367Sahrens 	off = fp->f_offset;
35745367Sahrens 	error = dmu_sendbackup(tosnap, fromsnap, zc->zc_obj, fp->f_vnode, &off);
35755367Sahrens 
35765367Sahrens 	if (VOP_SEEK(fp->f_vnode, fp->f_offset, &off, NULL) == 0)
35775367Sahrens 		fp->f_offset = off;
3578789Sahrens 	releasef(zc->zc_cookie);
3579789Sahrens 	if (fromsnap)
358010298SMatthew.Ahrens@Sun.COM 		dmu_objset_rele(fromsnap, FTAG);
358110298SMatthew.Ahrens@Sun.COM 	dmu_objset_rele(tosnap, FTAG);
3582789Sahrens 	return (error);
3583789Sahrens }
3584789Sahrens 
35851544Seschrock static int
35861544Seschrock zfs_ioc_inject_fault(zfs_cmd_t *zc)
35871544Seschrock {
35881544Seschrock 	int id, error;
35891544Seschrock 
35901544Seschrock 	error = zio_inject_fault(zc->zc_name, (int)zc->zc_guid, &id,
35911544Seschrock 	    &zc->zc_inject_record);
35921544Seschrock 
35931544Seschrock 	if (error == 0)
35941544Seschrock 		zc->zc_guid = (uint64_t)id;
35951544Seschrock 
35961544Seschrock 	return (error);
35971544Seschrock }
35981544Seschrock 
35991544Seschrock static int
36001544Seschrock zfs_ioc_clear_fault(zfs_cmd_t *zc)
36011544Seschrock {
36021544Seschrock 	return (zio_clear_fault((int)zc->zc_guid));
36031544Seschrock }
36041544Seschrock 
36051544Seschrock static int
36061544Seschrock zfs_ioc_inject_list_next(zfs_cmd_t *zc)
36071544Seschrock {
36081544Seschrock 	int id = (int)zc->zc_guid;
36091544Seschrock 	int error;
36101544Seschrock 
36111544Seschrock 	error = zio_inject_list_next(&id, zc->zc_name, sizeof (zc->zc_name),
36121544Seschrock 	    &zc->zc_inject_record);
36131544Seschrock 
36141544Seschrock 	zc->zc_guid = id;
36151544Seschrock 
36161544Seschrock 	return (error);
36171544Seschrock }
36181544Seschrock 
36191544Seschrock static int
36201544Seschrock zfs_ioc_error_log(zfs_cmd_t *zc)
36211544Seschrock {
36221544Seschrock 	spa_t *spa;
36231544Seschrock 	int error;
36242676Seschrock 	size_t count = (size_t)zc->zc_nvlist_dst_size;
36251544Seschrock 
36261544Seschrock 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
36271544Seschrock 		return (error);
36281544Seschrock 
36292676Seschrock 	error = spa_get_errlog(spa, (void *)(uintptr_t)zc->zc_nvlist_dst,
36301544Seschrock 	    &count);
36311544Seschrock 	if (error == 0)
36322676Seschrock 		zc->zc_nvlist_dst_size = count;
36331544Seschrock 	else
36342676Seschrock 		zc->zc_nvlist_dst_size = spa_get_errlog_size(spa);
36351544Seschrock 
36361544Seschrock 	spa_close(spa, FTAG);
36371544Seschrock 
36381544Seschrock 	return (error);
36391544Seschrock }
36401544Seschrock 
36411544Seschrock static int
36421544Seschrock zfs_ioc_clear(zfs_cmd_t *zc)
36431544Seschrock {
36441544Seschrock 	spa_t *spa;
36451544Seschrock 	vdev_t *vd;
36461544Seschrock 	int error;
36471544Seschrock 
36487294Sperrin 	/*
36497294Sperrin 	 * On zpool clear we also fix up missing slogs
36507294Sperrin 	 */
36517294Sperrin 	mutex_enter(&spa_namespace_lock);
36527294Sperrin 	spa = spa_lookup(zc->zc_name);
36537294Sperrin 	if (spa == NULL) {
36547294Sperrin 		mutex_exit(&spa_namespace_lock);
36557294Sperrin 		return (EIO);
36567294Sperrin 	}
365710922SJeff.Bonwick@Sun.COM 	if (spa_get_log_state(spa) == SPA_LOG_MISSING) {
36587294Sperrin 		/* we need to let spa_open/spa_load clear the chains */
365910922SJeff.Bonwick@Sun.COM 		spa_set_log_state(spa, SPA_LOG_CLEAR);
36607294Sperrin 	}
366110921STim.Haley@Sun.COM 	spa->spa_last_open_failed = 0;
36627294Sperrin 	mutex_exit(&spa_namespace_lock);
36637294Sperrin 
366410921STim.Haley@Sun.COM 	if (zc->zc_cookie == ZPOOL_NO_REWIND) {
366510921STim.Haley@Sun.COM 		error = spa_open(zc->zc_name, &spa, FTAG);
366610921STim.Haley@Sun.COM 	} else {
366710921STim.Haley@Sun.COM 		nvlist_t *policy;
366810921STim.Haley@Sun.COM 		nvlist_t *config = NULL;
366910921STim.Haley@Sun.COM 
367010921STim.Haley@Sun.COM 		if (zc->zc_nvlist_src == NULL)
367110921STim.Haley@Sun.COM 			return (EINVAL);
367210921STim.Haley@Sun.COM 
367310921STim.Haley@Sun.COM 		if ((error = get_nvlist(zc->zc_nvlist_src,
367410921STim.Haley@Sun.COM 		    zc->zc_nvlist_src_size, zc->zc_iflags, &policy)) == 0) {
367510921STim.Haley@Sun.COM 			error = spa_open_rewind(zc->zc_name, &spa, FTAG,
367610921STim.Haley@Sun.COM 			    policy, &config);
367710921STim.Haley@Sun.COM 			if (config != NULL) {
367810921STim.Haley@Sun.COM 				(void) put_nvlist(zc, config);
367910921STim.Haley@Sun.COM 				nvlist_free(config);
368010921STim.Haley@Sun.COM 			}
368110921STim.Haley@Sun.COM 			nvlist_free(policy);
368210921STim.Haley@Sun.COM 		}
368310921STim.Haley@Sun.COM 	}
368410921STim.Haley@Sun.COM 
368510921STim.Haley@Sun.COM 	if (error)
36861544Seschrock 		return (error);
36871544Seschrock 
368810685SGeorge.Wilson@Sun.COM 	spa_vdev_state_enter(spa, SCL_NONE);
36891544Seschrock 
36902676Seschrock 	if (zc->zc_guid == 0) {
36911544Seschrock 		vd = NULL;
36926643Seschrock 	} else {
36936643Seschrock 		vd = spa_lookup_by_guid(spa, zc->zc_guid, B_TRUE);
36945450Sbrendan 		if (vd == NULL) {
36957754SJeff.Bonwick@Sun.COM 			(void) spa_vdev_state_exit(spa, NULL, ENODEV);
36965450Sbrendan 			spa_close(spa, FTAG);
36975450Sbrendan 			return (ENODEV);
36985450Sbrendan 		}
36991544Seschrock 	}
37001544Seschrock 
37017754SJeff.Bonwick@Sun.COM 	vdev_clear(spa, vd);
37027754SJeff.Bonwick@Sun.COM 
37037754SJeff.Bonwick@Sun.COM 	(void) spa_vdev_state_exit(spa, NULL, 0);
37047754SJeff.Bonwick@Sun.COM 
37057754SJeff.Bonwick@Sun.COM 	/*
37067754SJeff.Bonwick@Sun.COM 	 * Resume any suspended I/Os.
37077754SJeff.Bonwick@Sun.COM 	 */
37089234SGeorge.Wilson@Sun.COM 	if (zio_resume(spa) != 0)
37099234SGeorge.Wilson@Sun.COM 		error = EIO;
37101544Seschrock 
37111544Seschrock 	spa_close(spa, FTAG);
37121544Seschrock 
37139234SGeorge.Wilson@Sun.COM 	return (error);
37141544Seschrock }
37151544Seschrock 
37165367Sahrens /*
37175367Sahrens  * inputs:
37185367Sahrens  * zc_name	name of filesystem
37195367Sahrens  * zc_value	name of origin snapshot
37205367Sahrens  *
372110588SEric.Taylor@Sun.COM  * outputs:
372210588SEric.Taylor@Sun.COM  * zc_string	name of conflicting snapshot, if there is one
37235367Sahrens  */
37241544Seschrock static int
37252082Seschrock zfs_ioc_promote(zfs_cmd_t *zc)
37262082Seschrock {
37272417Sahrens 	char *cp;
37282417Sahrens 
37292417Sahrens 	/*
37302417Sahrens 	 * We don't need to unmount *all* the origin fs's snapshots, but
37312417Sahrens 	 * it's easier.
37322417Sahrens 	 */
37332676Seschrock 	cp = strchr(zc->zc_value, '@');
37342417Sahrens 	if (cp)
37352417Sahrens 		*cp = '\0';
37362676Seschrock 	(void) dmu_objset_find(zc->zc_value,
37372417Sahrens 	    zfs_unmount_snap, NULL, DS_FIND_SNAPSHOTS);
373810588SEric.Taylor@Sun.COM 	return (dsl_dataset_promote(zc->zc_name, zc->zc_string));
37392082Seschrock }
37402082Seschrock 
37414543Smarks /*
37429396SMatthew.Ahrens@Sun.COM  * Retrieve a single {user|group}{used|quota}@... property.
37439396SMatthew.Ahrens@Sun.COM  *
37449396SMatthew.Ahrens@Sun.COM  * inputs:
37459396SMatthew.Ahrens@Sun.COM  * zc_name	name of filesystem
37469396SMatthew.Ahrens@Sun.COM  * zc_objset_type zfs_userquota_prop_t
37479396SMatthew.Ahrens@Sun.COM  * zc_value	domain name (eg. "S-1-234-567-89")
37489396SMatthew.Ahrens@Sun.COM  * zc_guid	RID/UID/GID
37499396SMatthew.Ahrens@Sun.COM  *
37509396SMatthew.Ahrens@Sun.COM  * outputs:
37519396SMatthew.Ahrens@Sun.COM  * zc_cookie	property value
37529396SMatthew.Ahrens@Sun.COM  */
37539396SMatthew.Ahrens@Sun.COM static int
37549396SMatthew.Ahrens@Sun.COM zfs_ioc_userspace_one(zfs_cmd_t *zc)
37559396SMatthew.Ahrens@Sun.COM {
37569396SMatthew.Ahrens@Sun.COM 	zfsvfs_t *zfsvfs;
37579396SMatthew.Ahrens@Sun.COM 	int error;
37589396SMatthew.Ahrens@Sun.COM 
37599396SMatthew.Ahrens@Sun.COM 	if (zc->zc_objset_type >= ZFS_NUM_USERQUOTA_PROPS)
37609396SMatthew.Ahrens@Sun.COM 		return (EINVAL);
37619396SMatthew.Ahrens@Sun.COM 
376210298SMatthew.Ahrens@Sun.COM 	error = zfsvfs_hold(zc->zc_name, FTAG, &zfsvfs);
37639396SMatthew.Ahrens@Sun.COM 	if (error)
37649396SMatthew.Ahrens@Sun.COM 		return (error);
37659396SMatthew.Ahrens@Sun.COM 
37669396SMatthew.Ahrens@Sun.COM 	error = zfs_userspace_one(zfsvfs,
37679396SMatthew.Ahrens@Sun.COM 	    zc->zc_objset_type, zc->zc_value, zc->zc_guid, &zc->zc_cookie);
37689396SMatthew.Ahrens@Sun.COM 	zfsvfs_rele(zfsvfs, FTAG);
37699396SMatthew.Ahrens@Sun.COM 
37709396SMatthew.Ahrens@Sun.COM 	return (error);
37719396SMatthew.Ahrens@Sun.COM }
37729396SMatthew.Ahrens@Sun.COM 
37739396SMatthew.Ahrens@Sun.COM /*
37749396SMatthew.Ahrens@Sun.COM  * inputs:
37759396SMatthew.Ahrens@Sun.COM  * zc_name		name of filesystem
37769396SMatthew.Ahrens@Sun.COM  * zc_cookie		zap cursor
37779396SMatthew.Ahrens@Sun.COM  * zc_objset_type	zfs_userquota_prop_t
37789396SMatthew.Ahrens@Sun.COM  * zc_nvlist_dst[_size] buffer to fill (not really an nvlist)
37799396SMatthew.Ahrens@Sun.COM  *
37809396SMatthew.Ahrens@Sun.COM  * outputs:
37819396SMatthew.Ahrens@Sun.COM  * zc_nvlist_dst[_size]	data buffer (array of zfs_useracct_t)
37829396SMatthew.Ahrens@Sun.COM  * zc_cookie	zap cursor
37839396SMatthew.Ahrens@Sun.COM  */
37849396SMatthew.Ahrens@Sun.COM static int
37859396SMatthew.Ahrens@Sun.COM zfs_ioc_userspace_many(zfs_cmd_t *zc)
37869396SMatthew.Ahrens@Sun.COM {
37879396SMatthew.Ahrens@Sun.COM 	zfsvfs_t *zfsvfs;
37889396SMatthew.Ahrens@Sun.COM 	int error;
37899396SMatthew.Ahrens@Sun.COM 
379010298SMatthew.Ahrens@Sun.COM 	error = zfsvfs_hold(zc->zc_name, FTAG, &zfsvfs);
37919396SMatthew.Ahrens@Sun.COM 	if (error)
37929396SMatthew.Ahrens@Sun.COM 		return (error);
37939396SMatthew.Ahrens@Sun.COM 
37949396SMatthew.Ahrens@Sun.COM 	int bufsize = zc->zc_nvlist_dst_size;
37959396SMatthew.Ahrens@Sun.COM 	void *buf = kmem_alloc(bufsize, KM_SLEEP);
37969396SMatthew.Ahrens@Sun.COM 
37979396SMatthew.Ahrens@Sun.COM 	error = zfs_userspace_many(zfsvfs, zc->zc_objset_type, &zc->zc_cookie,
37989396SMatthew.Ahrens@Sun.COM 	    buf, &zc->zc_nvlist_dst_size);
37999396SMatthew.Ahrens@Sun.COM 
38009396SMatthew.Ahrens@Sun.COM 	if (error == 0) {
38019396SMatthew.Ahrens@Sun.COM 		error = xcopyout(buf,
38029396SMatthew.Ahrens@Sun.COM 		    (void *)(uintptr_t)zc->zc_nvlist_dst,
38039396SMatthew.Ahrens@Sun.COM 		    zc->zc_nvlist_dst_size);
38049396SMatthew.Ahrens@Sun.COM 	}
38059396SMatthew.Ahrens@Sun.COM 	kmem_free(buf, bufsize);
38069396SMatthew.Ahrens@Sun.COM 	zfsvfs_rele(zfsvfs, FTAG);
38079396SMatthew.Ahrens@Sun.COM 
38089396SMatthew.Ahrens@Sun.COM 	return (error);
38099396SMatthew.Ahrens@Sun.COM }
38109396SMatthew.Ahrens@Sun.COM 
38119396SMatthew.Ahrens@Sun.COM /*
38129396SMatthew.Ahrens@Sun.COM  * inputs:
38139396SMatthew.Ahrens@Sun.COM  * zc_name		name of filesystem
38149396SMatthew.Ahrens@Sun.COM  *
38159396SMatthew.Ahrens@Sun.COM  * outputs:
38169396SMatthew.Ahrens@Sun.COM  * none
38179396SMatthew.Ahrens@Sun.COM  */
38189396SMatthew.Ahrens@Sun.COM static int
38199396SMatthew.Ahrens@Sun.COM zfs_ioc_userspace_upgrade(zfs_cmd_t *zc)
38209396SMatthew.Ahrens@Sun.COM {
38219396SMatthew.Ahrens@Sun.COM 	objset_t *os;
38229396SMatthew.Ahrens@Sun.COM 	int error;
38239396SMatthew.Ahrens@Sun.COM 	zfsvfs_t *zfsvfs;
38249396SMatthew.Ahrens@Sun.COM 
38259396SMatthew.Ahrens@Sun.COM 	if (getzfsvfs(zc->zc_name, &zfsvfs) == 0) {
382610298SMatthew.Ahrens@Sun.COM 		if (!dmu_objset_userused_enabled(zfsvfs->z_os)) {
38279396SMatthew.Ahrens@Sun.COM 			/*
38289396SMatthew.Ahrens@Sun.COM 			 * If userused is not enabled, it may be because the
38299396SMatthew.Ahrens@Sun.COM 			 * objset needs to be closed & reopened (to grow the
38309396SMatthew.Ahrens@Sun.COM 			 * objset_phys_t).  Suspend/resume the fs will do that.
38319396SMatthew.Ahrens@Sun.COM 			 */
383210298SMatthew.Ahrens@Sun.COM 			error = zfs_suspend_fs(zfsvfs);
383310298SMatthew.Ahrens@Sun.COM 			if (error == 0)
383410298SMatthew.Ahrens@Sun.COM 				error = zfs_resume_fs(zfsvfs, zc->zc_name);
38359396SMatthew.Ahrens@Sun.COM 		}
38369396SMatthew.Ahrens@Sun.COM 		if (error == 0)
38379396SMatthew.Ahrens@Sun.COM 			error = dmu_objset_userspace_upgrade(zfsvfs->z_os);
38389396SMatthew.Ahrens@Sun.COM 		VFS_RELE(zfsvfs->z_vfs);
38399396SMatthew.Ahrens@Sun.COM 	} else {
384010298SMatthew.Ahrens@Sun.COM 		/* XXX kind of reading contents without owning */
384110298SMatthew.Ahrens@Sun.COM 		error = dmu_objset_hold(zc->zc_name, FTAG, &os);
38429396SMatthew.Ahrens@Sun.COM 		if (error)
38439396SMatthew.Ahrens@Sun.COM 			return (error);
38449396SMatthew.Ahrens@Sun.COM 
38459396SMatthew.Ahrens@Sun.COM 		error = dmu_objset_userspace_upgrade(os);
384610298SMatthew.Ahrens@Sun.COM 		dmu_objset_rele(os, FTAG);
38479396SMatthew.Ahrens@Sun.COM 	}
38489396SMatthew.Ahrens@Sun.COM 
38499396SMatthew.Ahrens@Sun.COM 	return (error);
38509396SMatthew.Ahrens@Sun.COM }
38519396SMatthew.Ahrens@Sun.COM 
38529396SMatthew.Ahrens@Sun.COM /*
38534543Smarks  * We don't want to have a hard dependency
38544543Smarks  * against some special symbols in sharefs
38555331Samw  * nfs, and smbsrv.  Determine them if needed when
38564543Smarks  * the first file system is shared.
38575331Samw  * Neither sharefs, nfs or smbsrv are unloadable modules.
38584543Smarks  */
38595331Samw int (*znfsexport_fs)(void *arg);
38604543Smarks int (*zshare_fs)(enum sharefs_sys_op, share_t *, uint32_t);
38615331Samw int (*zsmbexport_fs)(void *arg, boolean_t add_share);
38625331Samw 
38635331Samw int zfs_nfsshare_inited;
38645331Samw int zfs_smbshare_inited;
38655331Samw 
38664543Smarks ddi_modhandle_t nfs_mod;
38674543Smarks ddi_modhandle_t sharefs_mod;
38685331Samw ddi_modhandle_t smbsrv_mod;
38694543Smarks kmutex_t zfs_share_lock;
38704543Smarks 
38714543Smarks static int
38725331Samw zfs_init_sharefs()
38735331Samw {
38745331Samw 	int error;
38755331Samw 
38765331Samw 	ASSERT(MUTEX_HELD(&zfs_share_lock));
38775331Samw 	/* Both NFS and SMB shares also require sharetab support. */
38785331Samw 	if (sharefs_mod == NULL && ((sharefs_mod =
38795331Samw 	    ddi_modopen("fs/sharefs",
38805331Samw 	    KRTLD_MODE_FIRST, &error)) == NULL)) {
38815331Samw 		return (ENOSYS);
38825331Samw 	}
38835331Samw 	if (zshare_fs == NULL && ((zshare_fs =
38845331Samw 	    (int (*)(enum sharefs_sys_op, share_t *, uint32_t))
38855331Samw 	    ddi_modsym(sharefs_mod, "sharefs_impl", &error)) == NULL)) {
38865331Samw 		return (ENOSYS);
38875331Samw 	}
38885331Samw 	return (0);
38895331Samw }
38905331Samw 
38915331Samw static int
38924543Smarks zfs_ioc_share(zfs_cmd_t *zc)
38934543Smarks {
38944543Smarks 	int error;
38954543Smarks 	int opcode;
38964543Smarks 
38975331Samw 	switch (zc->zc_share.z_sharetype) {
38985331Samw 	case ZFS_SHARE_NFS:
38995331Samw 	case ZFS_UNSHARE_NFS:
39005331Samw 		if (zfs_nfsshare_inited == 0) {
39015331Samw 			mutex_enter(&zfs_share_lock);
39025331Samw 			if (nfs_mod == NULL && ((nfs_mod = ddi_modopen("fs/nfs",
39035331Samw 			    KRTLD_MODE_FIRST, &error)) == NULL)) {
39045331Samw 				mutex_exit(&zfs_share_lock);
39055331Samw 				return (ENOSYS);
39065331Samw 			}
39075331Samw 			if (znfsexport_fs == NULL &&
39085331Samw 			    ((znfsexport_fs = (int (*)(void *))
39095331Samw 			    ddi_modsym(nfs_mod,
39105331Samw 			    "nfs_export", &error)) == NULL)) {
39115331Samw 				mutex_exit(&zfs_share_lock);
39125331Samw 				return (ENOSYS);
39135331Samw 			}
39145331Samw 			error = zfs_init_sharefs();
39155331Samw 			if (error) {
39165331Samw 				mutex_exit(&zfs_share_lock);
39175331Samw 				return (ENOSYS);
39185331Samw 			}
39195331Samw 			zfs_nfsshare_inited = 1;
39204543Smarks 			mutex_exit(&zfs_share_lock);
39214543Smarks 		}
39225331Samw 		break;
39235331Samw 	case ZFS_SHARE_SMB:
39245331Samw 	case ZFS_UNSHARE_SMB:
39255331Samw 		if (zfs_smbshare_inited == 0) {
39265331Samw 			mutex_enter(&zfs_share_lock);
39275331Samw 			if (smbsrv_mod == NULL && ((smbsrv_mod =
39285331Samw 			    ddi_modopen("drv/smbsrv",
39295331Samw 			    KRTLD_MODE_FIRST, &error)) == NULL)) {
39305331Samw 				mutex_exit(&zfs_share_lock);
39315331Samw 				return (ENOSYS);
39325331Samw 			}
39335331Samw 			if (zsmbexport_fs == NULL && ((zsmbexport_fs =
39345331Samw 			    (int (*)(void *, boolean_t))ddi_modsym(smbsrv_mod,
39356139Sjb150015 			    "smb_server_share", &error)) == NULL)) {
39365331Samw 				mutex_exit(&zfs_share_lock);
39375331Samw 				return (ENOSYS);
39385331Samw 			}
39395331Samw 			error = zfs_init_sharefs();
39405331Samw 			if (error) {
39415331Samw 				mutex_exit(&zfs_share_lock);
39425331Samw 				return (ENOSYS);
39435331Samw 			}
39445331Samw 			zfs_smbshare_inited = 1;
39454543Smarks 			mutex_exit(&zfs_share_lock);
39464543Smarks 		}
39475331Samw 		break;
39485331Samw 	default:
39495331Samw 		return (EINVAL);
39504543Smarks 	}
39514543Smarks 
39525331Samw 	switch (zc->zc_share.z_sharetype) {
39535331Samw 	case ZFS_SHARE_NFS:
39545331Samw 	case ZFS_UNSHARE_NFS:
39555331Samw 		if (error =
39565331Samw 		    znfsexport_fs((void *)
39575331Samw 		    (uintptr_t)zc->zc_share.z_exportdata))
39585331Samw 			return (error);
39595331Samw 		break;
39605331Samw 	case ZFS_SHARE_SMB:
39615331Samw 	case ZFS_UNSHARE_SMB:
39625331Samw 		if (error = zsmbexport_fs((void *)
39635331Samw 		    (uintptr_t)zc->zc_share.z_exportdata,
39645331Samw 		    zc->zc_share.z_sharetype == ZFS_SHARE_SMB ?
39658845Samw@Sun.COM 		    B_TRUE: B_FALSE)) {
39665331Samw 			return (error);
39675331Samw 		}
39685331Samw 		break;
39695331Samw 	}
39705331Samw 
39715331Samw 	opcode = (zc->zc_share.z_sharetype == ZFS_SHARE_NFS ||
39725331Samw 	    zc->zc_share.z_sharetype == ZFS_SHARE_SMB) ?
39734543Smarks 	    SHAREFS_ADD : SHAREFS_REMOVE;
39744543Smarks 
39755331Samw 	/*
39765331Samw 	 * Add or remove share from sharetab
39775331Samw 	 */
39784543Smarks 	error = zshare_fs(opcode,
39794543Smarks 	    (void *)(uintptr_t)zc->zc_share.z_sharedata,
39804543Smarks 	    zc->zc_share.z_sharemax);
39814543Smarks 
39824543Smarks 	return (error);
39834543Smarks 
39844543Smarks }
39854543Smarks 
39868845Samw@Sun.COM ace_t full_access[] = {
39878845Samw@Sun.COM 	{(uid_t)-1, ACE_ALL_PERMS, ACE_EVERYONE, 0}
39888845Samw@Sun.COM };
39898845Samw@Sun.COM 
39908845Samw@Sun.COM /*
39918845Samw@Sun.COM  * Remove all ACL files in shares dir
39928845Samw@Sun.COM  */
39938845Samw@Sun.COM static int
39948845Samw@Sun.COM zfs_smb_acl_purge(znode_t *dzp)
39958845Samw@Sun.COM {
39968845Samw@Sun.COM 	zap_cursor_t	zc;
39978845Samw@Sun.COM 	zap_attribute_t	zap;
39988845Samw@Sun.COM 	zfsvfs_t *zfsvfs = dzp->z_zfsvfs;
39998845Samw@Sun.COM 	int error;
40008845Samw@Sun.COM 
40018845Samw@Sun.COM 	for (zap_cursor_init(&zc, zfsvfs->z_os, dzp->z_id);
40028845Samw@Sun.COM 	    (error = zap_cursor_retrieve(&zc, &zap)) == 0;
40038845Samw@Sun.COM 	    zap_cursor_advance(&zc)) {
40048845Samw@Sun.COM 		if ((error = VOP_REMOVE(ZTOV(dzp), zap.za_name, kcred,
40058845Samw@Sun.COM 		    NULL, 0)) != 0)
40068845Samw@Sun.COM 			break;
40078845Samw@Sun.COM 	}
40088845Samw@Sun.COM 	zap_cursor_fini(&zc);
40098845Samw@Sun.COM 	return (error);
40108845Samw@Sun.COM }
40118845Samw@Sun.COM 
40128845Samw@Sun.COM static int
40138845Samw@Sun.COM zfs_ioc_smb_acl(zfs_cmd_t *zc)
40148845Samw@Sun.COM {
40158845Samw@Sun.COM 	vnode_t *vp;
40168845Samw@Sun.COM 	znode_t *dzp;
40178845Samw@Sun.COM 	vnode_t *resourcevp = NULL;
40188845Samw@Sun.COM 	znode_t *sharedir;
40198845Samw@Sun.COM 	zfsvfs_t *zfsvfs;
40208845Samw@Sun.COM 	nvlist_t *nvlist;
40218845Samw@Sun.COM 	char *src, *target;
40228845Samw@Sun.COM 	vattr_t vattr;
40238845Samw@Sun.COM 	vsecattr_t vsec;
40248845Samw@Sun.COM 	int error = 0;
40258845Samw@Sun.COM 
40268845Samw@Sun.COM 	if ((error = lookupname(zc->zc_value, UIO_SYSSPACE,
40278845Samw@Sun.COM 	    NO_FOLLOW, NULL, &vp)) != 0)
40288845Samw@Sun.COM 		return (error);
40298845Samw@Sun.COM 
40308845Samw@Sun.COM 	/* Now make sure mntpnt and dataset are ZFS */
40318845Samw@Sun.COM 
40328845Samw@Sun.COM 	if (vp->v_vfsp->vfs_fstype != zfsfstype ||
40338845Samw@Sun.COM 	    (strcmp((char *)refstr_value(vp->v_vfsp->vfs_resource),
40348845Samw@Sun.COM 	    zc->zc_name) != 0)) {
40358845Samw@Sun.COM 		VN_RELE(vp);
40368845Samw@Sun.COM 		return (EINVAL);
40378845Samw@Sun.COM 	}
40388845Samw@Sun.COM 
40398845Samw@Sun.COM 	dzp = VTOZ(vp);
40408845Samw@Sun.COM 	zfsvfs = dzp->z_zfsvfs;
40418845Samw@Sun.COM 	ZFS_ENTER(zfsvfs);
40428845Samw@Sun.COM 
40439030SMark.Shellenbaum@Sun.COM 	/*
40449030SMark.Shellenbaum@Sun.COM 	 * Create share dir if its missing.
40459030SMark.Shellenbaum@Sun.COM 	 */
40469030SMark.Shellenbaum@Sun.COM 	mutex_enter(&zfsvfs->z_lock);
40479030SMark.Shellenbaum@Sun.COM 	if (zfsvfs->z_shares_dir == 0) {
40489030SMark.Shellenbaum@Sun.COM 		dmu_tx_t *tx;
40499030SMark.Shellenbaum@Sun.COM 
40509030SMark.Shellenbaum@Sun.COM 		tx = dmu_tx_create(zfsvfs->z_os);
40519030SMark.Shellenbaum@Sun.COM 		dmu_tx_hold_zap(tx, MASTER_NODE_OBJ, TRUE,
40529030SMark.Shellenbaum@Sun.COM 		    ZFS_SHARES_DIR);
40539030SMark.Shellenbaum@Sun.COM 		dmu_tx_hold_zap(tx, DMU_NEW_OBJECT, FALSE, NULL);
40549030SMark.Shellenbaum@Sun.COM 		error = dmu_tx_assign(tx, TXG_WAIT);
40559030SMark.Shellenbaum@Sun.COM 		if (error) {
40569030SMark.Shellenbaum@Sun.COM 			dmu_tx_abort(tx);
40579030SMark.Shellenbaum@Sun.COM 		} else {
40589030SMark.Shellenbaum@Sun.COM 			error = zfs_create_share_dir(zfsvfs, tx);
40599030SMark.Shellenbaum@Sun.COM 			dmu_tx_commit(tx);
40609030SMark.Shellenbaum@Sun.COM 		}
40619030SMark.Shellenbaum@Sun.COM 		if (error) {
40629030SMark.Shellenbaum@Sun.COM 			mutex_exit(&zfsvfs->z_lock);
40639030SMark.Shellenbaum@Sun.COM 			VN_RELE(vp);
40649030SMark.Shellenbaum@Sun.COM 			ZFS_EXIT(zfsvfs);
40659030SMark.Shellenbaum@Sun.COM 			return (error);
40669030SMark.Shellenbaum@Sun.COM 		}
40679030SMark.Shellenbaum@Sun.COM 	}
40689030SMark.Shellenbaum@Sun.COM 	mutex_exit(&zfsvfs->z_lock);
40699030SMark.Shellenbaum@Sun.COM 
40709030SMark.Shellenbaum@Sun.COM 	ASSERT(zfsvfs->z_shares_dir);
40718845Samw@Sun.COM 	if ((error = zfs_zget(zfsvfs, zfsvfs->z_shares_dir, &sharedir)) != 0) {
40729030SMark.Shellenbaum@Sun.COM 		VN_RELE(vp);
40738845Samw@Sun.COM 		ZFS_EXIT(zfsvfs);
40748845Samw@Sun.COM 		return (error);
40758845Samw@Sun.COM 	}
40768845Samw@Sun.COM 
40778845Samw@Sun.COM 	switch (zc->zc_cookie) {
40788845Samw@Sun.COM 	case ZFS_SMB_ACL_ADD:
40798845Samw@Sun.COM 		vattr.va_mask = AT_MODE|AT_UID|AT_GID|AT_TYPE;
40808845Samw@Sun.COM 		vattr.va_type = VREG;
40818845Samw@Sun.COM 		vattr.va_mode = S_IFREG|0777;
40828845Samw@Sun.COM 		vattr.va_uid = 0;
40838845Samw@Sun.COM 		vattr.va_gid = 0;
40848845Samw@Sun.COM 
40858845Samw@Sun.COM 		vsec.vsa_mask = VSA_ACE;
40868845Samw@Sun.COM 		vsec.vsa_aclentp = &full_access;
40878845Samw@Sun.COM 		vsec.vsa_aclentsz = sizeof (full_access);
40888845Samw@Sun.COM 		vsec.vsa_aclcnt = 1;
40898845Samw@Sun.COM 
40908845Samw@Sun.COM 		error = VOP_CREATE(ZTOV(sharedir), zc->zc_string,
40918845Samw@Sun.COM 		    &vattr, EXCL, 0, &resourcevp, kcred, 0, NULL, &vsec);
40928845Samw@Sun.COM 		if (resourcevp)
40938845Samw@Sun.COM 			VN_RELE(resourcevp);
40948845Samw@Sun.COM 		break;
40958845Samw@Sun.COM 
40968845Samw@Sun.COM 	case ZFS_SMB_ACL_REMOVE:
40978845Samw@Sun.COM 		error = VOP_REMOVE(ZTOV(sharedir), zc->zc_string, kcred,
40988845Samw@Sun.COM 		    NULL, 0);
40998845Samw@Sun.COM 		break;
41008845Samw@Sun.COM 
41018845Samw@Sun.COM 	case ZFS_SMB_ACL_RENAME:
41028845Samw@Sun.COM 		if ((error = get_nvlist(zc->zc_nvlist_src,
41039643SEric.Taylor@Sun.COM 		    zc->zc_nvlist_src_size, zc->zc_iflags, &nvlist)) != 0) {
41048845Samw@Sun.COM 			VN_RELE(vp);
41058845Samw@Sun.COM 			ZFS_EXIT(zfsvfs);
41068845Samw@Sun.COM 			return (error);
41078845Samw@Sun.COM 		}
41088845Samw@Sun.COM 		if (nvlist_lookup_string(nvlist, ZFS_SMB_ACL_SRC, &src) ||
41098845Samw@Sun.COM 		    nvlist_lookup_string(nvlist, ZFS_SMB_ACL_TARGET,
41108845Samw@Sun.COM 		    &target)) {
41118845Samw@Sun.COM 			VN_RELE(vp);
41129179SMark.Shellenbaum@Sun.COM 			VN_RELE(ZTOV(sharedir));
41138845Samw@Sun.COM 			ZFS_EXIT(zfsvfs);
41148845Samw@Sun.COM 			return (error);
41158845Samw@Sun.COM 		}
41168845Samw@Sun.COM 		error = VOP_RENAME(ZTOV(sharedir), src, ZTOV(sharedir), target,
41178845Samw@Sun.COM 		    kcred, NULL, 0);
41188845Samw@Sun.COM 		nvlist_free(nvlist);
41198845Samw@Sun.COM 		break;
41208845Samw@Sun.COM 
41218845Samw@Sun.COM 	case ZFS_SMB_ACL_PURGE:
41228845Samw@Sun.COM 		error = zfs_smb_acl_purge(sharedir);
41238845Samw@Sun.COM 		break;
41248845Samw@Sun.COM 
41258845Samw@Sun.COM 	default:
41268845Samw@Sun.COM 		error = EINVAL;
41278845Samw@Sun.COM 		break;
41288845Samw@Sun.COM 	}
41298845Samw@Sun.COM 
41308845Samw@Sun.COM 	VN_RELE(vp);
41318845Samw@Sun.COM 	VN_RELE(ZTOV(sharedir));
41328845Samw@Sun.COM 
41338845Samw@Sun.COM 	ZFS_EXIT(zfsvfs);
41348845Samw@Sun.COM 
41358845Samw@Sun.COM 	return (error);
41368845Samw@Sun.COM }
41378845Samw@Sun.COM 
41384543Smarks /*
413910242Schris.kirby@sun.com  * inputs:
414010242Schris.kirby@sun.com  * zc_name	name of filesystem
414110242Schris.kirby@sun.com  * zc_value	short name of snap
414210242Schris.kirby@sun.com  * zc_string	user-supplied tag for this reference
414310242Schris.kirby@sun.com  * zc_cookie	recursive flag
414410342Schris.kirby@sun.com  * zc_temphold	set if hold is temporary
414510242Schris.kirby@sun.com  *
414610242Schris.kirby@sun.com  * outputs:		none
414710242Schris.kirby@sun.com  */
414810242Schris.kirby@sun.com static int
414910242Schris.kirby@sun.com zfs_ioc_hold(zfs_cmd_t *zc)
415010242Schris.kirby@sun.com {
415110242Schris.kirby@sun.com 	boolean_t recursive = zc->zc_cookie;
415210242Schris.kirby@sun.com 
415310242Schris.kirby@sun.com 	if (snapshot_namecheck(zc->zc_value, NULL, NULL) != 0)
415410242Schris.kirby@sun.com 		return (EINVAL);
415510242Schris.kirby@sun.com 
415610242Schris.kirby@sun.com 	return (dsl_dataset_user_hold(zc->zc_name, zc->zc_value,
415710342Schris.kirby@sun.com 	    zc->zc_string, recursive, zc->zc_temphold));
415810242Schris.kirby@sun.com }
415910242Schris.kirby@sun.com 
416010242Schris.kirby@sun.com /*
416110242Schris.kirby@sun.com  * inputs:
416210242Schris.kirby@sun.com  * zc_name	name of dataset from which we're releasing a user reference
416310242Schris.kirby@sun.com  * zc_value	short name of snap
416410242Schris.kirby@sun.com  * zc_string	user-supplied tag for this reference
416510242Schris.kirby@sun.com  * zc_cookie	recursive flag
416610242Schris.kirby@sun.com  *
416710242Schris.kirby@sun.com  * outputs:		none
416810242Schris.kirby@sun.com  */
416910242Schris.kirby@sun.com static int
417010242Schris.kirby@sun.com zfs_ioc_release(zfs_cmd_t *zc)
417110242Schris.kirby@sun.com {
417210242Schris.kirby@sun.com 	boolean_t recursive = zc->zc_cookie;
417310242Schris.kirby@sun.com 
417410242Schris.kirby@sun.com 	if (snapshot_namecheck(zc->zc_value, NULL, NULL) != 0)
417510242Schris.kirby@sun.com 		return (EINVAL);
417610242Schris.kirby@sun.com 
417710242Schris.kirby@sun.com 	return (dsl_dataset_user_release(zc->zc_name, zc->zc_value,
417810242Schris.kirby@sun.com 	    zc->zc_string, recursive));
417910242Schris.kirby@sun.com }
418010242Schris.kirby@sun.com 
418110242Schris.kirby@sun.com /*
418210242Schris.kirby@sun.com  * inputs:
418310242Schris.kirby@sun.com  * zc_name		name of filesystem
418410242Schris.kirby@sun.com  *
418510242Schris.kirby@sun.com  * outputs:
418610242Schris.kirby@sun.com  * zc_nvlist_src{_size}	nvlist of snapshot holds
418710242Schris.kirby@sun.com  */
418810242Schris.kirby@sun.com static int
418910242Schris.kirby@sun.com zfs_ioc_get_holds(zfs_cmd_t *zc)
419010242Schris.kirby@sun.com {
419110242Schris.kirby@sun.com 	nvlist_t *nvp;
419210242Schris.kirby@sun.com 	int error;
419310242Schris.kirby@sun.com 
419410242Schris.kirby@sun.com 	if ((error = dsl_dataset_get_holds(zc->zc_name, &nvp)) == 0) {
419510242Schris.kirby@sun.com 		error = put_nvlist(zc, nvp);
419610242Schris.kirby@sun.com 		nvlist_free(nvp);
419710242Schris.kirby@sun.com 	}
419810242Schris.kirby@sun.com 
419910242Schris.kirby@sun.com 	return (error);
420010242Schris.kirby@sun.com }
420110242Schris.kirby@sun.com 
420210242Schris.kirby@sun.com /*
42034988Sek110237  * pool create, destroy, and export don't log the history as part of
42044988Sek110237  * zfsdev_ioctl, but rather zfs_ioc_pool_create, and zfs_ioc_pool_export
42054988Sek110237  * do the logging of those commands.
42064543Smarks  */
4207789Sahrens static zfs_ioc_vec_t zfs_ioc_vec[] = {
42089234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_pool_create, zfs_secpolicy_config, POOL_NAME, B_FALSE,
42099234SGeorge.Wilson@Sun.COM 	    B_FALSE },
42109234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_pool_destroy,	zfs_secpolicy_config, POOL_NAME, B_FALSE,
42119234SGeorge.Wilson@Sun.COM 	    B_FALSE },
42129234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_pool_import, zfs_secpolicy_config, POOL_NAME, B_TRUE,
42139234SGeorge.Wilson@Sun.COM 	    B_FALSE },
42149234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_pool_export, zfs_secpolicy_config, POOL_NAME, B_FALSE,
42159234SGeorge.Wilson@Sun.COM 	    B_FALSE },
42169234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_pool_configs,	zfs_secpolicy_none, NO_NAME, B_FALSE,
42179234SGeorge.Wilson@Sun.COM 	    B_FALSE },
42189234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_pool_stats, zfs_secpolicy_read, POOL_NAME, B_FALSE,
42199234SGeorge.Wilson@Sun.COM 	    B_FALSE },
42209234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_pool_tryimport, zfs_secpolicy_config, NO_NAME, B_FALSE,
42219234SGeorge.Wilson@Sun.COM 	    B_FALSE },
42229234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_pool_scrub, zfs_secpolicy_config, POOL_NAME, B_TRUE,
42239234SGeorge.Wilson@Sun.COM 	    B_TRUE },
42249234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_pool_freeze, zfs_secpolicy_config, NO_NAME, B_FALSE,
42259234SGeorge.Wilson@Sun.COM 	    B_FALSE },
42269234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_pool_upgrade,	zfs_secpolicy_config, POOL_NAME, B_TRUE,
42279234SGeorge.Wilson@Sun.COM 	    B_TRUE },
42289234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_pool_get_history, zfs_secpolicy_config, POOL_NAME, B_FALSE,
42299234SGeorge.Wilson@Sun.COM 	    B_FALSE },
42309234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_vdev_add, zfs_secpolicy_config, POOL_NAME, B_TRUE,
42319234SGeorge.Wilson@Sun.COM 	    B_TRUE },
42329234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_vdev_remove, zfs_secpolicy_config, POOL_NAME, B_TRUE,
42339234SGeorge.Wilson@Sun.COM 	    B_TRUE },
42349234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_vdev_set_state, zfs_secpolicy_config,	POOL_NAME, B_TRUE,
42359234SGeorge.Wilson@Sun.COM 	    B_FALSE },
42369234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_vdev_attach, zfs_secpolicy_config, POOL_NAME, B_TRUE,
42379234SGeorge.Wilson@Sun.COM 	    B_TRUE },
42389234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_vdev_detach, zfs_secpolicy_config, POOL_NAME, B_TRUE,
42399234SGeorge.Wilson@Sun.COM 	    B_TRUE },
42409234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_vdev_setpath,	zfs_secpolicy_config, POOL_NAME, B_FALSE,
42419234SGeorge.Wilson@Sun.COM 	    B_TRUE },
42429425SEric.Schrock@Sun.COM 	{ zfs_ioc_vdev_setfru,	zfs_secpolicy_config, POOL_NAME, B_FALSE,
42439425SEric.Schrock@Sun.COM 	    B_TRUE },
42449234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_objset_stats,	zfs_secpolicy_read, DATASET_NAME, B_FALSE,
42459234SGeorge.Wilson@Sun.COM 	    B_FALSE },
42469234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_objset_zplprops, zfs_secpolicy_read, DATASET_NAME, B_FALSE,
42479234SGeorge.Wilson@Sun.COM 	    B_FALSE },
42489234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_dataset_list_next, zfs_secpolicy_read, DATASET_NAME, B_FALSE,
42499234SGeorge.Wilson@Sun.COM 	    B_FALSE },
42509234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_snapshot_list_next, zfs_secpolicy_read, DATASET_NAME, B_FALSE,
42519234SGeorge.Wilson@Sun.COM 	    B_FALSE },
42529234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_set_prop, zfs_secpolicy_none, DATASET_NAME, B_TRUE, B_TRUE },
42539234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_create, zfs_secpolicy_create, DATASET_NAME, B_TRUE, B_TRUE },
42549234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_destroy, zfs_secpolicy_destroy, DATASET_NAME, B_TRUE,
42559234SGeorge.Wilson@Sun.COM 	    B_TRUE},
42569234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_rollback, zfs_secpolicy_rollback, DATASET_NAME, B_TRUE,
42579234SGeorge.Wilson@Sun.COM 	    B_TRUE },
42589234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_rename, zfs_secpolicy_rename,	DATASET_NAME, B_TRUE, B_TRUE },
42599234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_recv, zfs_secpolicy_receive, DATASET_NAME, B_TRUE, B_TRUE },
42609234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_send, zfs_secpolicy_send, DATASET_NAME, B_TRUE, B_FALSE },
42619234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_inject_fault,	zfs_secpolicy_inject, NO_NAME, B_FALSE,
42629234SGeorge.Wilson@Sun.COM 	    B_FALSE },
42639234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_clear_fault, zfs_secpolicy_inject, NO_NAME, B_FALSE,
42649234SGeorge.Wilson@Sun.COM 	    B_FALSE },
42659234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_inject_list_next, zfs_secpolicy_inject, NO_NAME, B_FALSE,
42669234SGeorge.Wilson@Sun.COM 	    B_FALSE },
42679234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_error_log, zfs_secpolicy_inject, POOL_NAME, B_FALSE,
42689234SGeorge.Wilson@Sun.COM 	    B_FALSE },
42699234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_clear, zfs_secpolicy_config, POOL_NAME, B_TRUE, B_FALSE },
42709234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_promote, zfs_secpolicy_promote, DATASET_NAME, B_TRUE,
42719234SGeorge.Wilson@Sun.COM 	    B_TRUE },
42729234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_destroy_snaps, zfs_secpolicy_destroy,	DATASET_NAME, B_TRUE,
42739234SGeorge.Wilson@Sun.COM 	    B_TRUE },
42749234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_snapshot, zfs_secpolicy_snapshot, DATASET_NAME, B_TRUE,
42759234SGeorge.Wilson@Sun.COM 	    B_TRUE },
42769234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_dsobj_to_dsname, zfs_secpolicy_config, POOL_NAME, B_FALSE,
42779234SGeorge.Wilson@Sun.COM 	    B_FALSE },
427810233SGeorge.Wilson@Sun.COM 	{ zfs_ioc_obj_to_path, zfs_secpolicy_config, DATASET_NAME, B_FALSE,
427910233SGeorge.Wilson@Sun.COM 	    B_TRUE },
42809234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_pool_set_props, zfs_secpolicy_config,	POOL_NAME, B_TRUE,
42819234SGeorge.Wilson@Sun.COM 	    B_TRUE },
42829234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_pool_get_props, zfs_secpolicy_read, POOL_NAME, B_FALSE,
42839234SGeorge.Wilson@Sun.COM 	    B_FALSE },
42849234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_set_fsacl, zfs_secpolicy_fsacl, DATASET_NAME, B_TRUE,
42859234SGeorge.Wilson@Sun.COM 	    B_TRUE },
42869234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_get_fsacl, zfs_secpolicy_read, DATASET_NAME, B_FALSE,
42879234SGeorge.Wilson@Sun.COM 	    B_FALSE },
42889234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_iscsi_perm_check, zfs_secpolicy_iscsi, DATASET_NAME, B_FALSE,
42899234SGeorge.Wilson@Sun.COM 	    B_FALSE },
42909234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_share, zfs_secpolicy_share, DATASET_NAME, B_FALSE, B_FALSE },
42919234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_inherit_prop, zfs_secpolicy_inherit, DATASET_NAME, B_TRUE,
42929234SGeorge.Wilson@Sun.COM 	    B_TRUE },
42939234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_smb_acl, zfs_secpolicy_smb_acl, DATASET_NAME, B_FALSE,
42949396SMatthew.Ahrens@Sun.COM 	    B_FALSE },
42959396SMatthew.Ahrens@Sun.COM 	{ zfs_ioc_userspace_one, zfs_secpolicy_userspace_one,
42969396SMatthew.Ahrens@Sun.COM 	    DATASET_NAME, B_FALSE, B_FALSE },
42979396SMatthew.Ahrens@Sun.COM 	{ zfs_ioc_userspace_many, zfs_secpolicy_userspace_many,
42989396SMatthew.Ahrens@Sun.COM 	    DATASET_NAME, B_FALSE, B_FALSE },
42999396SMatthew.Ahrens@Sun.COM 	{ zfs_ioc_userspace_upgrade, zfs_secpolicy_userspace_upgrade,
43009396SMatthew.Ahrens@Sun.COM 	    DATASET_NAME, B_FALSE, B_TRUE },
430110242Schris.kirby@sun.com 	{ zfs_ioc_hold, zfs_secpolicy_hold, DATASET_NAME, B_TRUE, B_TRUE },
430210242Schris.kirby@sun.com 	{ zfs_ioc_release, zfs_secpolicy_release, DATASET_NAME, B_TRUE,
430310242Schris.kirby@sun.com 	    B_TRUE },
430410242Schris.kirby@sun.com 	{ zfs_ioc_get_holds, zfs_secpolicy_read, DATASET_NAME, B_FALSE,
430511022STom.Erickson@Sun.COM 	    B_TRUE },
430611022STom.Erickson@Sun.COM 	{ zfs_ioc_objset_recvd_props, zfs_secpolicy_read, DATASET_NAME, B_FALSE,
430711022STom.Erickson@Sun.COM 	    B_FALSE }
4308789Sahrens };
4309789Sahrens 
43109234SGeorge.Wilson@Sun.COM int
43119234SGeorge.Wilson@Sun.COM pool_status_check(const char *name, zfs_ioc_namecheck_t type)
43129234SGeorge.Wilson@Sun.COM {
43139234SGeorge.Wilson@Sun.COM 	spa_t *spa;
43149234SGeorge.Wilson@Sun.COM 	int error;
43159234SGeorge.Wilson@Sun.COM 
43169234SGeorge.Wilson@Sun.COM 	ASSERT(type == POOL_NAME || type == DATASET_NAME);
43179234SGeorge.Wilson@Sun.COM 
43189396SMatthew.Ahrens@Sun.COM 	error = spa_open(name, &spa, FTAG);
43199234SGeorge.Wilson@Sun.COM 	if (error == 0) {
43209234SGeorge.Wilson@Sun.COM 		if (spa_suspended(spa))
43219234SGeorge.Wilson@Sun.COM 			error = EAGAIN;
43229234SGeorge.Wilson@Sun.COM 		spa_close(spa, FTAG);
43239234SGeorge.Wilson@Sun.COM 	}
43249234SGeorge.Wilson@Sun.COM 	return (error);
43259234SGeorge.Wilson@Sun.COM }
43269234SGeorge.Wilson@Sun.COM 
4327789Sahrens static int
4328789Sahrens zfsdev_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *cr, int *rvalp)
4329789Sahrens {
4330789Sahrens 	zfs_cmd_t *zc;
4331789Sahrens 	uint_t vec;
43322199Sahrens 	int error, rc;
4333789Sahrens 
4334789Sahrens 	if (getminor(dev) != 0)
4335789Sahrens 		return (zvol_ioctl(dev, cmd, arg, flag, cr, rvalp));
4336789Sahrens 
4337789Sahrens 	vec = cmd - ZFS_IOC;
43384787Sahrens 	ASSERT3U(getmajor(dev), ==, ddi_driver_major(zfs_dip));
4339789Sahrens 
4340789Sahrens 	if (vec >= sizeof (zfs_ioc_vec) / sizeof (zfs_ioc_vec[0]))
4341789Sahrens 		return (EINVAL);
4342789Sahrens 
4343789Sahrens 	zc = kmem_zalloc(sizeof (zfs_cmd_t), KM_SLEEP);
4344789Sahrens 
43459643SEric.Taylor@Sun.COM 	error = ddi_copyin((void *)arg, zc, sizeof (zfs_cmd_t), flag);
4346789Sahrens 
434710588SEric.Taylor@Sun.COM 	if ((error == 0) && !(flag & FKIOCTL))
43484543Smarks 		error = zfs_ioc_vec[vec].zvec_secpolicy(zc, cr);
4349789Sahrens 
4350789Sahrens 	/*
4351789Sahrens 	 * Ensure that all pool/dataset names are valid before we pass down to
4352789Sahrens 	 * the lower layers.
4353789Sahrens 	 */
4354789Sahrens 	if (error == 0) {
4355789Sahrens 		zc->zc_name[sizeof (zc->zc_name) - 1] = '\0';
43569643SEric.Taylor@Sun.COM 		zc->zc_iflags = flag & FKIOCTL;
4357789Sahrens 		switch (zfs_ioc_vec[vec].zvec_namecheck) {
43584577Sahrens 		case POOL_NAME:
4359789Sahrens 			if (pool_namecheck(zc->zc_name, NULL, NULL) != 0)
4360789Sahrens 				error = EINVAL;
43619234SGeorge.Wilson@Sun.COM 			if (zfs_ioc_vec[vec].zvec_pool_check)
43629234SGeorge.Wilson@Sun.COM 				error = pool_status_check(zc->zc_name,
43639234SGeorge.Wilson@Sun.COM 				    zfs_ioc_vec[vec].zvec_namecheck);
4364789Sahrens 			break;
4365789Sahrens 
43664577Sahrens 		case DATASET_NAME:
4367789Sahrens 			if (dataset_namecheck(zc->zc_name, NULL, NULL) != 0)
4368789Sahrens 				error = EINVAL;
43699234SGeorge.Wilson@Sun.COM 			if (zfs_ioc_vec[vec].zvec_pool_check)
43709234SGeorge.Wilson@Sun.COM 				error = pool_status_check(zc->zc_name,
43719234SGeorge.Wilson@Sun.COM 				    zfs_ioc_vec[vec].zvec_namecheck);
4372789Sahrens 			break;
43732856Snd150628 
43744577Sahrens 		case NO_NAME:
43752856Snd150628 			break;
4376789Sahrens 		}
4377789Sahrens 	}
4378789Sahrens 
4379789Sahrens 	if (error == 0)
4380789Sahrens 		error = zfs_ioc_vec[vec].zvec_func(zc);
4381789Sahrens 
43829643SEric.Taylor@Sun.COM 	rc = ddi_copyout(zc, (void *)arg, sizeof (zfs_cmd_t), flag);
43834543Smarks 	if (error == 0) {
43842199Sahrens 		error = rc;
43859396SMatthew.Ahrens@Sun.COM 		if (zfs_ioc_vec[vec].zvec_his_log)
43864543Smarks 			zfs_log_history(zc);
43874543Smarks 	}
4388789Sahrens 
4389789Sahrens 	kmem_free(zc, sizeof (zfs_cmd_t));
4390789Sahrens 	return (error);
4391789Sahrens }
4392789Sahrens 
4393789Sahrens static int
4394789Sahrens zfs_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
4395789Sahrens {
4396789Sahrens 	if (cmd != DDI_ATTACH)
4397789Sahrens 		return (DDI_FAILURE);
4398789Sahrens 
4399789Sahrens 	if (ddi_create_minor_node(dip, "zfs", S_IFCHR, 0,
4400789Sahrens 	    DDI_PSEUDO, 0) == DDI_FAILURE)
4401789Sahrens 		return (DDI_FAILURE);
4402789Sahrens 
4403789Sahrens 	zfs_dip = dip;
4404789Sahrens 
4405789Sahrens 	ddi_report_dev(dip);
4406789Sahrens 
4407789Sahrens 	return (DDI_SUCCESS);
4408789Sahrens }
4409789Sahrens 
4410789Sahrens static int
4411789Sahrens zfs_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
4412789Sahrens {
4413789Sahrens 	if (spa_busy() || zfs_busy() || zvol_busy())
4414789Sahrens 		return (DDI_FAILURE);
4415789Sahrens 
4416789Sahrens 	if (cmd != DDI_DETACH)
4417789Sahrens 		return (DDI_FAILURE);
4418789Sahrens 
4419789Sahrens 	zfs_dip = NULL;
4420789Sahrens 
4421789Sahrens 	ddi_prop_remove_all(dip);
4422789Sahrens 	ddi_remove_minor_node(dip, NULL);
4423789Sahrens 
4424789Sahrens 	return (DDI_SUCCESS);
4425789Sahrens }
4426789Sahrens 
4427789Sahrens /*ARGSUSED*/
4428789Sahrens static int
4429789Sahrens zfs_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
4430789Sahrens {
4431789Sahrens 	switch (infocmd) {
4432789Sahrens 	case DDI_INFO_DEVT2DEVINFO:
4433789Sahrens 		*result = zfs_dip;
4434789Sahrens 		return (DDI_SUCCESS);
4435789Sahrens 
4436789Sahrens 	case DDI_INFO_DEVT2INSTANCE:
4437849Sbonwick 		*result = (void *)0;
4438789Sahrens 		return (DDI_SUCCESS);
4439789Sahrens 	}
4440789Sahrens 
4441789Sahrens 	return (DDI_FAILURE);
4442789Sahrens }
4443789Sahrens 
4444789Sahrens /*
4445789Sahrens  * OK, so this is a little weird.
4446789Sahrens  *
4447789Sahrens  * /dev/zfs is the control node, i.e. minor 0.
4448789Sahrens  * /dev/zvol/[r]dsk/pool/dataset are the zvols, minor > 0.
4449789Sahrens  *
4450789Sahrens  * /dev/zfs has basically nothing to do except serve up ioctls,
4451789Sahrens  * so most of the standard driver entry points are in zvol.c.
4452789Sahrens  */
4453789Sahrens static struct cb_ops zfs_cb_ops = {
4454789Sahrens 	zvol_open,	/* open */
4455789Sahrens 	zvol_close,	/* close */
4456789Sahrens 	zvol_strategy,	/* strategy */
4457789Sahrens 	nodev,		/* print */
44586423Sgw25295 	zvol_dump,	/* dump */
4459789Sahrens 	zvol_read,	/* read */
4460789Sahrens 	zvol_write,	/* write */
4461789Sahrens 	zfsdev_ioctl,	/* ioctl */
4462789Sahrens 	nodev,		/* devmap */
4463789Sahrens 	nodev,		/* mmap */
4464789Sahrens 	nodev,		/* segmap */
4465789Sahrens 	nochpoll,	/* poll */
4466789Sahrens 	ddi_prop_op,	/* prop_op */
4467789Sahrens 	NULL,		/* streamtab */
4468789Sahrens 	D_NEW | D_MP | D_64BIT,		/* Driver compatibility flag */
4469789Sahrens 	CB_REV,		/* version */
44703638Sbillm 	nodev,		/* async read */
44713638Sbillm 	nodev,		/* async write */
4472789Sahrens };
4473789Sahrens 
4474789Sahrens static struct dev_ops zfs_dev_ops = {
4475789Sahrens 	DEVO_REV,	/* version */
4476789Sahrens 	0,		/* refcnt */
4477789Sahrens 	zfs_info,	/* info */
4478789Sahrens 	nulldev,	/* identify */
4479789Sahrens 	nulldev,	/* probe */
4480789Sahrens 	zfs_attach,	/* attach */
4481789Sahrens 	zfs_detach,	/* detach */
4482789Sahrens 	nodev,		/* reset */
4483789Sahrens 	&zfs_cb_ops,	/* driver operations */
44847656SSherry.Moore@Sun.COM 	NULL,		/* no bus operations */
44857656SSherry.Moore@Sun.COM 	NULL,		/* power */
44867656SSherry.Moore@Sun.COM 	ddi_quiesce_not_needed,	/* quiesce */
4487789Sahrens };
4488789Sahrens 
4489789Sahrens static struct modldrv zfs_modldrv = {
44907656SSherry.Moore@Sun.COM 	&mod_driverops,
44917656SSherry.Moore@Sun.COM 	"ZFS storage pool",
44927656SSherry.Moore@Sun.COM 	&zfs_dev_ops
4493789Sahrens };
4494789Sahrens 
4495789Sahrens static struct modlinkage modlinkage = {
4496789Sahrens 	MODREV_1,
4497789Sahrens 	(void *)&zfs_modlfs,
4498789Sahrens 	(void *)&zfs_modldrv,
4499789Sahrens 	NULL
4500789Sahrens };
4501789Sahrens 
45024720Sfr157268 
45034720Sfr157268 uint_t zfs_fsyncer_key;
45045326Sek110237 extern uint_t rrw_tsd_key;
45054720Sfr157268 
4506789Sahrens int
4507789Sahrens _init(void)
4508789Sahrens {
4509789Sahrens 	int error;
4510789Sahrens 
4511849Sbonwick 	spa_init(FREAD | FWRITE);
4512849Sbonwick 	zfs_init();
4513849Sbonwick 	zvol_init();
4514849Sbonwick 
4515849Sbonwick 	if ((error = mod_install(&modlinkage)) != 0) {
4516849Sbonwick 		zvol_fini();
4517849Sbonwick 		zfs_fini();
4518849Sbonwick 		spa_fini();
4519789Sahrens 		return (error);
4520849Sbonwick 	}
4521789Sahrens 
45224720Sfr157268 	tsd_create(&zfs_fsyncer_key, NULL);
45235326Sek110237 	tsd_create(&rrw_tsd_key, NULL);
45244720Sfr157268 
4525789Sahrens 	error = ldi_ident_from_mod(&modlinkage, &zfs_li);
4526789Sahrens 	ASSERT(error == 0);
45274543Smarks 	mutex_init(&zfs_share_lock, NULL, MUTEX_DEFAULT, NULL);
4528789Sahrens 
4529789Sahrens 	return (0);
4530789Sahrens }
4531789Sahrens 
4532789Sahrens int
4533789Sahrens _fini(void)
4534789Sahrens {
4535789Sahrens 	int error;
4536789Sahrens 
45371544Seschrock 	if (spa_busy() || zfs_busy() || zvol_busy() || zio_injection_enabled)
4538789Sahrens 		return (EBUSY);
4539789Sahrens 
4540789Sahrens 	if ((error = mod_remove(&modlinkage)) != 0)
4541789Sahrens 		return (error);
4542789Sahrens 
4543789Sahrens 	zvol_fini();
4544789Sahrens 	zfs_fini();
4545789Sahrens 	spa_fini();
45465331Samw 	if (zfs_nfsshare_inited)
45474543Smarks 		(void) ddi_modclose(nfs_mod);
45485331Samw 	if (zfs_smbshare_inited)
45495331Samw 		(void) ddi_modclose(smbsrv_mod);
45505331Samw 	if (zfs_nfsshare_inited || zfs_smbshare_inited)
45514543Smarks 		(void) ddi_modclose(sharefs_mod);
4552789Sahrens 
45534720Sfr157268 	tsd_destroy(&zfs_fsyncer_key);
4554789Sahrens 	ldi_ident_release(zfs_li);
4555789Sahrens 	zfs_li = NULL;
45564543Smarks 	mutex_destroy(&zfs_share_lock);
4557789Sahrens 
4558789Sahrens 	return (error);
4559789Sahrens }
4560789Sahrens 
4561789Sahrens int
4562789Sahrens _info(struct modinfo *modinfop)
4563789Sahrens {
4564789Sahrens 	return (mod_info(&modlinkage, modinfop));
4565789Sahrens }
4566