xref: /onnv-gate/usr/src/uts/common/fs/zfs/zfs_ioctl.c (revision 10972:807794d41b3a)
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>
39*10972SRic.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>
45*10972SRic.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 
105*10972SRic.Aleshire@Sun.COM static char *setsl_tag = "setsl_tag";
106*10972SRic.Aleshire@Sun.COM 
1079396SMatthew.Ahrens@Sun.COM static int zfs_ioc_userspace_upgrade(zfs_cmd_t *zc);
1088536SDavid.Pacheco@Sun.COM static void clear_props(char *dataset, nvlist_t *props, nvlist_t *newprops);
1097184Stimh static int zfs_fill_zplprops_root(uint64_t, nvlist_t *, nvlist_t *,
1107184Stimh     boolean_t *);
1117184Stimh int zfs_set_prop_nvlist(const char *, nvlist_t *);
1127184Stimh 
113789Sahrens /* _NOTE(PRINTFLIKE(4)) - this is printf-like, but lint is too whiney */
114789Sahrens void
115789Sahrens __dprintf(const char *file, const char *func, int line, const char *fmt, ...)
116789Sahrens {
117789Sahrens 	const char *newfile;
118789Sahrens 	char buf[256];
119789Sahrens 	va_list adx;
120789Sahrens 
121789Sahrens 	/*
122789Sahrens 	 * Get rid of annoying "../common/" prefix to filename.
123789Sahrens 	 */
124789Sahrens 	newfile = strrchr(file, '/');
125789Sahrens 	if (newfile != NULL) {
126789Sahrens 		newfile = newfile + 1; /* Get rid of leading / */
127789Sahrens 	} else {
128789Sahrens 		newfile = file;
129789Sahrens 	}
130789Sahrens 
131789Sahrens 	va_start(adx, fmt);
132789Sahrens 	(void) vsnprintf(buf, sizeof (buf), fmt, adx);
133789Sahrens 	va_end(adx);
134789Sahrens 
135789Sahrens 	/*
136789Sahrens 	 * To get this data, use the zfs-dprintf probe as so:
137789Sahrens 	 * dtrace -q -n 'zfs-dprintf \
138789Sahrens 	 *	/stringof(arg0) == "dbuf.c"/ \
139789Sahrens 	 *	{printf("%s: %s", stringof(arg1), stringof(arg3))}'
140789Sahrens 	 * arg0 = file name
141789Sahrens 	 * arg1 = function name
142789Sahrens 	 * arg2 = line number
143789Sahrens 	 * arg3 = message
144789Sahrens 	 */
145789Sahrens 	DTRACE_PROBE4(zfs__dprintf,
146789Sahrens 	    char *, newfile, char *, func, int, line, char *, buf);
147789Sahrens }
148789Sahrens 
1494543Smarks static void
1504715Sek110237 history_str_free(char *buf)
1514715Sek110237 {
1524715Sek110237 	kmem_free(buf, HIS_MAX_RECORD_LEN);
1534715Sek110237 }
1544715Sek110237 
1554715Sek110237 static char *
1564715Sek110237 history_str_get(zfs_cmd_t *zc)
1574715Sek110237 {
1584715Sek110237 	char *buf;
1594715Sek110237 
1604715Sek110237 	if (zc->zc_history == NULL)
1614715Sek110237 		return (NULL);
1624715Sek110237 
1634715Sek110237 	buf = kmem_alloc(HIS_MAX_RECORD_LEN, KM_SLEEP);
1644715Sek110237 	if (copyinstr((void *)(uintptr_t)zc->zc_history,
1654715Sek110237 	    buf, HIS_MAX_RECORD_LEN, NULL) != 0) {
1664715Sek110237 		history_str_free(buf);
1674715Sek110237 		return (NULL);
1684715Sek110237 	}
1694715Sek110237 
1704715Sek110237 	buf[HIS_MAX_RECORD_LEN -1] = '\0';
1714715Sek110237 
1724715Sek110237 	return (buf);
1734715Sek110237 }
1744715Sek110237 
1755375Stimh /*
1767042Sgw25295  * Check to see if the named dataset is currently defined as bootable
1777042Sgw25295  */
1787042Sgw25295 static boolean_t
1797042Sgw25295 zfs_is_bootfs(const char *name)
1807042Sgw25295 {
18110298SMatthew.Ahrens@Sun.COM 	objset_t *os;
18210298SMatthew.Ahrens@Sun.COM 
18310298SMatthew.Ahrens@Sun.COM 	if (dmu_objset_hold(name, FTAG, &os) == 0) {
18410298SMatthew.Ahrens@Sun.COM 		boolean_t ret;
18510922SJeff.Bonwick@Sun.COM 		ret = (dmu_objset_id(os) == spa_bootfs(dmu_objset_spa(os)));
18610298SMatthew.Ahrens@Sun.COM 		dmu_objset_rele(os, FTAG);
18710298SMatthew.Ahrens@Sun.COM 		return (ret);
1887042Sgw25295 	}
18910298SMatthew.Ahrens@Sun.COM 	return (B_FALSE);
1907042Sgw25295 }
1917042Sgw25295 
1927042Sgw25295 /*
1937184Stimh  * zfs_earlier_version
1945375Stimh  *
1955375Stimh  *	Return non-zero if the spa version is less than requested version.
1965375Stimh  */
1975331Samw static int
1987184Stimh zfs_earlier_version(const char *name, int version)
1995331Samw {
2005331Samw 	spa_t *spa;
2015331Samw 
2025331Samw 	if (spa_open(name, &spa, FTAG) == 0) {
2035331Samw 		if (spa_version(spa) < version) {
2045331Samw 			spa_close(spa, FTAG);
2055331Samw 			return (1);
2065331Samw 		}
2075331Samw 		spa_close(spa, FTAG);
2085331Samw 	}
2095331Samw 	return (0);
2105331Samw }
2115331Samw 
2125977Smarks /*
2136689Smaybee  * zpl_earlier_version
2145977Smarks  *
2156689Smaybee  * Return TRUE if the ZPL version is less than requested version.
2165977Smarks  */
2176689Smaybee static boolean_t
2186689Smaybee zpl_earlier_version(const char *name, int version)
2195977Smarks {
2205977Smarks 	objset_t *os;
2216689Smaybee 	boolean_t rc = B_TRUE;
2225977Smarks 
22310298SMatthew.Ahrens@Sun.COM 	if (dmu_objset_hold(name, FTAG, &os) == 0) {
2246689Smaybee 		uint64_t zplversion;
2256689Smaybee 
22610298SMatthew.Ahrens@Sun.COM 		if (dmu_objset_type(os) != DMU_OST_ZFS) {
22710298SMatthew.Ahrens@Sun.COM 			dmu_objset_rele(os, FTAG);
22810298SMatthew.Ahrens@Sun.COM 			return (B_TRUE);
22910298SMatthew.Ahrens@Sun.COM 		}
23010298SMatthew.Ahrens@Sun.COM 		/* XXX reading from non-owned objset */
2316689Smaybee 		if (zfs_get_zplprop(os, ZFS_PROP_VERSION, &zplversion) == 0)
2326689Smaybee 			rc = zplversion < version;
23310298SMatthew.Ahrens@Sun.COM 		dmu_objset_rele(os, FTAG);
2345977Smarks 	}
2355977Smarks 	return (rc);
2365977Smarks }
2375977Smarks 
2384715Sek110237 static void
2394543Smarks zfs_log_history(zfs_cmd_t *zc)
2404543Smarks {
2414543Smarks 	spa_t *spa;
2424603Sahrens 	char *buf;
2434543Smarks 
2444715Sek110237 	if ((buf = history_str_get(zc)) == NULL)
2454577Sahrens 		return;
2464577Sahrens 
2474715Sek110237 	if (spa_open(zc->zc_name, &spa, FTAG) == 0) {
2484715Sek110237 		if (spa_version(spa) >= SPA_VERSION_ZPOOL_HISTORY)
2494715Sek110237 			(void) spa_history_log(spa, buf, LOG_CMD_NORMAL);
2504715Sek110237 		spa_close(spa, FTAG);
2514543Smarks 	}
2524715Sek110237 	history_str_free(buf);
2534543Smarks }
2544543Smarks 
255789Sahrens /*
256789Sahrens  * Policy for top-level read operations (list pools).  Requires no privileges,
257789Sahrens  * and can be used in the local zone, as there is no associated dataset.
258789Sahrens  */
259789Sahrens /* ARGSUSED */
260789Sahrens static int
2614543Smarks zfs_secpolicy_none(zfs_cmd_t *zc, cred_t *cr)
262789Sahrens {
263789Sahrens 	return (0);
264789Sahrens }
265789Sahrens 
266789Sahrens /*
267789Sahrens  * Policy for dataset read operations (list children, get statistics).  Requires
268789Sahrens  * no privileges, but must be visible in the local zone.
269789Sahrens  */
270789Sahrens /* ARGSUSED */
271789Sahrens static int
2724543Smarks zfs_secpolicy_read(zfs_cmd_t *zc, cred_t *cr)
273789Sahrens {
274789Sahrens 	if (INGLOBALZONE(curproc) ||
2754543Smarks 	    zone_dataset_visible(zc->zc_name, NULL))
276789Sahrens 		return (0);
277789Sahrens 
278789Sahrens 	return (ENOENT);
279789Sahrens }
280789Sahrens 
281789Sahrens static int
282789Sahrens zfs_dozonecheck(const char *dataset, cred_t *cr)
283789Sahrens {
284789Sahrens 	uint64_t zoned;
285789Sahrens 	int writable = 1;
286789Sahrens 
287789Sahrens 	/*
288789Sahrens 	 * The dataset must be visible by this zone -- check this first
289789Sahrens 	 * so they don't see EPERM on something they shouldn't know about.
290789Sahrens 	 */
291789Sahrens 	if (!INGLOBALZONE(curproc) &&
292789Sahrens 	    !zone_dataset_visible(dataset, &writable))
293789Sahrens 		return (ENOENT);
294789Sahrens 
295789Sahrens 	if (dsl_prop_get_integer(dataset, "zoned", &zoned, NULL))
296789Sahrens 		return (ENOENT);
297789Sahrens 
298789Sahrens 	if (INGLOBALZONE(curproc)) {
299789Sahrens 		/*
300789Sahrens 		 * If the fs is zoned, only root can access it from the
301789Sahrens 		 * global zone.
302789Sahrens 		 */
303789Sahrens 		if (secpolicy_zfs(cr) && zoned)
304789Sahrens 			return (EPERM);
305789Sahrens 	} else {
306789Sahrens 		/*
307789Sahrens 		 * If we are in a local zone, the 'zoned' property must be set.
308789Sahrens 		 */
309789Sahrens 		if (!zoned)
310789Sahrens 			return (EPERM);
311789Sahrens 
312789Sahrens 		/* must be writable by this zone */
313789Sahrens 		if (!writable)
314789Sahrens 			return (EPERM);
315789Sahrens 	}
316789Sahrens 	return (0);
317789Sahrens }
318789Sahrens 
319789Sahrens int
3204543Smarks zfs_secpolicy_write_perms(const char *name, const char *perm, cred_t *cr)
321789Sahrens {
322789Sahrens 	int error;
323789Sahrens 
3244543Smarks 	error = zfs_dozonecheck(name, cr);
3254543Smarks 	if (error == 0) {
3264543Smarks 		error = secpolicy_zfs(cr);
3274670Sahrens 		if (error)
3284543Smarks 			error = dsl_deleg_access(name, perm, cr);
3294543Smarks 	}
3304543Smarks 	return (error);
3314543Smarks }
3324543Smarks 
333*10972SRic.Aleshire@Sun.COM /*
334*10972SRic.Aleshire@Sun.COM  * Policy for setting the security label property.
335*10972SRic.Aleshire@Sun.COM  *
336*10972SRic.Aleshire@Sun.COM  * Returns 0 for success, non-zero for access and other errors.
337*10972SRic.Aleshire@Sun.COM  *
338*10972SRic.Aleshire@Sun.COM  * If the objset is non-NULL upon return, the caller is responsible
339*10972SRic.Aleshire@Sun.COM  * for dis-owning it, using the tag: setsl_tag.
340*10972SRic.Aleshire@Sun.COM  *
341*10972SRic.Aleshire@Sun.COM  */
342*10972SRic.Aleshire@Sun.COM static int
343*10972SRic.Aleshire@Sun.COM zfs_set_slabel_policy(const char *name, char *strval, cred_t *cr,
344*10972SRic.Aleshire@Sun.COM     objset_t **osp)
345*10972SRic.Aleshire@Sun.COM {
346*10972SRic.Aleshire@Sun.COM 	char		ds_hexsl[MAXNAMELEN];
347*10972SRic.Aleshire@Sun.COM 	bslabel_t	ds_sl, new_sl;
348*10972SRic.Aleshire@Sun.COM 	boolean_t	new_default = FALSE;
349*10972SRic.Aleshire@Sun.COM 	uint64_t	zoned;
350*10972SRic.Aleshire@Sun.COM 	int		needed_priv = -1;
351*10972SRic.Aleshire@Sun.COM 	int		error;
352*10972SRic.Aleshire@Sun.COM 
353*10972SRic.Aleshire@Sun.COM 	/* First get the existing dataset label. */
354*10972SRic.Aleshire@Sun.COM 	error = dsl_prop_get(name, zfs_prop_to_name(ZFS_PROP_MLSLABEL),
355*10972SRic.Aleshire@Sun.COM 	    1, sizeof (ds_hexsl), &ds_hexsl, NULL);
356*10972SRic.Aleshire@Sun.COM 	if (error)
357*10972SRic.Aleshire@Sun.COM 		return (EPERM);
358*10972SRic.Aleshire@Sun.COM 
359*10972SRic.Aleshire@Sun.COM 	if (strcasecmp(strval, ZFS_MLSLABEL_DEFAULT) == 0)
360*10972SRic.Aleshire@Sun.COM 		new_default = TRUE;
361*10972SRic.Aleshire@Sun.COM 
362*10972SRic.Aleshire@Sun.COM 	/* The label must be translatable */
363*10972SRic.Aleshire@Sun.COM 	if (!new_default && (hexstr_to_label(strval, &new_sl) != 0))
364*10972SRic.Aleshire@Sun.COM 		return (EINVAL);
365*10972SRic.Aleshire@Sun.COM 
366*10972SRic.Aleshire@Sun.COM 	/*
367*10972SRic.Aleshire@Sun.COM 	 * In a non-global zone, disallow attempts to set a label that
368*10972SRic.Aleshire@Sun.COM 	 * doesn't match that of the zone; otherwise no other checks
369*10972SRic.Aleshire@Sun.COM 	 * are needed.
370*10972SRic.Aleshire@Sun.COM 	 */
371*10972SRic.Aleshire@Sun.COM 	if (!INGLOBALZONE(curproc)) {
372*10972SRic.Aleshire@Sun.COM 		if (new_default || !blequal(&new_sl, CR_SL(CRED())))
373*10972SRic.Aleshire@Sun.COM 			return (EPERM);
374*10972SRic.Aleshire@Sun.COM 		return (0);
375*10972SRic.Aleshire@Sun.COM 	}
376*10972SRic.Aleshire@Sun.COM 
377*10972SRic.Aleshire@Sun.COM 	/*
378*10972SRic.Aleshire@Sun.COM 	 * For global-zone datasets (i.e., those whose zoned property is
379*10972SRic.Aleshire@Sun.COM 	 * "off", verify that the specified new label is valid for the
380*10972SRic.Aleshire@Sun.COM 	 * global zone.
381*10972SRic.Aleshire@Sun.COM 	 */
382*10972SRic.Aleshire@Sun.COM 	if (dsl_prop_get_integer(name,
383*10972SRic.Aleshire@Sun.COM 	    zfs_prop_to_name(ZFS_PROP_ZONED), &zoned, NULL))
384*10972SRic.Aleshire@Sun.COM 		return (EPERM);
385*10972SRic.Aleshire@Sun.COM 	if (!zoned) {
386*10972SRic.Aleshire@Sun.COM 		if (zfs_check_global_label(name, strval) != 0)
387*10972SRic.Aleshire@Sun.COM 			return (EPERM);
388*10972SRic.Aleshire@Sun.COM 	}
389*10972SRic.Aleshire@Sun.COM 
390*10972SRic.Aleshire@Sun.COM 	/*
391*10972SRic.Aleshire@Sun.COM 	 * If the existing dataset label is nondefault, check if the
392*10972SRic.Aleshire@Sun.COM 	 * dataset is mounted (label cannot be changed while mounted).
393*10972SRic.Aleshire@Sun.COM 	 * Get the zfsvfs; if there isn't one, then the dataset isn't
394*10972SRic.Aleshire@Sun.COM 	 * mounted (or isn't a dataset, doesn't exist, ...).
395*10972SRic.Aleshire@Sun.COM 	 */
396*10972SRic.Aleshire@Sun.COM 	if (strcasecmp(ds_hexsl, ZFS_MLSLABEL_DEFAULT) != 0) {
397*10972SRic.Aleshire@Sun.COM 		ASSERT(osp != NULL);
398*10972SRic.Aleshire@Sun.COM 		/*
399*10972SRic.Aleshire@Sun.COM 		 * Try to own the dataset; abort if there is any error,
400*10972SRic.Aleshire@Sun.COM 		 * (e.g., already mounted, in use, or other error).
401*10972SRic.Aleshire@Sun.COM 		 */
402*10972SRic.Aleshire@Sun.COM 		error = dmu_objset_own(name, DMU_OST_ZFS, B_TRUE,
403*10972SRic.Aleshire@Sun.COM 		    setsl_tag, osp);
404*10972SRic.Aleshire@Sun.COM 		if (error)
405*10972SRic.Aleshire@Sun.COM 			return (EPERM);
406*10972SRic.Aleshire@Sun.COM 
407*10972SRic.Aleshire@Sun.COM 		if (new_default) {
408*10972SRic.Aleshire@Sun.COM 			needed_priv = PRIV_FILE_DOWNGRADE_SL;
409*10972SRic.Aleshire@Sun.COM 			goto out_check;
410*10972SRic.Aleshire@Sun.COM 		}
411*10972SRic.Aleshire@Sun.COM 
412*10972SRic.Aleshire@Sun.COM 		if (hexstr_to_label(strval, &new_sl) != 0)
413*10972SRic.Aleshire@Sun.COM 			return (EPERM);
414*10972SRic.Aleshire@Sun.COM 
415*10972SRic.Aleshire@Sun.COM 		if (blstrictdom(&ds_sl, &new_sl))
416*10972SRic.Aleshire@Sun.COM 			needed_priv = PRIV_FILE_DOWNGRADE_SL;
417*10972SRic.Aleshire@Sun.COM 		else if (blstrictdom(&new_sl, &ds_sl))
418*10972SRic.Aleshire@Sun.COM 			needed_priv = PRIV_FILE_UPGRADE_SL;
419*10972SRic.Aleshire@Sun.COM 	} else {
420*10972SRic.Aleshire@Sun.COM 		/* dataset currently has a default label */
421*10972SRic.Aleshire@Sun.COM 		if (!new_default)
422*10972SRic.Aleshire@Sun.COM 			needed_priv = PRIV_FILE_UPGRADE_SL;
423*10972SRic.Aleshire@Sun.COM 	}
424*10972SRic.Aleshire@Sun.COM 
425*10972SRic.Aleshire@Sun.COM out_check:
426*10972SRic.Aleshire@Sun.COM 	if (needed_priv != -1)
427*10972SRic.Aleshire@Sun.COM 		return (PRIV_POLICY(cr, needed_priv, B_FALSE, EPERM, NULL));
428*10972SRic.Aleshire@Sun.COM 	return (0);
429*10972SRic.Aleshire@Sun.COM }
430*10972SRic.Aleshire@Sun.COM 
4314543Smarks static int
4324543Smarks zfs_secpolicy_setprop(const char *name, zfs_prop_t prop, cred_t *cr)
4334543Smarks {
4344543Smarks 	/*
4354543Smarks 	 * Check permissions for special properties.
4364543Smarks 	 */
4374543Smarks 	switch (prop) {
4384543Smarks 	case ZFS_PROP_ZONED:
4394543Smarks 		/*
4404543Smarks 		 * Disallow setting of 'zoned' from within a local zone.
4414543Smarks 		 */
4424543Smarks 		if (!INGLOBALZONE(curproc))
4434543Smarks 			return (EPERM);
4444543Smarks 		break;
445789Sahrens 
4464543Smarks 	case ZFS_PROP_QUOTA:
4474543Smarks 		if (!INGLOBALZONE(curproc)) {
4484543Smarks 			uint64_t zoned;
4494543Smarks 			char setpoint[MAXNAMELEN];
4504543Smarks 			/*
4514543Smarks 			 * Unprivileged users are allowed to modify the
4524543Smarks 			 * quota on things *under* (ie. contained by)
4534543Smarks 			 * the thing they own.
4544543Smarks 			 */
4554543Smarks 			if (dsl_prop_get_integer(name, "zoned", &zoned,
4564543Smarks 			    setpoint))
4574543Smarks 				return (EPERM);
4584670Sahrens 			if (!zoned || strlen(name) <= strlen(setpoint))
4594543Smarks 				return (EPERM);
4604543Smarks 		}
4614670Sahrens 		break;
462*10972SRic.Aleshire@Sun.COM 
463*10972SRic.Aleshire@Sun.COM 	case ZFS_PROP_MLSLABEL:
464*10972SRic.Aleshire@Sun.COM 		if (!is_system_labeled())
465*10972SRic.Aleshire@Sun.COM 			return (EPERM);
466*10972SRic.Aleshire@Sun.COM 		break;
4674543Smarks 	}
4684543Smarks 
4694787Sahrens 	return (zfs_secpolicy_write_perms(name, zfs_prop_to_name(prop), cr));
470789Sahrens }
471789Sahrens 
4724543Smarks int
4734543Smarks zfs_secpolicy_fsacl(zfs_cmd_t *zc, cred_t *cr)
4744543Smarks {
4754543Smarks 	int error;
4764543Smarks 
4774543Smarks 	error = zfs_dozonecheck(zc->zc_name, cr);
4784543Smarks 	if (error)
4794543Smarks 		return (error);
4804543Smarks 
4814543Smarks 	/*
4824543Smarks 	 * permission to set permissions will be evaluated later in
4834543Smarks 	 * dsl_deleg_can_allow()
4844543Smarks 	 */
4854543Smarks 	return (0);
4864543Smarks }
4874543Smarks 
4884543Smarks int
4894543Smarks zfs_secpolicy_rollback(zfs_cmd_t *zc, cred_t *cr)
4904543Smarks {
49110588SEric.Taylor@Sun.COM 	return (zfs_secpolicy_write_perms(zc->zc_name,
49210588SEric.Taylor@Sun.COM 	    ZFS_DELEG_PERM_ROLLBACK, cr));
4934543Smarks }
4944543Smarks 
4954543Smarks int
4964543Smarks zfs_secpolicy_send(zfs_cmd_t *zc, cred_t *cr)
4974543Smarks {
4984543Smarks 	return (zfs_secpolicy_write_perms(zc->zc_name,
4994543Smarks 	    ZFS_DELEG_PERM_SEND, cr));
5004543Smarks }
5014543Smarks 
5028845Samw@Sun.COM static int
5038845Samw@Sun.COM zfs_secpolicy_deleg_share(zfs_cmd_t *zc, cred_t *cr)
5048845Samw@Sun.COM {
5058845Samw@Sun.COM 	vnode_t *vp;
5068845Samw@Sun.COM 	int error;
5078845Samw@Sun.COM 
5088845Samw@Sun.COM 	if ((error = lookupname(zc->zc_value, UIO_SYSSPACE,
5098845Samw@Sun.COM 	    NO_FOLLOW, NULL, &vp)) != 0)
5108845Samw@Sun.COM 		return (error);
5118845Samw@Sun.COM 
5128845Samw@Sun.COM 	/* Now make sure mntpnt and dataset are ZFS */
5138845Samw@Sun.COM 
5148845Samw@Sun.COM 	if (vp->v_vfsp->vfs_fstype != zfsfstype ||
5158845Samw@Sun.COM 	    (strcmp((char *)refstr_value(vp->v_vfsp->vfs_resource),
5168845Samw@Sun.COM 	    zc->zc_name) != 0)) {
5178845Samw@Sun.COM 		VN_RELE(vp);
5188845Samw@Sun.COM 		return (EPERM);
5198845Samw@Sun.COM 	}
5208845Samw@Sun.COM 
5218845Samw@Sun.COM 	VN_RELE(vp);
5228845Samw@Sun.COM 	return (dsl_deleg_access(zc->zc_name,
5238845Samw@Sun.COM 	    ZFS_DELEG_PERM_SHARE, cr));
5248845Samw@Sun.COM }
5258845Samw@Sun.COM 
5264543Smarks int
5274543Smarks zfs_secpolicy_share(zfs_cmd_t *zc, cred_t *cr)
5284543Smarks {
5294543Smarks 	if (!INGLOBALZONE(curproc))
5304543Smarks 		return (EPERM);
5314543Smarks 
5325367Sahrens 	if (secpolicy_nfs(cr) == 0) {
5334543Smarks 		return (0);
5344543Smarks 	} else {
5358845Samw@Sun.COM 		return (zfs_secpolicy_deleg_share(zc, cr));
5368845Samw@Sun.COM 	}
5378845Samw@Sun.COM }
5388845Samw@Sun.COM 
5398845Samw@Sun.COM int
5408845Samw@Sun.COM zfs_secpolicy_smb_acl(zfs_cmd_t *zc, cred_t *cr)
5418845Samw@Sun.COM {
5428845Samw@Sun.COM 	if (!INGLOBALZONE(curproc))
5438845Samw@Sun.COM 		return (EPERM);
5448845Samw@Sun.COM 
5458845Samw@Sun.COM 	if (secpolicy_smb(cr) == 0) {
5468845Samw@Sun.COM 		return (0);
5478845Samw@Sun.COM 	} else {
5488845Samw@Sun.COM 		return (zfs_secpolicy_deleg_share(zc, cr));
5494543Smarks 	}
5504543Smarks }
5514543Smarks 
552789Sahrens static int
5534543Smarks zfs_get_parent(const char *datasetname, char *parent, int parentsize)
554789Sahrens {
555789Sahrens 	char *cp;
556789Sahrens 
557789Sahrens 	/*
558789Sahrens 	 * Remove the @bla or /bla from the end of the name to get the parent.
559789Sahrens 	 */
5604543Smarks 	(void) strncpy(parent, datasetname, parentsize);
5614543Smarks 	cp = strrchr(parent, '@');
562789Sahrens 	if (cp != NULL) {
563789Sahrens 		cp[0] = '\0';
564789Sahrens 	} else {
5654543Smarks 		cp = strrchr(parent, '/');
566789Sahrens 		if (cp == NULL)
567789Sahrens 			return (ENOENT);
568789Sahrens 		cp[0] = '\0';
569789Sahrens 	}
570789Sahrens 
5714543Smarks 	return (0);
5724543Smarks }
5734543Smarks 
5744543Smarks int
5754543Smarks zfs_secpolicy_destroy_perms(const char *name, cred_t *cr)
5764543Smarks {
5774543Smarks 	int error;
5784543Smarks 
5794543Smarks 	if ((error = zfs_secpolicy_write_perms(name,
5804543Smarks 	    ZFS_DELEG_PERM_MOUNT, cr)) != 0)
5814543Smarks 		return (error);
5824543Smarks 
5834543Smarks 	return (zfs_secpolicy_write_perms(name, ZFS_DELEG_PERM_DESTROY, cr));
5844543Smarks }
5854543Smarks 
5864543Smarks static int
5874543Smarks zfs_secpolicy_destroy(zfs_cmd_t *zc, cred_t *cr)
5884543Smarks {
5894543Smarks 	return (zfs_secpolicy_destroy_perms(zc->zc_name, cr));
5904543Smarks }
5914543Smarks 
5924543Smarks /*
5934543Smarks  * Must have sys_config privilege to check the iscsi permission
5944543Smarks  */
5954543Smarks /* ARGSUSED */
5964543Smarks static int
5974543Smarks zfs_secpolicy_iscsi(zfs_cmd_t *zc, cred_t *cr)
5984543Smarks {
5994543Smarks 	return (secpolicy_zfs(cr));
6004543Smarks }
6014543Smarks 
6024543Smarks int
6034543Smarks zfs_secpolicy_rename_perms(const char *from, const char *to, cred_t *cr)
6044543Smarks {
6054543Smarks 	char 	parentname[MAXNAMELEN];
6064543Smarks 	int	error;
6074543Smarks 
6084543Smarks 	if ((error = zfs_secpolicy_write_perms(from,
6094543Smarks 	    ZFS_DELEG_PERM_RENAME, cr)) != 0)
6104543Smarks 		return (error);
6114543Smarks 
6124543Smarks 	if ((error = zfs_secpolicy_write_perms(from,
6134543Smarks 	    ZFS_DELEG_PERM_MOUNT, cr)) != 0)
6144543Smarks 		return (error);
6154543Smarks 
6164543Smarks 	if ((error = zfs_get_parent(to, parentname,
6174543Smarks 	    sizeof (parentname))) != 0)
6184543Smarks 		return (error);
6194543Smarks 
6204543Smarks 	if ((error = zfs_secpolicy_write_perms(parentname,
6214543Smarks 	    ZFS_DELEG_PERM_CREATE, cr)) != 0)
6224543Smarks 		return (error);
6234543Smarks 
6244543Smarks 	if ((error = zfs_secpolicy_write_perms(parentname,
6254543Smarks 	    ZFS_DELEG_PERM_MOUNT, cr)) != 0)
6264543Smarks 		return (error);
6274543Smarks 
6284543Smarks 	return (error);
6294543Smarks }
6304543Smarks 
6314543Smarks static int
6324543Smarks zfs_secpolicy_rename(zfs_cmd_t *zc, cred_t *cr)
6334543Smarks {
6344543Smarks 	return (zfs_secpolicy_rename_perms(zc->zc_name, zc->zc_value, cr));
6354543Smarks }
6364543Smarks 
6374543Smarks static int
6384543Smarks zfs_secpolicy_promote(zfs_cmd_t *zc, cred_t *cr)
6394543Smarks {
6404543Smarks 	char 	parentname[MAXNAMELEN];
6414543Smarks 	objset_t *clone;
6424543Smarks 	int error;
6434543Smarks 
6444543Smarks 	error = zfs_secpolicy_write_perms(zc->zc_name,
6454543Smarks 	    ZFS_DELEG_PERM_PROMOTE, cr);
6464543Smarks 	if (error)
6474543Smarks 		return (error);
6484543Smarks 
64910298SMatthew.Ahrens@Sun.COM 	error = dmu_objset_hold(zc->zc_name, FTAG, &clone);
6504543Smarks 
6514543Smarks 	if (error == 0) {
6524543Smarks 		dsl_dataset_t *pclone = NULL;
6534543Smarks 		dsl_dir_t *dd;
65410298SMatthew.Ahrens@Sun.COM 		dd = clone->os_dsl_dataset->ds_dir;
6554543Smarks 
6564543Smarks 		rw_enter(&dd->dd_pool->dp_config_rwlock, RW_READER);
6576689Smaybee 		error = dsl_dataset_hold_obj(dd->dd_pool,
6586689Smaybee 		    dd->dd_phys->dd_origin_obj, FTAG, &pclone);
6594543Smarks 		rw_exit(&dd->dd_pool->dp_config_rwlock);
6604543Smarks 		if (error) {
66110298SMatthew.Ahrens@Sun.COM 			dmu_objset_rele(clone, FTAG);
6624543Smarks 			return (error);
6634543Smarks 		}
6644543Smarks 
6654543Smarks 		error = zfs_secpolicy_write_perms(zc->zc_name,
6664543Smarks 		    ZFS_DELEG_PERM_MOUNT, cr);
6674543Smarks 
6684543Smarks 		dsl_dataset_name(pclone, parentname);
66910298SMatthew.Ahrens@Sun.COM 		dmu_objset_rele(clone, FTAG);
6706689Smaybee 		dsl_dataset_rele(pclone, FTAG);
6714543Smarks 		if (error == 0)
6724543Smarks 			error = zfs_secpolicy_write_perms(parentname,
6734543Smarks 			    ZFS_DELEG_PERM_PROMOTE, cr);
6744543Smarks 	}
6754543Smarks 	return (error);
6764543Smarks }
6774543Smarks 
6784543Smarks static int
6794543Smarks zfs_secpolicy_receive(zfs_cmd_t *zc, cred_t *cr)
6804543Smarks {
6814543Smarks 	int error;
6824543Smarks 
6834543Smarks 	if ((error = zfs_secpolicy_write_perms(zc->zc_name,
6844543Smarks 	    ZFS_DELEG_PERM_RECEIVE, cr)) != 0)
6854543Smarks 		return (error);
6864543Smarks 
6874543Smarks 	if ((error = zfs_secpolicy_write_perms(zc->zc_name,
6884543Smarks 	    ZFS_DELEG_PERM_MOUNT, cr)) != 0)
6894543Smarks 		return (error);
6904543Smarks 
6914543Smarks 	return (zfs_secpolicy_write_perms(zc->zc_name,
6924543Smarks 	    ZFS_DELEG_PERM_CREATE, cr));
6934543Smarks }
6944543Smarks 
6954543Smarks int
6964543Smarks zfs_secpolicy_snapshot_perms(const char *name, cred_t *cr)
6974543Smarks {
69810588SEric.Taylor@Sun.COM 	return (zfs_secpolicy_write_perms(name,
69910588SEric.Taylor@Sun.COM 	    ZFS_DELEG_PERM_SNAPSHOT, cr));
7004543Smarks }
7014543Smarks 
7024543Smarks static int
7034543Smarks zfs_secpolicy_snapshot(zfs_cmd_t *zc, cred_t *cr)
7044543Smarks {
7054543Smarks 
7064543Smarks 	return (zfs_secpolicy_snapshot_perms(zc->zc_name, cr));
7074543Smarks }
7084543Smarks 
7094543Smarks static int
7104543Smarks zfs_secpolicy_create(zfs_cmd_t *zc, cred_t *cr)
7114543Smarks {
7124543Smarks 	char 	parentname[MAXNAMELEN];
7134543Smarks 	int 	error;
7144543Smarks 
7154543Smarks 	if ((error = zfs_get_parent(zc->zc_name, parentname,
7164543Smarks 	    sizeof (parentname))) != 0)
7174543Smarks 		return (error);
7184543Smarks 
7194543Smarks 	if (zc->zc_value[0] != '\0') {
7204543Smarks 		if ((error = zfs_secpolicy_write_perms(zc->zc_value,
7214543Smarks 		    ZFS_DELEG_PERM_CLONE, cr)) != 0)
7224543Smarks 			return (error);
7234543Smarks 	}
7244543Smarks 
7254543Smarks 	if ((error = zfs_secpolicy_write_perms(parentname,
7264543Smarks 	    ZFS_DELEG_PERM_CREATE, cr)) != 0)
7274543Smarks 		return (error);
7284543Smarks 
7294543Smarks 	error = zfs_secpolicy_write_perms(parentname,
7304543Smarks 	    ZFS_DELEG_PERM_MOUNT, cr);
7314543Smarks 
7324543Smarks 	return (error);
7334543Smarks }
7344543Smarks 
7354543Smarks static int
7364543Smarks zfs_secpolicy_umount(zfs_cmd_t *zc, cred_t *cr)
7374543Smarks {
7384543Smarks 	int error;
7394543Smarks 
7404543Smarks 	error = secpolicy_fs_unmount(cr, NULL);
7414543Smarks 	if (error) {
7424543Smarks 		error = dsl_deleg_access(zc->zc_name, ZFS_DELEG_PERM_MOUNT, cr);
7434543Smarks 	}
7444543Smarks 	return (error);
745789Sahrens }
746789Sahrens 
747789Sahrens /*
748789Sahrens  * Policy for pool operations - create/destroy pools, add vdevs, etc.  Requires
749789Sahrens  * SYS_CONFIG privilege, which is not available in a local zone.
750789Sahrens  */
751789Sahrens /* ARGSUSED */
752789Sahrens static int
7534543Smarks zfs_secpolicy_config(zfs_cmd_t *zc, cred_t *cr)
754789Sahrens {
755789Sahrens 	if (secpolicy_sys_config(cr, B_FALSE) != 0)
756789Sahrens 		return (EPERM);
757789Sahrens 
758789Sahrens 	return (0);
759789Sahrens }
760789Sahrens 
761789Sahrens /*
7621544Seschrock  * Policy for fault injection.  Requires all privileges.
7631544Seschrock  */
7641544Seschrock /* ARGSUSED */
7651544Seschrock static int
7664543Smarks zfs_secpolicy_inject(zfs_cmd_t *zc, cred_t *cr)
7671544Seschrock {
7681544Seschrock 	return (secpolicy_zinject(cr));
7691544Seschrock }
7701544Seschrock 
7714849Sahrens static int
7724849Sahrens zfs_secpolicy_inherit(zfs_cmd_t *zc, cred_t *cr)
7734849Sahrens {
7744849Sahrens 	zfs_prop_t prop = zfs_name_to_prop(zc->zc_value);
7754849Sahrens 
7765094Slling 	if (prop == ZPROP_INVAL) {
7774849Sahrens 		if (!zfs_prop_user(zc->zc_value))
7784849Sahrens 			return (EINVAL);
7794849Sahrens 		return (zfs_secpolicy_write_perms(zc->zc_name,
7804849Sahrens 		    ZFS_DELEG_PERM_USERPROP, cr));
7814849Sahrens 	} else {
7824849Sahrens 		if (!zfs_prop_inheritable(prop))
7834849Sahrens 			return (EINVAL);
7844849Sahrens 		return (zfs_secpolicy_setprop(zc->zc_name, prop, cr));
7854849Sahrens 	}
7864849Sahrens }
7874849Sahrens 
7889396SMatthew.Ahrens@Sun.COM static int
7899396SMatthew.Ahrens@Sun.COM zfs_secpolicy_userspace_one(zfs_cmd_t *zc, cred_t *cr)
7909396SMatthew.Ahrens@Sun.COM {
7919396SMatthew.Ahrens@Sun.COM 	int err = zfs_secpolicy_read(zc, cr);
7929396SMatthew.Ahrens@Sun.COM 	if (err)
7939396SMatthew.Ahrens@Sun.COM 		return (err);
7949396SMatthew.Ahrens@Sun.COM 
7959396SMatthew.Ahrens@Sun.COM 	if (zc->zc_objset_type >= ZFS_NUM_USERQUOTA_PROPS)
7969396SMatthew.Ahrens@Sun.COM 		return (EINVAL);
7979396SMatthew.Ahrens@Sun.COM 
7989396SMatthew.Ahrens@Sun.COM 	if (zc->zc_value[0] == 0) {
7999396SMatthew.Ahrens@Sun.COM 		/*
8009396SMatthew.Ahrens@Sun.COM 		 * They are asking about a posix uid/gid.  If it's
8019396SMatthew.Ahrens@Sun.COM 		 * themself, allow it.
8029396SMatthew.Ahrens@Sun.COM 		 */
8039396SMatthew.Ahrens@Sun.COM 		if (zc->zc_objset_type == ZFS_PROP_USERUSED ||
8049396SMatthew.Ahrens@Sun.COM 		    zc->zc_objset_type == ZFS_PROP_USERQUOTA) {
8059396SMatthew.Ahrens@Sun.COM 			if (zc->zc_guid == crgetuid(cr))
8069396SMatthew.Ahrens@Sun.COM 				return (0);
8079396SMatthew.Ahrens@Sun.COM 		} else {
8089396SMatthew.Ahrens@Sun.COM 			if (groupmember(zc->zc_guid, cr))
8099396SMatthew.Ahrens@Sun.COM 				return (0);
8109396SMatthew.Ahrens@Sun.COM 		}
8119396SMatthew.Ahrens@Sun.COM 	}
8129396SMatthew.Ahrens@Sun.COM 
8139396SMatthew.Ahrens@Sun.COM 	return (zfs_secpolicy_write_perms(zc->zc_name,
8149396SMatthew.Ahrens@Sun.COM 	    userquota_perms[zc->zc_objset_type], cr));
8159396SMatthew.Ahrens@Sun.COM }
8169396SMatthew.Ahrens@Sun.COM 
8179396SMatthew.Ahrens@Sun.COM static int
8189396SMatthew.Ahrens@Sun.COM zfs_secpolicy_userspace_many(zfs_cmd_t *zc, cred_t *cr)
8199396SMatthew.Ahrens@Sun.COM {
8209396SMatthew.Ahrens@Sun.COM 	int err = zfs_secpolicy_read(zc, cr);
8219396SMatthew.Ahrens@Sun.COM 	if (err)
8229396SMatthew.Ahrens@Sun.COM 		return (err);
8239396SMatthew.Ahrens@Sun.COM 
8249396SMatthew.Ahrens@Sun.COM 	if (zc->zc_objset_type >= ZFS_NUM_USERQUOTA_PROPS)
8259396SMatthew.Ahrens@Sun.COM 		return (EINVAL);
8269396SMatthew.Ahrens@Sun.COM 
8279396SMatthew.Ahrens@Sun.COM 	return (zfs_secpolicy_write_perms(zc->zc_name,
8289396SMatthew.Ahrens@Sun.COM 	    userquota_perms[zc->zc_objset_type], cr));
8299396SMatthew.Ahrens@Sun.COM }
8309396SMatthew.Ahrens@Sun.COM 
8319396SMatthew.Ahrens@Sun.COM static int
8329396SMatthew.Ahrens@Sun.COM zfs_secpolicy_userspace_upgrade(zfs_cmd_t *zc, cred_t *cr)
8339396SMatthew.Ahrens@Sun.COM {
8349396SMatthew.Ahrens@Sun.COM 	return (zfs_secpolicy_setprop(zc->zc_name, ZFS_PROP_VERSION, cr));
8359396SMatthew.Ahrens@Sun.COM }
8369396SMatthew.Ahrens@Sun.COM 
83710242Schris.kirby@sun.com static int
83810242Schris.kirby@sun.com zfs_secpolicy_hold(zfs_cmd_t *zc, cred_t *cr)
83910242Schris.kirby@sun.com {
84010242Schris.kirby@sun.com 	return (zfs_secpolicy_write_perms(zc->zc_name,
84110242Schris.kirby@sun.com 	    ZFS_DELEG_PERM_HOLD, cr));
84210242Schris.kirby@sun.com }
84310242Schris.kirby@sun.com 
84410242Schris.kirby@sun.com static int
84510242Schris.kirby@sun.com zfs_secpolicy_release(zfs_cmd_t *zc, cred_t *cr)
84610242Schris.kirby@sun.com {
84710242Schris.kirby@sun.com 	return (zfs_secpolicy_write_perms(zc->zc_name,
84810242Schris.kirby@sun.com 	    ZFS_DELEG_PERM_RELEASE, cr));
84910242Schris.kirby@sun.com }
85010242Schris.kirby@sun.com 
8511544Seschrock /*
852789Sahrens  * Returns the nvlist as specified by the user in the zfs_cmd_t.
853789Sahrens  */
854789Sahrens static int
8559643SEric.Taylor@Sun.COM get_nvlist(uint64_t nvl, uint64_t size, int iflag, nvlist_t **nvp)
856789Sahrens {
857789Sahrens 	char *packed;
858789Sahrens 	int error;
8595094Slling 	nvlist_t *list = NULL;
860789Sahrens 
861789Sahrens 	/*
8622676Seschrock 	 * Read in and unpack the user-supplied nvlist.
863789Sahrens 	 */
8645094Slling 	if (size == 0)
865789Sahrens 		return (EINVAL);
866789Sahrens 
867789Sahrens 	packed = kmem_alloc(size, KM_SLEEP);
868789Sahrens 
8699643SEric.Taylor@Sun.COM 	if ((error = ddi_copyin((void *)(uintptr_t)nvl, packed, size,
8709643SEric.Taylor@Sun.COM 	    iflag)) != 0) {
871789Sahrens 		kmem_free(packed, size);
872789Sahrens 		return (error);
873789Sahrens 	}
874789Sahrens 
8755094Slling 	if ((error = nvlist_unpack(packed, size, &list, 0)) != 0) {
876789Sahrens 		kmem_free(packed, size);
877789Sahrens 		return (error);
878789Sahrens 	}
879789Sahrens 
880789Sahrens 	kmem_free(packed, size);
881789Sahrens 
8825094Slling 	*nvp = list;
883789Sahrens 	return (0);
884789Sahrens }
885789Sahrens 
886789Sahrens static int
8872676Seschrock put_nvlist(zfs_cmd_t *zc, nvlist_t *nvl)
8882676Seschrock {
8892676Seschrock 	char *packed = NULL;
8902676Seschrock 	size_t size;
8912676Seschrock 	int error;
8922676Seschrock 
8932676Seschrock 	VERIFY(nvlist_size(nvl, &size, NV_ENCODE_NATIVE) == 0);
8942676Seschrock 
8952676Seschrock 	if (size > zc->zc_nvlist_dst_size) {
8962676Seschrock 		error = ENOMEM;
8972676Seschrock 	} else {
8984611Smarks 		packed = kmem_alloc(size, KM_SLEEP);
8992676Seschrock 		VERIFY(nvlist_pack(nvl, &packed, &size, NV_ENCODE_NATIVE,
9002676Seschrock 		    KM_SLEEP) == 0);
9019643SEric.Taylor@Sun.COM 		error = ddi_copyout(packed,
9029643SEric.Taylor@Sun.COM 		    (void *)(uintptr_t)zc->zc_nvlist_dst, size, zc->zc_iflags);
9032676Seschrock 		kmem_free(packed, size);
9042676Seschrock 	}
9052676Seschrock 
9062676Seschrock 	zc->zc_nvlist_dst_size = size;
9072676Seschrock 	return (error);
9082676Seschrock }
9092676Seschrock 
9102676Seschrock static int
9119396SMatthew.Ahrens@Sun.COM getzfsvfs(const char *dsname, zfsvfs_t **zvp)
9129396SMatthew.Ahrens@Sun.COM {
9139396SMatthew.Ahrens@Sun.COM 	objset_t *os;
9149396SMatthew.Ahrens@Sun.COM 	int error;
9159396SMatthew.Ahrens@Sun.COM 
91610298SMatthew.Ahrens@Sun.COM 	error = dmu_objset_hold(dsname, FTAG, &os);
9179396SMatthew.Ahrens@Sun.COM 	if (error)
9189396SMatthew.Ahrens@Sun.COM 		return (error);
91910298SMatthew.Ahrens@Sun.COM 	if (dmu_objset_type(os) != DMU_OST_ZFS) {
92010298SMatthew.Ahrens@Sun.COM 		dmu_objset_rele(os, FTAG);
92110298SMatthew.Ahrens@Sun.COM 		return (EINVAL);
92210298SMatthew.Ahrens@Sun.COM 	}
92310298SMatthew.Ahrens@Sun.COM 
92410298SMatthew.Ahrens@Sun.COM 	mutex_enter(&os->os_user_ptr_lock);
9259396SMatthew.Ahrens@Sun.COM 	*zvp = dmu_objset_get_user(os);
9269396SMatthew.Ahrens@Sun.COM 	if (*zvp) {
9279396SMatthew.Ahrens@Sun.COM 		VFS_HOLD((*zvp)->z_vfs);
9289396SMatthew.Ahrens@Sun.COM 	} else {
9299396SMatthew.Ahrens@Sun.COM 		error = ESRCH;
9309396SMatthew.Ahrens@Sun.COM 	}
93110298SMatthew.Ahrens@Sun.COM 	mutex_exit(&os->os_user_ptr_lock);
93210298SMatthew.Ahrens@Sun.COM 	dmu_objset_rele(os, FTAG);
9339396SMatthew.Ahrens@Sun.COM 	return (error);
9349396SMatthew.Ahrens@Sun.COM }
9359396SMatthew.Ahrens@Sun.COM 
9369396SMatthew.Ahrens@Sun.COM /*
9379396SMatthew.Ahrens@Sun.COM  * Find a zfsvfs_t for a mounted filesystem, or create our own, in which
9389396SMatthew.Ahrens@Sun.COM  * case its z_vfs will be NULL, and it will be opened as the owner.
9399396SMatthew.Ahrens@Sun.COM  */
9409396SMatthew.Ahrens@Sun.COM static int
94110298SMatthew.Ahrens@Sun.COM zfsvfs_hold(const char *name, void *tag, zfsvfs_t **zvp)
9429396SMatthew.Ahrens@Sun.COM {
9439396SMatthew.Ahrens@Sun.COM 	int error = 0;
9449396SMatthew.Ahrens@Sun.COM 
9459396SMatthew.Ahrens@Sun.COM 	if (getzfsvfs(name, zvp) != 0)
94610298SMatthew.Ahrens@Sun.COM 		error = zfsvfs_create(name, zvp);
9479396SMatthew.Ahrens@Sun.COM 	if (error == 0) {
9489396SMatthew.Ahrens@Sun.COM 		rrw_enter(&(*zvp)->z_teardown_lock, RW_READER, tag);
9499396SMatthew.Ahrens@Sun.COM 		if ((*zvp)->z_unmounted) {
9509396SMatthew.Ahrens@Sun.COM 			/*
9519396SMatthew.Ahrens@Sun.COM 			 * XXX we could probably try again, since the unmounting
9529396SMatthew.Ahrens@Sun.COM 			 * thread should be just about to disassociate the
9539396SMatthew.Ahrens@Sun.COM 			 * objset from the zfsvfs.
9549396SMatthew.Ahrens@Sun.COM 			 */
9559396SMatthew.Ahrens@Sun.COM 			rrw_exit(&(*zvp)->z_teardown_lock, tag);
9569396SMatthew.Ahrens@Sun.COM 			return (EBUSY);
9579396SMatthew.Ahrens@Sun.COM 		}
9589396SMatthew.Ahrens@Sun.COM 	}
9599396SMatthew.Ahrens@Sun.COM 	return (error);
9609396SMatthew.Ahrens@Sun.COM }
9619396SMatthew.Ahrens@Sun.COM 
9629396SMatthew.Ahrens@Sun.COM static void
9639396SMatthew.Ahrens@Sun.COM zfsvfs_rele(zfsvfs_t *zfsvfs, void *tag)
9649396SMatthew.Ahrens@Sun.COM {
9659396SMatthew.Ahrens@Sun.COM 	rrw_exit(&zfsvfs->z_teardown_lock, tag);
9669396SMatthew.Ahrens@Sun.COM 
9679396SMatthew.Ahrens@Sun.COM 	if (zfsvfs->z_vfs) {
9689396SMatthew.Ahrens@Sun.COM 		VFS_RELE(zfsvfs->z_vfs);
9699396SMatthew.Ahrens@Sun.COM 	} else {
97010298SMatthew.Ahrens@Sun.COM 		dmu_objset_disown(zfsvfs->z_os, zfsvfs);
9719396SMatthew.Ahrens@Sun.COM 		zfsvfs_free(zfsvfs);
9729396SMatthew.Ahrens@Sun.COM 	}
9739396SMatthew.Ahrens@Sun.COM }
9749396SMatthew.Ahrens@Sun.COM 
9759396SMatthew.Ahrens@Sun.COM static int
976789Sahrens zfs_ioc_pool_create(zfs_cmd_t *zc)
977789Sahrens {
978789Sahrens 	int error;
9795094Slling 	nvlist_t *config, *props = NULL;
9807184Stimh 	nvlist_t *rootprops = NULL;
9817184Stimh 	nvlist_t *zplprops = NULL;
9824715Sek110237 	char *buf;
983789Sahrens 
9845094Slling 	if (error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size,
9859643SEric.Taylor@Sun.COM 	    zc->zc_iflags, &config))
9864988Sek110237 		return (error);
9874715Sek110237 
9885094Slling 	if (zc->zc_nvlist_src_size != 0 && (error =
9899643SEric.Taylor@Sun.COM 	    get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
9909643SEric.Taylor@Sun.COM 	    zc->zc_iflags, &props))) {
9915094Slling 		nvlist_free(config);
9925094Slling 		return (error);
9935094Slling 	}
9945094Slling 
9957184Stimh 	if (props) {
9967184Stimh 		nvlist_t *nvl = NULL;
9977184Stimh 		uint64_t version = SPA_VERSION;
9987184Stimh 
9997184Stimh 		(void) nvlist_lookup_uint64(props,
10007184Stimh 		    zpool_prop_to_name(ZPOOL_PROP_VERSION), &version);
10017184Stimh 		if (version < SPA_VERSION_INITIAL || version > SPA_VERSION) {
10027184Stimh 			error = EINVAL;
10037184Stimh 			goto pool_props_bad;
10047184Stimh 		}
10057184Stimh 		(void) nvlist_lookup_nvlist(props, ZPOOL_ROOTFS_PROPS, &nvl);
10067184Stimh 		if (nvl) {
10077184Stimh 			error = nvlist_dup(nvl, &rootprops, KM_SLEEP);
10087184Stimh 			if (error != 0) {
10097184Stimh 				nvlist_free(config);
10107184Stimh 				nvlist_free(props);
10117184Stimh 				return (error);
10127184Stimh 			}
10137184Stimh 			(void) nvlist_remove_all(props, ZPOOL_ROOTFS_PROPS);
10147184Stimh 		}
10157184Stimh 		VERIFY(nvlist_alloc(&zplprops, NV_UNIQUE_NAME, KM_SLEEP) == 0);
10167184Stimh 		error = zfs_fill_zplprops_root(version, rootprops,
10177184Stimh 		    zplprops, NULL);
10187184Stimh 		if (error)
10197184Stimh 			goto pool_props_bad;
10207184Stimh 	}
10217184Stimh 
10224988Sek110237 	buf = history_str_get(zc);
1023789Sahrens 
10247184Stimh 	error = spa_create(zc->zc_name, config, props, buf, zplprops);
10257184Stimh 
10267184Stimh 	/*
10277184Stimh 	 * Set the remaining root properties
10287184Stimh 	 */
10297184Stimh 	if (!error &&
10307184Stimh 	    (error = zfs_set_prop_nvlist(zc->zc_name, rootprops)) != 0)
10317184Stimh 		(void) spa_destroy(zc->zc_name);
1032789Sahrens 
10334988Sek110237 	if (buf != NULL)
10344988Sek110237 		history_str_free(buf);
10355094Slling 
10367184Stimh pool_props_bad:
10377184Stimh 	nvlist_free(rootprops);
10387184Stimh 	nvlist_free(zplprops);
1039789Sahrens 	nvlist_free(config);
10407184Stimh 	nvlist_free(props);
10415094Slling 
1042789Sahrens 	return (error);
1043789Sahrens }
1044789Sahrens 
1045789Sahrens static int
1046789Sahrens zfs_ioc_pool_destroy(zfs_cmd_t *zc)
1047789Sahrens {
10484543Smarks 	int error;
10494543Smarks 	zfs_log_history(zc);
10504543Smarks 	error = spa_destroy(zc->zc_name);
105110588SEric.Taylor@Sun.COM 	if (error == 0)
105210588SEric.Taylor@Sun.COM 		zvol_remove_minors(zc->zc_name);
10534543Smarks 	return (error);
1054789Sahrens }
1055789Sahrens 
1056789Sahrens static int
1057789Sahrens zfs_ioc_pool_import(zfs_cmd_t *zc)
1058789Sahrens {
10595094Slling 	nvlist_t *config, *props = NULL;
1060789Sahrens 	uint64_t guid;
106110921STim.Haley@Sun.COM 	int error;
1062789Sahrens 
10635094Slling 	if ((error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size,
10649643SEric.Taylor@Sun.COM 	    zc->zc_iflags, &config)) != 0)
1065789Sahrens 		return (error);
1066789Sahrens 
10675094Slling 	if (zc->zc_nvlist_src_size != 0 && (error =
10689643SEric.Taylor@Sun.COM 	    get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
10699643SEric.Taylor@Sun.COM 	    zc->zc_iflags, &props))) {
10705094Slling 		nvlist_free(config);
10715094Slling 		return (error);
10725094Slling 	}
10735094Slling 
1074789Sahrens 	if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID, &guid) != 0 ||
10751544Seschrock 	    guid != zc->zc_guid)
1076789Sahrens 		error = EINVAL;
10776643Seschrock 	else if (zc->zc_cookie)
107810921STim.Haley@Sun.COM 		error = spa_import_verbatim(zc->zc_name, config, props);
1079789Sahrens 	else
10805094Slling 		error = spa_import(zc->zc_name, config, props);
1081789Sahrens 
108210921STim.Haley@Sun.COM 	if (zc->zc_nvlist_dst != 0)
108310921STim.Haley@Sun.COM 		(void) put_nvlist(zc, config);
108410921STim.Haley@Sun.COM 
1085789Sahrens 	nvlist_free(config);
1086789Sahrens 
10875094Slling 	if (props)
10885094Slling 		nvlist_free(props);
10895094Slling 
1090789Sahrens 	return (error);
1091789Sahrens }
1092789Sahrens 
1093789Sahrens static int
1094789Sahrens zfs_ioc_pool_export(zfs_cmd_t *zc)
1095789Sahrens {
10964543Smarks 	int error;
10977214Slling 	boolean_t force = (boolean_t)zc->zc_cookie;
10988211SGeorge.Wilson@Sun.COM 	boolean_t hardforce = (boolean_t)zc->zc_guid;
10997214Slling 
11004543Smarks 	zfs_log_history(zc);
11018211SGeorge.Wilson@Sun.COM 	error = spa_export(zc->zc_name, NULL, force, hardforce);
110210588SEric.Taylor@Sun.COM 	if (error == 0)
110310588SEric.Taylor@Sun.COM 		zvol_remove_minors(zc->zc_name);
11044543Smarks 	return (error);
1105789Sahrens }
1106789Sahrens 
1107789Sahrens static int
1108789Sahrens zfs_ioc_pool_configs(zfs_cmd_t *zc)
1109789Sahrens {
1110789Sahrens 	nvlist_t *configs;
1111789Sahrens 	int error;
1112789Sahrens 
1113789Sahrens 	if ((configs = spa_all_configs(&zc->zc_cookie)) == NULL)
1114789Sahrens 		return (EEXIST);
1115789Sahrens 
11162676Seschrock 	error = put_nvlist(zc, configs);
1117789Sahrens 
1118789Sahrens 	nvlist_free(configs);
1119789Sahrens 
1120789Sahrens 	return (error);
1121789Sahrens }
1122789Sahrens 
1123789Sahrens static int
1124789Sahrens zfs_ioc_pool_stats(zfs_cmd_t *zc)
1125789Sahrens {
1126789Sahrens 	nvlist_t *config;
1127789Sahrens 	int error;
11281544Seschrock 	int ret = 0;
1129789Sahrens 
11302676Seschrock 	error = spa_get_stats(zc->zc_name, &config, zc->zc_value,
11312676Seschrock 	    sizeof (zc->zc_value));
1132789Sahrens 
1133789Sahrens 	if (config != NULL) {
11342676Seschrock 		ret = put_nvlist(zc, config);
1135789Sahrens 		nvlist_free(config);
11361544Seschrock 
11371544Seschrock 		/*
11381544Seschrock 		 * The config may be present even if 'error' is non-zero.
11391544Seschrock 		 * In this case we return success, and preserve the real errno
11401544Seschrock 		 * in 'zc_cookie'.
11411544Seschrock 		 */
11421544Seschrock 		zc->zc_cookie = error;
1143789Sahrens 	} else {
11441544Seschrock 		ret = error;
1145789Sahrens 	}
1146789Sahrens 
11471544Seschrock 	return (ret);
1148789Sahrens }
1149789Sahrens 
1150789Sahrens /*
1151789Sahrens  * Try to import the given pool, returning pool stats as appropriate so that
1152789Sahrens  * user land knows which devices are available and overall pool health.
1153789Sahrens  */
1154789Sahrens static int
1155789Sahrens zfs_ioc_pool_tryimport(zfs_cmd_t *zc)
1156789Sahrens {
1157789Sahrens 	nvlist_t *tryconfig, *config;
1158789Sahrens 	int error;
1159789Sahrens 
11605094Slling 	if ((error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size,
11619643SEric.Taylor@Sun.COM 	    zc->zc_iflags, &tryconfig)) != 0)
1162789Sahrens 		return (error);
1163789Sahrens 
1164789Sahrens 	config = spa_tryimport(tryconfig);
1165789Sahrens 
1166789Sahrens 	nvlist_free(tryconfig);
1167789Sahrens 
1168789Sahrens 	if (config == NULL)
1169789Sahrens 		return (EINVAL);
1170789Sahrens 
11712676Seschrock 	error = put_nvlist(zc, config);
1172789Sahrens 	nvlist_free(config);
1173789Sahrens 
1174789Sahrens 	return (error);
1175789Sahrens }
1176789Sahrens 
1177789Sahrens static int
1178789Sahrens zfs_ioc_pool_scrub(zfs_cmd_t *zc)
1179789Sahrens {
1180789Sahrens 	spa_t *spa;
1181789Sahrens 	int error;
1182789Sahrens 
11832926Sek110237 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
11842926Sek110237 		return (error);
11852926Sek110237 
11867046Sahrens 	error = spa_scrub(spa, zc->zc_cookie);
11872926Sek110237 
11882926Sek110237 	spa_close(spa, FTAG);
11892926Sek110237 
1190789Sahrens 	return (error);
1191789Sahrens }
1192789Sahrens 
1193789Sahrens static int
1194789Sahrens zfs_ioc_pool_freeze(zfs_cmd_t *zc)
1195789Sahrens {
1196789Sahrens 	spa_t *spa;
1197789Sahrens 	int error;
1198789Sahrens 
1199789Sahrens 	error = spa_open(zc->zc_name, &spa, FTAG);
1200789Sahrens 	if (error == 0) {
1201789Sahrens 		spa_freeze(spa);
1202789Sahrens 		spa_close(spa, FTAG);
1203789Sahrens 	}
1204789Sahrens 	return (error);
1205789Sahrens }
1206789Sahrens 
1207789Sahrens static int
12081760Seschrock zfs_ioc_pool_upgrade(zfs_cmd_t *zc)
12091760Seschrock {
12101760Seschrock 	spa_t *spa;
12111760Seschrock 	int error;
12121760Seschrock 
12132926Sek110237 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
12142926Sek110237 		return (error);
12152926Sek110237 
12165118Slling 	if (zc->zc_cookie < spa_version(spa) || zc->zc_cookie > SPA_VERSION) {
12175118Slling 		spa_close(spa, FTAG);
12185118Slling 		return (EINVAL);
12195118Slling 	}
12205118Slling 
12215094Slling 	spa_upgrade(spa, zc->zc_cookie);
12222926Sek110237 	spa_close(spa, FTAG);
12232926Sek110237 
12242926Sek110237 	return (error);
12252926Sek110237 }
12262926Sek110237 
12272926Sek110237 static int
12282926Sek110237 zfs_ioc_pool_get_history(zfs_cmd_t *zc)
12292926Sek110237 {
12302926Sek110237 	spa_t *spa;
12312926Sek110237 	char *hist_buf;
12322926Sek110237 	uint64_t size;
12332926Sek110237 	int error;
12342926Sek110237 
12352926Sek110237 	if ((size = zc->zc_history_len) == 0)
12362926Sek110237 		return (EINVAL);
12372926Sek110237 
12382926Sek110237 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
12392926Sek110237 		return (error);
12402926Sek110237 
12414577Sahrens 	if (spa_version(spa) < SPA_VERSION_ZPOOL_HISTORY) {
12423863Sek110237 		spa_close(spa, FTAG);
12433863Sek110237 		return (ENOTSUP);
12443863Sek110237 	}
12453863Sek110237 
12462926Sek110237 	hist_buf = kmem_alloc(size, KM_SLEEP);
12472926Sek110237 	if ((error = spa_history_get(spa, &zc->zc_history_offset,
12482926Sek110237 	    &zc->zc_history_len, hist_buf)) == 0) {
12499643SEric.Taylor@Sun.COM 		error = ddi_copyout(hist_buf,
12509643SEric.Taylor@Sun.COM 		    (void *)(uintptr_t)zc->zc_history,
12519643SEric.Taylor@Sun.COM 		    zc->zc_history_len, zc->zc_iflags);
12522926Sek110237 	}
12532926Sek110237 
12542926Sek110237 	spa_close(spa, FTAG);
12552926Sek110237 	kmem_free(hist_buf, size);
12562926Sek110237 	return (error);
12572926Sek110237 }
12582926Sek110237 
12592926Sek110237 static int
12603444Sek110237 zfs_ioc_dsobj_to_dsname(zfs_cmd_t *zc)
12613444Sek110237 {
12623444Sek110237 	int error;
12633444Sek110237 
12643912Slling 	if (error = dsl_dsobj_to_dsname(zc->zc_name, zc->zc_obj, zc->zc_value))
12653444Sek110237 		return (error);
12663444Sek110237 
12673444Sek110237 	return (0);
12683444Sek110237 }
12693444Sek110237 
127010298SMatthew.Ahrens@Sun.COM /*
127110298SMatthew.Ahrens@Sun.COM  * inputs:
127210298SMatthew.Ahrens@Sun.COM  * zc_name		name of filesystem
127310298SMatthew.Ahrens@Sun.COM  * zc_obj		object to find
127410298SMatthew.Ahrens@Sun.COM  *
127510298SMatthew.Ahrens@Sun.COM  * outputs:
127610298SMatthew.Ahrens@Sun.COM  * zc_value		name of object
127710298SMatthew.Ahrens@Sun.COM  */
12783444Sek110237 static int
12793444Sek110237 zfs_ioc_obj_to_path(zfs_cmd_t *zc)
12803444Sek110237 {
128110298SMatthew.Ahrens@Sun.COM 	objset_t *os;
12823444Sek110237 	int error;
12833444Sek110237 
128410298SMatthew.Ahrens@Sun.COM 	/* XXX reading from objset not owned */
128510298SMatthew.Ahrens@Sun.COM 	if ((error = dmu_objset_hold(zc->zc_name, FTAG, &os)) != 0)
12863444Sek110237 		return (error);
128710298SMatthew.Ahrens@Sun.COM 	if (dmu_objset_type(os) != DMU_OST_ZFS) {
128810298SMatthew.Ahrens@Sun.COM 		dmu_objset_rele(os, FTAG);
128910298SMatthew.Ahrens@Sun.COM 		return (EINVAL);
129010298SMatthew.Ahrens@Sun.COM 	}
129110298SMatthew.Ahrens@Sun.COM 	error = zfs_obj_to_path(os, zc->zc_obj, zc->zc_value,
12923444Sek110237 	    sizeof (zc->zc_value));
129310298SMatthew.Ahrens@Sun.COM 	dmu_objset_rele(os, FTAG);
12943444Sek110237 
12953444Sek110237 	return (error);
12963444Sek110237 }
12973444Sek110237 
12983444Sek110237 static int
1299789Sahrens zfs_ioc_vdev_add(zfs_cmd_t *zc)
1300789Sahrens {
1301789Sahrens 	spa_t *spa;
1302789Sahrens 	int error;
13036423Sgw25295 	nvlist_t *config, **l2cache, **spares;
13046423Sgw25295 	uint_t nl2cache = 0, nspares = 0;
1305789Sahrens 
1306789Sahrens 	error = spa_open(zc->zc_name, &spa, FTAG);
1307789Sahrens 	if (error != 0)
1308789Sahrens 		return (error);
1309789Sahrens 
13105450Sbrendan 	error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size,
13119643SEric.Taylor@Sun.COM 	    zc->zc_iflags, &config);
13125450Sbrendan 	(void) nvlist_lookup_nvlist_array(config, ZPOOL_CONFIG_L2CACHE,
13135450Sbrendan 	    &l2cache, &nl2cache);
13145450Sbrendan 
13156423Sgw25295 	(void) nvlist_lookup_nvlist_array(config, ZPOOL_CONFIG_SPARES,
13166423Sgw25295 	    &spares, &nspares);
13176423Sgw25295 
13183912Slling 	/*
13193912Slling 	 * A root pool with concatenated devices is not supported.
13206423Sgw25295 	 * Thus, can not add a device to a root pool.
13216423Sgw25295 	 *
13226423Sgw25295 	 * Intent log device can not be added to a rootpool because
13236423Sgw25295 	 * during mountroot, zil is replayed, a seperated log device
13246423Sgw25295 	 * can not be accessed during the mountroot time.
13256423Sgw25295 	 *
13266423Sgw25295 	 * l2cache and spare devices are ok to be added to a rootpool.
13273912Slling 	 */
132810922SJeff.Bonwick@Sun.COM 	if (spa_bootfs(spa) != 0 && nl2cache == 0 && nspares == 0) {
13293912Slling 		spa_close(spa, FTAG);
13303912Slling 		return (EDOM);
13313912Slling 	}
13323912Slling 
13335450Sbrendan 	if (error == 0) {
1334789Sahrens 		error = spa_vdev_add(spa, config);
1335789Sahrens 		nvlist_free(config);
1336789Sahrens 	}
1337789Sahrens 	spa_close(spa, FTAG);
1338789Sahrens 	return (error);
1339789Sahrens }
1340789Sahrens 
1341789Sahrens static int
1342789Sahrens zfs_ioc_vdev_remove(zfs_cmd_t *zc)
1343789Sahrens {
13442082Seschrock 	spa_t *spa;
13452082Seschrock 	int error;
13462082Seschrock 
13472082Seschrock 	error = spa_open(zc->zc_name, &spa, FTAG);
13482082Seschrock 	if (error != 0)
13492082Seschrock 		return (error);
13502082Seschrock 	error = spa_vdev_remove(spa, zc->zc_guid, B_FALSE);
13512082Seschrock 	spa_close(spa, FTAG);
13522082Seschrock 	return (error);
1353789Sahrens }
1354789Sahrens 
1355789Sahrens static int
13564451Seschrock zfs_ioc_vdev_set_state(zfs_cmd_t *zc)
1357789Sahrens {
1358789Sahrens 	spa_t *spa;
1359789Sahrens 	int error;
13604451Seschrock 	vdev_state_t newstate = VDEV_STATE_UNKNOWN;
1361789Sahrens 
13622926Sek110237 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
1363789Sahrens 		return (error);
13644451Seschrock 	switch (zc->zc_cookie) {
13654451Seschrock 	case VDEV_STATE_ONLINE:
13664451Seschrock 		error = vdev_online(spa, zc->zc_guid, zc->zc_obj, &newstate);
13674451Seschrock 		break;
13684451Seschrock 
13694451Seschrock 	case VDEV_STATE_OFFLINE:
13704451Seschrock 		error = vdev_offline(spa, zc->zc_guid, zc->zc_obj);
13714451Seschrock 		break;
1372789Sahrens 
13734451Seschrock 	case VDEV_STATE_FAULTED:
137410817SEric.Schrock@Sun.COM 		if (zc->zc_obj != VDEV_AUX_ERR_EXCEEDED &&
137510817SEric.Schrock@Sun.COM 		    zc->zc_obj != VDEV_AUX_EXTERNAL)
137610817SEric.Schrock@Sun.COM 			zc->zc_obj = VDEV_AUX_ERR_EXCEEDED;
137710817SEric.Schrock@Sun.COM 
137810817SEric.Schrock@Sun.COM 		error = vdev_fault(spa, zc->zc_guid, zc->zc_obj);
13794451Seschrock 		break;
1380789Sahrens 
13814451Seschrock 	case VDEV_STATE_DEGRADED:
138210817SEric.Schrock@Sun.COM 		if (zc->zc_obj != VDEV_AUX_ERR_EXCEEDED &&
138310817SEric.Schrock@Sun.COM 		    zc->zc_obj != VDEV_AUX_EXTERNAL)
138410817SEric.Schrock@Sun.COM 			zc->zc_obj = VDEV_AUX_ERR_EXCEEDED;
138510817SEric.Schrock@Sun.COM 
138610817SEric.Schrock@Sun.COM 		error = vdev_degrade(spa, zc->zc_guid, zc->zc_obj);
13874451Seschrock 		break;
13884451Seschrock 
13894451Seschrock 	default:
13904451Seschrock 		error = EINVAL;
13914451Seschrock 	}
13924451Seschrock 	zc->zc_cookie = newstate;
1393789Sahrens 	spa_close(spa, FTAG);
1394789Sahrens 	return (error);
1395789Sahrens }
1396789Sahrens 
1397789Sahrens static int
1398789Sahrens zfs_ioc_vdev_attach(zfs_cmd_t *zc)
1399789Sahrens {
1400789Sahrens 	spa_t *spa;
1401789Sahrens 	int replacing = zc->zc_cookie;
1402789Sahrens 	nvlist_t *config;
1403789Sahrens 	int error;
1404789Sahrens 
14052926Sek110237 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
1406789Sahrens 		return (error);
1407789Sahrens 
14085094Slling 	if ((error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size,
14099643SEric.Taylor@Sun.COM 	    zc->zc_iflags, &config)) == 0) {
14101544Seschrock 		error = spa_vdev_attach(spa, zc->zc_guid, config, replacing);
1411789Sahrens 		nvlist_free(config);
1412789Sahrens 	}
1413789Sahrens 
1414789Sahrens 	spa_close(spa, FTAG);
1415789Sahrens 	return (error);
1416789Sahrens }
1417789Sahrens 
1418789Sahrens static int
1419789Sahrens zfs_ioc_vdev_detach(zfs_cmd_t *zc)
1420789Sahrens {
1421789Sahrens 	spa_t *spa;
1422789Sahrens 	int error;
1423789Sahrens 
14242926Sek110237 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
1425789Sahrens 		return (error);
1426789Sahrens 
14278241SJeff.Bonwick@Sun.COM 	error = spa_vdev_detach(spa, zc->zc_guid, 0, B_FALSE);
1428789Sahrens 
1429789Sahrens 	spa_close(spa, FTAG);
1430789Sahrens 	return (error);
1431789Sahrens }
1432789Sahrens 
1433789Sahrens static int
14341354Seschrock zfs_ioc_vdev_setpath(zfs_cmd_t *zc)
14351354Seschrock {
14361354Seschrock 	spa_t *spa;
14372676Seschrock 	char *path = zc->zc_value;
14381544Seschrock 	uint64_t guid = zc->zc_guid;
14391354Seschrock 	int error;
14401354Seschrock 
14411354Seschrock 	error = spa_open(zc->zc_name, &spa, FTAG);
14421354Seschrock 	if (error != 0)
14431354Seschrock 		return (error);
14441354Seschrock 
14451354Seschrock 	error = spa_vdev_setpath(spa, guid, path);
14461354Seschrock 	spa_close(spa, FTAG);
14471354Seschrock 	return (error);
14481354Seschrock }
14491354Seschrock 
14509425SEric.Schrock@Sun.COM static int
14519425SEric.Schrock@Sun.COM zfs_ioc_vdev_setfru(zfs_cmd_t *zc)
14529425SEric.Schrock@Sun.COM {
14539425SEric.Schrock@Sun.COM 	spa_t *spa;
14549425SEric.Schrock@Sun.COM 	char *fru = zc->zc_value;
14559425SEric.Schrock@Sun.COM 	uint64_t guid = zc->zc_guid;
14569425SEric.Schrock@Sun.COM 	int error;
14579425SEric.Schrock@Sun.COM 
14589425SEric.Schrock@Sun.COM 	error = spa_open(zc->zc_name, &spa, FTAG);
14599425SEric.Schrock@Sun.COM 	if (error != 0)
14609425SEric.Schrock@Sun.COM 		return (error);
14619425SEric.Schrock@Sun.COM 
14629425SEric.Schrock@Sun.COM 	error = spa_vdev_setfru(spa, guid, fru);
14639425SEric.Schrock@Sun.COM 	spa_close(spa, FTAG);
14649425SEric.Schrock@Sun.COM 	return (error);
14659425SEric.Schrock@Sun.COM }
14669425SEric.Schrock@Sun.COM 
14675367Sahrens /*
14685367Sahrens  * inputs:
14695367Sahrens  * zc_name		name of filesystem
14705367Sahrens  * zc_nvlist_dst_size	size of buffer for property nvlist
14715367Sahrens  *
14725367Sahrens  * outputs:
14735367Sahrens  * zc_objset_stats	stats
14745367Sahrens  * zc_nvlist_dst	property nvlist
14755367Sahrens  * zc_nvlist_dst_size	size of property nvlist
14765367Sahrens  */
14771354Seschrock static int
1478789Sahrens zfs_ioc_objset_stats(zfs_cmd_t *zc)
1479789Sahrens {
1480789Sahrens 	objset_t *os = NULL;
1481789Sahrens 	int error;
14821356Seschrock 	nvlist_t *nv;
1483789Sahrens 
148410298SMatthew.Ahrens@Sun.COM 	if (error = dmu_objset_hold(zc->zc_name, FTAG, &os))
1485789Sahrens 		return (error);
1486789Sahrens 
14872885Sahrens 	dmu_objset_fast_stat(os, &zc->zc_objset_stats);
1488789Sahrens 
14892856Snd150628 	if (zc->zc_nvlist_dst != 0 &&
14906689Smaybee 	    (error = dsl_prop_get_all(os, &nv, FALSE)) == 0) {
14912885Sahrens 		dmu_objset_stats(os, nv);
14923087Sahrens 		/*
14935147Srm160521 		 * NB: zvol_get_stats() will read the objset contents,
14943087Sahrens 		 * which we aren't supposed to do with a
14956689Smaybee 		 * DS_MODE_USER hold, because it could be
14963087Sahrens 		 * inconsistent.  So this is a bit of a workaround...
149710298SMatthew.Ahrens@Sun.COM 		 * XXX reading with out owning
14983087Sahrens 		 */
14994577Sahrens 		if (!zc->zc_objset_stats.dds_inconsistent) {
15004577Sahrens 			if (dmu_objset_type(os) == DMU_OST_ZVOL)
15014577Sahrens 				VERIFY(zvol_get_stats(os, nv) == 0);
15024577Sahrens 		}
15032676Seschrock 		error = put_nvlist(zc, nv);
15041356Seschrock 		nvlist_free(nv);
15051356Seschrock 	}
1506789Sahrens 
150710298SMatthew.Ahrens@Sun.COM 	dmu_objset_rele(os, FTAG);
1508789Sahrens 	return (error);
1509789Sahrens }
1510789Sahrens 
15115498Stimh static int
15125498Stimh nvl_add_zplprop(objset_t *os, nvlist_t *props, zfs_prop_t prop)
15135498Stimh {
15145498Stimh 	uint64_t value;
15155498Stimh 	int error;
15165498Stimh 
15175498Stimh 	/*
15185498Stimh 	 * zfs_get_zplprop() will either find a value or give us
15195498Stimh 	 * the default value (if there is one).
15205498Stimh 	 */
15215498Stimh 	if ((error = zfs_get_zplprop(os, prop, &value)) != 0)
15225498Stimh 		return (error);
15235498Stimh 	VERIFY(nvlist_add_uint64(props, zfs_prop_to_name(prop), value) == 0);
15245498Stimh 	return (0);
15255498Stimh }
15265498Stimh 
15275498Stimh /*
15285498Stimh  * inputs:
15295498Stimh  * zc_name		name of filesystem
15305498Stimh  * zc_nvlist_dst_size	size of buffer for zpl property nvlist
15315498Stimh  *
15325498Stimh  * outputs:
15335498Stimh  * zc_nvlist_dst	zpl property nvlist
15345498Stimh  * zc_nvlist_dst_size	size of zpl property nvlist
15355498Stimh  */
15365498Stimh static int
15375498Stimh zfs_ioc_objset_zplprops(zfs_cmd_t *zc)
15385498Stimh {
15395498Stimh 	objset_t *os;
15405498Stimh 	int err;
15415498Stimh 
154210298SMatthew.Ahrens@Sun.COM 	/* XXX reading without owning */
154310298SMatthew.Ahrens@Sun.COM 	if (err = dmu_objset_hold(zc->zc_name, FTAG, &os))
15445498Stimh 		return (err);
15455498Stimh 
15465498Stimh 	dmu_objset_fast_stat(os, &zc->zc_objset_stats);
15475498Stimh 
15485498Stimh 	/*
15495498Stimh 	 * NB: nvl_add_zplprop() will read the objset contents,
15506689Smaybee 	 * which we aren't supposed to do with a DS_MODE_USER
15516689Smaybee 	 * hold, because it could be inconsistent.
15525498Stimh 	 */
15535498Stimh 	if (zc->zc_nvlist_dst != NULL &&
15545498Stimh 	    !zc->zc_objset_stats.dds_inconsistent &&
15555498Stimh 	    dmu_objset_type(os) == DMU_OST_ZFS) {
15565498Stimh 		nvlist_t *nv;
15575498Stimh 
15585498Stimh 		VERIFY(nvlist_alloc(&nv, NV_UNIQUE_NAME, KM_SLEEP) == 0);
15595498Stimh 		if ((err = nvl_add_zplprop(os, nv, ZFS_PROP_VERSION)) == 0 &&
15605498Stimh 		    (err = nvl_add_zplprop(os, nv, ZFS_PROP_NORMALIZE)) == 0 &&
15615498Stimh 		    (err = nvl_add_zplprop(os, nv, ZFS_PROP_UTF8ONLY)) == 0 &&
15625498Stimh 		    (err = nvl_add_zplprop(os, nv, ZFS_PROP_CASE)) == 0)
15635498Stimh 			err = put_nvlist(zc, nv);
15645498Stimh 		nvlist_free(nv);
15655498Stimh 	} else {
15665498Stimh 		err = ENOENT;
15675498Stimh 	}
156810298SMatthew.Ahrens@Sun.COM 	dmu_objset_rele(os, FTAG);
15695498Stimh 	return (err);
15705498Stimh }
15715498Stimh 
15729396SMatthew.Ahrens@Sun.COM static boolean_t
15739396SMatthew.Ahrens@Sun.COM dataset_name_hidden(const char *name)
15749396SMatthew.Ahrens@Sun.COM {
15759396SMatthew.Ahrens@Sun.COM 	/*
15769396SMatthew.Ahrens@Sun.COM 	 * Skip over datasets that are not visible in this zone,
15779396SMatthew.Ahrens@Sun.COM 	 * internal datasets (which have a $ in their name), and
15789396SMatthew.Ahrens@Sun.COM 	 * temporary datasets (which have a % in their name).
15799396SMatthew.Ahrens@Sun.COM 	 */
15809396SMatthew.Ahrens@Sun.COM 	if (strchr(name, '$') != NULL)
15819396SMatthew.Ahrens@Sun.COM 		return (B_TRUE);
15829396SMatthew.Ahrens@Sun.COM 	if (strchr(name, '%') != NULL)
15839396SMatthew.Ahrens@Sun.COM 		return (B_TRUE);
15849396SMatthew.Ahrens@Sun.COM 	if (!INGLOBALZONE(curproc) && !zone_dataset_visible(name, NULL))
15859396SMatthew.Ahrens@Sun.COM 		return (B_TRUE);
15869396SMatthew.Ahrens@Sun.COM 	return (B_FALSE);
15879396SMatthew.Ahrens@Sun.COM }
15889396SMatthew.Ahrens@Sun.COM 
15895367Sahrens /*
15905367Sahrens  * inputs:
15915367Sahrens  * zc_name		name of filesystem
15925367Sahrens  * zc_cookie		zap cursor
15935367Sahrens  * zc_nvlist_dst_size	size of buffer for property nvlist
15945367Sahrens  *
15955367Sahrens  * outputs:
15965367Sahrens  * zc_name		name of next filesystem
15979396SMatthew.Ahrens@Sun.COM  * zc_cookie		zap cursor
15985367Sahrens  * zc_objset_stats	stats
15995367Sahrens  * zc_nvlist_dst	property nvlist
16005367Sahrens  * zc_nvlist_dst_size	size of property nvlist
16015367Sahrens  */
1602789Sahrens static int
1603789Sahrens zfs_ioc_dataset_list_next(zfs_cmd_t *zc)
1604789Sahrens {
1605885Sahrens 	objset_t *os;
1606789Sahrens 	int error;
1607789Sahrens 	char *p;
1608789Sahrens 
160910298SMatthew.Ahrens@Sun.COM 	if (error = dmu_objset_hold(zc->zc_name, FTAG, &os)) {
1610885Sahrens 		if (error == ENOENT)
1611885Sahrens 			error = ESRCH;
1612885Sahrens 		return (error);
1613789Sahrens 	}
1614789Sahrens 
1615789Sahrens 	p = strrchr(zc->zc_name, '/');
1616789Sahrens 	if (p == NULL || p[1] != '\0')
1617789Sahrens 		(void) strlcat(zc->zc_name, "/", sizeof (zc->zc_name));
1618789Sahrens 	p = zc->zc_name + strlen(zc->zc_name);
1619789Sahrens 
16208697SRichard.Morris@Sun.COM 	/*
16218697SRichard.Morris@Sun.COM 	 * Pre-fetch the datasets.  dmu_objset_prefetch() always returns 0
16228697SRichard.Morris@Sun.COM 	 * but is not declared void because its called by dmu_objset_find().
16238697SRichard.Morris@Sun.COM 	 */
16248415SRichard.Morris@Sun.COM 	if (zc->zc_cookie == 0) {
16258415SRichard.Morris@Sun.COM 		uint64_t cookie = 0;
16268415SRichard.Morris@Sun.COM 		int len = sizeof (zc->zc_name) - (p - zc->zc_name);
16278415SRichard.Morris@Sun.COM 
16288415SRichard.Morris@Sun.COM 		while (dmu_dir_list_next(os, len, p, NULL, &cookie) == 0)
16298697SRichard.Morris@Sun.COM 			(void) dmu_objset_prefetch(p, NULL);
16308415SRichard.Morris@Sun.COM 	}
16318415SRichard.Morris@Sun.COM 
1632789Sahrens 	do {
1633885Sahrens 		error = dmu_dir_list_next(os,
1634885Sahrens 		    sizeof (zc->zc_name) - (p - zc->zc_name), p,
1635885Sahrens 		    NULL, &zc->zc_cookie);
1636789Sahrens 		if (error == ENOENT)
1637789Sahrens 			error = ESRCH;
163810588SEric.Taylor@Sun.COM 	} while (error == 0 && dataset_name_hidden(zc->zc_name) &&
163910588SEric.Taylor@Sun.COM 	    !(zc->zc_iflags & FKIOCTL));
164010298SMatthew.Ahrens@Sun.COM 	dmu_objset_rele(os, FTAG);
1641789Sahrens 
164210588SEric.Taylor@Sun.COM 	/*
164310588SEric.Taylor@Sun.COM 	 * If it's an internal dataset (ie. with a '$' in its name),
164410588SEric.Taylor@Sun.COM 	 * don't try to get stats for it, otherwise we'll return ENOENT.
164510588SEric.Taylor@Sun.COM 	 */
164610588SEric.Taylor@Sun.COM 	if (error == 0 && strchr(zc->zc_name, '$') == NULL)
1647885Sahrens 		error = zfs_ioc_objset_stats(zc); /* fill in the stats */
1648789Sahrens 	return (error);
1649789Sahrens }
1650789Sahrens 
16515367Sahrens /*
16525367Sahrens  * inputs:
16535367Sahrens  * zc_name		name of filesystem
16545367Sahrens  * zc_cookie		zap cursor
16555367Sahrens  * zc_nvlist_dst_size	size of buffer for property nvlist
16565367Sahrens  *
16575367Sahrens  * outputs:
16585367Sahrens  * zc_name		name of next snapshot
16595367Sahrens  * zc_objset_stats	stats
16605367Sahrens  * zc_nvlist_dst	property nvlist
16615367Sahrens  * zc_nvlist_dst_size	size of property nvlist
16625367Sahrens  */
1663789Sahrens static int
1664789Sahrens zfs_ioc_snapshot_list_next(zfs_cmd_t *zc)
1665789Sahrens {
1666885Sahrens 	objset_t *os;
1667789Sahrens 	int error;
1668789Sahrens 
166910474SRichard.Morris@Sun.COM 	if (zc->zc_cookie == 0)
167010474SRichard.Morris@Sun.COM 		(void) dmu_objset_find(zc->zc_name, dmu_objset_prefetch,
167110474SRichard.Morris@Sun.COM 		    NULL, DS_FIND_SNAPSHOTS);
167210474SRichard.Morris@Sun.COM 
167310298SMatthew.Ahrens@Sun.COM 	error = dmu_objset_hold(zc->zc_name, FTAG, &os);
16746689Smaybee 	if (error)
16756689Smaybee 		return (error == ENOENT ? ESRCH : error);
1676789Sahrens 
16771003Slling 	/*
16781003Slling 	 * A dataset name of maximum length cannot have any snapshots,
16791003Slling 	 * so exit immediately.
16801003Slling 	 */
16811003Slling 	if (strlcat(zc->zc_name, "@", sizeof (zc->zc_name)) >= MAXNAMELEN) {
168210298SMatthew.Ahrens@Sun.COM 		dmu_objset_rele(os, FTAG);
16831003Slling 		return (ESRCH);
1684789Sahrens 	}
1685789Sahrens 
1686885Sahrens 	error = dmu_snapshot_list_next(os,
1687885Sahrens 	    sizeof (zc->zc_name) - strlen(zc->zc_name),
16885663Sck153898 	    zc->zc_name + strlen(zc->zc_name), NULL, &zc->zc_cookie, NULL);
168910298SMatthew.Ahrens@Sun.COM 	dmu_objset_rele(os, FTAG);
1690885Sahrens 	if (error == 0)
1691885Sahrens 		error = zfs_ioc_objset_stats(zc); /* fill in the stats */
16926689Smaybee 	else if (error == ENOENT)
16936689Smaybee 		error = ESRCH;
1694789Sahrens 
16955367Sahrens 	/* if we failed, undo the @ that we tacked on to zc_name */
16966689Smaybee 	if (error)
16975367Sahrens 		*strchr(zc->zc_name, '@') = '\0';
1698789Sahrens 	return (error);
1699789Sahrens }
1700789Sahrens 
17016423Sgw25295 int
17024787Sahrens zfs_set_prop_nvlist(const char *name, nvlist_t *nvl)
1703789Sahrens {
17042676Seschrock 	nvpair_t *elem;
17058724SRichard.Morris@Sun.COM 	int error = 0;
17062676Seschrock 	uint64_t intval;
17072676Seschrock 	char *strval;
17088697SRichard.Morris@Sun.COM 	nvlist_t *genericnvl;
17099396SMatthew.Ahrens@Sun.COM 	boolean_t issnap = (strchr(name, '@') != NULL);
17102676Seschrock 
17114543Smarks 	/*
17124543Smarks 	 * First validate permission to set all of the properties
17134543Smarks 	 */
17142676Seschrock 	elem = NULL;
17152676Seschrock 	while ((elem = nvlist_next_nvpair(nvl, elem)) != NULL) {
17164670Sahrens 		const char *propname = nvpair_name(elem);
17174670Sahrens 		zfs_prop_t prop = zfs_name_to_prop(propname);
17182676Seschrock 
17195094Slling 		if (prop == ZPROP_INVAL) {
17202676Seschrock 			/*
17212676Seschrock 			 * If this is a user-defined property, it must be a
17222676Seschrock 			 * string, and there is no further validation to do.
17232676Seschrock 			 */
17249396SMatthew.Ahrens@Sun.COM 			if (zfs_prop_user(propname) &&
17259396SMatthew.Ahrens@Sun.COM 			    nvpair_type(elem) == DATA_TYPE_STRING) {
17269396SMatthew.Ahrens@Sun.COM 				if (error = zfs_secpolicy_write_perms(name,
17279396SMatthew.Ahrens@Sun.COM 				    ZFS_DELEG_PERM_USERPROP, CRED()))
17289396SMatthew.Ahrens@Sun.COM 					return (error);
17299396SMatthew.Ahrens@Sun.COM 				continue;
17309396SMatthew.Ahrens@Sun.COM 			}
17319396SMatthew.Ahrens@Sun.COM 
17329396SMatthew.Ahrens@Sun.COM 			if (!issnap && zfs_prop_userquota(propname) &&
17339396SMatthew.Ahrens@Sun.COM 			    nvpair_type(elem) == DATA_TYPE_UINT64_ARRAY) {
17349396SMatthew.Ahrens@Sun.COM 				const char *perm;
17359396SMatthew.Ahrens@Sun.COM 				const char *up = zfs_userquota_prop_prefixes
17369396SMatthew.Ahrens@Sun.COM 				    [ZFS_PROP_USERQUOTA];
17379396SMatthew.Ahrens@Sun.COM 				if (strncmp(propname, up, strlen(up)) == 0)
17389396SMatthew.Ahrens@Sun.COM 					perm = ZFS_DELEG_PERM_USERQUOTA;
17399396SMatthew.Ahrens@Sun.COM 				else
17409396SMatthew.Ahrens@Sun.COM 					perm = ZFS_DELEG_PERM_GROUPQUOTA;
17419396SMatthew.Ahrens@Sun.COM 				if (error = zfs_secpolicy_write_perms(name,
17429396SMatthew.Ahrens@Sun.COM 				    perm, CRED()))
17439396SMatthew.Ahrens@Sun.COM 					return (error);
17449396SMatthew.Ahrens@Sun.COM 				continue;
17459396SMatthew.Ahrens@Sun.COM 			}
17469396SMatthew.Ahrens@Sun.COM 
17479396SMatthew.Ahrens@Sun.COM 			return (EINVAL);
17482676Seschrock 		}
17492676Seschrock 
17509396SMatthew.Ahrens@Sun.COM 		if (issnap)
17519396SMatthew.Ahrens@Sun.COM 			return (EINVAL);
17529396SMatthew.Ahrens@Sun.COM 
17534787Sahrens 		if ((error = zfs_secpolicy_setprop(name, prop, CRED())) != 0)
17544670Sahrens 			return (error);
17552676Seschrock 
17564670Sahrens 		/*
17574670Sahrens 		 * Check that this value is valid for this pool version
17584670Sahrens 		 */
17594670Sahrens 		switch (prop) {
17603886Sahl 		case ZFS_PROP_COMPRESSION:
17613886Sahl 			/*
17623886Sahl 			 * If the user specified gzip compression, make sure
17633886Sahl 			 * the SPA supports it. We ignore any errors here since
17643886Sahl 			 * we'll catch them later.
17653886Sahl 			 */
17663886Sahl 			if (nvpair_type(elem) == DATA_TYPE_UINT64 &&
17677042Sgw25295 			    nvpair_value_uint64(elem, &intval) == 0) {
17687042Sgw25295 				if (intval >= ZIO_COMPRESS_GZIP_1 &&
17697042Sgw25295 				    intval <= ZIO_COMPRESS_GZIP_9 &&
17707184Stimh 				    zfs_earlier_version(name,
17715331Samw 				    SPA_VERSION_GZIP_COMPRESSION))
17725331Samw 					return (ENOTSUP);
17737042Sgw25295 
177410922SJeff.Bonwick@Sun.COM 				if (intval == ZIO_COMPRESS_ZLE &&
177510922SJeff.Bonwick@Sun.COM 				    zfs_earlier_version(name,
177610922SJeff.Bonwick@Sun.COM 				    SPA_VERSION_ZLE_COMPRESSION))
177710922SJeff.Bonwick@Sun.COM 					return (ENOTSUP);
177810922SJeff.Bonwick@Sun.COM 
17797042Sgw25295 				/*
17807042Sgw25295 				 * If this is a bootable dataset then
17817042Sgw25295 				 * verify that the compression algorithm
17827042Sgw25295 				 * is supported for booting. We must return
17837042Sgw25295 				 * something other than ENOTSUP since it
17847042Sgw25295 				 * implies a downrev pool version.
17857042Sgw25295 				 */
17867042Sgw25295 				if (zfs_is_bootfs(name) &&
17877042Sgw25295 				    !BOOTFS_COMPRESS_VALID(intval))
17887042Sgw25295 					return (ERANGE);
17893886Sahl 			}
17903886Sahl 			break;
17914603Sahrens 
17924603Sahrens 		case ZFS_PROP_COPIES:
17939396SMatthew.Ahrens@Sun.COM 			if (zfs_earlier_version(name, SPA_VERSION_DITTO_BLOCKS))
17945331Samw 				return (ENOTSUP);
17954603Sahrens 			break;
17965977Smarks 
179710922SJeff.Bonwick@Sun.COM 		case ZFS_PROP_DEDUP:
179810922SJeff.Bonwick@Sun.COM 			if (zfs_earlier_version(name, SPA_VERSION_DEDUP))
179910922SJeff.Bonwick@Sun.COM 				return (ENOTSUP);
180010922SJeff.Bonwick@Sun.COM 			break;
180110922SJeff.Bonwick@Sun.COM 
18025977Smarks 		case ZFS_PROP_SHARESMB:
18036689Smaybee 			if (zpl_earlier_version(name, ZPL_VERSION_FUID))
18045977Smarks 				return (ENOTSUP);
18055977Smarks 			break;
18068053SMark.Shellenbaum@Sun.COM 
18078053SMark.Shellenbaum@Sun.COM 		case ZFS_PROP_ACLINHERIT:
18088053SMark.Shellenbaum@Sun.COM 			if (nvpair_type(elem) == DATA_TYPE_UINT64 &&
18098053SMark.Shellenbaum@Sun.COM 			    nvpair_value_uint64(elem, &intval) == 0)
18108053SMark.Shellenbaum@Sun.COM 				if (intval == ZFS_ACL_PASSTHROUGH_X &&
18118053SMark.Shellenbaum@Sun.COM 				    zfs_earlier_version(name,
18128053SMark.Shellenbaum@Sun.COM 				    SPA_VERSION_PASSTHROUGH_X))
18138053SMark.Shellenbaum@Sun.COM 					return (ENOTSUP);
18144603Sahrens 		}
18154543Smarks 	}
18164543Smarks 
18178697SRichard.Morris@Sun.COM 	VERIFY(nvlist_alloc(&genericnvl, NV_UNIQUE_NAME, KM_SLEEP) == 0);
18184543Smarks 	elem = NULL;
18194543Smarks 	while ((elem = nvlist_next_nvpair(nvl, elem)) != NULL) {
18204670Sahrens 		const char *propname = nvpair_name(elem);
18214670Sahrens 		zfs_prop_t prop = zfs_name_to_prop(propname);
18224543Smarks 
18235094Slling 		if (prop == ZPROP_INVAL) {
18249396SMatthew.Ahrens@Sun.COM 			if (zfs_prop_userquota(propname)) {
18259396SMatthew.Ahrens@Sun.COM 				uint64_t *valary;
18269396SMatthew.Ahrens@Sun.COM 				unsigned int vallen;
18279396SMatthew.Ahrens@Sun.COM 				const char *domain;
18289396SMatthew.Ahrens@Sun.COM 				zfs_userquota_prop_t type;
18299396SMatthew.Ahrens@Sun.COM 				uint64_t rid;
18309396SMatthew.Ahrens@Sun.COM 				uint64_t quota;
18319396SMatthew.Ahrens@Sun.COM 				zfsvfs_t *zfsvfs;
18329396SMatthew.Ahrens@Sun.COM 
18339396SMatthew.Ahrens@Sun.COM 				VERIFY(nvpair_value_uint64_array(elem,
18349396SMatthew.Ahrens@Sun.COM 				    &valary, &vallen) == 0);
18359396SMatthew.Ahrens@Sun.COM 				VERIFY(vallen == 3);
18369396SMatthew.Ahrens@Sun.COM 				type = valary[0];
18379396SMatthew.Ahrens@Sun.COM 				rid = valary[1];
18389396SMatthew.Ahrens@Sun.COM 				quota = valary[2];
183910969SMatthew.Ahrens@Sun.COM 				/*
184010969SMatthew.Ahrens@Sun.COM 				 * The propname is encoded as
184110969SMatthew.Ahrens@Sun.COM 				 * userquota@<rid>-<domain>.
184210969SMatthew.Ahrens@Sun.COM 				 */
184310969SMatthew.Ahrens@Sun.COM 				domain = strchr(propname, '-') + 1;
18449396SMatthew.Ahrens@Sun.COM 
184510298SMatthew.Ahrens@Sun.COM 				error = zfsvfs_hold(name, FTAG, &zfsvfs);
18469396SMatthew.Ahrens@Sun.COM 				if (error == 0) {
18479396SMatthew.Ahrens@Sun.COM 					error = zfs_set_userquota(zfsvfs,
18489396SMatthew.Ahrens@Sun.COM 					    type, domain, rid, quota);
18499396SMatthew.Ahrens@Sun.COM 					zfsvfs_rele(zfsvfs, FTAG);
18509396SMatthew.Ahrens@Sun.COM 				}
18519396SMatthew.Ahrens@Sun.COM 				if (error == 0)
18529396SMatthew.Ahrens@Sun.COM 					continue;
18539396SMatthew.Ahrens@Sun.COM 				else
18549396SMatthew.Ahrens@Sun.COM 					goto out;
18559396SMatthew.Ahrens@Sun.COM 			} else if (zfs_prop_user(propname)) {
18569396SMatthew.Ahrens@Sun.COM 				VERIFY(nvpair_value_string(elem, &strval) == 0);
18579396SMatthew.Ahrens@Sun.COM 				error = dsl_prop_set(name, propname, 1,
18589396SMatthew.Ahrens@Sun.COM 				    strlen(strval) + 1, strval);
18599396SMatthew.Ahrens@Sun.COM 				if (error == 0)
18609396SMatthew.Ahrens@Sun.COM 					continue;
18619396SMatthew.Ahrens@Sun.COM 				else
18629396SMatthew.Ahrens@Sun.COM 					goto out;
18639396SMatthew.Ahrens@Sun.COM 			}
18644543Smarks 		}
18652676Seschrock 
18662676Seschrock 		switch (prop) {
18672676Seschrock 		case ZFS_PROP_QUOTA:
18682676Seschrock 			if ((error = nvpair_value_uint64(elem, &intval)) != 0 ||
18694577Sahrens 			    (error = dsl_dir_set_quota(name, intval)) != 0)
18708697SRichard.Morris@Sun.COM 				goto out;
18712676Seschrock 			break;
18722676Seschrock 
18735378Sck153898 		case ZFS_PROP_REFQUOTA:
18745378Sck153898 			if ((error = nvpair_value_uint64(elem, &intval)) != 0 ||
18755378Sck153898 			    (error = dsl_dataset_set_quota(name, intval)) != 0)
18768697SRichard.Morris@Sun.COM 				goto out;
18775378Sck153898 			break;
18785378Sck153898 
18792676Seschrock 		case ZFS_PROP_RESERVATION:
18802676Seschrock 			if ((error = nvpair_value_uint64(elem, &intval)) != 0 ||
18812676Seschrock 			    (error = dsl_dir_set_reservation(name,
18822676Seschrock 			    intval)) != 0)
18838697SRichard.Morris@Sun.COM 				goto out;
18842676Seschrock 			break;
1885789Sahrens 
18865378Sck153898 		case ZFS_PROP_REFRESERVATION:
18875378Sck153898 			if ((error = nvpair_value_uint64(elem, &intval)) != 0 ||
18885378Sck153898 			    (error = dsl_dataset_set_reservation(name,
18895378Sck153898 			    intval)) != 0)
18908697SRichard.Morris@Sun.COM 				goto out;
18915378Sck153898 			break;
18925378Sck153898 
18932676Seschrock 		case ZFS_PROP_VOLSIZE:
18942676Seschrock 			if ((error = nvpair_value_uint64(elem, &intval)) != 0 ||
18954787Sahrens 			    (error = zvol_set_volsize(name,
18964787Sahrens 			    ddi_driver_major(zfs_dip), intval)) != 0)
18978697SRichard.Morris@Sun.COM 				goto out;
18982676Seschrock 			break;
18992676Seschrock 
19004577Sahrens 		case ZFS_PROP_VERSION:
19019396SMatthew.Ahrens@Sun.COM 		{
19029396SMatthew.Ahrens@Sun.COM 			zfsvfs_t *zfsvfs;
19039396SMatthew.Ahrens@Sun.COM 
19049396SMatthew.Ahrens@Sun.COM 			if ((error = nvpair_value_uint64(elem, &intval)) != 0)
19059396SMatthew.Ahrens@Sun.COM 				goto out;
190610298SMatthew.Ahrens@Sun.COM 			if ((error = zfsvfs_hold(name, FTAG, &zfsvfs)) != 0)
19079396SMatthew.Ahrens@Sun.COM 				goto out;
19089396SMatthew.Ahrens@Sun.COM 			error = zfs_set_version(zfsvfs, intval);
19099396SMatthew.Ahrens@Sun.COM 			zfsvfs_rele(zfsvfs, FTAG);
19109396SMatthew.Ahrens@Sun.COM 
19119396SMatthew.Ahrens@Sun.COM 			if (error == 0 && intval >= ZPL_VERSION_USERSPACE) {
19129396SMatthew.Ahrens@Sun.COM 				zfs_cmd_t zc = { 0 };
19139396SMatthew.Ahrens@Sun.COM 				(void) strcpy(zc.zc_name, name);
19149396SMatthew.Ahrens@Sun.COM 				(void) zfs_ioc_userspace_upgrade(&zc);
19159396SMatthew.Ahrens@Sun.COM 			}
19169396SMatthew.Ahrens@Sun.COM 			if (error)
19178697SRichard.Morris@Sun.COM 				goto out;
19182676Seschrock 			break;
19199396SMatthew.Ahrens@Sun.COM 		}
19202676Seschrock 
1921*10972SRic.Aleshire@Sun.COM 		case ZFS_PROP_MLSLABEL:
1922*10972SRic.Aleshire@Sun.COM 		{
1923*10972SRic.Aleshire@Sun.COM 			objset_t *os = NULL;
1924*10972SRic.Aleshire@Sun.COM 
1925*10972SRic.Aleshire@Sun.COM 			if ((error = nvpair_value_string(elem, &strval)) != 0)
1926*10972SRic.Aleshire@Sun.COM 				goto out;
1927*10972SRic.Aleshire@Sun.COM 			if ((error = zfs_set_slabel_policy(name, strval,
1928*10972SRic.Aleshire@Sun.COM 			    CRED(), &os)) != 0) {
1929*10972SRic.Aleshire@Sun.COM 				/* error; first release the dataset if needed */
1930*10972SRic.Aleshire@Sun.COM 				if (os)
1931*10972SRic.Aleshire@Sun.COM 					dmu_objset_disown(os, setsl_tag);
1932*10972SRic.Aleshire@Sun.COM 				goto out;
1933*10972SRic.Aleshire@Sun.COM 			}
1934*10972SRic.Aleshire@Sun.COM 
1935*10972SRic.Aleshire@Sun.COM 			error = nvlist_add_nvpair(genericnvl, elem);
1936*10972SRic.Aleshire@Sun.COM 			if (os)
1937*10972SRic.Aleshire@Sun.COM 				dmu_objset_disown(os, setsl_tag);
1938*10972SRic.Aleshire@Sun.COM 			if (error != 0)
1939*10972SRic.Aleshire@Sun.COM 				goto out;
1940*10972SRic.Aleshire@Sun.COM 			break;
1941*10972SRic.Aleshire@Sun.COM 		}
1942*10972SRic.Aleshire@Sun.COM 
19432676Seschrock 		default:
19442676Seschrock 			if (nvpair_type(elem) == DATA_TYPE_STRING) {
19452676Seschrock 				if (zfs_prop_get_type(prop) !=
19468697SRichard.Morris@Sun.COM 				    PROP_TYPE_STRING) {
19478697SRichard.Morris@Sun.COM 					error = EINVAL;
19488697SRichard.Morris@Sun.COM 					goto out;
19498697SRichard.Morris@Sun.COM 				}
19502676Seschrock 			} else if (nvpair_type(elem) == DATA_TYPE_UINT64) {
19512885Sahrens 				const char *unused;
19522885Sahrens 
19532717Seschrock 				VERIFY(nvpair_value_uint64(elem, &intval) == 0);
19542676Seschrock 
19552676Seschrock 				switch (zfs_prop_get_type(prop)) {
19564787Sahrens 				case PROP_TYPE_NUMBER:
19572676Seschrock 					break;
19584787Sahrens 				case PROP_TYPE_STRING:
19598697SRichard.Morris@Sun.COM 					error = EINVAL;
19608697SRichard.Morris@Sun.COM 					goto out;
19614787Sahrens 				case PROP_TYPE_INDEX:
19622717Seschrock 					if (zfs_prop_index_to_string(prop,
19638697SRichard.Morris@Sun.COM 					    intval, &unused) != 0) {
19648697SRichard.Morris@Sun.COM 						error = EINVAL;
19658697SRichard.Morris@Sun.COM 						goto out;
19668697SRichard.Morris@Sun.COM 					}
19672676Seschrock 					break;
19682676Seschrock 				default:
19694577Sahrens 					cmn_err(CE_PANIC,
19704577Sahrens 					    "unknown property type");
19712676Seschrock 					break;
19722676Seschrock 				}
19732676Seschrock 			} else {
19748697SRichard.Morris@Sun.COM 				error = EINVAL;
19758697SRichard.Morris@Sun.COM 				goto out;
19762676Seschrock 			}
19778697SRichard.Morris@Sun.COM 			if ((error = nvlist_add_nvpair(genericnvl, elem)) != 0)
19788697SRichard.Morris@Sun.COM 				goto out;
19792676Seschrock 		}
19802676Seschrock 	}
19812676Seschrock 
19828697SRichard.Morris@Sun.COM 	if (nvlist_next_nvpair(genericnvl, NULL) != NULL) {
19838697SRichard.Morris@Sun.COM 		error = dsl_props_set(name, genericnvl);
19848697SRichard.Morris@Sun.COM 	}
19858697SRichard.Morris@Sun.COM out:
19868697SRichard.Morris@Sun.COM 	nvlist_free(genericnvl);
19878697SRichard.Morris@Sun.COM 	return (error);
1988789Sahrens }
1989789Sahrens 
19905367Sahrens /*
19919355SMatthew.Ahrens@Sun.COM  * Check that all the properties are valid user properties.
19929355SMatthew.Ahrens@Sun.COM  */
19939355SMatthew.Ahrens@Sun.COM static int
19949355SMatthew.Ahrens@Sun.COM zfs_check_userprops(char *fsname, nvlist_t *nvl)
19959355SMatthew.Ahrens@Sun.COM {
19969355SMatthew.Ahrens@Sun.COM 	nvpair_t *elem = NULL;
19979355SMatthew.Ahrens@Sun.COM 	int error = 0;
19989355SMatthew.Ahrens@Sun.COM 
19999355SMatthew.Ahrens@Sun.COM 	while ((elem = nvlist_next_nvpair(nvl, elem)) != NULL) {
20009355SMatthew.Ahrens@Sun.COM 		const char *propname = nvpair_name(elem);
20019355SMatthew.Ahrens@Sun.COM 		char *valstr;
20029355SMatthew.Ahrens@Sun.COM 
20039355SMatthew.Ahrens@Sun.COM 		if (!zfs_prop_user(propname) ||
20049355SMatthew.Ahrens@Sun.COM 		    nvpair_type(elem) != DATA_TYPE_STRING)
20059355SMatthew.Ahrens@Sun.COM 			return (EINVAL);
20069355SMatthew.Ahrens@Sun.COM 
20079355SMatthew.Ahrens@Sun.COM 		if (error = zfs_secpolicy_write_perms(fsname,
20089355SMatthew.Ahrens@Sun.COM 		    ZFS_DELEG_PERM_USERPROP, CRED()))
20099355SMatthew.Ahrens@Sun.COM 			return (error);
20109355SMatthew.Ahrens@Sun.COM 
20119355SMatthew.Ahrens@Sun.COM 		if (strlen(propname) >= ZAP_MAXNAMELEN)
20129355SMatthew.Ahrens@Sun.COM 			return (ENAMETOOLONG);
20139355SMatthew.Ahrens@Sun.COM 
20149355SMatthew.Ahrens@Sun.COM 		VERIFY(nvpair_value_string(elem, &valstr) == 0);
20159355SMatthew.Ahrens@Sun.COM 		if (strlen(valstr) >= ZAP_MAXVALUELEN)
20169355SMatthew.Ahrens@Sun.COM 			return (E2BIG);
20179355SMatthew.Ahrens@Sun.COM 	}
20189355SMatthew.Ahrens@Sun.COM 	return (0);
20199355SMatthew.Ahrens@Sun.COM }
20209355SMatthew.Ahrens@Sun.COM 
20219355SMatthew.Ahrens@Sun.COM /*
20225367Sahrens  * inputs:
20235367Sahrens  * zc_name		name of filesystem
20248697SRichard.Morris@Sun.COM  * zc_value		name of property to set
20255367Sahrens  * zc_nvlist_src{_size}	nvlist of properties to apply
20267265Sahrens  * zc_cookie		clear existing local props?
20275367Sahrens  *
20285367Sahrens  * outputs:		none
20295367Sahrens  */
2030789Sahrens static int
20312676Seschrock zfs_ioc_set_prop(zfs_cmd_t *zc)
2032789Sahrens {
20332676Seschrock 	nvlist_t *nvl;
20342676Seschrock 	int error;
2035789Sahrens 
20365094Slling 	if ((error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
20379643SEric.Taylor@Sun.COM 	    zc->zc_iflags, &nvl)) != 0)
20382676Seschrock 		return (error);
20392676Seschrock 
20407265Sahrens 	if (zc->zc_cookie) {
20417265Sahrens 		nvlist_t *origprops;
20427265Sahrens 		objset_t *os;
20437265Sahrens 
204410298SMatthew.Ahrens@Sun.COM 		if (dmu_objset_hold(zc->zc_name, FTAG, &os) == 0) {
20457265Sahrens 			if (dsl_prop_get_all(os, &origprops, TRUE) == 0) {
20468536SDavid.Pacheco@Sun.COM 				clear_props(zc->zc_name, origprops, nvl);
20477265Sahrens 				nvlist_free(origprops);
20487265Sahrens 			}
204910298SMatthew.Ahrens@Sun.COM 			dmu_objset_rele(os, FTAG);
20507265Sahrens 		}
20517265Sahrens 
20527265Sahrens 	}
20537265Sahrens 
20544787Sahrens 	error = zfs_set_prop_nvlist(zc->zc_name, nvl);
20554543Smarks 
20562676Seschrock 	nvlist_free(nvl);
20572676Seschrock 	return (error);
2058789Sahrens }
2059789Sahrens 
20605367Sahrens /*
20615367Sahrens  * inputs:
20625367Sahrens  * zc_name		name of filesystem
20635367Sahrens  * zc_value		name of property to inherit
20645367Sahrens  *
20655367Sahrens  * outputs:		none
20665367Sahrens  */
2067789Sahrens static int
20684849Sahrens zfs_ioc_inherit_prop(zfs_cmd_t *zc)
20694849Sahrens {
20704849Sahrens 	/* the property name has been validated by zfs_secpolicy_inherit() */
20714849Sahrens 	return (dsl_prop_set(zc->zc_name, zc->zc_value, 0, 0, NULL));
20724849Sahrens }
20734849Sahrens 
20744849Sahrens static int
20754098Slling zfs_ioc_pool_set_props(zfs_cmd_t *zc)
20763912Slling {
20775094Slling 	nvlist_t *props;
20783912Slling 	spa_t *spa;
20795094Slling 	int error;
20808525SEric.Schrock@Sun.COM 	nvpair_t *elem;
20813912Slling 
20825094Slling 	if ((error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
20839643SEric.Taylor@Sun.COM 	    zc->zc_iflags, &props)))
20843912Slling 		return (error);
20853912Slling 
20868525SEric.Schrock@Sun.COM 	/*
20878525SEric.Schrock@Sun.COM 	 * If the only property is the configfile, then just do a spa_lookup()
20888525SEric.Schrock@Sun.COM 	 * to handle the faulted case.
20898525SEric.Schrock@Sun.COM 	 */
20908525SEric.Schrock@Sun.COM 	elem = nvlist_next_nvpair(props, NULL);
20918525SEric.Schrock@Sun.COM 	if (elem != NULL && strcmp(nvpair_name(elem),
20928525SEric.Schrock@Sun.COM 	    zpool_prop_to_name(ZPOOL_PROP_CACHEFILE)) == 0 &&
20938525SEric.Schrock@Sun.COM 	    nvlist_next_nvpair(props, elem) == NULL) {
20948525SEric.Schrock@Sun.COM 		mutex_enter(&spa_namespace_lock);
20958525SEric.Schrock@Sun.COM 		if ((spa = spa_lookup(zc->zc_name)) != NULL) {
20968525SEric.Schrock@Sun.COM 			spa_configfile_set(spa, props, B_FALSE);
20978525SEric.Schrock@Sun.COM 			spa_config_sync(spa, B_FALSE, B_TRUE);
20988525SEric.Schrock@Sun.COM 		}
20998525SEric.Schrock@Sun.COM 		mutex_exit(&spa_namespace_lock);
210010672SEric.Schrock@Sun.COM 		if (spa != NULL) {
210110672SEric.Schrock@Sun.COM 			nvlist_free(props);
21028525SEric.Schrock@Sun.COM 			return (0);
210310672SEric.Schrock@Sun.COM 		}
21048525SEric.Schrock@Sun.COM 	}
21058525SEric.Schrock@Sun.COM 
21063912Slling 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) {
21075094Slling 		nvlist_free(props);
21083912Slling 		return (error);
21093912Slling 	}
21103912Slling 
21115094Slling 	error = spa_prop_set(spa, props);
21123912Slling 
21135094Slling 	nvlist_free(props);
21143912Slling 	spa_close(spa, FTAG);
21153912Slling 
21163912Slling 	return (error);
21173912Slling }
21183912Slling 
21193912Slling static int
21204098Slling zfs_ioc_pool_get_props(zfs_cmd_t *zc)
21213912Slling {
21223912Slling 	spa_t *spa;
21233912Slling 	int error;
21243912Slling 	nvlist_t *nvp = NULL;
21253912Slling 
21268525SEric.Schrock@Sun.COM 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) {
21278525SEric.Schrock@Sun.COM 		/*
21288525SEric.Schrock@Sun.COM 		 * If the pool is faulted, there may be properties we can still
21298525SEric.Schrock@Sun.COM 		 * get (such as altroot and cachefile), so attempt to get them
21308525SEric.Schrock@Sun.COM 		 * anyway.
21318525SEric.Schrock@Sun.COM 		 */
21328525SEric.Schrock@Sun.COM 		mutex_enter(&spa_namespace_lock);
21338525SEric.Schrock@Sun.COM 		if ((spa = spa_lookup(zc->zc_name)) != NULL)
21348525SEric.Schrock@Sun.COM 			error = spa_prop_get(spa, &nvp);
21358525SEric.Schrock@Sun.COM 		mutex_exit(&spa_namespace_lock);
21368525SEric.Schrock@Sun.COM 	} else {
21378525SEric.Schrock@Sun.COM 		error = spa_prop_get(spa, &nvp);
21388525SEric.Schrock@Sun.COM 		spa_close(spa, FTAG);
21398525SEric.Schrock@Sun.COM 	}
21403912Slling 
21413912Slling 	if (error == 0 && zc->zc_nvlist_dst != NULL)
21423912Slling 		error = put_nvlist(zc, nvp);
21433912Slling 	else
21443912Slling 		error = EFAULT;
21453912Slling 
21468525SEric.Schrock@Sun.COM 	nvlist_free(nvp);
21473912Slling 	return (error);
21483912Slling }
21493912Slling 
21503912Slling static int
21514543Smarks zfs_ioc_iscsi_perm_check(zfs_cmd_t *zc)
21524543Smarks {
21534543Smarks 	nvlist_t *nvp;
21544543Smarks 	int error;
21554543Smarks 	uint32_t uid;
21564543Smarks 	uint32_t gid;
21574543Smarks 	uint32_t *groups;
21584543Smarks 	uint_t group_cnt;
21594543Smarks 	cred_t	*usercred;
21604543Smarks 
21615094Slling 	if ((error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
21629643SEric.Taylor@Sun.COM 	    zc->zc_iflags, &nvp)) != 0) {
21634543Smarks 		return (error);
21644543Smarks 	}
21654543Smarks 
21664543Smarks 	if ((error = nvlist_lookup_uint32(nvp,
21674543Smarks 	    ZFS_DELEG_PERM_UID, &uid)) != 0) {
21684543Smarks 		nvlist_free(nvp);
21694543Smarks 		return (EPERM);
21704543Smarks 	}
21714543Smarks 
21724543Smarks 	if ((error = nvlist_lookup_uint32(nvp,
21734543Smarks 	    ZFS_DELEG_PERM_GID, &gid)) != 0) {
21744543Smarks 		nvlist_free(nvp);
21754543Smarks 		return (EPERM);
21764543Smarks 	}
21774543Smarks 
21784543Smarks 	if ((error = nvlist_lookup_uint32_array(nvp, ZFS_DELEG_PERM_GROUPS,
21794543Smarks 	    &groups, &group_cnt)) != 0) {
21804543Smarks 		nvlist_free(nvp);
21814543Smarks 		return (EPERM);
21824543Smarks 	}
21834543Smarks 	usercred = cralloc();
21844543Smarks 	if ((crsetugid(usercred, uid, gid) != 0) ||
21854543Smarks 	    (crsetgroups(usercred, group_cnt, (gid_t *)groups) != 0)) {
21864543Smarks 		nvlist_free(nvp);
21874543Smarks 		crfree(usercred);
21884543Smarks 		return (EPERM);
21894543Smarks 	}
21904543Smarks 	nvlist_free(nvp);
21914543Smarks 	error = dsl_deleg_access(zc->zc_name,
21924787Sahrens 	    zfs_prop_to_name(ZFS_PROP_SHAREISCSI), usercred);
21934543Smarks 	crfree(usercred);
21944543Smarks 	return (error);
21954543Smarks }
21964543Smarks 
21975367Sahrens /*
21985367Sahrens  * inputs:
21995367Sahrens  * zc_name		name of filesystem
22005367Sahrens  * zc_nvlist_src{_size}	nvlist of delegated permissions
22015367Sahrens  * zc_perm_action	allow/unallow flag
22025367Sahrens  *
22035367Sahrens  * outputs:		none
22045367Sahrens  */
22054543Smarks static int
22064543Smarks zfs_ioc_set_fsacl(zfs_cmd_t *zc)
22074543Smarks {
22084543Smarks 	int error;
22094543Smarks 	nvlist_t *fsaclnv = NULL;
22104543Smarks 
22115094Slling 	if ((error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
22129643SEric.Taylor@Sun.COM 	    zc->zc_iflags, &fsaclnv)) != 0)
22134543Smarks 		return (error);
22144543Smarks 
22154543Smarks 	/*
22164543Smarks 	 * Verify nvlist is constructed correctly
22174543Smarks 	 */
22184543Smarks 	if ((error = zfs_deleg_verify_nvlist(fsaclnv)) != 0) {
22194543Smarks 		nvlist_free(fsaclnv);
22204543Smarks 		return (EINVAL);
22214543Smarks 	}
22224543Smarks 
22234543Smarks 	/*
22244543Smarks 	 * If we don't have PRIV_SYS_MOUNT, then validate
22254543Smarks 	 * that user is allowed to hand out each permission in
22264543Smarks 	 * the nvlist(s)
22274543Smarks 	 */
22284543Smarks 
22294787Sahrens 	error = secpolicy_zfs(CRED());
22304543Smarks 	if (error) {
22314787Sahrens 		if (zc->zc_perm_action == B_FALSE) {
22324787Sahrens 			error = dsl_deleg_can_allow(zc->zc_name,
22334787Sahrens 			    fsaclnv, CRED());
22344787Sahrens 		} else {
22354787Sahrens 			error = dsl_deleg_can_unallow(zc->zc_name,
22364787Sahrens 			    fsaclnv, CRED());
22374787Sahrens 		}
22384543Smarks 	}
22394543Smarks 
22404543Smarks 	if (error == 0)
22414543Smarks 		error = dsl_deleg_set(zc->zc_name, fsaclnv, zc->zc_perm_action);
22424543Smarks 
22434543Smarks 	nvlist_free(fsaclnv);
22444543Smarks 	return (error);
22454543Smarks }
22464543Smarks 
22475367Sahrens /*
22485367Sahrens  * inputs:
22495367Sahrens  * zc_name		name of filesystem
22505367Sahrens  *
22515367Sahrens  * outputs:
22525367Sahrens  * zc_nvlist_src{_size}	nvlist of delegated permissions
22535367Sahrens  */
22544543Smarks static int
22554543Smarks zfs_ioc_get_fsacl(zfs_cmd_t *zc)
22564543Smarks {
22574543Smarks 	nvlist_t *nvp;
22584543Smarks 	int error;
22594543Smarks 
22604543Smarks 	if ((error = dsl_deleg_get(zc->zc_name, &nvp)) == 0) {
22614543Smarks 		error = put_nvlist(zc, nvp);
22624543Smarks 		nvlist_free(nvp);
22634543Smarks 	}
22644543Smarks 
22654543Smarks 	return (error);
22664543Smarks }
22674543Smarks 
22685367Sahrens /*
2269789Sahrens  * Search the vfs list for a specified resource.  Returns a pointer to it
2270789Sahrens  * or NULL if no suitable entry is found. The caller of this routine
2271789Sahrens  * is responsible for releasing the returned vfs pointer.
2272789Sahrens  */
2273789Sahrens static vfs_t *
2274789Sahrens zfs_get_vfs(const char *resource)
2275789Sahrens {
2276789Sahrens 	struct vfs *vfsp;
2277789Sahrens 	struct vfs *vfs_found = NULL;
2278789Sahrens 
2279789Sahrens 	vfs_list_read_lock();
2280789Sahrens 	vfsp = rootvfs;
2281789Sahrens 	do {
2282789Sahrens 		if (strcmp(refstr_value(vfsp->vfs_resource), resource) == 0) {
2283789Sahrens 			VFS_HOLD(vfsp);
2284789Sahrens 			vfs_found = vfsp;
2285789Sahrens 			break;
2286789Sahrens 		}
2287789Sahrens 		vfsp = vfsp->vfs_next;
2288789Sahrens 	} while (vfsp != rootvfs);
2289789Sahrens 	vfs_list_unlock();
2290789Sahrens 	return (vfs_found);
2291789Sahrens }
2292789Sahrens 
22934543Smarks /* ARGSUSED */
2294789Sahrens static void
22954543Smarks zfs_create_cb(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx)
2296789Sahrens {
22975331Samw 	zfs_creat_t *zct = arg;
22985498Stimh 
22995498Stimh 	zfs_create_fs(os, cr, zct->zct_zplprops, tx);
23005331Samw }
23015331Samw 
23025498Stimh #define	ZFS_PROP_UNDEFINED	((uint64_t)-1)
23035498Stimh 
23045331Samw /*
23055498Stimh  * inputs:
23067184Stimh  * createprops		list of properties requested by creator
23077184Stimh  * default_zplver	zpl version to use if unspecified in createprops
23087184Stimh  * fuids_ok		fuids allowed in this version of the spa?
23097184Stimh  * os			parent objset pointer (NULL if root fs)
23105331Samw  *
23115498Stimh  * outputs:
23125498Stimh  * zplprops	values for the zplprops we attach to the master node object
23137184Stimh  * is_ci	true if requested file system will be purely case-insensitive
23145331Samw  *
23155498Stimh  * Determine the settings for utf8only, normalization and
23165498Stimh  * casesensitivity.  Specific values may have been requested by the
23175498Stimh  * creator and/or we can inherit values from the parent dataset.  If
23185498Stimh  * the file system is of too early a vintage, a creator can not
23195498Stimh  * request settings for these properties, even if the requested
23205498Stimh  * setting is the default value.  We don't actually want to create dsl
23215498Stimh  * properties for these, so remove them from the source nvlist after
23225498Stimh  * processing.
23235331Samw  */
23245331Samw static int
23259396SMatthew.Ahrens@Sun.COM zfs_fill_zplprops_impl(objset_t *os, uint64_t zplver,
23267184Stimh     boolean_t fuids_ok, nvlist_t *createprops, nvlist_t *zplprops,
23277184Stimh     boolean_t *is_ci)
23285331Samw {
23295498Stimh 	uint64_t sense = ZFS_PROP_UNDEFINED;
23305498Stimh 	uint64_t norm = ZFS_PROP_UNDEFINED;
23315498Stimh 	uint64_t u8 = ZFS_PROP_UNDEFINED;
23325498Stimh 
23335498Stimh 	ASSERT(zplprops != NULL);
23345498Stimh 
23355375Stimh 	/*
23365498Stimh 	 * Pull out creator prop choices, if any.
23375375Stimh 	 */
23385498Stimh 	if (createprops) {
23395498Stimh 		(void) nvlist_lookup_uint64(createprops,
23407184Stimh 		    zfs_prop_to_name(ZFS_PROP_VERSION), &zplver);
23417184Stimh 		(void) nvlist_lookup_uint64(createprops,
23425498Stimh 		    zfs_prop_to_name(ZFS_PROP_NORMALIZE), &norm);
23435498Stimh 		(void) nvlist_remove_all(createprops,
23445498Stimh 		    zfs_prop_to_name(ZFS_PROP_NORMALIZE));
23455498Stimh 		(void) nvlist_lookup_uint64(createprops,
23465498Stimh 		    zfs_prop_to_name(ZFS_PROP_UTF8ONLY), &u8);
23475498Stimh 		(void) nvlist_remove_all(createprops,
23485498Stimh 		    zfs_prop_to_name(ZFS_PROP_UTF8ONLY));
23495498Stimh 		(void) nvlist_lookup_uint64(createprops,
23505498Stimh 		    zfs_prop_to_name(ZFS_PROP_CASE), &sense);
23515498Stimh 		(void) nvlist_remove_all(createprops,
23525498Stimh 		    zfs_prop_to_name(ZFS_PROP_CASE));
23535331Samw 	}
23545331Samw 
23555375Stimh 	/*
23567184Stimh 	 * If the zpl version requested is whacky or the file system
23577184Stimh 	 * or pool is version is too "young" to support normalization
23587184Stimh 	 * and the creator tried to set a value for one of the props,
23597184Stimh 	 * error out.
23605498Stimh 	 */
23617184Stimh 	if ((zplver < ZPL_VERSION_INITIAL || zplver > ZPL_VERSION) ||
23627184Stimh 	    (zplver >= ZPL_VERSION_FUID && !fuids_ok) ||
23637184Stimh 	    (zplver < ZPL_VERSION_NORMALIZATION &&
23645498Stimh 	    (norm != ZFS_PROP_UNDEFINED || u8 != ZFS_PROP_UNDEFINED ||
23657184Stimh 	    sense != ZFS_PROP_UNDEFINED)))
23665498Stimh 		return (ENOTSUP);
23675498Stimh 
23685498Stimh 	/*
23695498Stimh 	 * Put the version in the zplprops
23705498Stimh 	 */
23715498Stimh 	VERIFY(nvlist_add_uint64(zplprops,
23725498Stimh 	    zfs_prop_to_name(ZFS_PROP_VERSION), zplver) == 0);
23735498Stimh 
23745498Stimh 	if (norm == ZFS_PROP_UNDEFINED)
23755498Stimh 		VERIFY(zfs_get_zplprop(os, ZFS_PROP_NORMALIZE, &norm) == 0);
23765498Stimh 	VERIFY(nvlist_add_uint64(zplprops,
23775498Stimh 	    zfs_prop_to_name(ZFS_PROP_NORMALIZE), norm) == 0);
23785498Stimh 
23795498Stimh 	/*
23805498Stimh 	 * If we're normalizing, names must always be valid UTF-8 strings.
23815498Stimh 	 */
23825498Stimh 	if (norm)
23835498Stimh 		u8 = 1;
23845498Stimh 	if (u8 == ZFS_PROP_UNDEFINED)
23855498Stimh 		VERIFY(zfs_get_zplprop(os, ZFS_PROP_UTF8ONLY, &u8) == 0);
23865498Stimh 	VERIFY(nvlist_add_uint64(zplprops,
23875498Stimh 	    zfs_prop_to_name(ZFS_PROP_UTF8ONLY), u8) == 0);
23885498Stimh 
23895498Stimh 	if (sense == ZFS_PROP_UNDEFINED)
23905498Stimh 		VERIFY(zfs_get_zplprop(os, ZFS_PROP_CASE, &sense) == 0);
23915498Stimh 	VERIFY(nvlist_add_uint64(zplprops,
23925498Stimh 	    zfs_prop_to_name(ZFS_PROP_CASE), sense) == 0);
23935498Stimh 
23946492Stimh 	if (is_ci)
23956492Stimh 		*is_ci = (sense == ZFS_CASE_INSENSITIVE);
23966492Stimh 
23977184Stimh 	return (0);
23987184Stimh }
23997184Stimh 
24007184Stimh static int
24017184Stimh zfs_fill_zplprops(const char *dataset, nvlist_t *createprops,
24027184Stimh     nvlist_t *zplprops, boolean_t *is_ci)
24037184Stimh {
24047184Stimh 	boolean_t fuids_ok = B_TRUE;
24057184Stimh 	uint64_t zplver = ZPL_VERSION;
24067184Stimh 	objset_t *os = NULL;
24077184Stimh 	char parentname[MAXNAMELEN];
24087184Stimh 	char *cp;
24097184Stimh 	int error;
24107184Stimh 
24117184Stimh 	(void) strlcpy(parentname, dataset, sizeof (parentname));
24127184Stimh 	cp = strrchr(parentname, '/');
24137184Stimh 	ASSERT(cp != NULL);
24147184Stimh 	cp[0] = '\0';
24157184Stimh 
24169396SMatthew.Ahrens@Sun.COM 	if (zfs_earlier_version(dataset, SPA_VERSION_USERSPACE))
24179396SMatthew.Ahrens@Sun.COM 		zplver = ZPL_VERSION_USERSPACE - 1;
24187184Stimh 	if (zfs_earlier_version(dataset, SPA_VERSION_FUID)) {
24197184Stimh 		zplver = ZPL_VERSION_FUID - 1;
24207184Stimh 		fuids_ok = B_FALSE;
24217184Stimh 	}
24227184Stimh 
24237184Stimh 	/*
24247184Stimh 	 * Open parent object set so we can inherit zplprop values.
24257184Stimh 	 */
242610298SMatthew.Ahrens@Sun.COM 	if ((error = dmu_objset_hold(parentname, FTAG, &os)) != 0)
24277184Stimh 		return (error);
24287184Stimh 
24297184Stimh 	error = zfs_fill_zplprops_impl(os, zplver, fuids_ok, createprops,
24307184Stimh 	    zplprops, is_ci);
243110298SMatthew.Ahrens@Sun.COM 	dmu_objset_rele(os, FTAG);
24327184Stimh 	return (error);
24337184Stimh }
24347184Stimh 
24357184Stimh static int
24367184Stimh zfs_fill_zplprops_root(uint64_t spa_vers, nvlist_t *createprops,
24377184Stimh     nvlist_t *zplprops, boolean_t *is_ci)
24387184Stimh {
24397184Stimh 	boolean_t fuids_ok = B_TRUE;
24407184Stimh 	uint64_t zplver = ZPL_VERSION;
24417184Stimh 	int error;
24427184Stimh 
24437184Stimh 	if (spa_vers < SPA_VERSION_FUID) {
24447184Stimh 		zplver = ZPL_VERSION_FUID - 1;
24457184Stimh 		fuids_ok = B_FALSE;
24467184Stimh 	}
24477184Stimh 
24487184Stimh 	error = zfs_fill_zplprops_impl(NULL, zplver, fuids_ok, createprops,
24497184Stimh 	    zplprops, is_ci);
24507184Stimh 	return (error);
2451789Sahrens }
2452789Sahrens 
24535367Sahrens /*
24545367Sahrens  * inputs:
24555367Sahrens  * zc_objset_type	type of objset to create (fs vs zvol)
24565367Sahrens  * zc_name		name of new objset
24575367Sahrens  * zc_value		name of snapshot to clone from (may be empty)
24585367Sahrens  * zc_nvlist_src{_size}	nvlist of properties to apply
24595367Sahrens  *
24605498Stimh  * outputs: none
24615367Sahrens  */
2462789Sahrens static int
2463789Sahrens zfs_ioc_create(zfs_cmd_t *zc)
2464789Sahrens {
2465789Sahrens 	objset_t *clone;
2466789Sahrens 	int error = 0;
24675331Samw 	zfs_creat_t zct;
24684543Smarks 	nvlist_t *nvprops = NULL;
24694543Smarks 	void (*cbfunc)(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx);
2470789Sahrens 	dmu_objset_type_t type = zc->zc_objset_type;
2471789Sahrens 
2472789Sahrens 	switch (type) {
2473789Sahrens 
2474789Sahrens 	case DMU_OST_ZFS:
2475789Sahrens 		cbfunc = zfs_create_cb;
2476789Sahrens 		break;
2477789Sahrens 
2478789Sahrens 	case DMU_OST_ZVOL:
2479789Sahrens 		cbfunc = zvol_create_cb;
2480789Sahrens 		break;
2481789Sahrens 
2482789Sahrens 	default:
24832199Sahrens 		cbfunc = NULL;
24846423Sgw25295 		break;
24852199Sahrens 	}
24865326Sek110237 	if (strchr(zc->zc_name, '@') ||
24875326Sek110237 	    strchr(zc->zc_name, '%'))
2488789Sahrens 		return (EINVAL);
2489789Sahrens 
24902676Seschrock 	if (zc->zc_nvlist_src != NULL &&
24915094Slling 	    (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
24929643SEric.Taylor@Sun.COM 	    zc->zc_iflags, &nvprops)) != 0)
24932676Seschrock 		return (error);
24942676Seschrock 
24955498Stimh 	zct.zct_zplprops = NULL;
24965331Samw 	zct.zct_props = nvprops;
24975331Samw 
24982676Seschrock 	if (zc->zc_value[0] != '\0') {
2499789Sahrens 		/*
2500789Sahrens 		 * We're creating a clone of an existing snapshot.
2501789Sahrens 		 */
25022676Seschrock 		zc->zc_value[sizeof (zc->zc_value) - 1] = '\0';
25032676Seschrock 		if (dataset_namecheck(zc->zc_value, NULL, NULL) != 0) {
25044543Smarks 			nvlist_free(nvprops);
2505789Sahrens 			return (EINVAL);
25062676Seschrock 		}
2507789Sahrens 
250810298SMatthew.Ahrens@Sun.COM 		error = dmu_objset_hold(zc->zc_value, FTAG, &clone);
25092676Seschrock 		if (error) {
25104543Smarks 			nvlist_free(nvprops);
2511789Sahrens 			return (error);
25122676Seschrock 		}
25136492Stimh 
251410272SMatthew.Ahrens@Sun.COM 		error = dmu_objset_clone(zc->zc_name, dmu_objset_ds(clone), 0);
251510298SMatthew.Ahrens@Sun.COM 		dmu_objset_rele(clone, FTAG);
25165331Samw 		if (error) {
25175331Samw 			nvlist_free(nvprops);
25185331Samw 			return (error);
25195331Samw 		}
2520789Sahrens 	} else {
25216492Stimh 		boolean_t is_insensitive = B_FALSE;
25226492Stimh 
25232676Seschrock 		if (cbfunc == NULL) {
25244543Smarks 			nvlist_free(nvprops);
25252199Sahrens 			return (EINVAL);
25262676Seschrock 		}
25272676Seschrock 
2528789Sahrens 		if (type == DMU_OST_ZVOL) {
25292676Seschrock 			uint64_t volsize, volblocksize;
25302676Seschrock 
25314543Smarks 			if (nvprops == NULL ||
25324543Smarks 			    nvlist_lookup_uint64(nvprops,
25332676Seschrock 			    zfs_prop_to_name(ZFS_PROP_VOLSIZE),
25342676Seschrock 			    &volsize) != 0) {
25354543Smarks 				nvlist_free(nvprops);
25362676Seschrock 				return (EINVAL);
25372676Seschrock 			}
25382676Seschrock 
25394543Smarks 			if ((error = nvlist_lookup_uint64(nvprops,
25402676Seschrock 			    zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE),
25412676Seschrock 			    &volblocksize)) != 0 && error != ENOENT) {
25424543Smarks 				nvlist_free(nvprops);
25432676Seschrock 				return (EINVAL);
25442676Seschrock 			}
25451133Seschrock 
25462676Seschrock 			if (error != 0)
25472676Seschrock 				volblocksize = zfs_prop_default_numeric(
25482676Seschrock 				    ZFS_PROP_VOLBLOCKSIZE);
25492676Seschrock 
25502676Seschrock 			if ((error = zvol_check_volblocksize(
25512676Seschrock 			    volblocksize)) != 0 ||
25522676Seschrock 			    (error = zvol_check_volsize(volsize,
25532676Seschrock 			    volblocksize)) != 0) {
25544543Smarks 				nvlist_free(nvprops);
2555789Sahrens 				return (error);
25562676Seschrock 			}
25574577Sahrens 		} else if (type == DMU_OST_ZFS) {
25585331Samw 			int error;
25595331Samw 
25605498Stimh 			/*
25615331Samw 			 * We have to have normalization and
25625331Samw 			 * case-folding flags correct when we do the
25635331Samw 			 * file system creation, so go figure them out
25645498Stimh 			 * now.
25655331Samw 			 */
25665498Stimh 			VERIFY(nvlist_alloc(&zct.zct_zplprops,
25675498Stimh 			    NV_UNIQUE_NAME, KM_SLEEP) == 0);
25685498Stimh 			error = zfs_fill_zplprops(zc->zc_name, nvprops,
25697184Stimh 			    zct.zct_zplprops, &is_insensitive);
25705331Samw 			if (error != 0) {
25715331Samw 				nvlist_free(nvprops);
25725498Stimh 				nvlist_free(zct.zct_zplprops);
25735331Samw 				return (error);
25744577Sahrens 			}
25752676Seschrock 		}
257610272SMatthew.Ahrens@Sun.COM 		error = dmu_objset_create(zc->zc_name, type,
25776492Stimh 		    is_insensitive ? DS_FLAG_CI_DATASET : 0, cbfunc, &zct);
25785498Stimh 		nvlist_free(zct.zct_zplprops);
2579789Sahrens 	}
25802676Seschrock 
25812676Seschrock 	/*
25822676Seschrock 	 * It would be nice to do this atomically.
25832676Seschrock 	 */
25842676Seschrock 	if (error == 0) {
25854787Sahrens 		if ((error = zfs_set_prop_nvlist(zc->zc_name, nvprops)) != 0)
258610242Schris.kirby@sun.com 			(void) dmu_objset_destroy(zc->zc_name, B_FALSE);
25872676Seschrock 	}
25884543Smarks 	nvlist_free(nvprops);
2589789Sahrens 	return (error);
2590789Sahrens }
2591789Sahrens 
25925367Sahrens /*
25935367Sahrens  * inputs:
25945367Sahrens  * zc_name	name of filesystem
25955367Sahrens  * zc_value	short name of snapshot
25965367Sahrens  * zc_cookie	recursive flag
25979396SMatthew.Ahrens@Sun.COM  * zc_nvlist_src[_size] property list
25985367Sahrens  *
259910588SEric.Taylor@Sun.COM  * outputs:
260010588SEric.Taylor@Sun.COM  * zc_value	short snapname (i.e. part after the '@')
26015367Sahrens  */
2602789Sahrens static int
26032199Sahrens zfs_ioc_snapshot(zfs_cmd_t *zc)
26042199Sahrens {
26057265Sahrens 	nvlist_t *nvprops = NULL;
26067265Sahrens 	int error;
26077265Sahrens 	boolean_t recursive = zc->zc_cookie;
26087265Sahrens 
26092676Seschrock 	if (snapshot_namecheck(zc->zc_value, NULL, NULL) != 0)
26102199Sahrens 		return (EINVAL);
26117265Sahrens 
26127265Sahrens 	if (zc->zc_nvlist_src != NULL &&
26137265Sahrens 	    (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
26149643SEric.Taylor@Sun.COM 	    zc->zc_iflags, &nvprops)) != 0)
26157265Sahrens 		return (error);
26167265Sahrens 
26179355SMatthew.Ahrens@Sun.COM 	error = zfs_check_userprops(zc->zc_name, nvprops);
26189355SMatthew.Ahrens@Sun.COM 	if (error)
26199355SMatthew.Ahrens@Sun.COM 		goto out;
26209355SMatthew.Ahrens@Sun.COM 
26219355SMatthew.Ahrens@Sun.COM 	if (nvprops != NULL && nvlist_next_nvpair(nvprops, NULL) != NULL &&
26229355SMatthew.Ahrens@Sun.COM 	    zfs_earlier_version(zc->zc_name, SPA_VERSION_SNAP_PROPS)) {
26239355SMatthew.Ahrens@Sun.COM 		error = ENOTSUP;
26249355SMatthew.Ahrens@Sun.COM 		goto out;
26257265Sahrens 	}
26269355SMatthew.Ahrens@Sun.COM 
26279355SMatthew.Ahrens@Sun.COM 	error = dmu_objset_snapshot(zc->zc_name, zc->zc_value,
26289355SMatthew.Ahrens@Sun.COM 	    nvprops, recursive);
26299355SMatthew.Ahrens@Sun.COM 
26309355SMatthew.Ahrens@Sun.COM out:
26317265Sahrens 	nvlist_free(nvprops);
26327265Sahrens 	return (error);
26332199Sahrens }
26342199Sahrens 
26354007Smmusante int
26362199Sahrens zfs_unmount_snap(char *name, void *arg)
2637789Sahrens {
26382417Sahrens 	vfs_t *vfsp = NULL;
26392199Sahrens 
26406689Smaybee 	if (arg) {
26416689Smaybee 		char *snapname = arg;
26426689Smaybee 		int len = strlen(name) + strlen(snapname) + 2;
26436689Smaybee 		char *buf = kmem_alloc(len, KM_SLEEP);
26446689Smaybee 
26456689Smaybee 		(void) strcpy(buf, name);
26466689Smaybee 		(void) strcat(buf, "@");
26476689Smaybee 		(void) strcat(buf, snapname);
26486689Smaybee 		vfsp = zfs_get_vfs(buf);
26496689Smaybee 		kmem_free(buf, len);
26502417Sahrens 	} else if (strchr(name, '@')) {
26512199Sahrens 		vfsp = zfs_get_vfs(name);
26522199Sahrens 	}
26532199Sahrens 
26542199Sahrens 	if (vfsp) {
26552199Sahrens 		/*
26562199Sahrens 		 * Always force the unmount for snapshots.
26572199Sahrens 		 */
26582199Sahrens 		int flag = MS_FORCE;
2659789Sahrens 		int err;
2660789Sahrens 
26612199Sahrens 		if ((err = vn_vfswlock(vfsp->vfs_vnodecovered)) != 0) {
26622199Sahrens 			VFS_RELE(vfsp);
26632199Sahrens 			return (err);
26642199Sahrens 		}
26652199Sahrens 		VFS_RELE(vfsp);
26662199Sahrens 		if ((err = dounmount(vfsp, flag, kcred)) != 0)
26672199Sahrens 			return (err);
26682199Sahrens 	}
26692199Sahrens 	return (0);
26702199Sahrens }
26712199Sahrens 
26725367Sahrens /*
26735367Sahrens  * inputs:
267410242Schris.kirby@sun.com  * zc_name		name of filesystem
267510242Schris.kirby@sun.com  * zc_value		short name of snapshot
267610242Schris.kirby@sun.com  * zc_defer_destroy	mark for deferred destroy
26775367Sahrens  *
26785367Sahrens  * outputs:	none
26795367Sahrens  */
26802199Sahrens static int
26812199Sahrens zfs_ioc_destroy_snaps(zfs_cmd_t *zc)
26822199Sahrens {
26832199Sahrens 	int err;
2684789Sahrens 
26852676Seschrock 	if (snapshot_namecheck(zc->zc_value, NULL, NULL) != 0)
26862199Sahrens 		return (EINVAL);
26872199Sahrens 	err = dmu_objset_find(zc->zc_name,
26882676Seschrock 	    zfs_unmount_snap, zc->zc_value, DS_FIND_CHILDREN);
26892199Sahrens 	if (err)
26902199Sahrens 		return (err);
269110242Schris.kirby@sun.com 	return (dmu_snapshots_destroy(zc->zc_name, zc->zc_value,
269210242Schris.kirby@sun.com 	    zc->zc_defer_destroy));
26932199Sahrens }
26942199Sahrens 
26955367Sahrens /*
26965367Sahrens  * inputs:
26975367Sahrens  * zc_name		name of dataset to destroy
26985367Sahrens  * zc_objset_type	type of objset
269910242Schris.kirby@sun.com  * zc_defer_destroy	mark for deferred destroy
27005367Sahrens  *
27015367Sahrens  * outputs:		none
27025367Sahrens  */
27032199Sahrens static int
27042199Sahrens zfs_ioc_destroy(zfs_cmd_t *zc)
27052199Sahrens {
270610588SEric.Taylor@Sun.COM 	int err;
27072199Sahrens 	if (strchr(zc->zc_name, '@') && zc->zc_objset_type == DMU_OST_ZFS) {
270810588SEric.Taylor@Sun.COM 		err = zfs_unmount_snap(zc->zc_name, NULL);
27092199Sahrens 		if (err)
27102199Sahrens 			return (err);
2711789Sahrens 	}
2712789Sahrens 
271310588SEric.Taylor@Sun.COM 	err = dmu_objset_destroy(zc->zc_name, zc->zc_defer_destroy);
271410588SEric.Taylor@Sun.COM 	if (zc->zc_objset_type == DMU_OST_ZVOL && err == 0)
271510693Schris.kirby@sun.com 		(void) zvol_remove_minor(zc->zc_name);
271610588SEric.Taylor@Sun.COM 	return (err);
2717789Sahrens }
2718789Sahrens 
27195367Sahrens /*
27205367Sahrens  * inputs:
27215446Sahrens  * zc_name	name of dataset to rollback (to most recent snapshot)
27225367Sahrens  *
27235367Sahrens  * outputs:	none
27245367Sahrens  */
2725789Sahrens static int
2726789Sahrens zfs_ioc_rollback(zfs_cmd_t *zc)
2727789Sahrens {
272810272SMatthew.Ahrens@Sun.COM 	dsl_dataset_t *ds, *clone;
27295446Sahrens 	int error;
273010272SMatthew.Ahrens@Sun.COM 	zfsvfs_t *zfsvfs;
273110272SMatthew.Ahrens@Sun.COM 	char *clone_name;
273210272SMatthew.Ahrens@Sun.COM 
273310272SMatthew.Ahrens@Sun.COM 	error = dsl_dataset_hold(zc->zc_name, FTAG, &ds);
273410272SMatthew.Ahrens@Sun.COM 	if (error)
273510272SMatthew.Ahrens@Sun.COM 		return (error);
273610272SMatthew.Ahrens@Sun.COM 
273710272SMatthew.Ahrens@Sun.COM 	/* must not be a snapshot */
273810272SMatthew.Ahrens@Sun.COM 	if (dsl_dataset_is_snapshot(ds)) {
273910272SMatthew.Ahrens@Sun.COM 		dsl_dataset_rele(ds, FTAG);
274010272SMatthew.Ahrens@Sun.COM 		return (EINVAL);
274110272SMatthew.Ahrens@Sun.COM 	}
274210272SMatthew.Ahrens@Sun.COM 
274310272SMatthew.Ahrens@Sun.COM 	/* must have a most recent snapshot */
274410272SMatthew.Ahrens@Sun.COM 	if (ds->ds_phys->ds_prev_snap_txg < TXG_INITIAL) {
274510272SMatthew.Ahrens@Sun.COM 		dsl_dataset_rele(ds, FTAG);
274610272SMatthew.Ahrens@Sun.COM 		return (EINVAL);
274710272SMatthew.Ahrens@Sun.COM 	}
27485446Sahrens 
27495446Sahrens 	/*
275010272SMatthew.Ahrens@Sun.COM 	 * Create clone of most recent snapshot.
27515446Sahrens 	 */
275210272SMatthew.Ahrens@Sun.COM 	clone_name = kmem_asprintf("%s/%%rollback", zc->zc_name);
275310272SMatthew.Ahrens@Sun.COM 	error = dmu_objset_clone(clone_name, ds->ds_prev, DS_FLAG_INCONSISTENT);
27545446Sahrens 	if (error)
275510272SMatthew.Ahrens@Sun.COM 		goto out;
275610272SMatthew.Ahrens@Sun.COM 
275710298SMatthew.Ahrens@Sun.COM 	error = dsl_dataset_own(clone_name, B_TRUE, FTAG, &clone);
275810272SMatthew.Ahrens@Sun.COM 	if (error)
275910272SMatthew.Ahrens@Sun.COM 		goto out;
276010272SMatthew.Ahrens@Sun.COM 
276110272SMatthew.Ahrens@Sun.COM 	/*
276210272SMatthew.Ahrens@Sun.COM 	 * Do clone swap.
276310272SMatthew.Ahrens@Sun.COM 	 */
27649396SMatthew.Ahrens@Sun.COM 	if (getzfsvfs(zc->zc_name, &zfsvfs) == 0) {
276510298SMatthew.Ahrens@Sun.COM 		error = zfs_suspend_fs(zfsvfs);
27666083Sek110237 		if (error == 0) {
27676083Sek110237 			int resume_err;
27686083Sek110237 
276910272SMatthew.Ahrens@Sun.COM 			if (dsl_dataset_tryown(ds, B_FALSE, FTAG)) {
277010272SMatthew.Ahrens@Sun.COM 				error = dsl_dataset_clone_swap(clone, ds,
277110272SMatthew.Ahrens@Sun.COM 				    B_TRUE);
277210272SMatthew.Ahrens@Sun.COM 				dsl_dataset_disown(ds, FTAG);
277310272SMatthew.Ahrens@Sun.COM 				ds = NULL;
277410272SMatthew.Ahrens@Sun.COM 			} else {
277510272SMatthew.Ahrens@Sun.COM 				error = EBUSY;
277610272SMatthew.Ahrens@Sun.COM 			}
277710298SMatthew.Ahrens@Sun.COM 			resume_err = zfs_resume_fs(zfsvfs, zc->zc_name);
27786083Sek110237 			error = error ? error : resume_err;
27796083Sek110237 		}
27805446Sahrens 		VFS_RELE(zfsvfs->z_vfs);
27815446Sahrens 	} else {
278210272SMatthew.Ahrens@Sun.COM 		if (dsl_dataset_tryown(ds, B_FALSE, FTAG)) {
278310272SMatthew.Ahrens@Sun.COM 			error = dsl_dataset_clone_swap(clone, ds, B_TRUE);
278410272SMatthew.Ahrens@Sun.COM 			dsl_dataset_disown(ds, FTAG);
278510272SMatthew.Ahrens@Sun.COM 			ds = NULL;
278610272SMatthew.Ahrens@Sun.COM 		} else {
278710272SMatthew.Ahrens@Sun.COM 			error = EBUSY;
278810272SMatthew.Ahrens@Sun.COM 		}
27895446Sahrens 	}
279010272SMatthew.Ahrens@Sun.COM 
279110272SMatthew.Ahrens@Sun.COM 	/*
279210272SMatthew.Ahrens@Sun.COM 	 * Destroy clone (which also closes it).
279310272SMatthew.Ahrens@Sun.COM 	 */
279410272SMatthew.Ahrens@Sun.COM 	(void) dsl_dataset_destroy(clone, FTAG, B_FALSE);
279510272SMatthew.Ahrens@Sun.COM 
279610272SMatthew.Ahrens@Sun.COM out:
279710272SMatthew.Ahrens@Sun.COM 	strfree(clone_name);
279810272SMatthew.Ahrens@Sun.COM 	if (ds)
279910272SMatthew.Ahrens@Sun.COM 		dsl_dataset_rele(ds, FTAG);
28005446Sahrens 	return (error);
2801789Sahrens }
2802789Sahrens 
28035367Sahrens /*
28045367Sahrens  * inputs:
28055367Sahrens  * zc_name	old name of dataset
28065367Sahrens  * zc_value	new name of dataset
28075367Sahrens  * zc_cookie	recursive flag (only valid for snapshots)
28085367Sahrens  *
28095367Sahrens  * outputs:	none
28105367Sahrens  */
2811789Sahrens static int
2812789Sahrens zfs_ioc_rename(zfs_cmd_t *zc)
2813789Sahrens {
28144490Svb160487 	boolean_t recursive = zc->zc_cookie & 1;
28154007Smmusante 
28162676Seschrock 	zc->zc_value[sizeof (zc->zc_value) - 1] = '\0';
28175326Sek110237 	if (dataset_namecheck(zc->zc_value, NULL, NULL) != 0 ||
28185326Sek110237 	    strchr(zc->zc_value, '%'))
2819789Sahrens 		return (EINVAL);
2820789Sahrens 
28214007Smmusante 	/*
28224007Smmusante 	 * Unmount snapshot unless we're doing a recursive rename,
28234007Smmusante 	 * in which case the dataset code figures out which snapshots
28244007Smmusante 	 * to unmount.
28254007Smmusante 	 */
28264007Smmusante 	if (!recursive && strchr(zc->zc_name, '@') != NULL &&
2827789Sahrens 	    zc->zc_objset_type == DMU_OST_ZFS) {
28282199Sahrens 		int err = zfs_unmount_snap(zc->zc_name, NULL);
28292199Sahrens 		if (err)
28302199Sahrens 			return (err);
2831789Sahrens 	}
283210588SEric.Taylor@Sun.COM 	if (zc->zc_objset_type == DMU_OST_ZVOL)
283310588SEric.Taylor@Sun.COM 		(void) zvol_remove_minor(zc->zc_name);
28344007Smmusante 	return (dmu_objset_rename(zc->zc_name, zc->zc_value, recursive));
2835789Sahrens }
2836789Sahrens 
28376689Smaybee static void
28388536SDavid.Pacheco@Sun.COM clear_props(char *dataset, nvlist_t *props, nvlist_t *newprops)
28396689Smaybee {
28406689Smaybee 	zfs_cmd_t *zc;
28416689Smaybee 	nvpair_t *prop;
28426689Smaybee 
28436689Smaybee 	if (props == NULL)
28446689Smaybee 		return;
28456689Smaybee 	zc = kmem_alloc(sizeof (zfs_cmd_t), KM_SLEEP);
28466689Smaybee 	(void) strcpy(zc->zc_name, dataset);
28476689Smaybee 	for (prop = nvlist_next_nvpair(props, NULL); prop;
28486689Smaybee 	    prop = nvlist_next_nvpair(props, prop)) {
28498536SDavid.Pacheco@Sun.COM 		if (newprops != NULL &&
28508536SDavid.Pacheco@Sun.COM 		    nvlist_exists(newprops, nvpair_name(prop)))
28518536SDavid.Pacheco@Sun.COM 			continue;
28526689Smaybee 		(void) strcpy(zc->zc_value, nvpair_name(prop));
28536689Smaybee 		if (zfs_secpolicy_inherit(zc, CRED()) == 0)
28546689Smaybee 			(void) zfs_ioc_inherit_prop(zc);
28556689Smaybee 	}
28566689Smaybee 	kmem_free(zc, sizeof (zfs_cmd_t));
28576689Smaybee }
28586689Smaybee 
28595367Sahrens /*
28605367Sahrens  * inputs:
28615367Sahrens  * zc_name		name of containing filesystem
28625367Sahrens  * zc_nvlist_src{_size}	nvlist of properties to apply
28635367Sahrens  * zc_value		name of snapshot to create
28645367Sahrens  * zc_string		name of clone origin (if DRR_FLAG_CLONE)
28655367Sahrens  * zc_cookie		file descriptor to recv from
28665367Sahrens  * zc_begin_record	the BEGIN record of the stream (not byteswapped)
28675367Sahrens  * zc_guid		force flag
28685367Sahrens  *
28695367Sahrens  * outputs:
28705367Sahrens  * zc_cookie		number of bytes read
28715367Sahrens  */
2872789Sahrens static int
28735367Sahrens zfs_ioc_recv(zfs_cmd_t *zc)
2874789Sahrens {
2875789Sahrens 	file_t *fp;
28765326Sek110237 	objset_t *os;
28775367Sahrens 	dmu_recv_cookie_t drc;
28785326Sek110237 	boolean_t force = (boolean_t)zc->zc_guid;
2879789Sahrens 	int error, fd;
28805367Sahrens 	offset_t off;
28815367Sahrens 	nvlist_t *props = NULL;
28826689Smaybee 	nvlist_t *origprops = NULL;
28835367Sahrens 	objset_t *origin = NULL;
28845367Sahrens 	char *tosnap;
28855367Sahrens 	char tofs[ZFS_MAXNAMELEN];
2886789Sahrens 
28873265Sahrens 	if (dataset_namecheck(zc->zc_value, NULL, NULL) != 0 ||
28885326Sek110237 	    strchr(zc->zc_value, '@') == NULL ||
28895326Sek110237 	    strchr(zc->zc_value, '%'))
28903265Sahrens 		return (EINVAL);
28913265Sahrens 
28925367Sahrens 	(void) strcpy(tofs, zc->zc_value);
28935367Sahrens 	tosnap = strchr(tofs, '@');
28945367Sahrens 	*tosnap = '\0';
28955367Sahrens 	tosnap++;
28965367Sahrens 
28975367Sahrens 	if (zc->zc_nvlist_src != NULL &&
28985367Sahrens 	    (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
28999643SEric.Taylor@Sun.COM 	    zc->zc_iflags, &props)) != 0)
29005367Sahrens 		return (error);
29015367Sahrens 
2902789Sahrens 	fd = zc->zc_cookie;
2903789Sahrens 	fp = getf(fd);
29045367Sahrens 	if (fp == NULL) {
29055367Sahrens 		nvlist_free(props);
2906789Sahrens 		return (EBADF);
29075367Sahrens 	}
29085326Sek110237 
290910298SMatthew.Ahrens@Sun.COM 	if (props && dmu_objset_hold(tofs, FTAG, &os) == 0) {
29106689Smaybee 		/*
29116689Smaybee 		 * If new properties are supplied, they are to completely
29126689Smaybee 		 * replace the existing ones, so stash away the existing ones.
29136689Smaybee 		 */
291410298SMatthew.Ahrens@Sun.COM 		(void) dsl_prop_get_all(os, &origprops, B_TRUE);
291510298SMatthew.Ahrens@Sun.COM 
291610298SMatthew.Ahrens@Sun.COM 		dmu_objset_rele(os, FTAG);
29175326Sek110237 	}
29185326Sek110237 
29195367Sahrens 	if (zc->zc_string[0]) {
292010298SMatthew.Ahrens@Sun.COM 		error = dmu_objset_hold(zc->zc_string, FTAG, &origin);
29216689Smaybee 		if (error)
29226689Smaybee 			goto out;
29235367Sahrens 	}
29245367Sahrens 
29255367Sahrens 	error = dmu_recv_begin(tofs, tosnap, &zc->zc_begin_record,
292610204SMatthew.Ahrens@Sun.COM 	    force, origin, &drc);
29275367Sahrens 	if (origin)
292810298SMatthew.Ahrens@Sun.COM 		dmu_objset_rele(origin, FTAG);
29296689Smaybee 	if (error)
29306689Smaybee 		goto out;
29315326Sek110237 
29325326Sek110237 	/*
29336689Smaybee 	 * Reset properties.  We do this before we receive the stream
29346689Smaybee 	 * so that the properties are applied to the new data.
29355326Sek110237 	 */
29365367Sahrens 	if (props) {
29378536SDavid.Pacheco@Sun.COM 		clear_props(tofs, origprops, props);
29386689Smaybee 		/*
29396689Smaybee 		 * XXX - Note, this is all-or-nothing; should be best-effort.
29406689Smaybee 		 */
29416689Smaybee 		(void) zfs_set_prop_nvlist(tofs, props);
29425367Sahrens 	}
29435367Sahrens 
29445367Sahrens 	off = fp->f_offset;
29455367Sahrens 	error = dmu_recv_stream(&drc, fp->f_vnode, &off);
29465367Sahrens 
294710204SMatthew.Ahrens@Sun.COM 	if (error == 0) {
294810204SMatthew.Ahrens@Sun.COM 		zfsvfs_t *zfsvfs = NULL;
294910204SMatthew.Ahrens@Sun.COM 
295010204SMatthew.Ahrens@Sun.COM 		if (getzfsvfs(tofs, &zfsvfs) == 0) {
295110204SMatthew.Ahrens@Sun.COM 			/* online recv */
295210204SMatthew.Ahrens@Sun.COM 			int end_err;
295310298SMatthew.Ahrens@Sun.COM 
295410298SMatthew.Ahrens@Sun.COM 			error = zfs_suspend_fs(zfsvfs);
295510204SMatthew.Ahrens@Sun.COM 			/*
295610204SMatthew.Ahrens@Sun.COM 			 * If the suspend fails, then the recv_end will
295710204SMatthew.Ahrens@Sun.COM 			 * likely also fail, and clean up after itself.
295810204SMatthew.Ahrens@Sun.COM 			 */
295910204SMatthew.Ahrens@Sun.COM 			end_err = dmu_recv_end(&drc);
296010204SMatthew.Ahrens@Sun.COM 			if (error == 0) {
296110204SMatthew.Ahrens@Sun.COM 				int resume_err =
296210298SMatthew.Ahrens@Sun.COM 				    zfs_resume_fs(zfsvfs, tofs);
296310204SMatthew.Ahrens@Sun.COM 				error = error ? error : resume_err;
296410204SMatthew.Ahrens@Sun.COM 			}
296510204SMatthew.Ahrens@Sun.COM 			error = error ? error : end_err;
296610204SMatthew.Ahrens@Sun.COM 			VFS_RELE(zfsvfs->z_vfs);
296710204SMatthew.Ahrens@Sun.COM 		} else {
29686689Smaybee 			error = dmu_recv_end(&drc);
29695367Sahrens 		}
29706083Sek110237 	}
29715367Sahrens 
29725367Sahrens 	zc->zc_cookie = off - fp->f_offset;
29735367Sahrens 	if (VOP_SEEK(fp->f_vnode, fp->f_offset, &off, NULL) == 0)
29745367Sahrens 		fp->f_offset = off;
29752885Sahrens 
29766689Smaybee 	/*
29776689Smaybee 	 * On error, restore the original props.
29786689Smaybee 	 */
29796689Smaybee 	if (error && props) {
29808536SDavid.Pacheco@Sun.COM 		clear_props(tofs, props, NULL);
29816689Smaybee 		(void) zfs_set_prop_nvlist(tofs, origprops);
29826689Smaybee 	}
29836689Smaybee out:
29846689Smaybee 	nvlist_free(props);
29856689Smaybee 	nvlist_free(origprops);
2986789Sahrens 	releasef(fd);
2987789Sahrens 	return (error);
2988789Sahrens }
2989789Sahrens 
29905367Sahrens /*
29915367Sahrens  * inputs:
29925367Sahrens  * zc_name	name of snapshot to send
29935367Sahrens  * zc_value	short name of incremental fromsnap (may be empty)
29945367Sahrens  * zc_cookie	file descriptor to send stream to
29955367Sahrens  * zc_obj	fromorigin flag (mutually exclusive with zc_value)
29965367Sahrens  *
29975367Sahrens  * outputs: none
29985367Sahrens  */
2999789Sahrens static int
30005367Sahrens zfs_ioc_send(zfs_cmd_t *zc)
3001789Sahrens {
3002789Sahrens 	objset_t *fromsnap = NULL;
3003789Sahrens 	objset_t *tosnap;
3004789Sahrens 	file_t *fp;
3005789Sahrens 	int error;
30065367Sahrens 	offset_t off;
3007789Sahrens 
300810298SMatthew.Ahrens@Sun.COM 	error = dmu_objset_hold(zc->zc_name, FTAG, &tosnap);
3009789Sahrens 	if (error)
3010789Sahrens 		return (error);
3011789Sahrens 
30122676Seschrock 	if (zc->zc_value[0] != '\0') {
30138012SEric.Taylor@Sun.COM 		char *buf;
30142885Sahrens 		char *cp;
30152885Sahrens 
30168012SEric.Taylor@Sun.COM 		buf = kmem_alloc(MAXPATHLEN, KM_SLEEP);
30178012SEric.Taylor@Sun.COM 		(void) strncpy(buf, zc->zc_name, MAXPATHLEN);
30182885Sahrens 		cp = strchr(buf, '@');
30192885Sahrens 		if (cp)
30202885Sahrens 			*(cp+1) = 0;
30218012SEric.Taylor@Sun.COM 		(void) strncat(buf, zc->zc_value, MAXPATHLEN);
302210298SMatthew.Ahrens@Sun.COM 		error = dmu_objset_hold(buf, FTAG, &fromsnap);
30238012SEric.Taylor@Sun.COM 		kmem_free(buf, MAXPATHLEN);
3024789Sahrens 		if (error) {
302510298SMatthew.Ahrens@Sun.COM 			dmu_objset_rele(tosnap, FTAG);
3026789Sahrens 			return (error);
3027789Sahrens 		}
3028789Sahrens 	}
3029789Sahrens 
3030789Sahrens 	fp = getf(zc->zc_cookie);
3031789Sahrens 	if (fp == NULL) {
303210298SMatthew.Ahrens@Sun.COM 		dmu_objset_rele(tosnap, FTAG);
3033789Sahrens 		if (fromsnap)
303410298SMatthew.Ahrens@Sun.COM 			dmu_objset_rele(fromsnap, FTAG);
3035789Sahrens 		return (EBADF);
3036789Sahrens 	}
3037789Sahrens 
30385367Sahrens 	off = fp->f_offset;
30395367Sahrens 	error = dmu_sendbackup(tosnap, fromsnap, zc->zc_obj, fp->f_vnode, &off);
30405367Sahrens 
30415367Sahrens 	if (VOP_SEEK(fp->f_vnode, fp->f_offset, &off, NULL) == 0)
30425367Sahrens 		fp->f_offset = off;
3043789Sahrens 	releasef(zc->zc_cookie);
3044789Sahrens 	if (fromsnap)
304510298SMatthew.Ahrens@Sun.COM 		dmu_objset_rele(fromsnap, FTAG);
304610298SMatthew.Ahrens@Sun.COM 	dmu_objset_rele(tosnap, FTAG);
3047789Sahrens 	return (error);
3048789Sahrens }
3049789Sahrens 
30501544Seschrock static int
30511544Seschrock zfs_ioc_inject_fault(zfs_cmd_t *zc)
30521544Seschrock {
30531544Seschrock 	int id, error;
30541544Seschrock 
30551544Seschrock 	error = zio_inject_fault(zc->zc_name, (int)zc->zc_guid, &id,
30561544Seschrock 	    &zc->zc_inject_record);
30571544Seschrock 
30581544Seschrock 	if (error == 0)
30591544Seschrock 		zc->zc_guid = (uint64_t)id;
30601544Seschrock 
30611544Seschrock 	return (error);
30621544Seschrock }
30631544Seschrock 
30641544Seschrock static int
30651544Seschrock zfs_ioc_clear_fault(zfs_cmd_t *zc)
30661544Seschrock {
30671544Seschrock 	return (zio_clear_fault((int)zc->zc_guid));
30681544Seschrock }
30691544Seschrock 
30701544Seschrock static int
30711544Seschrock zfs_ioc_inject_list_next(zfs_cmd_t *zc)
30721544Seschrock {
30731544Seschrock 	int id = (int)zc->zc_guid;
30741544Seschrock 	int error;
30751544Seschrock 
30761544Seschrock 	error = zio_inject_list_next(&id, zc->zc_name, sizeof (zc->zc_name),
30771544Seschrock 	    &zc->zc_inject_record);
30781544Seschrock 
30791544Seschrock 	zc->zc_guid = id;
30801544Seschrock 
30811544Seschrock 	return (error);
30821544Seschrock }
30831544Seschrock 
30841544Seschrock static int
30851544Seschrock zfs_ioc_error_log(zfs_cmd_t *zc)
30861544Seschrock {
30871544Seschrock 	spa_t *spa;
30881544Seschrock 	int error;
30892676Seschrock 	size_t count = (size_t)zc->zc_nvlist_dst_size;
30901544Seschrock 
30911544Seschrock 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
30921544Seschrock 		return (error);
30931544Seschrock 
30942676Seschrock 	error = spa_get_errlog(spa, (void *)(uintptr_t)zc->zc_nvlist_dst,
30951544Seschrock 	    &count);
30961544Seschrock 	if (error == 0)
30972676Seschrock 		zc->zc_nvlist_dst_size = count;
30981544Seschrock 	else
30992676Seschrock 		zc->zc_nvlist_dst_size = spa_get_errlog_size(spa);
31001544Seschrock 
31011544Seschrock 	spa_close(spa, FTAG);
31021544Seschrock 
31031544Seschrock 	return (error);
31041544Seschrock }
31051544Seschrock 
31061544Seschrock static int
31071544Seschrock zfs_ioc_clear(zfs_cmd_t *zc)
31081544Seschrock {
31091544Seschrock 	spa_t *spa;
31101544Seschrock 	vdev_t *vd;
31111544Seschrock 	int error;
31121544Seschrock 
31137294Sperrin 	/*
31147294Sperrin 	 * On zpool clear we also fix up missing slogs
31157294Sperrin 	 */
31167294Sperrin 	mutex_enter(&spa_namespace_lock);
31177294Sperrin 	spa = spa_lookup(zc->zc_name);
31187294Sperrin 	if (spa == NULL) {
31197294Sperrin 		mutex_exit(&spa_namespace_lock);
31207294Sperrin 		return (EIO);
31217294Sperrin 	}
312210922SJeff.Bonwick@Sun.COM 	if (spa_get_log_state(spa) == SPA_LOG_MISSING) {
31237294Sperrin 		/* we need to let spa_open/spa_load clear the chains */
312410922SJeff.Bonwick@Sun.COM 		spa_set_log_state(spa, SPA_LOG_CLEAR);
31257294Sperrin 	}
312610921STim.Haley@Sun.COM 	spa->spa_last_open_failed = 0;
31277294Sperrin 	mutex_exit(&spa_namespace_lock);
31287294Sperrin 
312910921STim.Haley@Sun.COM 	if (zc->zc_cookie == ZPOOL_NO_REWIND) {
313010921STim.Haley@Sun.COM 		error = spa_open(zc->zc_name, &spa, FTAG);
313110921STim.Haley@Sun.COM 	} else {
313210921STim.Haley@Sun.COM 		nvlist_t *policy;
313310921STim.Haley@Sun.COM 		nvlist_t *config = NULL;
313410921STim.Haley@Sun.COM 
313510921STim.Haley@Sun.COM 		if (zc->zc_nvlist_src == NULL)
313610921STim.Haley@Sun.COM 			return (EINVAL);
313710921STim.Haley@Sun.COM 
313810921STim.Haley@Sun.COM 		if ((error = get_nvlist(zc->zc_nvlist_src,
313910921STim.Haley@Sun.COM 		    zc->zc_nvlist_src_size, zc->zc_iflags, &policy)) == 0) {
314010921STim.Haley@Sun.COM 			error = spa_open_rewind(zc->zc_name, &spa, FTAG,
314110921STim.Haley@Sun.COM 			    policy, &config);
314210921STim.Haley@Sun.COM 			if (config != NULL) {
314310921STim.Haley@Sun.COM 				(void) put_nvlist(zc, config);
314410921STim.Haley@Sun.COM 				nvlist_free(config);
314510921STim.Haley@Sun.COM 			}
314610921STim.Haley@Sun.COM 			nvlist_free(policy);
314710921STim.Haley@Sun.COM 		}
314810921STim.Haley@Sun.COM 	}
314910921STim.Haley@Sun.COM 
315010921STim.Haley@Sun.COM 	if (error)
31511544Seschrock 		return (error);
31521544Seschrock 
315310685SGeorge.Wilson@Sun.COM 	spa_vdev_state_enter(spa, SCL_NONE);
31541544Seschrock 
31552676Seschrock 	if (zc->zc_guid == 0) {
31561544Seschrock 		vd = NULL;
31576643Seschrock 	} else {
31586643Seschrock 		vd = spa_lookup_by_guid(spa, zc->zc_guid, B_TRUE);
31595450Sbrendan 		if (vd == NULL) {
31607754SJeff.Bonwick@Sun.COM 			(void) spa_vdev_state_exit(spa, NULL, ENODEV);
31615450Sbrendan 			spa_close(spa, FTAG);
31625450Sbrendan 			return (ENODEV);
31635450Sbrendan 		}
31641544Seschrock 	}
31651544Seschrock 
31667754SJeff.Bonwick@Sun.COM 	vdev_clear(spa, vd);
31677754SJeff.Bonwick@Sun.COM 
31687754SJeff.Bonwick@Sun.COM 	(void) spa_vdev_state_exit(spa, NULL, 0);
31697754SJeff.Bonwick@Sun.COM 
31707754SJeff.Bonwick@Sun.COM 	/*
31717754SJeff.Bonwick@Sun.COM 	 * Resume any suspended I/Os.
31727754SJeff.Bonwick@Sun.COM 	 */
31739234SGeorge.Wilson@Sun.COM 	if (zio_resume(spa) != 0)
31749234SGeorge.Wilson@Sun.COM 		error = EIO;
31751544Seschrock 
31761544Seschrock 	spa_close(spa, FTAG);
31771544Seschrock 
31789234SGeorge.Wilson@Sun.COM 	return (error);
31791544Seschrock }
31801544Seschrock 
31815367Sahrens /*
31825367Sahrens  * inputs:
31835367Sahrens  * zc_name	name of filesystem
31845367Sahrens  * zc_value	name of origin snapshot
31855367Sahrens  *
318610588SEric.Taylor@Sun.COM  * outputs:
318710588SEric.Taylor@Sun.COM  * zc_string	name of conflicting snapshot, if there is one
31885367Sahrens  */
31891544Seschrock static int
31902082Seschrock zfs_ioc_promote(zfs_cmd_t *zc)
31912082Seschrock {
31922417Sahrens 	char *cp;
31932417Sahrens 
31942417Sahrens 	/*
31952417Sahrens 	 * We don't need to unmount *all* the origin fs's snapshots, but
31962417Sahrens 	 * it's easier.
31972417Sahrens 	 */
31982676Seschrock 	cp = strchr(zc->zc_value, '@');
31992417Sahrens 	if (cp)
32002417Sahrens 		*cp = '\0';
32012676Seschrock 	(void) dmu_objset_find(zc->zc_value,
32022417Sahrens 	    zfs_unmount_snap, NULL, DS_FIND_SNAPSHOTS);
320310588SEric.Taylor@Sun.COM 	return (dsl_dataset_promote(zc->zc_name, zc->zc_string));
32042082Seschrock }
32052082Seschrock 
32064543Smarks /*
32079396SMatthew.Ahrens@Sun.COM  * Retrieve a single {user|group}{used|quota}@... property.
32089396SMatthew.Ahrens@Sun.COM  *
32099396SMatthew.Ahrens@Sun.COM  * inputs:
32109396SMatthew.Ahrens@Sun.COM  * zc_name	name of filesystem
32119396SMatthew.Ahrens@Sun.COM  * zc_objset_type zfs_userquota_prop_t
32129396SMatthew.Ahrens@Sun.COM  * zc_value	domain name (eg. "S-1-234-567-89")
32139396SMatthew.Ahrens@Sun.COM  * zc_guid	RID/UID/GID
32149396SMatthew.Ahrens@Sun.COM  *
32159396SMatthew.Ahrens@Sun.COM  * outputs:
32169396SMatthew.Ahrens@Sun.COM  * zc_cookie	property value
32179396SMatthew.Ahrens@Sun.COM  */
32189396SMatthew.Ahrens@Sun.COM static int
32199396SMatthew.Ahrens@Sun.COM zfs_ioc_userspace_one(zfs_cmd_t *zc)
32209396SMatthew.Ahrens@Sun.COM {
32219396SMatthew.Ahrens@Sun.COM 	zfsvfs_t *zfsvfs;
32229396SMatthew.Ahrens@Sun.COM 	int error;
32239396SMatthew.Ahrens@Sun.COM 
32249396SMatthew.Ahrens@Sun.COM 	if (zc->zc_objset_type >= ZFS_NUM_USERQUOTA_PROPS)
32259396SMatthew.Ahrens@Sun.COM 		return (EINVAL);
32269396SMatthew.Ahrens@Sun.COM 
322710298SMatthew.Ahrens@Sun.COM 	error = zfsvfs_hold(zc->zc_name, FTAG, &zfsvfs);
32289396SMatthew.Ahrens@Sun.COM 	if (error)
32299396SMatthew.Ahrens@Sun.COM 		return (error);
32309396SMatthew.Ahrens@Sun.COM 
32319396SMatthew.Ahrens@Sun.COM 	error = zfs_userspace_one(zfsvfs,
32329396SMatthew.Ahrens@Sun.COM 	    zc->zc_objset_type, zc->zc_value, zc->zc_guid, &zc->zc_cookie);
32339396SMatthew.Ahrens@Sun.COM 	zfsvfs_rele(zfsvfs, FTAG);
32349396SMatthew.Ahrens@Sun.COM 
32359396SMatthew.Ahrens@Sun.COM 	return (error);
32369396SMatthew.Ahrens@Sun.COM }
32379396SMatthew.Ahrens@Sun.COM 
32389396SMatthew.Ahrens@Sun.COM /*
32399396SMatthew.Ahrens@Sun.COM  * inputs:
32409396SMatthew.Ahrens@Sun.COM  * zc_name		name of filesystem
32419396SMatthew.Ahrens@Sun.COM  * zc_cookie		zap cursor
32429396SMatthew.Ahrens@Sun.COM  * zc_objset_type	zfs_userquota_prop_t
32439396SMatthew.Ahrens@Sun.COM  * zc_nvlist_dst[_size] buffer to fill (not really an nvlist)
32449396SMatthew.Ahrens@Sun.COM  *
32459396SMatthew.Ahrens@Sun.COM  * outputs:
32469396SMatthew.Ahrens@Sun.COM  * zc_nvlist_dst[_size]	data buffer (array of zfs_useracct_t)
32479396SMatthew.Ahrens@Sun.COM  * zc_cookie	zap cursor
32489396SMatthew.Ahrens@Sun.COM  */
32499396SMatthew.Ahrens@Sun.COM static int
32509396SMatthew.Ahrens@Sun.COM zfs_ioc_userspace_many(zfs_cmd_t *zc)
32519396SMatthew.Ahrens@Sun.COM {
32529396SMatthew.Ahrens@Sun.COM 	zfsvfs_t *zfsvfs;
32539396SMatthew.Ahrens@Sun.COM 	int error;
32549396SMatthew.Ahrens@Sun.COM 
325510298SMatthew.Ahrens@Sun.COM 	error = zfsvfs_hold(zc->zc_name, FTAG, &zfsvfs);
32569396SMatthew.Ahrens@Sun.COM 	if (error)
32579396SMatthew.Ahrens@Sun.COM 		return (error);
32589396SMatthew.Ahrens@Sun.COM 
32599396SMatthew.Ahrens@Sun.COM 	int bufsize = zc->zc_nvlist_dst_size;
32609396SMatthew.Ahrens@Sun.COM 	void *buf = kmem_alloc(bufsize, KM_SLEEP);
32619396SMatthew.Ahrens@Sun.COM 
32629396SMatthew.Ahrens@Sun.COM 	error = zfs_userspace_many(zfsvfs, zc->zc_objset_type, &zc->zc_cookie,
32639396SMatthew.Ahrens@Sun.COM 	    buf, &zc->zc_nvlist_dst_size);
32649396SMatthew.Ahrens@Sun.COM 
32659396SMatthew.Ahrens@Sun.COM 	if (error == 0) {
32669396SMatthew.Ahrens@Sun.COM 		error = xcopyout(buf,
32679396SMatthew.Ahrens@Sun.COM 		    (void *)(uintptr_t)zc->zc_nvlist_dst,
32689396SMatthew.Ahrens@Sun.COM 		    zc->zc_nvlist_dst_size);
32699396SMatthew.Ahrens@Sun.COM 	}
32709396SMatthew.Ahrens@Sun.COM 	kmem_free(buf, bufsize);
32719396SMatthew.Ahrens@Sun.COM 	zfsvfs_rele(zfsvfs, FTAG);
32729396SMatthew.Ahrens@Sun.COM 
32739396SMatthew.Ahrens@Sun.COM 	return (error);
32749396SMatthew.Ahrens@Sun.COM }
32759396SMatthew.Ahrens@Sun.COM 
32769396SMatthew.Ahrens@Sun.COM /*
32779396SMatthew.Ahrens@Sun.COM  * inputs:
32789396SMatthew.Ahrens@Sun.COM  * zc_name		name of filesystem
32799396SMatthew.Ahrens@Sun.COM  *
32809396SMatthew.Ahrens@Sun.COM  * outputs:
32819396SMatthew.Ahrens@Sun.COM  * none
32829396SMatthew.Ahrens@Sun.COM  */
32839396SMatthew.Ahrens@Sun.COM static int
32849396SMatthew.Ahrens@Sun.COM zfs_ioc_userspace_upgrade(zfs_cmd_t *zc)
32859396SMatthew.Ahrens@Sun.COM {
32869396SMatthew.Ahrens@Sun.COM 	objset_t *os;
32879396SMatthew.Ahrens@Sun.COM 	int error;
32889396SMatthew.Ahrens@Sun.COM 	zfsvfs_t *zfsvfs;
32899396SMatthew.Ahrens@Sun.COM 
32909396SMatthew.Ahrens@Sun.COM 	if (getzfsvfs(zc->zc_name, &zfsvfs) == 0) {
329110298SMatthew.Ahrens@Sun.COM 		if (!dmu_objset_userused_enabled(zfsvfs->z_os)) {
32929396SMatthew.Ahrens@Sun.COM 			/*
32939396SMatthew.Ahrens@Sun.COM 			 * If userused is not enabled, it may be because the
32949396SMatthew.Ahrens@Sun.COM 			 * objset needs to be closed & reopened (to grow the
32959396SMatthew.Ahrens@Sun.COM 			 * objset_phys_t).  Suspend/resume the fs will do that.
32969396SMatthew.Ahrens@Sun.COM 			 */
329710298SMatthew.Ahrens@Sun.COM 			error = zfs_suspend_fs(zfsvfs);
329810298SMatthew.Ahrens@Sun.COM 			if (error == 0)
329910298SMatthew.Ahrens@Sun.COM 				error = zfs_resume_fs(zfsvfs, zc->zc_name);
33009396SMatthew.Ahrens@Sun.COM 		}
33019396SMatthew.Ahrens@Sun.COM 		if (error == 0)
33029396SMatthew.Ahrens@Sun.COM 			error = dmu_objset_userspace_upgrade(zfsvfs->z_os);
33039396SMatthew.Ahrens@Sun.COM 		VFS_RELE(zfsvfs->z_vfs);
33049396SMatthew.Ahrens@Sun.COM 	} else {
330510298SMatthew.Ahrens@Sun.COM 		/* XXX kind of reading contents without owning */
330610298SMatthew.Ahrens@Sun.COM 		error = dmu_objset_hold(zc->zc_name, FTAG, &os);
33079396SMatthew.Ahrens@Sun.COM 		if (error)
33089396SMatthew.Ahrens@Sun.COM 			return (error);
33099396SMatthew.Ahrens@Sun.COM 
33109396SMatthew.Ahrens@Sun.COM 		error = dmu_objset_userspace_upgrade(os);
331110298SMatthew.Ahrens@Sun.COM 		dmu_objset_rele(os, FTAG);
33129396SMatthew.Ahrens@Sun.COM 	}
33139396SMatthew.Ahrens@Sun.COM 
33149396SMatthew.Ahrens@Sun.COM 	return (error);
33159396SMatthew.Ahrens@Sun.COM }
33169396SMatthew.Ahrens@Sun.COM 
33179396SMatthew.Ahrens@Sun.COM /*
33184543Smarks  * We don't want to have a hard dependency
33194543Smarks  * against some special symbols in sharefs
33205331Samw  * nfs, and smbsrv.  Determine them if needed when
33214543Smarks  * the first file system is shared.
33225331Samw  * Neither sharefs, nfs or smbsrv are unloadable modules.
33234543Smarks  */
33245331Samw int (*znfsexport_fs)(void *arg);
33254543Smarks int (*zshare_fs)(enum sharefs_sys_op, share_t *, uint32_t);
33265331Samw int (*zsmbexport_fs)(void *arg, boolean_t add_share);
33275331Samw 
33285331Samw int zfs_nfsshare_inited;
33295331Samw int zfs_smbshare_inited;
33305331Samw 
33314543Smarks ddi_modhandle_t nfs_mod;
33324543Smarks ddi_modhandle_t sharefs_mod;
33335331Samw ddi_modhandle_t smbsrv_mod;
33344543Smarks kmutex_t zfs_share_lock;
33354543Smarks 
33364543Smarks static int
33375331Samw zfs_init_sharefs()
33385331Samw {
33395331Samw 	int error;
33405331Samw 
33415331Samw 	ASSERT(MUTEX_HELD(&zfs_share_lock));
33425331Samw 	/* Both NFS and SMB shares also require sharetab support. */
33435331Samw 	if (sharefs_mod == NULL && ((sharefs_mod =
33445331Samw 	    ddi_modopen("fs/sharefs",
33455331Samw 	    KRTLD_MODE_FIRST, &error)) == NULL)) {
33465331Samw 		return (ENOSYS);
33475331Samw 	}
33485331Samw 	if (zshare_fs == NULL && ((zshare_fs =
33495331Samw 	    (int (*)(enum sharefs_sys_op, share_t *, uint32_t))
33505331Samw 	    ddi_modsym(sharefs_mod, "sharefs_impl", &error)) == NULL)) {
33515331Samw 		return (ENOSYS);
33525331Samw 	}
33535331Samw 	return (0);
33545331Samw }
33555331Samw 
33565331Samw static int
33574543Smarks zfs_ioc_share(zfs_cmd_t *zc)
33584543Smarks {
33594543Smarks 	int error;
33604543Smarks 	int opcode;
33614543Smarks 
33625331Samw 	switch (zc->zc_share.z_sharetype) {
33635331Samw 	case ZFS_SHARE_NFS:
33645331Samw 	case ZFS_UNSHARE_NFS:
33655331Samw 		if (zfs_nfsshare_inited == 0) {
33665331Samw 			mutex_enter(&zfs_share_lock);
33675331Samw 			if (nfs_mod == NULL && ((nfs_mod = ddi_modopen("fs/nfs",
33685331Samw 			    KRTLD_MODE_FIRST, &error)) == NULL)) {
33695331Samw 				mutex_exit(&zfs_share_lock);
33705331Samw 				return (ENOSYS);
33715331Samw 			}
33725331Samw 			if (znfsexport_fs == NULL &&
33735331Samw 			    ((znfsexport_fs = (int (*)(void *))
33745331Samw 			    ddi_modsym(nfs_mod,
33755331Samw 			    "nfs_export", &error)) == NULL)) {
33765331Samw 				mutex_exit(&zfs_share_lock);
33775331Samw 				return (ENOSYS);
33785331Samw 			}
33795331Samw 			error = zfs_init_sharefs();
33805331Samw 			if (error) {
33815331Samw 				mutex_exit(&zfs_share_lock);
33825331Samw 				return (ENOSYS);
33835331Samw 			}
33845331Samw 			zfs_nfsshare_inited = 1;
33854543Smarks 			mutex_exit(&zfs_share_lock);
33864543Smarks 		}
33875331Samw 		break;
33885331Samw 	case ZFS_SHARE_SMB:
33895331Samw 	case ZFS_UNSHARE_SMB:
33905331Samw 		if (zfs_smbshare_inited == 0) {
33915331Samw 			mutex_enter(&zfs_share_lock);
33925331Samw 			if (smbsrv_mod == NULL && ((smbsrv_mod =
33935331Samw 			    ddi_modopen("drv/smbsrv",
33945331Samw 			    KRTLD_MODE_FIRST, &error)) == NULL)) {
33955331Samw 				mutex_exit(&zfs_share_lock);
33965331Samw 				return (ENOSYS);
33975331Samw 			}
33985331Samw 			if (zsmbexport_fs == NULL && ((zsmbexport_fs =
33995331Samw 			    (int (*)(void *, boolean_t))ddi_modsym(smbsrv_mod,
34006139Sjb150015 			    "smb_server_share", &error)) == NULL)) {
34015331Samw 				mutex_exit(&zfs_share_lock);
34025331Samw 				return (ENOSYS);
34035331Samw 			}
34045331Samw 			error = zfs_init_sharefs();
34055331Samw 			if (error) {
34065331Samw 				mutex_exit(&zfs_share_lock);
34075331Samw 				return (ENOSYS);
34085331Samw 			}
34095331Samw 			zfs_smbshare_inited = 1;
34104543Smarks 			mutex_exit(&zfs_share_lock);
34114543Smarks 		}
34125331Samw 		break;
34135331Samw 	default:
34145331Samw 		return (EINVAL);
34154543Smarks 	}
34164543Smarks 
34175331Samw 	switch (zc->zc_share.z_sharetype) {
34185331Samw 	case ZFS_SHARE_NFS:
34195331Samw 	case ZFS_UNSHARE_NFS:
34205331Samw 		if (error =
34215331Samw 		    znfsexport_fs((void *)
34225331Samw 		    (uintptr_t)zc->zc_share.z_exportdata))
34235331Samw 			return (error);
34245331Samw 		break;
34255331Samw 	case ZFS_SHARE_SMB:
34265331Samw 	case ZFS_UNSHARE_SMB:
34275331Samw 		if (error = zsmbexport_fs((void *)
34285331Samw 		    (uintptr_t)zc->zc_share.z_exportdata,
34295331Samw 		    zc->zc_share.z_sharetype == ZFS_SHARE_SMB ?
34308845Samw@Sun.COM 		    B_TRUE: B_FALSE)) {
34315331Samw 			return (error);
34325331Samw 		}
34335331Samw 		break;
34345331Samw 	}
34355331Samw 
34365331Samw 	opcode = (zc->zc_share.z_sharetype == ZFS_SHARE_NFS ||
34375331Samw 	    zc->zc_share.z_sharetype == ZFS_SHARE_SMB) ?
34384543Smarks 	    SHAREFS_ADD : SHAREFS_REMOVE;
34394543Smarks 
34405331Samw 	/*
34415331Samw 	 * Add or remove share from sharetab
34425331Samw 	 */
34434543Smarks 	error = zshare_fs(opcode,
34444543Smarks 	    (void *)(uintptr_t)zc->zc_share.z_sharedata,
34454543Smarks 	    zc->zc_share.z_sharemax);
34464543Smarks 
34474543Smarks 	return (error);
34484543Smarks 
34494543Smarks }
34504543Smarks 
34518845Samw@Sun.COM ace_t full_access[] = {
34528845Samw@Sun.COM 	{(uid_t)-1, ACE_ALL_PERMS, ACE_EVERYONE, 0}
34538845Samw@Sun.COM };
34548845Samw@Sun.COM 
34558845Samw@Sun.COM /*
34568845Samw@Sun.COM  * Remove all ACL files in shares dir
34578845Samw@Sun.COM  */
34588845Samw@Sun.COM static int
34598845Samw@Sun.COM zfs_smb_acl_purge(znode_t *dzp)
34608845Samw@Sun.COM {
34618845Samw@Sun.COM 	zap_cursor_t	zc;
34628845Samw@Sun.COM 	zap_attribute_t	zap;
34638845Samw@Sun.COM 	zfsvfs_t *zfsvfs = dzp->z_zfsvfs;
34648845Samw@Sun.COM 	int error;
34658845Samw@Sun.COM 
34668845Samw@Sun.COM 	for (zap_cursor_init(&zc, zfsvfs->z_os, dzp->z_id);
34678845Samw@Sun.COM 	    (error = zap_cursor_retrieve(&zc, &zap)) == 0;
34688845Samw@Sun.COM 	    zap_cursor_advance(&zc)) {
34698845Samw@Sun.COM 		if ((error = VOP_REMOVE(ZTOV(dzp), zap.za_name, kcred,
34708845Samw@Sun.COM 		    NULL, 0)) != 0)
34718845Samw@Sun.COM 			break;
34728845Samw@Sun.COM 	}
34738845Samw@Sun.COM 	zap_cursor_fini(&zc);
34748845Samw@Sun.COM 	return (error);
34758845Samw@Sun.COM }
34768845Samw@Sun.COM 
34778845Samw@Sun.COM static int
34788845Samw@Sun.COM zfs_ioc_smb_acl(zfs_cmd_t *zc)
34798845Samw@Sun.COM {
34808845Samw@Sun.COM 	vnode_t *vp;
34818845Samw@Sun.COM 	znode_t *dzp;
34828845Samw@Sun.COM 	vnode_t *resourcevp = NULL;
34838845Samw@Sun.COM 	znode_t *sharedir;
34848845Samw@Sun.COM 	zfsvfs_t *zfsvfs;
34858845Samw@Sun.COM 	nvlist_t *nvlist;
34868845Samw@Sun.COM 	char *src, *target;
34878845Samw@Sun.COM 	vattr_t vattr;
34888845Samw@Sun.COM 	vsecattr_t vsec;
34898845Samw@Sun.COM 	int error = 0;
34908845Samw@Sun.COM 
34918845Samw@Sun.COM 	if ((error = lookupname(zc->zc_value, UIO_SYSSPACE,
34928845Samw@Sun.COM 	    NO_FOLLOW, NULL, &vp)) != 0)
34938845Samw@Sun.COM 		return (error);
34948845Samw@Sun.COM 
34958845Samw@Sun.COM 	/* Now make sure mntpnt and dataset are ZFS */
34968845Samw@Sun.COM 
34978845Samw@Sun.COM 	if (vp->v_vfsp->vfs_fstype != zfsfstype ||
34988845Samw@Sun.COM 	    (strcmp((char *)refstr_value(vp->v_vfsp->vfs_resource),
34998845Samw@Sun.COM 	    zc->zc_name) != 0)) {
35008845Samw@Sun.COM 		VN_RELE(vp);
35018845Samw@Sun.COM 		return (EINVAL);
35028845Samw@Sun.COM 	}
35038845Samw@Sun.COM 
35048845Samw@Sun.COM 	dzp = VTOZ(vp);
35058845Samw@Sun.COM 	zfsvfs = dzp->z_zfsvfs;
35068845Samw@Sun.COM 	ZFS_ENTER(zfsvfs);
35078845Samw@Sun.COM 
35089030SMark.Shellenbaum@Sun.COM 	/*
35099030SMark.Shellenbaum@Sun.COM 	 * Create share dir if its missing.
35109030SMark.Shellenbaum@Sun.COM 	 */
35119030SMark.Shellenbaum@Sun.COM 	mutex_enter(&zfsvfs->z_lock);
35129030SMark.Shellenbaum@Sun.COM 	if (zfsvfs->z_shares_dir == 0) {
35139030SMark.Shellenbaum@Sun.COM 		dmu_tx_t *tx;
35149030SMark.Shellenbaum@Sun.COM 
35159030SMark.Shellenbaum@Sun.COM 		tx = dmu_tx_create(zfsvfs->z_os);
35169030SMark.Shellenbaum@Sun.COM 		dmu_tx_hold_zap(tx, MASTER_NODE_OBJ, TRUE,
35179030SMark.Shellenbaum@Sun.COM 		    ZFS_SHARES_DIR);
35189030SMark.Shellenbaum@Sun.COM 		dmu_tx_hold_zap(tx, DMU_NEW_OBJECT, FALSE, NULL);
35199030SMark.Shellenbaum@Sun.COM 		error = dmu_tx_assign(tx, TXG_WAIT);
35209030SMark.Shellenbaum@Sun.COM 		if (error) {
35219030SMark.Shellenbaum@Sun.COM 			dmu_tx_abort(tx);
35229030SMark.Shellenbaum@Sun.COM 		} else {
35239030SMark.Shellenbaum@Sun.COM 			error = zfs_create_share_dir(zfsvfs, tx);
35249030SMark.Shellenbaum@Sun.COM 			dmu_tx_commit(tx);
35259030SMark.Shellenbaum@Sun.COM 		}
35269030SMark.Shellenbaum@Sun.COM 		if (error) {
35279030SMark.Shellenbaum@Sun.COM 			mutex_exit(&zfsvfs->z_lock);
35289030SMark.Shellenbaum@Sun.COM 			VN_RELE(vp);
35299030SMark.Shellenbaum@Sun.COM 			ZFS_EXIT(zfsvfs);
35309030SMark.Shellenbaum@Sun.COM 			return (error);
35319030SMark.Shellenbaum@Sun.COM 		}
35329030SMark.Shellenbaum@Sun.COM 	}
35339030SMark.Shellenbaum@Sun.COM 	mutex_exit(&zfsvfs->z_lock);
35349030SMark.Shellenbaum@Sun.COM 
35359030SMark.Shellenbaum@Sun.COM 	ASSERT(zfsvfs->z_shares_dir);
35368845Samw@Sun.COM 	if ((error = zfs_zget(zfsvfs, zfsvfs->z_shares_dir, &sharedir)) != 0) {
35379030SMark.Shellenbaum@Sun.COM 		VN_RELE(vp);
35388845Samw@Sun.COM 		ZFS_EXIT(zfsvfs);
35398845Samw@Sun.COM 		return (error);
35408845Samw@Sun.COM 	}
35418845Samw@Sun.COM 
35428845Samw@Sun.COM 	switch (zc->zc_cookie) {
35438845Samw@Sun.COM 	case ZFS_SMB_ACL_ADD:
35448845Samw@Sun.COM 		vattr.va_mask = AT_MODE|AT_UID|AT_GID|AT_TYPE;
35458845Samw@Sun.COM 		vattr.va_type = VREG;
35468845Samw@Sun.COM 		vattr.va_mode = S_IFREG|0777;
35478845Samw@Sun.COM 		vattr.va_uid = 0;
35488845Samw@Sun.COM 		vattr.va_gid = 0;
35498845Samw@Sun.COM 
35508845Samw@Sun.COM 		vsec.vsa_mask = VSA_ACE;
35518845Samw@Sun.COM 		vsec.vsa_aclentp = &full_access;
35528845Samw@Sun.COM 		vsec.vsa_aclentsz = sizeof (full_access);
35538845Samw@Sun.COM 		vsec.vsa_aclcnt = 1;
35548845Samw@Sun.COM 
35558845Samw@Sun.COM 		error = VOP_CREATE(ZTOV(sharedir), zc->zc_string,
35568845Samw@Sun.COM 		    &vattr, EXCL, 0, &resourcevp, kcred, 0, NULL, &vsec);
35578845Samw@Sun.COM 		if (resourcevp)
35588845Samw@Sun.COM 			VN_RELE(resourcevp);
35598845Samw@Sun.COM 		break;
35608845Samw@Sun.COM 
35618845Samw@Sun.COM 	case ZFS_SMB_ACL_REMOVE:
35628845Samw@Sun.COM 		error = VOP_REMOVE(ZTOV(sharedir), zc->zc_string, kcred,
35638845Samw@Sun.COM 		    NULL, 0);
35648845Samw@Sun.COM 		break;
35658845Samw@Sun.COM 
35668845Samw@Sun.COM 	case ZFS_SMB_ACL_RENAME:
35678845Samw@Sun.COM 		if ((error = get_nvlist(zc->zc_nvlist_src,
35689643SEric.Taylor@Sun.COM 		    zc->zc_nvlist_src_size, zc->zc_iflags, &nvlist)) != 0) {
35698845Samw@Sun.COM 			VN_RELE(vp);
35708845Samw@Sun.COM 			ZFS_EXIT(zfsvfs);
35718845Samw@Sun.COM 			return (error);
35728845Samw@Sun.COM 		}
35738845Samw@Sun.COM 		if (nvlist_lookup_string(nvlist, ZFS_SMB_ACL_SRC, &src) ||
35748845Samw@Sun.COM 		    nvlist_lookup_string(nvlist, ZFS_SMB_ACL_TARGET,
35758845Samw@Sun.COM 		    &target)) {
35768845Samw@Sun.COM 			VN_RELE(vp);
35779179SMark.Shellenbaum@Sun.COM 			VN_RELE(ZTOV(sharedir));
35788845Samw@Sun.COM 			ZFS_EXIT(zfsvfs);
35798845Samw@Sun.COM 			return (error);
35808845Samw@Sun.COM 		}
35818845Samw@Sun.COM 		error = VOP_RENAME(ZTOV(sharedir), src, ZTOV(sharedir), target,
35828845Samw@Sun.COM 		    kcred, NULL, 0);
35838845Samw@Sun.COM 		nvlist_free(nvlist);
35848845Samw@Sun.COM 		break;
35858845Samw@Sun.COM 
35868845Samw@Sun.COM 	case ZFS_SMB_ACL_PURGE:
35878845Samw@Sun.COM 		error = zfs_smb_acl_purge(sharedir);
35888845Samw@Sun.COM 		break;
35898845Samw@Sun.COM 
35908845Samw@Sun.COM 	default:
35918845Samw@Sun.COM 		error = EINVAL;
35928845Samw@Sun.COM 		break;
35938845Samw@Sun.COM 	}
35948845Samw@Sun.COM 
35958845Samw@Sun.COM 	VN_RELE(vp);
35968845Samw@Sun.COM 	VN_RELE(ZTOV(sharedir));
35978845Samw@Sun.COM 
35988845Samw@Sun.COM 	ZFS_EXIT(zfsvfs);
35998845Samw@Sun.COM 
36008845Samw@Sun.COM 	return (error);
36018845Samw@Sun.COM }
36028845Samw@Sun.COM 
36034543Smarks /*
360410242Schris.kirby@sun.com  * inputs:
360510242Schris.kirby@sun.com  * zc_name	name of filesystem
360610242Schris.kirby@sun.com  * zc_value	short name of snap
360710242Schris.kirby@sun.com  * zc_string	user-supplied tag for this reference
360810242Schris.kirby@sun.com  * zc_cookie	recursive flag
360910342Schris.kirby@sun.com  * zc_temphold	set if hold is temporary
361010242Schris.kirby@sun.com  *
361110242Schris.kirby@sun.com  * outputs:		none
361210242Schris.kirby@sun.com  */
361310242Schris.kirby@sun.com static int
361410242Schris.kirby@sun.com zfs_ioc_hold(zfs_cmd_t *zc)
361510242Schris.kirby@sun.com {
361610242Schris.kirby@sun.com 	boolean_t recursive = zc->zc_cookie;
361710242Schris.kirby@sun.com 
361810242Schris.kirby@sun.com 	if (snapshot_namecheck(zc->zc_value, NULL, NULL) != 0)
361910242Schris.kirby@sun.com 		return (EINVAL);
362010242Schris.kirby@sun.com 
362110242Schris.kirby@sun.com 	return (dsl_dataset_user_hold(zc->zc_name, zc->zc_value,
362210342Schris.kirby@sun.com 	    zc->zc_string, recursive, zc->zc_temphold));
362310242Schris.kirby@sun.com }
362410242Schris.kirby@sun.com 
362510242Schris.kirby@sun.com /*
362610242Schris.kirby@sun.com  * inputs:
362710242Schris.kirby@sun.com  * zc_name	name of dataset from which we're releasing a user reference
362810242Schris.kirby@sun.com  * zc_value	short name of snap
362910242Schris.kirby@sun.com  * zc_string	user-supplied tag for this reference
363010242Schris.kirby@sun.com  * zc_cookie	recursive flag
363110242Schris.kirby@sun.com  *
363210242Schris.kirby@sun.com  * outputs:		none
363310242Schris.kirby@sun.com  */
363410242Schris.kirby@sun.com static int
363510242Schris.kirby@sun.com zfs_ioc_release(zfs_cmd_t *zc)
363610242Schris.kirby@sun.com {
363710242Schris.kirby@sun.com 	boolean_t recursive = zc->zc_cookie;
363810242Schris.kirby@sun.com 
363910242Schris.kirby@sun.com 	if (snapshot_namecheck(zc->zc_value, NULL, NULL) != 0)
364010242Schris.kirby@sun.com 		return (EINVAL);
364110242Schris.kirby@sun.com 
364210242Schris.kirby@sun.com 	return (dsl_dataset_user_release(zc->zc_name, zc->zc_value,
364310242Schris.kirby@sun.com 	    zc->zc_string, recursive));
364410242Schris.kirby@sun.com }
364510242Schris.kirby@sun.com 
364610242Schris.kirby@sun.com /*
364710242Schris.kirby@sun.com  * inputs:
364810242Schris.kirby@sun.com  * zc_name		name of filesystem
364910242Schris.kirby@sun.com  *
365010242Schris.kirby@sun.com  * outputs:
365110242Schris.kirby@sun.com  * zc_nvlist_src{_size}	nvlist of snapshot holds
365210242Schris.kirby@sun.com  */
365310242Schris.kirby@sun.com static int
365410242Schris.kirby@sun.com zfs_ioc_get_holds(zfs_cmd_t *zc)
365510242Schris.kirby@sun.com {
365610242Schris.kirby@sun.com 	nvlist_t *nvp;
365710242Schris.kirby@sun.com 	int error;
365810242Schris.kirby@sun.com 
365910242Schris.kirby@sun.com 	if ((error = dsl_dataset_get_holds(zc->zc_name, &nvp)) == 0) {
366010242Schris.kirby@sun.com 		error = put_nvlist(zc, nvp);
366110242Schris.kirby@sun.com 		nvlist_free(nvp);
366210242Schris.kirby@sun.com 	}
366310242Schris.kirby@sun.com 
366410242Schris.kirby@sun.com 	return (error);
366510242Schris.kirby@sun.com }
366610242Schris.kirby@sun.com 
366710242Schris.kirby@sun.com /*
36684988Sek110237  * pool create, destroy, and export don't log the history as part of
36694988Sek110237  * zfsdev_ioctl, but rather zfs_ioc_pool_create, and zfs_ioc_pool_export
36704988Sek110237  * do the logging of those commands.
36714543Smarks  */
3672789Sahrens static zfs_ioc_vec_t zfs_ioc_vec[] = {
36739234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_pool_create, zfs_secpolicy_config, POOL_NAME, B_FALSE,
36749234SGeorge.Wilson@Sun.COM 	    B_FALSE },
36759234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_pool_destroy,	zfs_secpolicy_config, POOL_NAME, B_FALSE,
36769234SGeorge.Wilson@Sun.COM 	    B_FALSE },
36779234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_pool_import, zfs_secpolicy_config, POOL_NAME, B_TRUE,
36789234SGeorge.Wilson@Sun.COM 	    B_FALSE },
36799234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_pool_export, zfs_secpolicy_config, POOL_NAME, B_FALSE,
36809234SGeorge.Wilson@Sun.COM 	    B_FALSE },
36819234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_pool_configs,	zfs_secpolicy_none, NO_NAME, B_FALSE,
36829234SGeorge.Wilson@Sun.COM 	    B_FALSE },
36839234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_pool_stats, zfs_secpolicy_read, POOL_NAME, B_FALSE,
36849234SGeorge.Wilson@Sun.COM 	    B_FALSE },
36859234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_pool_tryimport, zfs_secpolicy_config, NO_NAME, B_FALSE,
36869234SGeorge.Wilson@Sun.COM 	    B_FALSE },
36879234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_pool_scrub, zfs_secpolicy_config, POOL_NAME, B_TRUE,
36889234SGeorge.Wilson@Sun.COM 	    B_TRUE },
36899234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_pool_freeze, zfs_secpolicy_config, NO_NAME, B_FALSE,
36909234SGeorge.Wilson@Sun.COM 	    B_FALSE },
36919234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_pool_upgrade,	zfs_secpolicy_config, POOL_NAME, B_TRUE,
36929234SGeorge.Wilson@Sun.COM 	    B_TRUE },
36939234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_pool_get_history, zfs_secpolicy_config, POOL_NAME, B_FALSE,
36949234SGeorge.Wilson@Sun.COM 	    B_FALSE },
36959234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_vdev_add, zfs_secpolicy_config, POOL_NAME, B_TRUE,
36969234SGeorge.Wilson@Sun.COM 	    B_TRUE },
36979234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_vdev_remove, zfs_secpolicy_config, POOL_NAME, B_TRUE,
36989234SGeorge.Wilson@Sun.COM 	    B_TRUE },
36999234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_vdev_set_state, zfs_secpolicy_config,	POOL_NAME, B_TRUE,
37009234SGeorge.Wilson@Sun.COM 	    B_FALSE },
37019234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_vdev_attach, zfs_secpolicy_config, POOL_NAME, B_TRUE,
37029234SGeorge.Wilson@Sun.COM 	    B_TRUE },
37039234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_vdev_detach, zfs_secpolicy_config, POOL_NAME, B_TRUE,
37049234SGeorge.Wilson@Sun.COM 	    B_TRUE },
37059234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_vdev_setpath,	zfs_secpolicy_config, POOL_NAME, B_FALSE,
37069234SGeorge.Wilson@Sun.COM 	    B_TRUE },
37079425SEric.Schrock@Sun.COM 	{ zfs_ioc_vdev_setfru,	zfs_secpolicy_config, POOL_NAME, B_FALSE,
37089425SEric.Schrock@Sun.COM 	    B_TRUE },
37099234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_objset_stats,	zfs_secpolicy_read, DATASET_NAME, B_FALSE,
37109234SGeorge.Wilson@Sun.COM 	    B_FALSE },
37119234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_objset_zplprops, zfs_secpolicy_read, DATASET_NAME, B_FALSE,
37129234SGeorge.Wilson@Sun.COM 	    B_FALSE },
37139234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_dataset_list_next, zfs_secpolicy_read, DATASET_NAME, B_FALSE,
37149234SGeorge.Wilson@Sun.COM 	    B_FALSE },
37159234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_snapshot_list_next, zfs_secpolicy_read, DATASET_NAME, B_FALSE,
37169234SGeorge.Wilson@Sun.COM 	    B_FALSE },
37179234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_set_prop, zfs_secpolicy_none, DATASET_NAME, B_TRUE, B_TRUE },
37189234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_create, zfs_secpolicy_create, DATASET_NAME, B_TRUE, B_TRUE },
37199234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_destroy, zfs_secpolicy_destroy, DATASET_NAME, B_TRUE,
37209234SGeorge.Wilson@Sun.COM 	    B_TRUE},
37219234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_rollback, zfs_secpolicy_rollback, DATASET_NAME, B_TRUE,
37229234SGeorge.Wilson@Sun.COM 	    B_TRUE },
37239234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_rename, zfs_secpolicy_rename,	DATASET_NAME, B_TRUE, B_TRUE },
37249234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_recv, zfs_secpolicy_receive, DATASET_NAME, B_TRUE, B_TRUE },
37259234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_send, zfs_secpolicy_send, DATASET_NAME, B_TRUE, B_FALSE },
37269234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_inject_fault,	zfs_secpolicy_inject, NO_NAME, B_FALSE,
37279234SGeorge.Wilson@Sun.COM 	    B_FALSE },
37289234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_clear_fault, zfs_secpolicy_inject, NO_NAME, B_FALSE,
37299234SGeorge.Wilson@Sun.COM 	    B_FALSE },
37309234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_inject_list_next, zfs_secpolicy_inject, NO_NAME, B_FALSE,
37319234SGeorge.Wilson@Sun.COM 	    B_FALSE },
37329234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_error_log, zfs_secpolicy_inject, POOL_NAME, B_FALSE,
37339234SGeorge.Wilson@Sun.COM 	    B_FALSE },
37349234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_clear, zfs_secpolicy_config, POOL_NAME, B_TRUE, B_FALSE },
37359234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_promote, zfs_secpolicy_promote, DATASET_NAME, B_TRUE,
37369234SGeorge.Wilson@Sun.COM 	    B_TRUE },
37379234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_destroy_snaps, zfs_secpolicy_destroy,	DATASET_NAME, B_TRUE,
37389234SGeorge.Wilson@Sun.COM 	    B_TRUE },
37399234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_snapshot, zfs_secpolicy_snapshot, DATASET_NAME, B_TRUE,
37409234SGeorge.Wilson@Sun.COM 	    B_TRUE },
37419234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_dsobj_to_dsname, zfs_secpolicy_config, POOL_NAME, B_FALSE,
37429234SGeorge.Wilson@Sun.COM 	    B_FALSE },
374310233SGeorge.Wilson@Sun.COM 	{ zfs_ioc_obj_to_path, zfs_secpolicy_config, DATASET_NAME, B_FALSE,
374410233SGeorge.Wilson@Sun.COM 	    B_TRUE },
37459234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_pool_set_props, zfs_secpolicy_config,	POOL_NAME, B_TRUE,
37469234SGeorge.Wilson@Sun.COM 	    B_TRUE },
37479234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_pool_get_props, zfs_secpolicy_read, POOL_NAME, B_FALSE,
37489234SGeorge.Wilson@Sun.COM 	    B_FALSE },
37499234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_set_fsacl, zfs_secpolicy_fsacl, DATASET_NAME, B_TRUE,
37509234SGeorge.Wilson@Sun.COM 	    B_TRUE },
37519234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_get_fsacl, zfs_secpolicy_read, DATASET_NAME, B_FALSE,
37529234SGeorge.Wilson@Sun.COM 	    B_FALSE },
37539234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_iscsi_perm_check, zfs_secpolicy_iscsi, DATASET_NAME, B_FALSE,
37549234SGeorge.Wilson@Sun.COM 	    B_FALSE },
37559234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_share, zfs_secpolicy_share, DATASET_NAME, B_FALSE, B_FALSE },
37569234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_inherit_prop, zfs_secpolicy_inherit, DATASET_NAME, B_TRUE,
37579234SGeorge.Wilson@Sun.COM 	    B_TRUE },
37589234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_smb_acl, zfs_secpolicy_smb_acl, DATASET_NAME, B_FALSE,
37599396SMatthew.Ahrens@Sun.COM 	    B_FALSE },
37609396SMatthew.Ahrens@Sun.COM 	{ zfs_ioc_userspace_one, zfs_secpolicy_userspace_one,
37619396SMatthew.Ahrens@Sun.COM 	    DATASET_NAME, B_FALSE, B_FALSE },
37629396SMatthew.Ahrens@Sun.COM 	{ zfs_ioc_userspace_many, zfs_secpolicy_userspace_many,
37639396SMatthew.Ahrens@Sun.COM 	    DATASET_NAME, B_FALSE, B_FALSE },
37649396SMatthew.Ahrens@Sun.COM 	{ zfs_ioc_userspace_upgrade, zfs_secpolicy_userspace_upgrade,
37659396SMatthew.Ahrens@Sun.COM 	    DATASET_NAME, B_FALSE, B_TRUE },
376610242Schris.kirby@sun.com 	{ zfs_ioc_hold, zfs_secpolicy_hold, DATASET_NAME, B_TRUE, B_TRUE },
376710242Schris.kirby@sun.com 	{ zfs_ioc_release, zfs_secpolicy_release, DATASET_NAME, B_TRUE,
376810242Schris.kirby@sun.com 	    B_TRUE },
376910242Schris.kirby@sun.com 	{ zfs_ioc_get_holds, zfs_secpolicy_read, DATASET_NAME, B_FALSE,
377010242Schris.kirby@sun.com 	    B_TRUE }
3771789Sahrens };
3772789Sahrens 
37739234SGeorge.Wilson@Sun.COM int
37749234SGeorge.Wilson@Sun.COM pool_status_check(const char *name, zfs_ioc_namecheck_t type)
37759234SGeorge.Wilson@Sun.COM {
37769234SGeorge.Wilson@Sun.COM 	spa_t *spa;
37779234SGeorge.Wilson@Sun.COM 	int error;
37789234SGeorge.Wilson@Sun.COM 
37799234SGeorge.Wilson@Sun.COM 	ASSERT(type == POOL_NAME || type == DATASET_NAME);
37809234SGeorge.Wilson@Sun.COM 
37819396SMatthew.Ahrens@Sun.COM 	error = spa_open(name, &spa, FTAG);
37829234SGeorge.Wilson@Sun.COM 	if (error == 0) {
37839234SGeorge.Wilson@Sun.COM 		if (spa_suspended(spa))
37849234SGeorge.Wilson@Sun.COM 			error = EAGAIN;
37859234SGeorge.Wilson@Sun.COM 		spa_close(spa, FTAG);
37869234SGeorge.Wilson@Sun.COM 	}
37879234SGeorge.Wilson@Sun.COM 	return (error);
37889234SGeorge.Wilson@Sun.COM }
37899234SGeorge.Wilson@Sun.COM 
3790789Sahrens static int
3791789Sahrens zfsdev_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *cr, int *rvalp)
3792789Sahrens {
3793789Sahrens 	zfs_cmd_t *zc;
3794789Sahrens 	uint_t vec;
37952199Sahrens 	int error, rc;
3796789Sahrens 
3797789Sahrens 	if (getminor(dev) != 0)
3798789Sahrens 		return (zvol_ioctl(dev, cmd, arg, flag, cr, rvalp));
3799789Sahrens 
3800789Sahrens 	vec = cmd - ZFS_IOC;
38014787Sahrens 	ASSERT3U(getmajor(dev), ==, ddi_driver_major(zfs_dip));
3802789Sahrens 
3803789Sahrens 	if (vec >= sizeof (zfs_ioc_vec) / sizeof (zfs_ioc_vec[0]))
3804789Sahrens 		return (EINVAL);
3805789Sahrens 
3806789Sahrens 	zc = kmem_zalloc(sizeof (zfs_cmd_t), KM_SLEEP);
3807789Sahrens 
38089643SEric.Taylor@Sun.COM 	error = ddi_copyin((void *)arg, zc, sizeof (zfs_cmd_t), flag);
3809789Sahrens 
381010588SEric.Taylor@Sun.COM 	if ((error == 0) && !(flag & FKIOCTL))
38114543Smarks 		error = zfs_ioc_vec[vec].zvec_secpolicy(zc, cr);
3812789Sahrens 
3813789Sahrens 	/*
3814789Sahrens 	 * Ensure that all pool/dataset names are valid before we pass down to
3815789Sahrens 	 * the lower layers.
3816789Sahrens 	 */
3817789Sahrens 	if (error == 0) {
3818789Sahrens 		zc->zc_name[sizeof (zc->zc_name) - 1] = '\0';
38199643SEric.Taylor@Sun.COM 		zc->zc_iflags = flag & FKIOCTL;
3820789Sahrens 		switch (zfs_ioc_vec[vec].zvec_namecheck) {
38214577Sahrens 		case POOL_NAME:
3822789Sahrens 			if (pool_namecheck(zc->zc_name, NULL, NULL) != 0)
3823789Sahrens 				error = EINVAL;
38249234SGeorge.Wilson@Sun.COM 			if (zfs_ioc_vec[vec].zvec_pool_check)
38259234SGeorge.Wilson@Sun.COM 				error = pool_status_check(zc->zc_name,
38269234SGeorge.Wilson@Sun.COM 				    zfs_ioc_vec[vec].zvec_namecheck);
3827789Sahrens 			break;
3828789Sahrens 
38294577Sahrens 		case DATASET_NAME:
3830789Sahrens 			if (dataset_namecheck(zc->zc_name, NULL, NULL) != 0)
3831789Sahrens 				error = EINVAL;
38329234SGeorge.Wilson@Sun.COM 			if (zfs_ioc_vec[vec].zvec_pool_check)
38339234SGeorge.Wilson@Sun.COM 				error = pool_status_check(zc->zc_name,
38349234SGeorge.Wilson@Sun.COM 				    zfs_ioc_vec[vec].zvec_namecheck);
3835789Sahrens 			break;
38362856Snd150628 
38374577Sahrens 		case NO_NAME:
38382856Snd150628 			break;
3839789Sahrens 		}
3840789Sahrens 	}
3841789Sahrens 
3842789Sahrens 	if (error == 0)
3843789Sahrens 		error = zfs_ioc_vec[vec].zvec_func(zc);
3844789Sahrens 
38459643SEric.Taylor@Sun.COM 	rc = ddi_copyout(zc, (void *)arg, sizeof (zfs_cmd_t), flag);
38464543Smarks 	if (error == 0) {
38472199Sahrens 		error = rc;
38489396SMatthew.Ahrens@Sun.COM 		if (zfs_ioc_vec[vec].zvec_his_log)
38494543Smarks 			zfs_log_history(zc);
38504543Smarks 	}
3851789Sahrens 
3852789Sahrens 	kmem_free(zc, sizeof (zfs_cmd_t));
3853789Sahrens 	return (error);
3854789Sahrens }
3855789Sahrens 
3856789Sahrens static int
3857789Sahrens zfs_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
3858789Sahrens {
3859789Sahrens 	if (cmd != DDI_ATTACH)
3860789Sahrens 		return (DDI_FAILURE);
3861789Sahrens 
3862789Sahrens 	if (ddi_create_minor_node(dip, "zfs", S_IFCHR, 0,
3863789Sahrens 	    DDI_PSEUDO, 0) == DDI_FAILURE)
3864789Sahrens 		return (DDI_FAILURE);
3865789Sahrens 
3866789Sahrens 	zfs_dip = dip;
3867789Sahrens 
3868789Sahrens 	ddi_report_dev(dip);
3869789Sahrens 
3870789Sahrens 	return (DDI_SUCCESS);
3871789Sahrens }
3872789Sahrens 
3873789Sahrens static int
3874789Sahrens zfs_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
3875789Sahrens {
3876789Sahrens 	if (spa_busy() || zfs_busy() || zvol_busy())
3877789Sahrens 		return (DDI_FAILURE);
3878789Sahrens 
3879789Sahrens 	if (cmd != DDI_DETACH)
3880789Sahrens 		return (DDI_FAILURE);
3881789Sahrens 
3882789Sahrens 	zfs_dip = NULL;
3883789Sahrens 
3884789Sahrens 	ddi_prop_remove_all(dip);
3885789Sahrens 	ddi_remove_minor_node(dip, NULL);
3886789Sahrens 
3887789Sahrens 	return (DDI_SUCCESS);
3888789Sahrens }
3889789Sahrens 
3890789Sahrens /*ARGSUSED*/
3891789Sahrens static int
3892789Sahrens zfs_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
3893789Sahrens {
3894789Sahrens 	switch (infocmd) {
3895789Sahrens 	case DDI_INFO_DEVT2DEVINFO:
3896789Sahrens 		*result = zfs_dip;
3897789Sahrens 		return (DDI_SUCCESS);
3898789Sahrens 
3899789Sahrens 	case DDI_INFO_DEVT2INSTANCE:
3900849Sbonwick 		*result = (void *)0;
3901789Sahrens 		return (DDI_SUCCESS);
3902789Sahrens 	}
3903789Sahrens 
3904789Sahrens 	return (DDI_FAILURE);
3905789Sahrens }
3906789Sahrens 
3907789Sahrens /*
3908789Sahrens  * OK, so this is a little weird.
3909789Sahrens  *
3910789Sahrens  * /dev/zfs is the control node, i.e. minor 0.
3911789Sahrens  * /dev/zvol/[r]dsk/pool/dataset are the zvols, minor > 0.
3912789Sahrens  *
3913789Sahrens  * /dev/zfs has basically nothing to do except serve up ioctls,
3914789Sahrens  * so most of the standard driver entry points are in zvol.c.
3915789Sahrens  */
3916789Sahrens static struct cb_ops zfs_cb_ops = {
3917789Sahrens 	zvol_open,	/* open */
3918789Sahrens 	zvol_close,	/* close */
3919789Sahrens 	zvol_strategy,	/* strategy */
3920789Sahrens 	nodev,		/* print */
39216423Sgw25295 	zvol_dump,	/* dump */
3922789Sahrens 	zvol_read,	/* read */
3923789Sahrens 	zvol_write,	/* write */
3924789Sahrens 	zfsdev_ioctl,	/* ioctl */
3925789Sahrens 	nodev,		/* devmap */
3926789Sahrens 	nodev,		/* mmap */
3927789Sahrens 	nodev,		/* segmap */
3928789Sahrens 	nochpoll,	/* poll */
3929789Sahrens 	ddi_prop_op,	/* prop_op */
3930789Sahrens 	NULL,		/* streamtab */
3931789Sahrens 	D_NEW | D_MP | D_64BIT,		/* Driver compatibility flag */
3932789Sahrens 	CB_REV,		/* version */
39333638Sbillm 	nodev,		/* async read */
39343638Sbillm 	nodev,		/* async write */
3935789Sahrens };
3936789Sahrens 
3937789Sahrens static struct dev_ops zfs_dev_ops = {
3938789Sahrens 	DEVO_REV,	/* version */
3939789Sahrens 	0,		/* refcnt */
3940789Sahrens 	zfs_info,	/* info */
3941789Sahrens 	nulldev,	/* identify */
3942789Sahrens 	nulldev,	/* probe */
3943789Sahrens 	zfs_attach,	/* attach */
3944789Sahrens 	zfs_detach,	/* detach */
3945789Sahrens 	nodev,		/* reset */
3946789Sahrens 	&zfs_cb_ops,	/* driver operations */
39477656SSherry.Moore@Sun.COM 	NULL,		/* no bus operations */
39487656SSherry.Moore@Sun.COM 	NULL,		/* power */
39497656SSherry.Moore@Sun.COM 	ddi_quiesce_not_needed,	/* quiesce */
3950789Sahrens };
3951789Sahrens 
3952789Sahrens static struct modldrv zfs_modldrv = {
39537656SSherry.Moore@Sun.COM 	&mod_driverops,
39547656SSherry.Moore@Sun.COM 	"ZFS storage pool",
39557656SSherry.Moore@Sun.COM 	&zfs_dev_ops
3956789Sahrens };
3957789Sahrens 
3958789Sahrens static struct modlinkage modlinkage = {
3959789Sahrens 	MODREV_1,
3960789Sahrens 	(void *)&zfs_modlfs,
3961789Sahrens 	(void *)&zfs_modldrv,
3962789Sahrens 	NULL
3963789Sahrens };
3964789Sahrens 
39654720Sfr157268 
39664720Sfr157268 uint_t zfs_fsyncer_key;
39675326Sek110237 extern uint_t rrw_tsd_key;
39684720Sfr157268 
3969789Sahrens int
3970789Sahrens _init(void)
3971789Sahrens {
3972789Sahrens 	int error;
3973789Sahrens 
3974849Sbonwick 	spa_init(FREAD | FWRITE);
3975849Sbonwick 	zfs_init();
3976849Sbonwick 	zvol_init();
3977849Sbonwick 
3978849Sbonwick 	if ((error = mod_install(&modlinkage)) != 0) {
3979849Sbonwick 		zvol_fini();
3980849Sbonwick 		zfs_fini();
3981849Sbonwick 		spa_fini();
3982789Sahrens 		return (error);
3983849Sbonwick 	}
3984789Sahrens 
39854720Sfr157268 	tsd_create(&zfs_fsyncer_key, NULL);
39865326Sek110237 	tsd_create(&rrw_tsd_key, NULL);
39874720Sfr157268 
3988789Sahrens 	error = ldi_ident_from_mod(&modlinkage, &zfs_li);
3989789Sahrens 	ASSERT(error == 0);
39904543Smarks 	mutex_init(&zfs_share_lock, NULL, MUTEX_DEFAULT, NULL);
3991789Sahrens 
3992789Sahrens 	return (0);
3993789Sahrens }
3994789Sahrens 
3995789Sahrens int
3996789Sahrens _fini(void)
3997789Sahrens {
3998789Sahrens 	int error;
3999789Sahrens 
40001544Seschrock 	if (spa_busy() || zfs_busy() || zvol_busy() || zio_injection_enabled)
4001789Sahrens 		return (EBUSY);
4002789Sahrens 
4003789Sahrens 	if ((error = mod_remove(&modlinkage)) != 0)
4004789Sahrens 		return (error);
4005789Sahrens 
4006789Sahrens 	zvol_fini();
4007789Sahrens 	zfs_fini();
4008789Sahrens 	spa_fini();
40095331Samw 	if (zfs_nfsshare_inited)
40104543Smarks 		(void) ddi_modclose(nfs_mod);
40115331Samw 	if (zfs_smbshare_inited)
40125331Samw 		(void) ddi_modclose(smbsrv_mod);
40135331Samw 	if (zfs_nfsshare_inited || zfs_smbshare_inited)
40144543Smarks 		(void) ddi_modclose(sharefs_mod);
4015789Sahrens 
40164720Sfr157268 	tsd_destroy(&zfs_fsyncer_key);
4017789Sahrens 	ldi_ident_release(zfs_li);
4018789Sahrens 	zfs_li = NULL;
40194543Smarks 	mutex_destroy(&zfs_share_lock);
4020789Sahrens 
4021789Sahrens 	return (error);
4022789Sahrens }
4023789Sahrens 
4024789Sahrens int
4025789Sahrens _info(struct modinfo *modinfop)
4026789Sahrens {
4027789Sahrens 	return (mod_info(&modlinkage, modinfop));
4028789Sahrens }
4029