xref: /onnv-gate/usr/src/uts/common/fs/zfs/zfs_ioctl.c (revision 13049:bda0decf867b)
1789Sahrens /*
2789Sahrens  * CDDL HEADER START
3789Sahrens  *
4789Sahrens  * The contents of this file are subject to the terms of the
51485Slling  * Common Development and Distribution License (the "License").
61485Slling  * You may not use this file except in compliance with the License.
7789Sahrens  *
8789Sahrens  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9789Sahrens  * or http://www.opensolaris.org/os/licensing.
10789Sahrens  * See the License for the specific language governing permissions
11789Sahrens  * and limitations under the License.
12789Sahrens  *
13789Sahrens  * When distributing Covered Code, include this CDDL HEADER in each
14789Sahrens  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15789Sahrens  * If applicable, add the following below this CDDL HEADER, with the
16789Sahrens  * fields enclosed by brackets "[]" replaced with your own identifying
17789Sahrens  * information: Portions Copyright [yyyy] [name of copyright owner]
18789Sahrens  *
19789Sahrens  * CDDL HEADER END
20789Sahrens  */
21789Sahrens /*
2212296SLin.Ling@Sun.COM  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
23789Sahrens  */
24789Sahrens 
25789Sahrens #include <sys/types.h>
26789Sahrens #include <sys/param.h>
27789Sahrens #include <sys/errno.h>
28789Sahrens #include <sys/uio.h>
29789Sahrens #include <sys/buf.h>
30789Sahrens #include <sys/modctl.h>
31789Sahrens #include <sys/open.h>
32789Sahrens #include <sys/file.h>
33789Sahrens #include <sys/kmem.h>
34789Sahrens #include <sys/conf.h>
35789Sahrens #include <sys/cmn_err.h>
36789Sahrens #include <sys/stat.h>
37789Sahrens #include <sys/zfs_ioctl.h>
3810972SRic.Aleshire@Sun.COM #include <sys/zfs_vfsops.h>
395331Samw #include <sys/zfs_znode.h>
40789Sahrens #include <sys/zap.h>
41789Sahrens #include <sys/spa.h>
423912Slling #include <sys/spa_impl.h>
43789Sahrens #include <sys/vdev.h>
4410972SRic.Aleshire@Sun.COM #include <sys/priv_impl.h>
45789Sahrens #include <sys/dmu.h>
46789Sahrens #include <sys/dsl_dir.h>
47789Sahrens #include <sys/dsl_dataset.h>
48789Sahrens #include <sys/dsl_prop.h>
494543Smarks #include <sys/dsl_deleg.h>
504543Smarks #include <sys/dmu_objset.h>
51789Sahrens #include <sys/ddi.h>
52789Sahrens #include <sys/sunddi.h>
53789Sahrens #include <sys/sunldi.h>
54789Sahrens #include <sys/policy.h>
55789Sahrens #include <sys/zone.h>
56789Sahrens #include <sys/nvpair.h>
57789Sahrens #include <sys/pathname.h>
58789Sahrens #include <sys/mount.h>
59789Sahrens #include <sys/sdt.h>
60789Sahrens #include <sys/fs/zfs.h>
61789Sahrens #include <sys/zfs_ctldir.h>
625331Samw #include <sys/zfs_dir.h>
6312527SChris.Kirby@oracle.com #include <sys/zfs_onexit.h>
642885Sahrens #include <sys/zvol.h>
6512296SLin.Ling@Sun.COM #include <sys/dsl_scan.h>
664543Smarks #include <sharefs/share.h>
675326Sek110237 #include <sys/dmu_objset.h>
68789Sahrens 
69789Sahrens #include "zfs_namecheck.h"
702676Seschrock #include "zfs_prop.h"
714543Smarks #include "zfs_deleg.h"
7211935SMark.Shellenbaum@Sun.COM #include "zfs_comutil.h"
73789Sahrens 
74789Sahrens extern struct modlfs zfs_modlfs;
75789Sahrens 
76789Sahrens extern void zfs_init(void);
77789Sahrens extern void zfs_fini(void);
78789Sahrens 
79789Sahrens ldi_ident_t zfs_li = NULL;
80789Sahrens dev_info_t *zfs_dip;
81789Sahrens 
82789Sahrens typedef int zfs_ioc_func_t(zfs_cmd_t *);
834543Smarks typedef int zfs_secpolicy_func_t(zfs_cmd_t *, cred_t *);
84789Sahrens 
859234SGeorge.Wilson@Sun.COM typedef enum {
869234SGeorge.Wilson@Sun.COM 	NO_NAME,
879234SGeorge.Wilson@Sun.COM 	POOL_NAME,
889234SGeorge.Wilson@Sun.COM 	DATASET_NAME
899234SGeorge.Wilson@Sun.COM } zfs_ioc_namecheck_t;
909234SGeorge.Wilson@Sun.COM 
91*13049SGeorge.Wilson@Sun.COM typedef enum {
92*13049SGeorge.Wilson@Sun.COM 	POOL_CHECK_NONE		= 1 << 0,
93*13049SGeorge.Wilson@Sun.COM 	POOL_CHECK_SUSPENDED	= 1 << 1,
94*13049SGeorge.Wilson@Sun.COM 	POOL_CHECK_READONLY	= 1 << 2
95*13049SGeorge.Wilson@Sun.COM } zfs_ioc_poolcheck_t;
96*13049SGeorge.Wilson@Sun.COM 
97789Sahrens typedef struct zfs_ioc_vec {
98789Sahrens 	zfs_ioc_func_t		*zvec_func;
99789Sahrens 	zfs_secpolicy_func_t	*zvec_secpolicy;
1009234SGeorge.Wilson@Sun.COM 	zfs_ioc_namecheck_t	zvec_namecheck;
1014543Smarks 	boolean_t		zvec_his_log;
102*13049SGeorge.Wilson@Sun.COM 	zfs_ioc_poolcheck_t	zvec_pool_check;
103789Sahrens } zfs_ioc_vec_t;
104789Sahrens 
1059396SMatthew.Ahrens@Sun.COM /* This array is indexed by zfs_userquota_prop_t */
1069396SMatthew.Ahrens@Sun.COM static const char *userquota_perms[] = {
1079396SMatthew.Ahrens@Sun.COM 	ZFS_DELEG_PERM_USERUSED,
1089396SMatthew.Ahrens@Sun.COM 	ZFS_DELEG_PERM_USERQUOTA,
1099396SMatthew.Ahrens@Sun.COM 	ZFS_DELEG_PERM_GROUPUSED,
1109396SMatthew.Ahrens@Sun.COM 	ZFS_DELEG_PERM_GROUPQUOTA,
1119396SMatthew.Ahrens@Sun.COM };
1129396SMatthew.Ahrens@Sun.COM 
1139396SMatthew.Ahrens@Sun.COM static int zfs_ioc_userspace_upgrade(zfs_cmd_t *zc);
11411022STom.Erickson@Sun.COM static int zfs_check_settable(const char *name, nvpair_t *property,
11511022STom.Erickson@Sun.COM     cred_t *cr);
11611022STom.Erickson@Sun.COM static int zfs_check_clearable(char *dataset, nvlist_t *props,
11711022STom.Erickson@Sun.COM     nvlist_t **errors);
1187184Stimh static int zfs_fill_zplprops_root(uint64_t, nvlist_t *, nvlist_t *,
1197184Stimh     boolean_t *);
12011022STom.Erickson@Sun.COM int zfs_set_prop_nvlist(const char *, zprop_source_t, nvlist_t *, nvlist_t **);
1217184Stimh 
122789Sahrens /* _NOTE(PRINTFLIKE(4)) - this is printf-like, but lint is too whiney */
123789Sahrens void
__dprintf(const char * file,const char * func,int line,const char * fmt,...)124789Sahrens __dprintf(const char *file, const char *func, int line, const char *fmt, ...)
125789Sahrens {
126789Sahrens 	const char *newfile;
12712296SLin.Ling@Sun.COM 	char buf[512];
128789Sahrens 	va_list adx;
129789Sahrens 
130789Sahrens 	/*
131789Sahrens 	 * Get rid of annoying "../common/" prefix to filename.
132789Sahrens 	 */
133789Sahrens 	newfile = strrchr(file, '/');
134789Sahrens 	if (newfile != NULL) {
135789Sahrens 		newfile = newfile + 1; /* Get rid of leading / */
136789Sahrens 	} else {
137789Sahrens 		newfile = file;
138789Sahrens 	}
139789Sahrens 
140789Sahrens 	va_start(adx, fmt);
141789Sahrens 	(void) vsnprintf(buf, sizeof (buf), fmt, adx);
142789Sahrens 	va_end(adx);
143789Sahrens 
144789Sahrens 	/*
145789Sahrens 	 * To get this data, use the zfs-dprintf probe as so:
146789Sahrens 	 * dtrace -q -n 'zfs-dprintf \
147789Sahrens 	 *	/stringof(arg0) == "dbuf.c"/ \
148789Sahrens 	 *	{printf("%s: %s", stringof(arg1), stringof(arg3))}'
149789Sahrens 	 * arg0 = file name
150789Sahrens 	 * arg1 = function name
151789Sahrens 	 * arg2 = line number
152789Sahrens 	 * arg3 = message
153789Sahrens 	 */
154789Sahrens 	DTRACE_PROBE4(zfs__dprintf,
155789Sahrens 	    char *, newfile, char *, func, int, line, char *, buf);
156789Sahrens }
157789Sahrens 
1584543Smarks static void
history_str_free(char * buf)1594715Sek110237 history_str_free(char *buf)
1604715Sek110237 {
1614715Sek110237 	kmem_free(buf, HIS_MAX_RECORD_LEN);
1624715Sek110237 }
1634715Sek110237 
1644715Sek110237 static char *
history_str_get(zfs_cmd_t * zc)1654715Sek110237 history_str_get(zfs_cmd_t *zc)
1664715Sek110237 {
1674715Sek110237 	char *buf;
1684715Sek110237 
1694715Sek110237 	if (zc->zc_history == NULL)
1704715Sek110237 		return (NULL);
1714715Sek110237 
1724715Sek110237 	buf = kmem_alloc(HIS_MAX_RECORD_LEN, KM_SLEEP);
1734715Sek110237 	if (copyinstr((void *)(uintptr_t)zc->zc_history,
1744715Sek110237 	    buf, HIS_MAX_RECORD_LEN, NULL) != 0) {
1754715Sek110237 		history_str_free(buf);
1764715Sek110237 		return (NULL);
1774715Sek110237 	}
1784715Sek110237 
1794715Sek110237 	buf[HIS_MAX_RECORD_LEN -1] = '\0';
1804715Sek110237 
1814715Sek110237 	return (buf);
1824715Sek110237 }
1834715Sek110237 
1845375Stimh /*
1857042Sgw25295  * Check to see if the named dataset is currently defined as bootable
1867042Sgw25295  */
1877042Sgw25295 static boolean_t
zfs_is_bootfs(const char * name)1887042Sgw25295 zfs_is_bootfs(const char *name)
1897042Sgw25295 {
19010298SMatthew.Ahrens@Sun.COM 	objset_t *os;
19110298SMatthew.Ahrens@Sun.COM 
19210298SMatthew.Ahrens@Sun.COM 	if (dmu_objset_hold(name, FTAG, &os) == 0) {
19310298SMatthew.Ahrens@Sun.COM 		boolean_t ret;
19410922SJeff.Bonwick@Sun.COM 		ret = (dmu_objset_id(os) == spa_bootfs(dmu_objset_spa(os)));
19510298SMatthew.Ahrens@Sun.COM 		dmu_objset_rele(os, FTAG);
19610298SMatthew.Ahrens@Sun.COM 		return (ret);
1977042Sgw25295 	}
19810298SMatthew.Ahrens@Sun.COM 	return (B_FALSE);
1997042Sgw25295 }
2007042Sgw25295 
2017042Sgw25295 /*
2027184Stimh  * zfs_earlier_version
2035375Stimh  *
2045375Stimh  *	Return non-zero if the spa version is less than requested version.
2055375Stimh  */
2065331Samw static int
zfs_earlier_version(const char * name,int version)2077184Stimh zfs_earlier_version(const char *name, int version)
2085331Samw {
2095331Samw 	spa_t *spa;
2105331Samw 
2115331Samw 	if (spa_open(name, &spa, FTAG) == 0) {
2125331Samw 		if (spa_version(spa) < version) {
2135331Samw 			spa_close(spa, FTAG);
2145331Samw 			return (1);
2155331Samw 		}
2165331Samw 		spa_close(spa, FTAG);
2175331Samw 	}
2185331Samw 	return (0);
2195331Samw }
2205331Samw 
2215977Smarks /*
2226689Smaybee  * zpl_earlier_version
2235977Smarks  *
2246689Smaybee  * Return TRUE if the ZPL version is less than requested version.
2255977Smarks  */
2266689Smaybee static boolean_t
zpl_earlier_version(const char * name,int version)2276689Smaybee zpl_earlier_version(const char *name, int version)
2285977Smarks {
2295977Smarks 	objset_t *os;
2306689Smaybee 	boolean_t rc = B_TRUE;
2315977Smarks 
23210298SMatthew.Ahrens@Sun.COM 	if (dmu_objset_hold(name, FTAG, &os) == 0) {
2336689Smaybee 		uint64_t zplversion;
2346689Smaybee 
23510298SMatthew.Ahrens@Sun.COM 		if (dmu_objset_type(os) != DMU_OST_ZFS) {
23610298SMatthew.Ahrens@Sun.COM 			dmu_objset_rele(os, FTAG);
23710298SMatthew.Ahrens@Sun.COM 			return (B_TRUE);
23810298SMatthew.Ahrens@Sun.COM 		}
23910298SMatthew.Ahrens@Sun.COM 		/* XXX reading from non-owned objset */
2406689Smaybee 		if (zfs_get_zplprop(os, ZFS_PROP_VERSION, &zplversion) == 0)
2416689Smaybee 			rc = zplversion < version;
24210298SMatthew.Ahrens@Sun.COM 		dmu_objset_rele(os, FTAG);
2435977Smarks 	}
2445977Smarks 	return (rc);
2455977Smarks }
2465977Smarks 
2474715Sek110237 static void
zfs_log_history(zfs_cmd_t * zc)2484543Smarks zfs_log_history(zfs_cmd_t *zc)
2494543Smarks {
2504543Smarks 	spa_t *spa;
2514603Sahrens 	char *buf;
2524543Smarks 
2534715Sek110237 	if ((buf = history_str_get(zc)) == NULL)
2544577Sahrens 		return;
2554577Sahrens 
2564715Sek110237 	if (spa_open(zc->zc_name, &spa, FTAG) == 0) {
2574715Sek110237 		if (spa_version(spa) >= SPA_VERSION_ZPOOL_HISTORY)
2584715Sek110237 			(void) spa_history_log(spa, buf, LOG_CMD_NORMAL);
2594715Sek110237 		spa_close(spa, FTAG);
2604543Smarks 	}
2614715Sek110237 	history_str_free(buf);
2624543Smarks }
2634543Smarks 
264789Sahrens /*
265789Sahrens  * Policy for top-level read operations (list pools).  Requires no privileges,
266789Sahrens  * and can be used in the local zone, as there is no associated dataset.
267789Sahrens  */
268789Sahrens /* ARGSUSED */
269789Sahrens static int
zfs_secpolicy_none(zfs_cmd_t * zc,cred_t * cr)2704543Smarks zfs_secpolicy_none(zfs_cmd_t *zc, cred_t *cr)
271789Sahrens {
272789Sahrens 	return (0);
273789Sahrens }
274789Sahrens 
275789Sahrens /*
276789Sahrens  * Policy for dataset read operations (list children, get statistics).  Requires
277789Sahrens  * no privileges, but must be visible in the local zone.
278789Sahrens  */
279789Sahrens /* ARGSUSED */
280789Sahrens static int
zfs_secpolicy_read(zfs_cmd_t * zc,cred_t * cr)2814543Smarks zfs_secpolicy_read(zfs_cmd_t *zc, cred_t *cr)
282789Sahrens {
283789Sahrens 	if (INGLOBALZONE(curproc) ||
2844543Smarks 	    zone_dataset_visible(zc->zc_name, NULL))
285789Sahrens 		return (0);
286789Sahrens 
287789Sahrens 	return (ENOENT);
288789Sahrens }
289789Sahrens 
290789Sahrens static int
zfs_dozonecheck_impl(const char * dataset,uint64_t zoned,cred_t * cr)29112786SChris.Kirby@oracle.com zfs_dozonecheck_impl(const char *dataset, uint64_t zoned, cred_t *cr)
292789Sahrens {
293789Sahrens 	int writable = 1;
294789Sahrens 
295789Sahrens 	/*
296789Sahrens 	 * The dataset must be visible by this zone -- check this first
297789Sahrens 	 * so they don't see EPERM on something they shouldn't know about.
298789Sahrens 	 */
299789Sahrens 	if (!INGLOBALZONE(curproc) &&
300789Sahrens 	    !zone_dataset_visible(dataset, &writable))
301789Sahrens 		return (ENOENT);
302789Sahrens 
303789Sahrens 	if (INGLOBALZONE(curproc)) {
304789Sahrens 		/*
305789Sahrens 		 * If the fs is zoned, only root can access it from the
306789Sahrens 		 * global zone.
307789Sahrens 		 */
308789Sahrens 		if (secpolicy_zfs(cr) && zoned)
309789Sahrens 			return (EPERM);
310789Sahrens 	} else {
311789Sahrens 		/*
312789Sahrens 		 * If we are in a local zone, the 'zoned' property must be set.
313789Sahrens 		 */
314789Sahrens 		if (!zoned)
315789Sahrens 			return (EPERM);
316789Sahrens 
317789Sahrens 		/* must be writable by this zone */
318789Sahrens 		if (!writable)
319789Sahrens 			return (EPERM);
320789Sahrens 	}
321789Sahrens 	return (0);
322789Sahrens }
323789Sahrens 
32412786SChris.Kirby@oracle.com static int
zfs_dozonecheck(const char * dataset,cred_t * cr)32512786SChris.Kirby@oracle.com zfs_dozonecheck(const char *dataset, cred_t *cr)
32612786SChris.Kirby@oracle.com {
32712786SChris.Kirby@oracle.com 	uint64_t zoned;
32812786SChris.Kirby@oracle.com 
32912786SChris.Kirby@oracle.com 	if (dsl_prop_get_integer(dataset, "zoned", &zoned, NULL))
33012786SChris.Kirby@oracle.com 		return (ENOENT);
33112786SChris.Kirby@oracle.com 
33212786SChris.Kirby@oracle.com 	return (zfs_dozonecheck_impl(dataset, zoned, cr));
33312786SChris.Kirby@oracle.com }
33412786SChris.Kirby@oracle.com 
33512786SChris.Kirby@oracle.com static int
zfs_dozonecheck_ds(const char * dataset,dsl_dataset_t * ds,cred_t * cr)33612786SChris.Kirby@oracle.com zfs_dozonecheck_ds(const char *dataset, dsl_dataset_t *ds, cred_t *cr)
33712786SChris.Kirby@oracle.com {
33812786SChris.Kirby@oracle.com 	uint64_t zoned;
33912786SChris.Kirby@oracle.com 
34012786SChris.Kirby@oracle.com 	rw_enter(&ds->ds_dir->dd_pool->dp_config_rwlock, RW_READER);
34112786SChris.Kirby@oracle.com 	if (dsl_prop_get_ds(ds, "zoned", 8, 1, &zoned, NULL)) {
34212786SChris.Kirby@oracle.com 		rw_exit(&ds->ds_dir->dd_pool->dp_config_rwlock);
34312786SChris.Kirby@oracle.com 		return (ENOENT);
34412786SChris.Kirby@oracle.com 	}
34512786SChris.Kirby@oracle.com 	rw_exit(&ds->ds_dir->dd_pool->dp_config_rwlock);
34612786SChris.Kirby@oracle.com 
34712786SChris.Kirby@oracle.com 	return (zfs_dozonecheck_impl(dataset, zoned, cr));
34812786SChris.Kirby@oracle.com }
34912786SChris.Kirby@oracle.com 
350789Sahrens int
zfs_secpolicy_write_perms(const char * name,const char * perm,cred_t * cr)3514543Smarks zfs_secpolicy_write_perms(const char *name, const char *perm, cred_t *cr)
352789Sahrens {
353789Sahrens 	int error;
354789Sahrens 
3554543Smarks 	error = zfs_dozonecheck(name, cr);
3564543Smarks 	if (error == 0) {
3574543Smarks 		error = secpolicy_zfs(cr);
3584670Sahrens 		if (error)
3594543Smarks 			error = dsl_deleg_access(name, perm, cr);
3604543Smarks 	}
3614543Smarks 	return (error);
3624543Smarks }
3634543Smarks 
36412786SChris.Kirby@oracle.com int
zfs_secpolicy_write_perms_ds(const char * name,dsl_dataset_t * ds,const char * perm,cred_t * cr)36512786SChris.Kirby@oracle.com zfs_secpolicy_write_perms_ds(const char *name, dsl_dataset_t *ds,
36612786SChris.Kirby@oracle.com     const char *perm, cred_t *cr)
36712786SChris.Kirby@oracle.com {
36812786SChris.Kirby@oracle.com 	int error;
36912786SChris.Kirby@oracle.com 
37012786SChris.Kirby@oracle.com 	error = zfs_dozonecheck_ds(name, ds, cr);
37112786SChris.Kirby@oracle.com 	if (error == 0) {
37212786SChris.Kirby@oracle.com 		error = secpolicy_zfs(cr);
37312786SChris.Kirby@oracle.com 		if (error)
37412786SChris.Kirby@oracle.com 			error = dsl_deleg_access_impl(ds, perm, cr);
37512786SChris.Kirby@oracle.com 	}
37612786SChris.Kirby@oracle.com 	return (error);
37712786SChris.Kirby@oracle.com }
37812786SChris.Kirby@oracle.com 
37910972SRic.Aleshire@Sun.COM /*
38010972SRic.Aleshire@Sun.COM  * Policy for setting the security label property.
38110972SRic.Aleshire@Sun.COM  *
38210972SRic.Aleshire@Sun.COM  * Returns 0 for success, non-zero for access and other errors.
38310972SRic.Aleshire@Sun.COM  */
38410972SRic.Aleshire@Sun.COM static int
zfs_set_slabel_policy(const char * name,char * strval,cred_t * cr)38511022STom.Erickson@Sun.COM zfs_set_slabel_policy(const char *name, char *strval, cred_t *cr)
38610972SRic.Aleshire@Sun.COM {
38710972SRic.Aleshire@Sun.COM 	char		ds_hexsl[MAXNAMELEN];
38810972SRic.Aleshire@Sun.COM 	bslabel_t	ds_sl, new_sl;
38910972SRic.Aleshire@Sun.COM 	boolean_t	new_default = FALSE;
39010972SRic.Aleshire@Sun.COM 	uint64_t	zoned;
39110972SRic.Aleshire@Sun.COM 	int		needed_priv = -1;
39210972SRic.Aleshire@Sun.COM 	int		error;
39310972SRic.Aleshire@Sun.COM 
39410972SRic.Aleshire@Sun.COM 	/* First get the existing dataset label. */
39510972SRic.Aleshire@Sun.COM 	error = dsl_prop_get(name, zfs_prop_to_name(ZFS_PROP_MLSLABEL),
39610972SRic.Aleshire@Sun.COM 	    1, sizeof (ds_hexsl), &ds_hexsl, NULL);
39710972SRic.Aleshire@Sun.COM 	if (error)
39810972SRic.Aleshire@Sun.COM 		return (EPERM);
39910972SRic.Aleshire@Sun.COM 
40010972SRic.Aleshire@Sun.COM 	if (strcasecmp(strval, ZFS_MLSLABEL_DEFAULT) == 0)
40110972SRic.Aleshire@Sun.COM 		new_default = TRUE;
40210972SRic.Aleshire@Sun.COM 
40310972SRic.Aleshire@Sun.COM 	/* The label must be translatable */
40410972SRic.Aleshire@Sun.COM 	if (!new_default && (hexstr_to_label(strval, &new_sl) != 0))
40510972SRic.Aleshire@Sun.COM 		return (EINVAL);
40610972SRic.Aleshire@Sun.COM 
40710972SRic.Aleshire@Sun.COM 	/*
40810972SRic.Aleshire@Sun.COM 	 * In a non-global zone, disallow attempts to set a label that
40910972SRic.Aleshire@Sun.COM 	 * doesn't match that of the zone; otherwise no other checks
41010972SRic.Aleshire@Sun.COM 	 * are needed.
41110972SRic.Aleshire@Sun.COM 	 */
41210972SRic.Aleshire@Sun.COM 	if (!INGLOBALZONE(curproc)) {
41310972SRic.Aleshire@Sun.COM 		if (new_default || !blequal(&new_sl, CR_SL(CRED())))
41410972SRic.Aleshire@Sun.COM 			return (EPERM);
41510972SRic.Aleshire@Sun.COM 		return (0);
41610972SRic.Aleshire@Sun.COM 	}
41710972SRic.Aleshire@Sun.COM 
41810972SRic.Aleshire@Sun.COM 	/*
41910972SRic.Aleshire@Sun.COM 	 * For global-zone datasets (i.e., those whose zoned property is
42010972SRic.Aleshire@Sun.COM 	 * "off", verify that the specified new label is valid for the
42110972SRic.Aleshire@Sun.COM 	 * global zone.
42210972SRic.Aleshire@Sun.COM 	 */
42310972SRic.Aleshire@Sun.COM 	if (dsl_prop_get_integer(name,
42410972SRic.Aleshire@Sun.COM 	    zfs_prop_to_name(ZFS_PROP_ZONED), &zoned, NULL))
42510972SRic.Aleshire@Sun.COM 		return (EPERM);
42610972SRic.Aleshire@Sun.COM 	if (!zoned) {
42710972SRic.Aleshire@Sun.COM 		if (zfs_check_global_label(name, strval) != 0)
42810972SRic.Aleshire@Sun.COM 			return (EPERM);
42910972SRic.Aleshire@Sun.COM 	}
43010972SRic.Aleshire@Sun.COM 
43110972SRic.Aleshire@Sun.COM 	/*
43210972SRic.Aleshire@Sun.COM 	 * If the existing dataset label is nondefault, check if the
43310972SRic.Aleshire@Sun.COM 	 * dataset is mounted (label cannot be changed while mounted).
43410972SRic.Aleshire@Sun.COM 	 * Get the zfsvfs; if there isn't one, then the dataset isn't
43510972SRic.Aleshire@Sun.COM 	 * mounted (or isn't a dataset, doesn't exist, ...).
43610972SRic.Aleshire@Sun.COM 	 */
43710972SRic.Aleshire@Sun.COM 	if (strcasecmp(ds_hexsl, ZFS_MLSLABEL_DEFAULT) != 0) {
43811022STom.Erickson@Sun.COM 		objset_t *os;
43911022STom.Erickson@Sun.COM 		static char *setsl_tag = "setsl_tag";
44011022STom.Erickson@Sun.COM 
44110972SRic.Aleshire@Sun.COM 		/*
44210972SRic.Aleshire@Sun.COM 		 * Try to own the dataset; abort if there is any error,
44310972SRic.Aleshire@Sun.COM 		 * (e.g., already mounted, in use, or other error).
44410972SRic.Aleshire@Sun.COM 		 */
44510972SRic.Aleshire@Sun.COM 		error = dmu_objset_own(name, DMU_OST_ZFS, B_TRUE,
44611022STom.Erickson@Sun.COM 		    setsl_tag, &os);
44710972SRic.Aleshire@Sun.COM 		if (error)
44810972SRic.Aleshire@Sun.COM 			return (EPERM);
44910972SRic.Aleshire@Sun.COM 
45011022STom.Erickson@Sun.COM 		dmu_objset_disown(os, setsl_tag);
45111022STom.Erickson@Sun.COM 
45210972SRic.Aleshire@Sun.COM 		if (new_default) {
45310972SRic.Aleshire@Sun.COM 			needed_priv = PRIV_FILE_DOWNGRADE_SL;
45410972SRic.Aleshire@Sun.COM 			goto out_check;
45510972SRic.Aleshire@Sun.COM 		}
45610972SRic.Aleshire@Sun.COM 
45710972SRic.Aleshire@Sun.COM 		if (hexstr_to_label(strval, &new_sl) != 0)
45810972SRic.Aleshire@Sun.COM 			return (EPERM);
45910972SRic.Aleshire@Sun.COM 
46010972SRic.Aleshire@Sun.COM 		if (blstrictdom(&ds_sl, &new_sl))
46110972SRic.Aleshire@Sun.COM 			needed_priv = PRIV_FILE_DOWNGRADE_SL;
46210972SRic.Aleshire@Sun.COM 		else if (blstrictdom(&new_sl, &ds_sl))
46310972SRic.Aleshire@Sun.COM 			needed_priv = PRIV_FILE_UPGRADE_SL;
46410972SRic.Aleshire@Sun.COM 	} else {
46510972SRic.Aleshire@Sun.COM 		/* dataset currently has a default label */
46610972SRic.Aleshire@Sun.COM 		if (!new_default)
46710972SRic.Aleshire@Sun.COM 			needed_priv = PRIV_FILE_UPGRADE_SL;
46810972SRic.Aleshire@Sun.COM 	}
46910972SRic.Aleshire@Sun.COM 
47010972SRic.Aleshire@Sun.COM out_check:
47110972SRic.Aleshire@Sun.COM 	if (needed_priv != -1)
47210972SRic.Aleshire@Sun.COM 		return (PRIV_POLICY(cr, needed_priv, B_FALSE, EPERM, NULL));
47310972SRic.Aleshire@Sun.COM 	return (0);
47410972SRic.Aleshire@Sun.COM }
47510972SRic.Aleshire@Sun.COM 
4764543Smarks static int
zfs_secpolicy_setprop(const char * dsname,zfs_prop_t prop,nvpair_t * propval,cred_t * cr)47711022STom.Erickson@Sun.COM zfs_secpolicy_setprop(const char *dsname, zfs_prop_t prop, nvpair_t *propval,
47811022STom.Erickson@Sun.COM     cred_t *cr)
4794543Smarks {
48011022STom.Erickson@Sun.COM 	char *strval;
48111022STom.Erickson@Sun.COM 
4824543Smarks 	/*
4834543Smarks 	 * Check permissions for special properties.
4844543Smarks 	 */
4854543Smarks 	switch (prop) {
4864543Smarks 	case ZFS_PROP_ZONED:
4874543Smarks 		/*
4884543Smarks 		 * Disallow setting of 'zoned' from within a local zone.
4894543Smarks 		 */
4904543Smarks 		if (!INGLOBALZONE(curproc))
4914543Smarks 			return (EPERM);
4924543Smarks 		break;
493789Sahrens 
4944543Smarks 	case ZFS_PROP_QUOTA:
4954543Smarks 		if (!INGLOBALZONE(curproc)) {
4964543Smarks 			uint64_t zoned;
4974543Smarks 			char setpoint[MAXNAMELEN];
4984543Smarks 			/*
4994543Smarks 			 * Unprivileged users are allowed to modify the
5004543Smarks 			 * quota on things *under* (ie. contained by)
5014543Smarks 			 * the thing they own.
5024543Smarks 			 */
50311022STom.Erickson@Sun.COM 			if (dsl_prop_get_integer(dsname, "zoned", &zoned,
5044543Smarks 			    setpoint))
5054543Smarks 				return (EPERM);
50611022STom.Erickson@Sun.COM 			if (!zoned || strlen(dsname) <= strlen(setpoint))
5074543Smarks 				return (EPERM);
5084543Smarks 		}
5094670Sahrens 		break;
51010972SRic.Aleshire@Sun.COM 
51110972SRic.Aleshire@Sun.COM 	case ZFS_PROP_MLSLABEL:
51210972SRic.Aleshire@Sun.COM 		if (!is_system_labeled())
51310972SRic.Aleshire@Sun.COM 			return (EPERM);
51411022STom.Erickson@Sun.COM 
51511022STom.Erickson@Sun.COM 		if (nvpair_value_string(propval, &strval) == 0) {
51611022STom.Erickson@Sun.COM 			int err;
51711022STom.Erickson@Sun.COM 
51811022STom.Erickson@Sun.COM 			err = zfs_set_slabel_policy(dsname, strval, CRED());
51911022STom.Erickson@Sun.COM 			if (err != 0)
52011022STom.Erickson@Sun.COM 				return (err);
52111022STom.Erickson@Sun.COM 		}
52210972SRic.Aleshire@Sun.COM 		break;
5234543Smarks 	}
5244543Smarks 
52511022STom.Erickson@Sun.COM 	return (zfs_secpolicy_write_perms(dsname, zfs_prop_to_name(prop), cr));
526789Sahrens }
527789Sahrens 
5284543Smarks int
zfs_secpolicy_fsacl(zfs_cmd_t * zc,cred_t * cr)5294543Smarks zfs_secpolicy_fsacl(zfs_cmd_t *zc, cred_t *cr)
5304543Smarks {
5314543Smarks 	int error;
5324543Smarks 
5334543Smarks 	error = zfs_dozonecheck(zc->zc_name, cr);
5344543Smarks 	if (error)
5354543Smarks 		return (error);
5364543Smarks 
5374543Smarks 	/*
5384543Smarks 	 * permission to set permissions will be evaluated later in
5394543Smarks 	 * dsl_deleg_can_allow()
5404543Smarks 	 */
5414543Smarks 	return (0);
5424543Smarks }
5434543Smarks 
5444543Smarks int
zfs_secpolicy_rollback(zfs_cmd_t * zc,cred_t * cr)5454543Smarks zfs_secpolicy_rollback(zfs_cmd_t *zc, cred_t *cr)
5464543Smarks {
54710588SEric.Taylor@Sun.COM 	return (zfs_secpolicy_write_perms(zc->zc_name,
54810588SEric.Taylor@Sun.COM 	    ZFS_DELEG_PERM_ROLLBACK, cr));
5494543Smarks }
5504543Smarks 
5514543Smarks int
zfs_secpolicy_send(zfs_cmd_t * zc,cred_t * cr)5524543Smarks zfs_secpolicy_send(zfs_cmd_t *zc, cred_t *cr)
5534543Smarks {
55412786SChris.Kirby@oracle.com 	spa_t *spa;
55512786SChris.Kirby@oracle.com 	dsl_pool_t *dp;
55612786SChris.Kirby@oracle.com 	dsl_dataset_t *ds;
55712786SChris.Kirby@oracle.com 	char *cp;
55812786SChris.Kirby@oracle.com 	int error;
55912786SChris.Kirby@oracle.com 
56012786SChris.Kirby@oracle.com 	/*
56112786SChris.Kirby@oracle.com 	 * Generate the current snapshot name from the given objsetid, then
56212786SChris.Kirby@oracle.com 	 * use that name for the secpolicy/zone checks.
56312786SChris.Kirby@oracle.com 	 */
56412786SChris.Kirby@oracle.com 	cp = strchr(zc->zc_name, '@');
56512786SChris.Kirby@oracle.com 	if (cp == NULL)
56612786SChris.Kirby@oracle.com 		return (EINVAL);
56712786SChris.Kirby@oracle.com 	error = spa_open(zc->zc_name, &spa, FTAG);
56812786SChris.Kirby@oracle.com 	if (error)
56912786SChris.Kirby@oracle.com 		return (error);
57012786SChris.Kirby@oracle.com 
57112786SChris.Kirby@oracle.com 	dp = spa_get_dsl(spa);
57212786SChris.Kirby@oracle.com 	rw_enter(&dp->dp_config_rwlock, RW_READER);
57312786SChris.Kirby@oracle.com 	error = dsl_dataset_hold_obj(dp, zc->zc_sendobj, FTAG, &ds);
57412786SChris.Kirby@oracle.com 	rw_exit(&dp->dp_config_rwlock);
57512786SChris.Kirby@oracle.com 	spa_close(spa, FTAG);
57612786SChris.Kirby@oracle.com 	if (error)
57712786SChris.Kirby@oracle.com 		return (error);
57812786SChris.Kirby@oracle.com 
57912786SChris.Kirby@oracle.com 	dsl_dataset_name(ds, zc->zc_name);
58012786SChris.Kirby@oracle.com 
58112786SChris.Kirby@oracle.com 	error = zfs_secpolicy_write_perms_ds(zc->zc_name, ds,
58212786SChris.Kirby@oracle.com 	    ZFS_DELEG_PERM_SEND, cr);
58312786SChris.Kirby@oracle.com 	dsl_dataset_rele(ds, FTAG);
58412786SChris.Kirby@oracle.com 
58512786SChris.Kirby@oracle.com 	return (error);
5864543Smarks }
5874543Smarks 
5888845Samw@Sun.COM static int
zfs_secpolicy_deleg_share(zfs_cmd_t * zc,cred_t * cr)5898845Samw@Sun.COM zfs_secpolicy_deleg_share(zfs_cmd_t *zc, cred_t *cr)
5908845Samw@Sun.COM {
5918845Samw@Sun.COM 	vnode_t *vp;
5928845Samw@Sun.COM 	int error;
5938845Samw@Sun.COM 
5948845Samw@Sun.COM 	if ((error = lookupname(zc->zc_value, UIO_SYSSPACE,
5958845Samw@Sun.COM 	    NO_FOLLOW, NULL, &vp)) != 0)
5968845Samw@Sun.COM 		return (error);
5978845Samw@Sun.COM 
5988845Samw@Sun.COM 	/* Now make sure mntpnt and dataset are ZFS */
5998845Samw@Sun.COM 
6008845Samw@Sun.COM 	if (vp->v_vfsp->vfs_fstype != zfsfstype ||
6018845Samw@Sun.COM 	    (strcmp((char *)refstr_value(vp->v_vfsp->vfs_resource),
6028845Samw@Sun.COM 	    zc->zc_name) != 0)) {
6038845Samw@Sun.COM 		VN_RELE(vp);
6048845Samw@Sun.COM 		return (EPERM);
6058845Samw@Sun.COM 	}
6068845Samw@Sun.COM 
6078845Samw@Sun.COM 	VN_RELE(vp);
6088845Samw@Sun.COM 	return (dsl_deleg_access(zc->zc_name,
6098845Samw@Sun.COM 	    ZFS_DELEG_PERM_SHARE, cr));
6108845Samw@Sun.COM }
6118845Samw@Sun.COM 
6124543Smarks int
zfs_secpolicy_share(zfs_cmd_t * zc,cred_t * cr)6134543Smarks zfs_secpolicy_share(zfs_cmd_t *zc, cred_t *cr)
6144543Smarks {
6154543Smarks 	if (!INGLOBALZONE(curproc))
6164543Smarks 		return (EPERM);
6174543Smarks 
6185367Sahrens 	if (secpolicy_nfs(cr) == 0) {
6194543Smarks 		return (0);
6204543Smarks 	} else {
6218845Samw@Sun.COM 		return (zfs_secpolicy_deleg_share(zc, cr));
6228845Samw@Sun.COM 	}
6238845Samw@Sun.COM }
6248845Samw@Sun.COM 
6258845Samw@Sun.COM int
zfs_secpolicy_smb_acl(zfs_cmd_t * zc,cred_t * cr)6268845Samw@Sun.COM zfs_secpolicy_smb_acl(zfs_cmd_t *zc, cred_t *cr)
6278845Samw@Sun.COM {
6288845Samw@Sun.COM 	if (!INGLOBALZONE(curproc))
6298845Samw@Sun.COM 		return (EPERM);
6308845Samw@Sun.COM 
6318845Samw@Sun.COM 	if (secpolicy_smb(cr) == 0) {
6328845Samw@Sun.COM 		return (0);
6338845Samw@Sun.COM 	} else {
6348845Samw@Sun.COM 		return (zfs_secpolicy_deleg_share(zc, cr));
6354543Smarks 	}
6364543Smarks }
6374543Smarks 
638789Sahrens static int
zfs_get_parent(const char * datasetname,char * parent,int parentsize)6394543Smarks zfs_get_parent(const char *datasetname, char *parent, int parentsize)
640789Sahrens {
641789Sahrens 	char *cp;
642789Sahrens 
643789Sahrens 	/*
644789Sahrens 	 * Remove the @bla or /bla from the end of the name to get the parent.
645789Sahrens 	 */
6464543Smarks 	(void) strncpy(parent, datasetname, parentsize);
6474543Smarks 	cp = strrchr(parent, '@');
648789Sahrens 	if (cp != NULL) {
649789Sahrens 		cp[0] = '\0';
650789Sahrens 	} else {
6514543Smarks 		cp = strrchr(parent, '/');
652789Sahrens 		if (cp == NULL)
653789Sahrens 			return (ENOENT);
654789Sahrens 		cp[0] = '\0';
655789Sahrens 	}
656789Sahrens 
6574543Smarks 	return (0);
6584543Smarks }
6594543Smarks 
6604543Smarks int
zfs_secpolicy_destroy_perms(const char * name,cred_t * cr)6614543Smarks zfs_secpolicy_destroy_perms(const char *name, cred_t *cr)
6624543Smarks {
6634543Smarks 	int error;
6644543Smarks 
6654543Smarks 	if ((error = zfs_secpolicy_write_perms(name,
6664543Smarks 	    ZFS_DELEG_PERM_MOUNT, cr)) != 0)
6674543Smarks 		return (error);
6684543Smarks 
6694543Smarks 	return (zfs_secpolicy_write_perms(name, ZFS_DELEG_PERM_DESTROY, cr));
6704543Smarks }
6714543Smarks 
6724543Smarks static int
zfs_secpolicy_destroy(zfs_cmd_t * zc,cred_t * cr)6734543Smarks zfs_secpolicy_destroy(zfs_cmd_t *zc, cred_t *cr)
6744543Smarks {
6754543Smarks 	return (zfs_secpolicy_destroy_perms(zc->zc_name, cr));
6764543Smarks }
6774543Smarks 
6784543Smarks /*
67911314Swilliam.gorrell@sun.com  * Destroying snapshots with delegated permissions requires
68011314Swilliam.gorrell@sun.com  * descendent mount and destroy permissions.
68111314Swilliam.gorrell@sun.com  * Reassemble the full filesystem@snap name so dsl_deleg_access()
68211314Swilliam.gorrell@sun.com  * can do the correct permission check.
68311314Swilliam.gorrell@sun.com  *
68411314Swilliam.gorrell@sun.com  * Since this routine is used when doing a recursive destroy of snapshots
68511314Swilliam.gorrell@sun.com  * and destroying snapshots requires descendent permissions, a successfull
68611314Swilliam.gorrell@sun.com  * check of the top level snapshot applies to snapshots of all descendent
68711314Swilliam.gorrell@sun.com  * datasets as well.
68811314Swilliam.gorrell@sun.com  */
68911314Swilliam.gorrell@sun.com static int
zfs_secpolicy_destroy_snaps(zfs_cmd_t * zc,cred_t * cr)69011314Swilliam.gorrell@sun.com zfs_secpolicy_destroy_snaps(zfs_cmd_t *zc, cred_t *cr)
69111314Swilliam.gorrell@sun.com {
69211314Swilliam.gorrell@sun.com 	int error;
69311314Swilliam.gorrell@sun.com 	char *dsname;
69411314Swilliam.gorrell@sun.com 
69511314Swilliam.gorrell@sun.com 	dsname = kmem_asprintf("%s@%s", zc->zc_name, zc->zc_value);
69611314Swilliam.gorrell@sun.com 
69711314Swilliam.gorrell@sun.com 	error = zfs_secpolicy_destroy_perms(dsname, cr);
69811314Swilliam.gorrell@sun.com 
69911314Swilliam.gorrell@sun.com 	strfree(dsname);
70011314Swilliam.gorrell@sun.com 	return (error);
70111314Swilliam.gorrell@sun.com }
70211314Swilliam.gorrell@sun.com 
7034543Smarks int
zfs_secpolicy_rename_perms(const char * from,const char * to,cred_t * cr)7044543Smarks zfs_secpolicy_rename_perms(const char *from, const char *to, cred_t *cr)
7054543Smarks {
70611022STom.Erickson@Sun.COM 	char	parentname[MAXNAMELEN];
7074543Smarks 	int	error;
7084543Smarks 
7094543Smarks 	if ((error = zfs_secpolicy_write_perms(from,
7104543Smarks 	    ZFS_DELEG_PERM_RENAME, cr)) != 0)
7114543Smarks 		return (error);
7124543Smarks 
7134543Smarks 	if ((error = zfs_secpolicy_write_perms(from,
7144543Smarks 	    ZFS_DELEG_PERM_MOUNT, cr)) != 0)
7154543Smarks 		return (error);
7164543Smarks 
7174543Smarks 	if ((error = zfs_get_parent(to, parentname,
7184543Smarks 	    sizeof (parentname))) != 0)
7194543Smarks 		return (error);
7204543Smarks 
7214543Smarks 	if ((error = zfs_secpolicy_write_perms(parentname,
7224543Smarks 	    ZFS_DELEG_PERM_CREATE, cr)) != 0)
7234543Smarks 		return (error);
7244543Smarks 
7254543Smarks 	if ((error = zfs_secpolicy_write_perms(parentname,
7264543Smarks 	    ZFS_DELEG_PERM_MOUNT, cr)) != 0)
7274543Smarks 		return (error);
7284543Smarks 
7294543Smarks 	return (error);
7304543Smarks }
7314543Smarks 
7324543Smarks static int
zfs_secpolicy_rename(zfs_cmd_t * zc,cred_t * cr)7334543Smarks zfs_secpolicy_rename(zfs_cmd_t *zc, cred_t *cr)
7344543Smarks {
7354543Smarks 	return (zfs_secpolicy_rename_perms(zc->zc_name, zc->zc_value, cr));
7364543Smarks }
7374543Smarks 
7384543Smarks static int
zfs_secpolicy_promote(zfs_cmd_t * zc,cred_t * cr)7394543Smarks zfs_secpolicy_promote(zfs_cmd_t *zc, cred_t *cr)
7404543Smarks {
74111022STom.Erickson@Sun.COM 	char	parentname[MAXNAMELEN];
7424543Smarks 	objset_t *clone;
7434543Smarks 	int error;
7444543Smarks 
7454543Smarks 	error = zfs_secpolicy_write_perms(zc->zc_name,
7464543Smarks 	    ZFS_DELEG_PERM_PROMOTE, cr);
7474543Smarks 	if (error)
7484543Smarks 		return (error);
7494543Smarks 
75010298SMatthew.Ahrens@Sun.COM 	error = dmu_objset_hold(zc->zc_name, FTAG, &clone);
7514543Smarks 
7524543Smarks 	if (error == 0) {
7534543Smarks 		dsl_dataset_t *pclone = NULL;
7544543Smarks 		dsl_dir_t *dd;
75510298SMatthew.Ahrens@Sun.COM 		dd = clone->os_dsl_dataset->ds_dir;
7564543Smarks 
7574543Smarks 		rw_enter(&dd->dd_pool->dp_config_rwlock, RW_READER);
7586689Smaybee 		error = dsl_dataset_hold_obj(dd->dd_pool,
7596689Smaybee 		    dd->dd_phys->dd_origin_obj, FTAG, &pclone);
7604543Smarks 		rw_exit(&dd->dd_pool->dp_config_rwlock);
7614543Smarks 		if (error) {
76210298SMatthew.Ahrens@Sun.COM 			dmu_objset_rele(clone, FTAG);
7634543Smarks 			return (error);
7644543Smarks 		}
7654543Smarks 
7664543Smarks 		error = zfs_secpolicy_write_perms(zc->zc_name,
7674543Smarks 		    ZFS_DELEG_PERM_MOUNT, cr);
7684543Smarks 
7694543Smarks 		dsl_dataset_name(pclone, parentname);
77010298SMatthew.Ahrens@Sun.COM 		dmu_objset_rele(clone, FTAG);
7716689Smaybee 		dsl_dataset_rele(pclone, FTAG);
7724543Smarks 		if (error == 0)
7734543Smarks 			error = zfs_secpolicy_write_perms(parentname,
7744543Smarks 			    ZFS_DELEG_PERM_PROMOTE, cr);
7754543Smarks 	}
7764543Smarks 	return (error);
7774543Smarks }
7784543Smarks 
7794543Smarks static int
zfs_secpolicy_receive(zfs_cmd_t * zc,cred_t * cr)7804543Smarks zfs_secpolicy_receive(zfs_cmd_t *zc, cred_t *cr)
7814543Smarks {
7824543Smarks 	int error;
7834543Smarks 
7844543Smarks 	if ((error = zfs_secpolicy_write_perms(zc->zc_name,
7854543Smarks 	    ZFS_DELEG_PERM_RECEIVE, cr)) != 0)
7864543Smarks 		return (error);
7874543Smarks 
7884543Smarks 	if ((error = zfs_secpolicy_write_perms(zc->zc_name,
7894543Smarks 	    ZFS_DELEG_PERM_MOUNT, cr)) != 0)
7904543Smarks 		return (error);
7914543Smarks 
7924543Smarks 	return (zfs_secpolicy_write_perms(zc->zc_name,
7934543Smarks 	    ZFS_DELEG_PERM_CREATE, cr));
7944543Smarks }
7954543Smarks 
7964543Smarks int
zfs_secpolicy_snapshot_perms(const char * name,cred_t * cr)7974543Smarks zfs_secpolicy_snapshot_perms(const char *name, cred_t *cr)
7984543Smarks {
79910588SEric.Taylor@Sun.COM 	return (zfs_secpolicy_write_perms(name,
80010588SEric.Taylor@Sun.COM 	    ZFS_DELEG_PERM_SNAPSHOT, cr));
8014543Smarks }
8024543Smarks 
8034543Smarks static int
zfs_secpolicy_snapshot(zfs_cmd_t * zc,cred_t * cr)8044543Smarks zfs_secpolicy_snapshot(zfs_cmd_t *zc, cred_t *cr)
8054543Smarks {
8064543Smarks 
8074543Smarks 	return (zfs_secpolicy_snapshot_perms(zc->zc_name, cr));
8084543Smarks }
8094543Smarks 
8104543Smarks static int
zfs_secpolicy_create(zfs_cmd_t * zc,cred_t * cr)8114543Smarks zfs_secpolicy_create(zfs_cmd_t *zc, cred_t *cr)
8124543Smarks {
81311022STom.Erickson@Sun.COM 	char	parentname[MAXNAMELEN];
81411022STom.Erickson@Sun.COM 	int	error;
8154543Smarks 
8164543Smarks 	if ((error = zfs_get_parent(zc->zc_name, parentname,
8174543Smarks 	    sizeof (parentname))) != 0)
8184543Smarks 		return (error);
8194543Smarks 
8204543Smarks 	if (zc->zc_value[0] != '\0') {
8214543Smarks 		if ((error = zfs_secpolicy_write_perms(zc->zc_value,
8224543Smarks 		    ZFS_DELEG_PERM_CLONE, cr)) != 0)
8234543Smarks 			return (error);
8244543Smarks 	}
8254543Smarks 
8264543Smarks 	if ((error = zfs_secpolicy_write_perms(parentname,
8274543Smarks 	    ZFS_DELEG_PERM_CREATE, cr)) != 0)
8284543Smarks 		return (error);
8294543Smarks 
8304543Smarks 	error = zfs_secpolicy_write_perms(parentname,
8314543Smarks 	    ZFS_DELEG_PERM_MOUNT, cr);
8324543Smarks 
8334543Smarks 	return (error);
8344543Smarks }
8354543Smarks 
8364543Smarks static int
zfs_secpolicy_umount(zfs_cmd_t * zc,cred_t * cr)8374543Smarks zfs_secpolicy_umount(zfs_cmd_t *zc, cred_t *cr)
8384543Smarks {
8394543Smarks 	int error;
8404543Smarks 
8414543Smarks 	error = secpolicy_fs_unmount(cr, NULL);
8424543Smarks 	if (error) {
8434543Smarks 		error = dsl_deleg_access(zc->zc_name, ZFS_DELEG_PERM_MOUNT, cr);
8444543Smarks 	}
8454543Smarks 	return (error);
846789Sahrens }
847789Sahrens 
848789Sahrens /*
849789Sahrens  * Policy for pool operations - create/destroy pools, add vdevs, etc.  Requires
850789Sahrens  * SYS_CONFIG privilege, which is not available in a local zone.
851789Sahrens  */
852789Sahrens /* ARGSUSED */
853789Sahrens static int
zfs_secpolicy_config(zfs_cmd_t * zc,cred_t * cr)8544543Smarks zfs_secpolicy_config(zfs_cmd_t *zc, cred_t *cr)
855789Sahrens {
856789Sahrens 	if (secpolicy_sys_config(cr, B_FALSE) != 0)
857789Sahrens 		return (EPERM);
858789Sahrens 
859789Sahrens 	return (0);
860789Sahrens }
861789Sahrens 
862789Sahrens /*
86313043STim.Haley@Sun.COM  * Policy for object to name lookups.
86413043STim.Haley@Sun.COM  */
86513043STim.Haley@Sun.COM /* ARGSUSED */
86613043STim.Haley@Sun.COM static int
zfs_secpolicy_diff(zfs_cmd_t * zc,cred_t * cr)86713043STim.Haley@Sun.COM zfs_secpolicy_diff(zfs_cmd_t *zc, cred_t *cr)
86813043STim.Haley@Sun.COM {
86913043STim.Haley@Sun.COM 	int error;
87013043STim.Haley@Sun.COM 
87113043STim.Haley@Sun.COM 	if ((error = secpolicy_sys_config(cr, B_FALSE)) == 0)
87213043STim.Haley@Sun.COM 		return (0);
87313043STim.Haley@Sun.COM 
87413043STim.Haley@Sun.COM 	error = zfs_secpolicy_write_perms(zc->zc_name, ZFS_DELEG_PERM_DIFF, cr);
87513043STim.Haley@Sun.COM 	return (error);
87613043STim.Haley@Sun.COM }
87713043STim.Haley@Sun.COM 
87813043STim.Haley@Sun.COM /*
8791544Seschrock  * Policy for fault injection.  Requires all privileges.
8801544Seschrock  */
8811544Seschrock /* ARGSUSED */
8821544Seschrock static int
zfs_secpolicy_inject(zfs_cmd_t * zc,cred_t * cr)8834543Smarks zfs_secpolicy_inject(zfs_cmd_t *zc, cred_t *cr)
8841544Seschrock {
8851544Seschrock 	return (secpolicy_zinject(cr));
8861544Seschrock }
8871544Seschrock 
8884849Sahrens static int
zfs_secpolicy_inherit(zfs_cmd_t * zc,cred_t * cr)8894849Sahrens zfs_secpolicy_inherit(zfs_cmd_t *zc, cred_t *cr)
8904849Sahrens {
8914849Sahrens 	zfs_prop_t prop = zfs_name_to_prop(zc->zc_value);
8924849Sahrens 
8935094Slling 	if (prop == ZPROP_INVAL) {
8944849Sahrens 		if (!zfs_prop_user(zc->zc_value))
8954849Sahrens 			return (EINVAL);
8964849Sahrens 		return (zfs_secpolicy_write_perms(zc->zc_name,
8974849Sahrens 		    ZFS_DELEG_PERM_USERPROP, cr));
8984849Sahrens 	} else {
89911022STom.Erickson@Sun.COM 		return (zfs_secpolicy_setprop(zc->zc_name, prop,
90011022STom.Erickson@Sun.COM 		    NULL, cr));
9014849Sahrens 	}
9024849Sahrens }
9034849Sahrens 
9049396SMatthew.Ahrens@Sun.COM static int
zfs_secpolicy_userspace_one(zfs_cmd_t * zc,cred_t * cr)9059396SMatthew.Ahrens@Sun.COM zfs_secpolicy_userspace_one(zfs_cmd_t *zc, cred_t *cr)
9069396SMatthew.Ahrens@Sun.COM {
9079396SMatthew.Ahrens@Sun.COM 	int err = zfs_secpolicy_read(zc, cr);
9089396SMatthew.Ahrens@Sun.COM 	if (err)
9099396SMatthew.Ahrens@Sun.COM 		return (err);
9109396SMatthew.Ahrens@Sun.COM 
9119396SMatthew.Ahrens@Sun.COM 	if (zc->zc_objset_type >= ZFS_NUM_USERQUOTA_PROPS)
9129396SMatthew.Ahrens@Sun.COM 		return (EINVAL);
9139396SMatthew.Ahrens@Sun.COM 
9149396SMatthew.Ahrens@Sun.COM 	if (zc->zc_value[0] == 0) {
9159396SMatthew.Ahrens@Sun.COM 		/*
9169396SMatthew.Ahrens@Sun.COM 		 * They are asking about a posix uid/gid.  If it's
9179396SMatthew.Ahrens@Sun.COM 		 * themself, allow it.
9189396SMatthew.Ahrens@Sun.COM 		 */
9199396SMatthew.Ahrens@Sun.COM 		if (zc->zc_objset_type == ZFS_PROP_USERUSED ||
9209396SMatthew.Ahrens@Sun.COM 		    zc->zc_objset_type == ZFS_PROP_USERQUOTA) {
9219396SMatthew.Ahrens@Sun.COM 			if (zc->zc_guid == crgetuid(cr))
9229396SMatthew.Ahrens@Sun.COM 				return (0);
9239396SMatthew.Ahrens@Sun.COM 		} else {
9249396SMatthew.Ahrens@Sun.COM 			if (groupmember(zc->zc_guid, cr))
9259396SMatthew.Ahrens@Sun.COM 				return (0);
9269396SMatthew.Ahrens@Sun.COM 		}
9279396SMatthew.Ahrens@Sun.COM 	}
9289396SMatthew.Ahrens@Sun.COM 
9299396SMatthew.Ahrens@Sun.COM 	return (zfs_secpolicy_write_perms(zc->zc_name,
9309396SMatthew.Ahrens@Sun.COM 	    userquota_perms[zc->zc_objset_type], cr));
9319396SMatthew.Ahrens@Sun.COM }
9329396SMatthew.Ahrens@Sun.COM 
9339396SMatthew.Ahrens@Sun.COM static int
zfs_secpolicy_userspace_many(zfs_cmd_t * zc,cred_t * cr)9349396SMatthew.Ahrens@Sun.COM zfs_secpolicy_userspace_many(zfs_cmd_t *zc, cred_t *cr)
9359396SMatthew.Ahrens@Sun.COM {
9369396SMatthew.Ahrens@Sun.COM 	int err = zfs_secpolicy_read(zc, cr);
9379396SMatthew.Ahrens@Sun.COM 	if (err)
9389396SMatthew.Ahrens@Sun.COM 		return (err);
9399396SMatthew.Ahrens@Sun.COM 
9409396SMatthew.Ahrens@Sun.COM 	if (zc->zc_objset_type >= ZFS_NUM_USERQUOTA_PROPS)
9419396SMatthew.Ahrens@Sun.COM 		return (EINVAL);
9429396SMatthew.Ahrens@Sun.COM 
9439396SMatthew.Ahrens@Sun.COM 	return (zfs_secpolicy_write_perms(zc->zc_name,
9449396SMatthew.Ahrens@Sun.COM 	    userquota_perms[zc->zc_objset_type], cr));
9459396SMatthew.Ahrens@Sun.COM }
9469396SMatthew.Ahrens@Sun.COM 
9479396SMatthew.Ahrens@Sun.COM static int
zfs_secpolicy_userspace_upgrade(zfs_cmd_t * zc,cred_t * cr)9489396SMatthew.Ahrens@Sun.COM zfs_secpolicy_userspace_upgrade(zfs_cmd_t *zc, cred_t *cr)
9499396SMatthew.Ahrens@Sun.COM {
95011022STom.Erickson@Sun.COM 	return (zfs_secpolicy_setprop(zc->zc_name, ZFS_PROP_VERSION,
95111022STom.Erickson@Sun.COM 	    NULL, cr));
9529396SMatthew.Ahrens@Sun.COM }
9539396SMatthew.Ahrens@Sun.COM 
95410242Schris.kirby@sun.com static int
zfs_secpolicy_hold(zfs_cmd_t * zc,cred_t * cr)95510242Schris.kirby@sun.com zfs_secpolicy_hold(zfs_cmd_t *zc, cred_t *cr)
95610242Schris.kirby@sun.com {
95710242Schris.kirby@sun.com 	return (zfs_secpolicy_write_perms(zc->zc_name,
95810242Schris.kirby@sun.com 	    ZFS_DELEG_PERM_HOLD, cr));
95910242Schris.kirby@sun.com }
96010242Schris.kirby@sun.com 
96110242Schris.kirby@sun.com static int
zfs_secpolicy_release(zfs_cmd_t * zc,cred_t * cr)96210242Schris.kirby@sun.com zfs_secpolicy_release(zfs_cmd_t *zc, cred_t *cr)
96310242Schris.kirby@sun.com {
96410242Schris.kirby@sun.com 	return (zfs_secpolicy_write_perms(zc->zc_name,
96510242Schris.kirby@sun.com 	    ZFS_DELEG_PERM_RELEASE, cr));
96610242Schris.kirby@sun.com }
96710242Schris.kirby@sun.com 
9681544Seschrock /*
96913043STim.Haley@Sun.COM  * Policy for allowing temporary snapshots to be taken or released
97013043STim.Haley@Sun.COM  */
97113043STim.Haley@Sun.COM static int
zfs_secpolicy_tmp_snapshot(zfs_cmd_t * zc,cred_t * cr)97213043STim.Haley@Sun.COM zfs_secpolicy_tmp_snapshot(zfs_cmd_t *zc, cred_t *cr)
97313043STim.Haley@Sun.COM {
97413043STim.Haley@Sun.COM 	/*
97513043STim.Haley@Sun.COM 	 * A temporary snapshot is the same as a snapshot,
97613043STim.Haley@Sun.COM 	 * hold, destroy and release all rolled into one.
97713043STim.Haley@Sun.COM 	 * Delegated diff alone is sufficient that we allow this.
97813043STim.Haley@Sun.COM 	 */
97913043STim.Haley@Sun.COM 	int error;
98013043STim.Haley@Sun.COM 
98113043STim.Haley@Sun.COM 	if ((error = zfs_secpolicy_write_perms(zc->zc_name,
98213043STim.Haley@Sun.COM 	    ZFS_DELEG_PERM_DIFF, cr)) == 0)
98313043STim.Haley@Sun.COM 		return (0);
98413043STim.Haley@Sun.COM 
98513043STim.Haley@Sun.COM 	error = zfs_secpolicy_snapshot(zc, cr);
98613043STim.Haley@Sun.COM 	if (!error)
98713043STim.Haley@Sun.COM 		error = zfs_secpolicy_hold(zc, cr);
98813043STim.Haley@Sun.COM 	if (!error)
98913043STim.Haley@Sun.COM 		error = zfs_secpolicy_release(zc, cr);
99013043STim.Haley@Sun.COM 	if (!error)
99113043STim.Haley@Sun.COM 		error = zfs_secpolicy_destroy(zc, cr);
99213043STim.Haley@Sun.COM 	return (error);
99313043STim.Haley@Sun.COM }
99413043STim.Haley@Sun.COM 
99513043STim.Haley@Sun.COM /*
996789Sahrens  * Returns the nvlist as specified by the user in the zfs_cmd_t.
997789Sahrens  */
998789Sahrens static int
get_nvlist(uint64_t nvl,uint64_t size,int iflag,nvlist_t ** nvp)9999643SEric.Taylor@Sun.COM get_nvlist(uint64_t nvl, uint64_t size, int iflag, nvlist_t **nvp)
1000789Sahrens {
1001789Sahrens 	char *packed;
1002789Sahrens 	int error;
10035094Slling 	nvlist_t *list = NULL;
1004789Sahrens 
1005789Sahrens 	/*
10062676Seschrock 	 * Read in and unpack the user-supplied nvlist.
1007789Sahrens 	 */
10085094Slling 	if (size == 0)
1009789Sahrens 		return (EINVAL);
1010789Sahrens 
1011789Sahrens 	packed = kmem_alloc(size, KM_SLEEP);
1012789Sahrens 
10139643SEric.Taylor@Sun.COM 	if ((error = ddi_copyin((void *)(uintptr_t)nvl, packed, size,
10149643SEric.Taylor@Sun.COM 	    iflag)) != 0) {
1015789Sahrens 		kmem_free(packed, size);
1016789Sahrens 		return (error);
1017789Sahrens 	}
1018789Sahrens 
10195094Slling 	if ((error = nvlist_unpack(packed, size, &list, 0)) != 0) {
1020789Sahrens 		kmem_free(packed, size);
1021789Sahrens 		return (error);
1022789Sahrens 	}
1023789Sahrens 
1024789Sahrens 	kmem_free(packed, size);
1025789Sahrens 
10265094Slling 	*nvp = list;
1027789Sahrens 	return (0);
1028789Sahrens }
1029789Sahrens 
1030789Sahrens static int
fit_error_list(zfs_cmd_t * zc,nvlist_t ** errors)103111022STom.Erickson@Sun.COM fit_error_list(zfs_cmd_t *zc, nvlist_t **errors)
103211022STom.Erickson@Sun.COM {
103311022STom.Erickson@Sun.COM 	size_t size;
103411022STom.Erickson@Sun.COM 
103511022STom.Erickson@Sun.COM 	VERIFY(nvlist_size(*errors, &size, NV_ENCODE_NATIVE) == 0);
103611022STom.Erickson@Sun.COM 
103711022STom.Erickson@Sun.COM 	if (size > zc->zc_nvlist_dst_size) {
103811022STom.Erickson@Sun.COM 		nvpair_t *more_errors;
103911022STom.Erickson@Sun.COM 		int n = 0;
104011022STom.Erickson@Sun.COM 
104111022STom.Erickson@Sun.COM 		if (zc->zc_nvlist_dst_size < 1024)
104211022STom.Erickson@Sun.COM 			return (ENOMEM);
104311022STom.Erickson@Sun.COM 
104411022STom.Erickson@Sun.COM 		VERIFY(nvlist_add_int32(*errors, ZPROP_N_MORE_ERRORS, 0) == 0);
104511022STom.Erickson@Sun.COM 		more_errors = nvlist_prev_nvpair(*errors, NULL);
104611022STom.Erickson@Sun.COM 
104711022STom.Erickson@Sun.COM 		do {
104811022STom.Erickson@Sun.COM 			nvpair_t *pair = nvlist_prev_nvpair(*errors,
104911022STom.Erickson@Sun.COM 			    more_errors);
105011022STom.Erickson@Sun.COM 			VERIFY(nvlist_remove_nvpair(*errors, pair) == 0);
105111022STom.Erickson@Sun.COM 			n++;
105211022STom.Erickson@Sun.COM 			VERIFY(nvlist_size(*errors, &size,
105311022STom.Erickson@Sun.COM 			    NV_ENCODE_NATIVE) == 0);
105411022STom.Erickson@Sun.COM 		} while (size > zc->zc_nvlist_dst_size);
105511022STom.Erickson@Sun.COM 
105611022STom.Erickson@Sun.COM 		VERIFY(nvlist_remove_nvpair(*errors, more_errors) == 0);
105711022STom.Erickson@Sun.COM 		VERIFY(nvlist_add_int32(*errors, ZPROP_N_MORE_ERRORS, n) == 0);
105811022STom.Erickson@Sun.COM 		ASSERT(nvlist_size(*errors, &size, NV_ENCODE_NATIVE) == 0);
105911022STom.Erickson@Sun.COM 		ASSERT(size <= zc->zc_nvlist_dst_size);
106011022STom.Erickson@Sun.COM 	}
106111022STom.Erickson@Sun.COM 
106211022STom.Erickson@Sun.COM 	return (0);
106311022STom.Erickson@Sun.COM }
106411022STom.Erickson@Sun.COM 
106511022STom.Erickson@Sun.COM static int
put_nvlist(zfs_cmd_t * zc,nvlist_t * nvl)10662676Seschrock put_nvlist(zfs_cmd_t *zc, nvlist_t *nvl)
10672676Seschrock {
10682676Seschrock 	char *packed = NULL;
106911807SSam.Falkner@Sun.COM 	int error = 0;
10702676Seschrock 	size_t size;
10712676Seschrock 
10722676Seschrock 	VERIFY(nvlist_size(nvl, &size, NV_ENCODE_NATIVE) == 0);
10732676Seschrock 
10742676Seschrock 	if (size > zc->zc_nvlist_dst_size) {
10752676Seschrock 		error = ENOMEM;
10762676Seschrock 	} else {
10774611Smarks 		packed = kmem_alloc(size, KM_SLEEP);
10782676Seschrock 		VERIFY(nvlist_pack(nvl, &packed, &size, NV_ENCODE_NATIVE,
10792676Seschrock 		    KM_SLEEP) == 0);
108011807SSam.Falkner@Sun.COM 		if (ddi_copyout(packed, (void *)(uintptr_t)zc->zc_nvlist_dst,
108111807SSam.Falkner@Sun.COM 		    size, zc->zc_iflags) != 0)
108211807SSam.Falkner@Sun.COM 			error = EFAULT;
10832676Seschrock 		kmem_free(packed, size);
10842676Seschrock 	}
10852676Seschrock 
10862676Seschrock 	zc->zc_nvlist_dst_size = size;
10872676Seschrock 	return (error);
10882676Seschrock }
10892676Seschrock 
10902676Seschrock static int
getzfsvfs(const char * dsname,zfsvfs_t ** zfvp)109111185SSean.McEnroe@Sun.COM getzfsvfs(const char *dsname, zfsvfs_t **zfvp)
10929396SMatthew.Ahrens@Sun.COM {
10939396SMatthew.Ahrens@Sun.COM 	objset_t *os;
10949396SMatthew.Ahrens@Sun.COM 	int error;
10959396SMatthew.Ahrens@Sun.COM 
109610298SMatthew.Ahrens@Sun.COM 	error = dmu_objset_hold(dsname, FTAG, &os);
10979396SMatthew.Ahrens@Sun.COM 	if (error)
10989396SMatthew.Ahrens@Sun.COM 		return (error);
109910298SMatthew.Ahrens@Sun.COM 	if (dmu_objset_type(os) != DMU_OST_ZFS) {
110010298SMatthew.Ahrens@Sun.COM 		dmu_objset_rele(os, FTAG);
110110298SMatthew.Ahrens@Sun.COM 		return (EINVAL);
110210298SMatthew.Ahrens@Sun.COM 	}
110310298SMatthew.Ahrens@Sun.COM 
110410298SMatthew.Ahrens@Sun.COM 	mutex_enter(&os->os_user_ptr_lock);
110511185SSean.McEnroe@Sun.COM 	*zfvp = dmu_objset_get_user(os);
110611185SSean.McEnroe@Sun.COM 	if (*zfvp) {
110711185SSean.McEnroe@Sun.COM 		VFS_HOLD((*zfvp)->z_vfs);
11089396SMatthew.Ahrens@Sun.COM 	} else {
11099396SMatthew.Ahrens@Sun.COM 		error = ESRCH;
11109396SMatthew.Ahrens@Sun.COM 	}
111110298SMatthew.Ahrens@Sun.COM 	mutex_exit(&os->os_user_ptr_lock);
111210298SMatthew.Ahrens@Sun.COM 	dmu_objset_rele(os, FTAG);
11139396SMatthew.Ahrens@Sun.COM 	return (error);
11149396SMatthew.Ahrens@Sun.COM }
11159396SMatthew.Ahrens@Sun.COM 
11169396SMatthew.Ahrens@Sun.COM /*
11179396SMatthew.Ahrens@Sun.COM  * Find a zfsvfs_t for a mounted filesystem, or create our own, in which
11189396SMatthew.Ahrens@Sun.COM  * case its z_vfs will be NULL, and it will be opened as the owner.
11199396SMatthew.Ahrens@Sun.COM  */
11209396SMatthew.Ahrens@Sun.COM static int
zfsvfs_hold(const char * name,void * tag,zfsvfs_t ** zfvp,boolean_t writer)112112620SMark.Shellenbaum@Oracle.COM zfsvfs_hold(const char *name, void *tag, zfsvfs_t **zfvp, boolean_t writer)
11229396SMatthew.Ahrens@Sun.COM {
11239396SMatthew.Ahrens@Sun.COM 	int error = 0;
11249396SMatthew.Ahrens@Sun.COM 
112511185SSean.McEnroe@Sun.COM 	if (getzfsvfs(name, zfvp) != 0)
112611185SSean.McEnroe@Sun.COM 		error = zfsvfs_create(name, zfvp);
11279396SMatthew.Ahrens@Sun.COM 	if (error == 0) {
112812620SMark.Shellenbaum@Oracle.COM 		rrw_enter(&(*zfvp)->z_teardown_lock, (writer) ? RW_WRITER :
112912620SMark.Shellenbaum@Oracle.COM 		    RW_READER, tag);
113011185SSean.McEnroe@Sun.COM 		if ((*zfvp)->z_unmounted) {
11319396SMatthew.Ahrens@Sun.COM 			/*
11329396SMatthew.Ahrens@Sun.COM 			 * XXX we could probably try again, since the unmounting
11339396SMatthew.Ahrens@Sun.COM 			 * thread should be just about to disassociate the
11349396SMatthew.Ahrens@Sun.COM 			 * objset from the zfsvfs.
11359396SMatthew.Ahrens@Sun.COM 			 */
113611185SSean.McEnroe@Sun.COM 			rrw_exit(&(*zfvp)->z_teardown_lock, tag);
11379396SMatthew.Ahrens@Sun.COM 			return (EBUSY);
11389396SMatthew.Ahrens@Sun.COM 		}
11399396SMatthew.Ahrens@Sun.COM 	}
11409396SMatthew.Ahrens@Sun.COM 	return (error);
11419396SMatthew.Ahrens@Sun.COM }
11429396SMatthew.Ahrens@Sun.COM 
11439396SMatthew.Ahrens@Sun.COM static void
zfsvfs_rele(zfsvfs_t * zfsvfs,void * tag)11449396SMatthew.Ahrens@Sun.COM zfsvfs_rele(zfsvfs_t *zfsvfs, void *tag)
11459396SMatthew.Ahrens@Sun.COM {
11469396SMatthew.Ahrens@Sun.COM 	rrw_exit(&zfsvfs->z_teardown_lock, tag);
11479396SMatthew.Ahrens@Sun.COM 
11489396SMatthew.Ahrens@Sun.COM 	if (zfsvfs->z_vfs) {
11499396SMatthew.Ahrens@Sun.COM 		VFS_RELE(zfsvfs->z_vfs);
11509396SMatthew.Ahrens@Sun.COM 	} else {
115110298SMatthew.Ahrens@Sun.COM 		dmu_objset_disown(zfsvfs->z_os, zfsvfs);
11529396SMatthew.Ahrens@Sun.COM 		zfsvfs_free(zfsvfs);
11539396SMatthew.Ahrens@Sun.COM 	}
11549396SMatthew.Ahrens@Sun.COM }
11559396SMatthew.Ahrens@Sun.COM 
11569396SMatthew.Ahrens@Sun.COM static int
zfs_ioc_pool_create(zfs_cmd_t * zc)1157789Sahrens zfs_ioc_pool_create(zfs_cmd_t *zc)
1158789Sahrens {
1159789Sahrens 	int error;
11605094Slling 	nvlist_t *config, *props = NULL;
11617184Stimh 	nvlist_t *rootprops = NULL;
11627184Stimh 	nvlist_t *zplprops = NULL;
11634715Sek110237 	char *buf;
1164789Sahrens 
11655094Slling 	if (error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size,
11669643SEric.Taylor@Sun.COM 	    zc->zc_iflags, &config))
11674988Sek110237 		return (error);
11684715Sek110237 
11695094Slling 	if (zc->zc_nvlist_src_size != 0 && (error =
11709643SEric.Taylor@Sun.COM 	    get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
11719643SEric.Taylor@Sun.COM 	    zc->zc_iflags, &props))) {
11725094Slling 		nvlist_free(config);
11735094Slling 		return (error);
11745094Slling 	}
11755094Slling 
11767184Stimh 	if (props) {
11777184Stimh 		nvlist_t *nvl = NULL;
11787184Stimh 		uint64_t version = SPA_VERSION;
11797184Stimh 
11807184Stimh 		(void) nvlist_lookup_uint64(props,
11817184Stimh 		    zpool_prop_to_name(ZPOOL_PROP_VERSION), &version);
11827184Stimh 		if (version < SPA_VERSION_INITIAL || version > SPA_VERSION) {
11837184Stimh 			error = EINVAL;
11847184Stimh 			goto pool_props_bad;
11857184Stimh 		}
11867184Stimh 		(void) nvlist_lookup_nvlist(props, ZPOOL_ROOTFS_PROPS, &nvl);
11877184Stimh 		if (nvl) {
11887184Stimh 			error = nvlist_dup(nvl, &rootprops, KM_SLEEP);
11897184Stimh 			if (error != 0) {
11907184Stimh 				nvlist_free(config);
11917184Stimh 				nvlist_free(props);
11927184Stimh 				return (error);
11937184Stimh 			}
11947184Stimh 			(void) nvlist_remove_all(props, ZPOOL_ROOTFS_PROPS);
11957184Stimh 		}
11967184Stimh 		VERIFY(nvlist_alloc(&zplprops, NV_UNIQUE_NAME, KM_SLEEP) == 0);
11977184Stimh 		error = zfs_fill_zplprops_root(version, rootprops,
11987184Stimh 		    zplprops, NULL);
11997184Stimh 		if (error)
12007184Stimh 			goto pool_props_bad;
12017184Stimh 	}
12027184Stimh 
12034988Sek110237 	buf = history_str_get(zc);
1204789Sahrens 
12057184Stimh 	error = spa_create(zc->zc_name, config, props, buf, zplprops);
12067184Stimh 
12077184Stimh 	/*
12087184Stimh 	 * Set the remaining root properties
12097184Stimh 	 */
121011022STom.Erickson@Sun.COM 	if (!error && (error = zfs_set_prop_nvlist(zc->zc_name,
121111022STom.Erickson@Sun.COM 	    ZPROP_SRC_LOCAL, rootprops, NULL)) != 0)
12127184Stimh 		(void) spa_destroy(zc->zc_name);
1213789Sahrens 
12144988Sek110237 	if (buf != NULL)
12154988Sek110237 		history_str_free(buf);
12165094Slling 
12177184Stimh pool_props_bad:
12187184Stimh 	nvlist_free(rootprops);
12197184Stimh 	nvlist_free(zplprops);
1220789Sahrens 	nvlist_free(config);
12217184Stimh 	nvlist_free(props);
12225094Slling 
1223789Sahrens 	return (error);
1224789Sahrens }
1225789Sahrens 
1226789Sahrens static int
zfs_ioc_pool_destroy(zfs_cmd_t * zc)1227789Sahrens zfs_ioc_pool_destroy(zfs_cmd_t *zc)
1228789Sahrens {
12294543Smarks 	int error;
12304543Smarks 	zfs_log_history(zc);
12314543Smarks 	error = spa_destroy(zc->zc_name);
123210588SEric.Taylor@Sun.COM 	if (error == 0)
123310588SEric.Taylor@Sun.COM 		zvol_remove_minors(zc->zc_name);
12344543Smarks 	return (error);
1235789Sahrens }
1236789Sahrens 
1237789Sahrens static int
zfs_ioc_pool_import(zfs_cmd_t * zc)1238789Sahrens zfs_ioc_pool_import(zfs_cmd_t *zc)
1239789Sahrens {
12405094Slling 	nvlist_t *config, *props = NULL;
1241789Sahrens 	uint64_t guid;
124210921STim.Haley@Sun.COM 	int error;
1243789Sahrens 
12445094Slling 	if ((error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size,
12459643SEric.Taylor@Sun.COM 	    zc->zc_iflags, &config)) != 0)
1246789Sahrens 		return (error);
1247789Sahrens 
12485094Slling 	if (zc->zc_nvlist_src_size != 0 && (error =
12499643SEric.Taylor@Sun.COM 	    get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
12509643SEric.Taylor@Sun.COM 	    zc->zc_iflags, &props))) {
12515094Slling 		nvlist_free(config);
12525094Slling 		return (error);
12535094Slling 	}
12545094Slling 
1255789Sahrens 	if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID, &guid) != 0 ||
12561544Seschrock 	    guid != zc->zc_guid)
1257789Sahrens 		error = EINVAL;
1258789Sahrens 	else
125912949SGeorge.Wilson@Sun.COM 		error = spa_import(zc->zc_name, config, props, zc->zc_cookie);
126012949SGeorge.Wilson@Sun.COM 
126112949SGeorge.Wilson@Sun.COM 	if (zc->zc_nvlist_dst != 0) {
126212949SGeorge.Wilson@Sun.COM 		int err;
126312949SGeorge.Wilson@Sun.COM 
126412949SGeorge.Wilson@Sun.COM 		if ((err = put_nvlist(zc, config)) != 0)
126512949SGeorge.Wilson@Sun.COM 			error = err;
126612949SGeorge.Wilson@Sun.COM 	}
126710921STim.Haley@Sun.COM 
1268789Sahrens 	nvlist_free(config);
1269789Sahrens 
12705094Slling 	if (props)
12715094Slling 		nvlist_free(props);
12725094Slling 
1273789Sahrens 	return (error);
1274789Sahrens }
1275789Sahrens 
1276789Sahrens static int
zfs_ioc_pool_export(zfs_cmd_t * zc)1277789Sahrens zfs_ioc_pool_export(zfs_cmd_t *zc)
1278789Sahrens {
12794543Smarks 	int error;
12807214Slling 	boolean_t force = (boolean_t)zc->zc_cookie;
12818211SGeorge.Wilson@Sun.COM 	boolean_t hardforce = (boolean_t)zc->zc_guid;
12827214Slling 
12834543Smarks 	zfs_log_history(zc);
12848211SGeorge.Wilson@Sun.COM 	error = spa_export(zc->zc_name, NULL, force, hardforce);
128510588SEric.Taylor@Sun.COM 	if (error == 0)
128610588SEric.Taylor@Sun.COM 		zvol_remove_minors(zc->zc_name);
12874543Smarks 	return (error);
1288789Sahrens }
1289789Sahrens 
1290789Sahrens static int
zfs_ioc_pool_configs(zfs_cmd_t * zc)1291789Sahrens zfs_ioc_pool_configs(zfs_cmd_t *zc)
1292789Sahrens {
1293789Sahrens 	nvlist_t *configs;
1294789Sahrens 	int error;
1295789Sahrens 
1296789Sahrens 	if ((configs = spa_all_configs(&zc->zc_cookie)) == NULL)
1297789Sahrens 		return (EEXIST);
1298789Sahrens 
12992676Seschrock 	error = put_nvlist(zc, configs);
1300789Sahrens 
1301789Sahrens 	nvlist_free(configs);
1302789Sahrens 
1303789Sahrens 	return (error);
1304789Sahrens }
1305789Sahrens 
1306789Sahrens static int
zfs_ioc_pool_stats(zfs_cmd_t * zc)1307789Sahrens zfs_ioc_pool_stats(zfs_cmd_t *zc)
1308789Sahrens {
1309789Sahrens 	nvlist_t *config;
1310789Sahrens 	int error;
13111544Seschrock 	int ret = 0;
1312789Sahrens 
13132676Seschrock 	error = spa_get_stats(zc->zc_name, &config, zc->zc_value,
13142676Seschrock 	    sizeof (zc->zc_value));
1315789Sahrens 
1316789Sahrens 	if (config != NULL) {
13172676Seschrock 		ret = put_nvlist(zc, config);
1318789Sahrens 		nvlist_free(config);
13191544Seschrock 
13201544Seschrock 		/*
13211544Seschrock 		 * The config may be present even if 'error' is non-zero.
13221544Seschrock 		 * In this case we return success, and preserve the real errno
13231544Seschrock 		 * in 'zc_cookie'.
13241544Seschrock 		 */
13251544Seschrock 		zc->zc_cookie = error;
1326789Sahrens 	} else {
13271544Seschrock 		ret = error;
1328789Sahrens 	}
1329789Sahrens 
13301544Seschrock 	return (ret);
1331789Sahrens }
1332789Sahrens 
1333789Sahrens /*
1334789Sahrens  * Try to import the given pool, returning pool stats as appropriate so that
1335789Sahrens  * user land knows which devices are available and overall pool health.
1336789Sahrens  */
1337789Sahrens static int
zfs_ioc_pool_tryimport(zfs_cmd_t * zc)1338789Sahrens zfs_ioc_pool_tryimport(zfs_cmd_t *zc)
1339789Sahrens {
1340789Sahrens 	nvlist_t *tryconfig, *config;
1341789Sahrens 	int error;
1342789Sahrens 
13435094Slling 	if ((error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size,
13449643SEric.Taylor@Sun.COM 	    zc->zc_iflags, &tryconfig)) != 0)
1345789Sahrens 		return (error);
1346789Sahrens 
1347789Sahrens 	config = spa_tryimport(tryconfig);
1348789Sahrens 
1349789Sahrens 	nvlist_free(tryconfig);
1350789Sahrens 
1351789Sahrens 	if (config == NULL)
1352789Sahrens 		return (EINVAL);
1353789Sahrens 
13542676Seschrock 	error = put_nvlist(zc, config);
1355789Sahrens 	nvlist_free(config);
1356789Sahrens 
1357789Sahrens 	return (error);
1358789Sahrens }
1359789Sahrens 
136012296SLin.Ling@Sun.COM /*
136112296SLin.Ling@Sun.COM  * inputs:
136212296SLin.Ling@Sun.COM  * zc_name              name of the pool
136312296SLin.Ling@Sun.COM  * zc_cookie            scan func (pool_scan_func_t)
136412296SLin.Ling@Sun.COM  */
1365789Sahrens static int
zfs_ioc_pool_scan(zfs_cmd_t * zc)136612296SLin.Ling@Sun.COM zfs_ioc_pool_scan(zfs_cmd_t *zc)
1367789Sahrens {
1368789Sahrens 	spa_t *spa;
1369789Sahrens 	int error;
1370789Sahrens 
13712926Sek110237 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
13722926Sek110237 		return (error);
13732926Sek110237 
137412296SLin.Ling@Sun.COM 	if (zc->zc_cookie == POOL_SCAN_NONE)
137512296SLin.Ling@Sun.COM 		error = spa_scan_stop(spa);
137612296SLin.Ling@Sun.COM 	else
137712296SLin.Ling@Sun.COM 		error = spa_scan(spa, zc->zc_cookie);
13782926Sek110237 
13792926Sek110237 	spa_close(spa, FTAG);
13802926Sek110237 
1381789Sahrens 	return (error);
1382789Sahrens }
1383789Sahrens 
1384789Sahrens static int
zfs_ioc_pool_freeze(zfs_cmd_t * zc)1385789Sahrens zfs_ioc_pool_freeze(zfs_cmd_t *zc)
1386789Sahrens {
1387789Sahrens 	spa_t *spa;
1388789Sahrens 	int error;
1389789Sahrens 
1390789Sahrens 	error = spa_open(zc->zc_name, &spa, FTAG);
1391789Sahrens 	if (error == 0) {
1392789Sahrens 		spa_freeze(spa);
1393789Sahrens 		spa_close(spa, FTAG);
1394789Sahrens 	}
1395789Sahrens 	return (error);
1396789Sahrens }
1397789Sahrens 
1398789Sahrens static int
zfs_ioc_pool_upgrade(zfs_cmd_t * zc)13991760Seschrock zfs_ioc_pool_upgrade(zfs_cmd_t *zc)
14001760Seschrock {
14011760Seschrock 	spa_t *spa;
14021760Seschrock 	int error;
14031760Seschrock 
14042926Sek110237 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
14052926Sek110237 		return (error);
14062926Sek110237 
14075118Slling 	if (zc->zc_cookie < spa_version(spa) || zc->zc_cookie > SPA_VERSION) {
14085118Slling 		spa_close(spa, FTAG);
14095118Slling 		return (EINVAL);
14105118Slling 	}
14115118Slling 
14125094Slling 	spa_upgrade(spa, zc->zc_cookie);
14132926Sek110237 	spa_close(spa, FTAG);
14142926Sek110237 
14152926Sek110237 	return (error);
14162926Sek110237 }
14172926Sek110237 
14182926Sek110237 static int
zfs_ioc_pool_get_history(zfs_cmd_t * zc)14192926Sek110237 zfs_ioc_pool_get_history(zfs_cmd_t *zc)
14202926Sek110237 {
14212926Sek110237 	spa_t *spa;
14222926Sek110237 	char *hist_buf;
14232926Sek110237 	uint64_t size;
14242926Sek110237 	int error;
14252926Sek110237 
14262926Sek110237 	if ((size = zc->zc_history_len) == 0)
14272926Sek110237 		return (EINVAL);
14282926Sek110237 
14292926Sek110237 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
14302926Sek110237 		return (error);
14312926Sek110237 
14324577Sahrens 	if (spa_version(spa) < SPA_VERSION_ZPOOL_HISTORY) {
14333863Sek110237 		spa_close(spa, FTAG);
14343863Sek110237 		return (ENOTSUP);
14353863Sek110237 	}
14363863Sek110237 
14372926Sek110237 	hist_buf = kmem_alloc(size, KM_SLEEP);
14382926Sek110237 	if ((error = spa_history_get(spa, &zc->zc_history_offset,
14392926Sek110237 	    &zc->zc_history_len, hist_buf)) == 0) {
14409643SEric.Taylor@Sun.COM 		error = ddi_copyout(hist_buf,
14419643SEric.Taylor@Sun.COM 		    (void *)(uintptr_t)zc->zc_history,
14429643SEric.Taylor@Sun.COM 		    zc->zc_history_len, zc->zc_iflags);
14432926Sek110237 	}
14442926Sek110237 
14452926Sek110237 	spa_close(spa, FTAG);
14462926Sek110237 	kmem_free(hist_buf, size);
14472926Sek110237 	return (error);
14482926Sek110237 }
14492926Sek110237 
14502926Sek110237 static int
zfs_ioc_dsobj_to_dsname(zfs_cmd_t * zc)14513444Sek110237 zfs_ioc_dsobj_to_dsname(zfs_cmd_t *zc)
14523444Sek110237 {
14533444Sek110237 	int error;
14543444Sek110237 
14553912Slling 	if (error = dsl_dsobj_to_dsname(zc->zc_name, zc->zc_obj, zc->zc_value))
14563444Sek110237 		return (error);
14573444Sek110237 
14583444Sek110237 	return (0);
14593444Sek110237 }
14603444Sek110237 
146110298SMatthew.Ahrens@Sun.COM /*
146210298SMatthew.Ahrens@Sun.COM  * inputs:
146310298SMatthew.Ahrens@Sun.COM  * zc_name		name of filesystem
146410298SMatthew.Ahrens@Sun.COM  * zc_obj		object to find
146510298SMatthew.Ahrens@Sun.COM  *
146610298SMatthew.Ahrens@Sun.COM  * outputs:
146710298SMatthew.Ahrens@Sun.COM  * zc_value		name of object
146810298SMatthew.Ahrens@Sun.COM  */
14693444Sek110237 static int
zfs_ioc_obj_to_path(zfs_cmd_t * zc)14703444Sek110237 zfs_ioc_obj_to_path(zfs_cmd_t *zc)
14713444Sek110237 {
147210298SMatthew.Ahrens@Sun.COM 	objset_t *os;
14733444Sek110237 	int error;
14743444Sek110237 
147510298SMatthew.Ahrens@Sun.COM 	/* XXX reading from objset not owned */
147610298SMatthew.Ahrens@Sun.COM 	if ((error = dmu_objset_hold(zc->zc_name, FTAG, &os)) != 0)
14773444Sek110237 		return (error);
147810298SMatthew.Ahrens@Sun.COM 	if (dmu_objset_type(os) != DMU_OST_ZFS) {
147910298SMatthew.Ahrens@Sun.COM 		dmu_objset_rele(os, FTAG);
148010298SMatthew.Ahrens@Sun.COM 		return (EINVAL);
148110298SMatthew.Ahrens@Sun.COM 	}
148210298SMatthew.Ahrens@Sun.COM 	error = zfs_obj_to_path(os, zc->zc_obj, zc->zc_value,
14833444Sek110237 	    sizeof (zc->zc_value));
148410298SMatthew.Ahrens@Sun.COM 	dmu_objset_rele(os, FTAG);
14853444Sek110237 
14863444Sek110237 	return (error);
14873444Sek110237 }
14883444Sek110237 
148913043STim.Haley@Sun.COM /*
149013043STim.Haley@Sun.COM  * inputs:
149113043STim.Haley@Sun.COM  * zc_name		name of filesystem
149213043STim.Haley@Sun.COM  * zc_obj		object to find
149313043STim.Haley@Sun.COM  *
149413043STim.Haley@Sun.COM  * outputs:
149513043STim.Haley@Sun.COM  * zc_stat		stats on object
149613043STim.Haley@Sun.COM  * zc_value		path to object
149713043STim.Haley@Sun.COM  */
149813043STim.Haley@Sun.COM static int
zfs_ioc_obj_to_stats(zfs_cmd_t * zc)149913043STim.Haley@Sun.COM zfs_ioc_obj_to_stats(zfs_cmd_t *zc)
150013043STim.Haley@Sun.COM {
150113043STim.Haley@Sun.COM 	objset_t *os;
150213043STim.Haley@Sun.COM 	int error;
150313043STim.Haley@Sun.COM 
150413043STim.Haley@Sun.COM 	/* XXX reading from objset not owned */
150513043STim.Haley@Sun.COM 	if ((error = dmu_objset_hold(zc->zc_name, FTAG, &os)) != 0)
150613043STim.Haley@Sun.COM 		return (error);
150713043STim.Haley@Sun.COM 	if (dmu_objset_type(os) != DMU_OST_ZFS) {
150813043STim.Haley@Sun.COM 		dmu_objset_rele(os, FTAG);
150913043STim.Haley@Sun.COM 		return (EINVAL);
151013043STim.Haley@Sun.COM 	}
151113043STim.Haley@Sun.COM 	error = zfs_obj_to_stats(os, zc->zc_obj, &zc->zc_stat, zc->zc_value,
151213043STim.Haley@Sun.COM 	    sizeof (zc->zc_value));
151313043STim.Haley@Sun.COM 	dmu_objset_rele(os, FTAG);
151413043STim.Haley@Sun.COM 
151513043STim.Haley@Sun.COM 	return (error);
151613043STim.Haley@Sun.COM }
151713043STim.Haley@Sun.COM 
15183444Sek110237 static int
zfs_ioc_vdev_add(zfs_cmd_t * zc)1519789Sahrens zfs_ioc_vdev_add(zfs_cmd_t *zc)
1520789Sahrens {
1521789Sahrens 	spa_t *spa;
1522789Sahrens 	int error;
15236423Sgw25295 	nvlist_t *config, **l2cache, **spares;
15246423Sgw25295 	uint_t nl2cache = 0, nspares = 0;
1525789Sahrens 
1526789Sahrens 	error = spa_open(zc->zc_name, &spa, FTAG);
1527789Sahrens 	if (error != 0)
1528789Sahrens 		return (error);
1529789Sahrens 
15305450Sbrendan 	error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size,
15319643SEric.Taylor@Sun.COM 	    zc->zc_iflags, &config);
15325450Sbrendan 	(void) nvlist_lookup_nvlist_array(config, ZPOOL_CONFIG_L2CACHE,
15335450Sbrendan 	    &l2cache, &nl2cache);
15345450Sbrendan 
15356423Sgw25295 	(void) nvlist_lookup_nvlist_array(config, ZPOOL_CONFIG_SPARES,
15366423Sgw25295 	    &spares, &nspares);
15376423Sgw25295 
15383912Slling 	/*
15393912Slling 	 * A root pool with concatenated devices is not supported.
15406423Sgw25295 	 * Thus, can not add a device to a root pool.
15416423Sgw25295 	 *
15426423Sgw25295 	 * Intent log device can not be added to a rootpool because
15436423Sgw25295 	 * during mountroot, zil is replayed, a seperated log device
15446423Sgw25295 	 * can not be accessed during the mountroot time.
15456423Sgw25295 	 *
15466423Sgw25295 	 * l2cache and spare devices are ok to be added to a rootpool.
15473912Slling 	 */
154810922SJeff.Bonwick@Sun.COM 	if (spa_bootfs(spa) != 0 && nl2cache == 0 && nspares == 0) {
154911422SMark.Musante@Sun.COM 		nvlist_free(config);
15503912Slling 		spa_close(spa, FTAG);
15513912Slling 		return (EDOM);
15523912Slling 	}
15533912Slling 
15545450Sbrendan 	if (error == 0) {
1555789Sahrens 		error = spa_vdev_add(spa, config);
1556789Sahrens 		nvlist_free(config);
1557789Sahrens 	}
1558789Sahrens 	spa_close(spa, FTAG);
1559789Sahrens 	return (error);
1560789Sahrens }
1561789Sahrens 
156212296SLin.Ling@Sun.COM /*
156312296SLin.Ling@Sun.COM  * inputs:
156412296SLin.Ling@Sun.COM  * zc_name		name of the pool
156512296SLin.Ling@Sun.COM  * zc_nvlist_conf	nvlist of devices to remove
156612296SLin.Ling@Sun.COM  * zc_cookie		to stop the remove?
156712296SLin.Ling@Sun.COM  */
1568789Sahrens static int
zfs_ioc_vdev_remove(zfs_cmd_t * zc)1569789Sahrens zfs_ioc_vdev_remove(zfs_cmd_t *zc)
1570789Sahrens {
15712082Seschrock 	spa_t *spa;
15722082Seschrock 	int error;
15732082Seschrock 
15742082Seschrock 	error = spa_open(zc->zc_name, &spa, FTAG);
15752082Seschrock 	if (error != 0)
15762082Seschrock 		return (error);
15772082Seschrock 	error = spa_vdev_remove(spa, zc->zc_guid, B_FALSE);
15782082Seschrock 	spa_close(spa, FTAG);
15792082Seschrock 	return (error);
1580789Sahrens }
1581789Sahrens 
1582789Sahrens static int
zfs_ioc_vdev_set_state(zfs_cmd_t * zc)15834451Seschrock zfs_ioc_vdev_set_state(zfs_cmd_t *zc)
1584789Sahrens {
1585789Sahrens 	spa_t *spa;
1586789Sahrens 	int error;
15874451Seschrock 	vdev_state_t newstate = VDEV_STATE_UNKNOWN;
1588789Sahrens 
15892926Sek110237 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
1590789Sahrens 		return (error);
15914451Seschrock 	switch (zc->zc_cookie) {
15924451Seschrock 	case VDEV_STATE_ONLINE:
15934451Seschrock 		error = vdev_online(spa, zc->zc_guid, zc->zc_obj, &newstate);
15944451Seschrock 		break;
15954451Seschrock 
15964451Seschrock 	case VDEV_STATE_OFFLINE:
15974451Seschrock 		error = vdev_offline(spa, zc->zc_guid, zc->zc_obj);
15984451Seschrock 		break;
1599789Sahrens 
16004451Seschrock 	case VDEV_STATE_FAULTED:
160110817SEric.Schrock@Sun.COM 		if (zc->zc_obj != VDEV_AUX_ERR_EXCEEDED &&
160210817SEric.Schrock@Sun.COM 		    zc->zc_obj != VDEV_AUX_EXTERNAL)
160310817SEric.Schrock@Sun.COM 			zc->zc_obj = VDEV_AUX_ERR_EXCEEDED;
160410817SEric.Schrock@Sun.COM 
160510817SEric.Schrock@Sun.COM 		error = vdev_fault(spa, zc->zc_guid, zc->zc_obj);
16064451Seschrock 		break;
1607789Sahrens 
16084451Seschrock 	case VDEV_STATE_DEGRADED:
160910817SEric.Schrock@Sun.COM 		if (zc->zc_obj != VDEV_AUX_ERR_EXCEEDED &&
161010817SEric.Schrock@Sun.COM 		    zc->zc_obj != VDEV_AUX_EXTERNAL)
161110817SEric.Schrock@Sun.COM 			zc->zc_obj = VDEV_AUX_ERR_EXCEEDED;
161210817SEric.Schrock@Sun.COM 
161310817SEric.Schrock@Sun.COM 		error = vdev_degrade(spa, zc->zc_guid, zc->zc_obj);
16144451Seschrock 		break;
16154451Seschrock 
16164451Seschrock 	default:
16174451Seschrock 		error = EINVAL;
16184451Seschrock 	}
16194451Seschrock 	zc->zc_cookie = newstate;
1620789Sahrens 	spa_close(spa, FTAG);
1621789Sahrens 	return (error);
1622789Sahrens }
1623789Sahrens 
1624789Sahrens static int
zfs_ioc_vdev_attach(zfs_cmd_t * zc)1625789Sahrens zfs_ioc_vdev_attach(zfs_cmd_t *zc)
1626789Sahrens {
1627789Sahrens 	spa_t *spa;
1628789Sahrens 	int replacing = zc->zc_cookie;
1629789Sahrens 	nvlist_t *config;
1630789Sahrens 	int error;
1631789Sahrens 
16322926Sek110237 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
1633789Sahrens 		return (error);
1634789Sahrens 
16355094Slling 	if ((error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size,
16369643SEric.Taylor@Sun.COM 	    zc->zc_iflags, &config)) == 0) {
16371544Seschrock 		error = spa_vdev_attach(spa, zc->zc_guid, config, replacing);
1638789Sahrens 		nvlist_free(config);
1639789Sahrens 	}
1640789Sahrens 
1641789Sahrens 	spa_close(spa, FTAG);
1642789Sahrens 	return (error);
1643789Sahrens }
1644789Sahrens 
1645789Sahrens static int
zfs_ioc_vdev_detach(zfs_cmd_t * zc)1646789Sahrens zfs_ioc_vdev_detach(zfs_cmd_t *zc)
1647789Sahrens {
1648789Sahrens 	spa_t *spa;
1649789Sahrens 	int error;
1650789Sahrens 
16512926Sek110237 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
1652789Sahrens 		return (error);
1653789Sahrens 
16548241SJeff.Bonwick@Sun.COM 	error = spa_vdev_detach(spa, zc->zc_guid, 0, B_FALSE);
1655789Sahrens 
1656789Sahrens 	spa_close(spa, FTAG);
1657789Sahrens 	return (error);
1658789Sahrens }
1659789Sahrens 
1660789Sahrens static int
zfs_ioc_vdev_split(zfs_cmd_t * zc)166111422SMark.Musante@Sun.COM zfs_ioc_vdev_split(zfs_cmd_t *zc)
166211422SMark.Musante@Sun.COM {
166311422SMark.Musante@Sun.COM 	spa_t *spa;
166411422SMark.Musante@Sun.COM 	nvlist_t *config, *props = NULL;
166511422SMark.Musante@Sun.COM 	int error;
166611422SMark.Musante@Sun.COM 	boolean_t exp = !!(zc->zc_cookie & ZPOOL_EXPORT_AFTER_SPLIT);
166711422SMark.Musante@Sun.COM 
166811422SMark.Musante@Sun.COM 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
166911422SMark.Musante@Sun.COM 		return (error);
167011422SMark.Musante@Sun.COM 
167111422SMark.Musante@Sun.COM 	if (error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size,
167211422SMark.Musante@Sun.COM 	    zc->zc_iflags, &config)) {
167311422SMark.Musante@Sun.COM 		spa_close(spa, FTAG);
167411422SMark.Musante@Sun.COM 		return (error);
167511422SMark.Musante@Sun.COM 	}
167611422SMark.Musante@Sun.COM 
167711422SMark.Musante@Sun.COM 	if (zc->zc_nvlist_src_size != 0 && (error =
167811422SMark.Musante@Sun.COM 	    get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
167911422SMark.Musante@Sun.COM 	    zc->zc_iflags, &props))) {
168011422SMark.Musante@Sun.COM 		spa_close(spa, FTAG);
168111422SMark.Musante@Sun.COM 		nvlist_free(config);
168211422SMark.Musante@Sun.COM 		return (error);
168311422SMark.Musante@Sun.COM 	}
168411422SMark.Musante@Sun.COM 
168511422SMark.Musante@Sun.COM 	error = spa_vdev_split_mirror(spa, zc->zc_string, config, props, exp);
168611422SMark.Musante@Sun.COM 
168711422SMark.Musante@Sun.COM 	spa_close(spa, FTAG);
168811422SMark.Musante@Sun.COM 
168911422SMark.Musante@Sun.COM 	nvlist_free(config);
169011422SMark.Musante@Sun.COM 	nvlist_free(props);
169111422SMark.Musante@Sun.COM 
169211422SMark.Musante@Sun.COM 	return (error);
169311422SMark.Musante@Sun.COM }
169411422SMark.Musante@Sun.COM 
169511422SMark.Musante@Sun.COM static int
zfs_ioc_vdev_setpath(zfs_cmd_t * zc)16961354Seschrock zfs_ioc_vdev_setpath(zfs_cmd_t *zc)
16971354Seschrock {
16981354Seschrock 	spa_t *spa;
16992676Seschrock 	char *path = zc->zc_value;
17001544Seschrock 	uint64_t guid = zc->zc_guid;
17011354Seschrock 	int error;
17021354Seschrock 
17031354Seschrock 	error = spa_open(zc->zc_name, &spa, FTAG);
17041354Seschrock 	if (error != 0)
17051354Seschrock 		return (error);
17061354Seschrock 
17071354Seschrock 	error = spa_vdev_setpath(spa, guid, path);
17081354Seschrock 	spa_close(spa, FTAG);
17091354Seschrock 	return (error);
17101354Seschrock }
17111354Seschrock 
17129425SEric.Schrock@Sun.COM static int
zfs_ioc_vdev_setfru(zfs_cmd_t * zc)17139425SEric.Schrock@Sun.COM zfs_ioc_vdev_setfru(zfs_cmd_t *zc)
17149425SEric.Schrock@Sun.COM {
17159425SEric.Schrock@Sun.COM 	spa_t *spa;
17169425SEric.Schrock@Sun.COM 	char *fru = zc->zc_value;
17179425SEric.Schrock@Sun.COM 	uint64_t guid = zc->zc_guid;
17189425SEric.Schrock@Sun.COM 	int error;
17199425SEric.Schrock@Sun.COM 
17209425SEric.Schrock@Sun.COM 	error = spa_open(zc->zc_name, &spa, FTAG);
17219425SEric.Schrock@Sun.COM 	if (error != 0)
17229425SEric.Schrock@Sun.COM 		return (error);
17239425SEric.Schrock@Sun.COM 
17249425SEric.Schrock@Sun.COM 	error = spa_vdev_setfru(spa, guid, fru);
17259425SEric.Schrock@Sun.COM 	spa_close(spa, FTAG);
17269425SEric.Schrock@Sun.COM 	return (error);
17279425SEric.Schrock@Sun.COM }
17289425SEric.Schrock@Sun.COM 
17291354Seschrock static int
zfs_ioc_objset_stats_impl(zfs_cmd_t * zc,objset_t * os)173012786SChris.Kirby@oracle.com zfs_ioc_objset_stats_impl(zfs_cmd_t *zc, objset_t *os)
1731789Sahrens {
173212786SChris.Kirby@oracle.com 	int error = 0;
17331356Seschrock 	nvlist_t *nv;
1734789Sahrens 
17352885Sahrens 	dmu_objset_fast_stat(os, &zc->zc_objset_stats);
1736789Sahrens 
17372856Snd150628 	if (zc->zc_nvlist_dst != 0 &&
173811022STom.Erickson@Sun.COM 	    (error = dsl_prop_get_all(os, &nv)) == 0) {
17392885Sahrens 		dmu_objset_stats(os, nv);
17403087Sahrens 		/*
17415147Srm160521 		 * NB: zvol_get_stats() will read the objset contents,
17423087Sahrens 		 * which we aren't supposed to do with a
17436689Smaybee 		 * DS_MODE_USER hold, because it could be
17443087Sahrens 		 * inconsistent.  So this is a bit of a workaround...
174510298SMatthew.Ahrens@Sun.COM 		 * XXX reading with out owning
17463087Sahrens 		 */
17474577Sahrens 		if (!zc->zc_objset_stats.dds_inconsistent) {
17484577Sahrens 			if (dmu_objset_type(os) == DMU_OST_ZVOL)
17494577Sahrens 				VERIFY(zvol_get_stats(os, nv) == 0);
17504577Sahrens 		}
17512676Seschrock 		error = put_nvlist(zc, nv);
17521356Seschrock 		nvlist_free(nv);
17531356Seschrock 	}
1754789Sahrens 
175512786SChris.Kirby@oracle.com 	return (error);
175612786SChris.Kirby@oracle.com }
175712786SChris.Kirby@oracle.com 
175812786SChris.Kirby@oracle.com /*
175912786SChris.Kirby@oracle.com  * inputs:
176012786SChris.Kirby@oracle.com  * zc_name		name of filesystem
176112786SChris.Kirby@oracle.com  * zc_nvlist_dst_size	size of buffer for property nvlist
176212786SChris.Kirby@oracle.com  *
176312786SChris.Kirby@oracle.com  * outputs:
176412786SChris.Kirby@oracle.com  * zc_objset_stats	stats
176512786SChris.Kirby@oracle.com  * zc_nvlist_dst	property nvlist
176612786SChris.Kirby@oracle.com  * zc_nvlist_dst_size	size of property nvlist
176712786SChris.Kirby@oracle.com  */
176812786SChris.Kirby@oracle.com static int
zfs_ioc_objset_stats(zfs_cmd_t * zc)176912786SChris.Kirby@oracle.com zfs_ioc_objset_stats(zfs_cmd_t *zc)
177012786SChris.Kirby@oracle.com {
177112786SChris.Kirby@oracle.com 	objset_t *os = NULL;
177212786SChris.Kirby@oracle.com 	int error;
177312786SChris.Kirby@oracle.com 
177412786SChris.Kirby@oracle.com 	if (error = dmu_objset_hold(zc->zc_name, FTAG, &os))
177512786SChris.Kirby@oracle.com 		return (error);
177612786SChris.Kirby@oracle.com 
177712786SChris.Kirby@oracle.com 	error = zfs_ioc_objset_stats_impl(zc, os);
177812786SChris.Kirby@oracle.com 
177910298SMatthew.Ahrens@Sun.COM 	dmu_objset_rele(os, FTAG);
178012786SChris.Kirby@oracle.com 
1781789Sahrens 	return (error);
1782789Sahrens }
1783789Sahrens 
178411022STom.Erickson@Sun.COM /*
178511022STom.Erickson@Sun.COM  * inputs:
178611022STom.Erickson@Sun.COM  * zc_name		name of filesystem
178711022STom.Erickson@Sun.COM  * zc_nvlist_dst_size	size of buffer for property nvlist
178811022STom.Erickson@Sun.COM  *
178911022STom.Erickson@Sun.COM  * outputs:
179011022STom.Erickson@Sun.COM  * zc_nvlist_dst	received property nvlist
179111022STom.Erickson@Sun.COM  * zc_nvlist_dst_size	size of received property nvlist
179211022STom.Erickson@Sun.COM  *
179311022STom.Erickson@Sun.COM  * Gets received properties (distinct from local properties on or after
179411022STom.Erickson@Sun.COM  * SPA_VERSION_RECVD_PROPS) for callers who want to differentiate received from
179511022STom.Erickson@Sun.COM  * local property values.
179611022STom.Erickson@Sun.COM  */
179711022STom.Erickson@Sun.COM static int
zfs_ioc_objset_recvd_props(zfs_cmd_t * zc)179811022STom.Erickson@Sun.COM zfs_ioc_objset_recvd_props(zfs_cmd_t *zc)
179911022STom.Erickson@Sun.COM {
180011022STom.Erickson@Sun.COM 	objset_t *os = NULL;
180111022STom.Erickson@Sun.COM 	int error;
180211022STom.Erickson@Sun.COM 	nvlist_t *nv;
180311022STom.Erickson@Sun.COM 
180411022STom.Erickson@Sun.COM 	if (error = dmu_objset_hold(zc->zc_name, FTAG, &os))
180511022STom.Erickson@Sun.COM 		return (error);
180611022STom.Erickson@Sun.COM 
180711022STom.Erickson@Sun.COM 	/*
180811022STom.Erickson@Sun.COM 	 * Without this check, we would return local property values if the
180911022STom.Erickson@Sun.COM 	 * caller has not already received properties on or after
181011022STom.Erickson@Sun.COM 	 * SPA_VERSION_RECVD_PROPS.
181111022STom.Erickson@Sun.COM 	 */
181211022STom.Erickson@Sun.COM 	if (!dsl_prop_get_hasrecvd(os)) {
181311022STom.Erickson@Sun.COM 		dmu_objset_rele(os, FTAG);
181411022STom.Erickson@Sun.COM 		return (ENOTSUP);
181511022STom.Erickson@Sun.COM 	}
181611022STom.Erickson@Sun.COM 
181711022STom.Erickson@Sun.COM 	if (zc->zc_nvlist_dst != 0 &&
181811022STom.Erickson@Sun.COM 	    (error = dsl_prop_get_received(os, &nv)) == 0) {
181911022STom.Erickson@Sun.COM 		error = put_nvlist(zc, nv);
182011022STom.Erickson@Sun.COM 		nvlist_free(nv);
182111022STom.Erickson@Sun.COM 	}
182211022STom.Erickson@Sun.COM 
182311022STom.Erickson@Sun.COM 	dmu_objset_rele(os, FTAG);
182411022STom.Erickson@Sun.COM 	return (error);
182511022STom.Erickson@Sun.COM }
182611022STom.Erickson@Sun.COM 
18275498Stimh static int
nvl_add_zplprop(objset_t * os,nvlist_t * props,zfs_prop_t prop)18285498Stimh nvl_add_zplprop(objset_t *os, nvlist_t *props, zfs_prop_t prop)
18295498Stimh {
18305498Stimh 	uint64_t value;
18315498Stimh 	int error;
18325498Stimh 
18335498Stimh 	/*
18345498Stimh 	 * zfs_get_zplprop() will either find a value or give us
18355498Stimh 	 * the default value (if there is one).
18365498Stimh 	 */
18375498Stimh 	if ((error = zfs_get_zplprop(os, prop, &value)) != 0)
18385498Stimh 		return (error);
18395498Stimh 	VERIFY(nvlist_add_uint64(props, zfs_prop_to_name(prop), value) == 0);
18405498Stimh 	return (0);
18415498Stimh }
18425498Stimh 
18435498Stimh /*
18445498Stimh  * inputs:
18455498Stimh  * zc_name		name of filesystem
18465498Stimh  * zc_nvlist_dst_size	size of buffer for zpl property nvlist
18475498Stimh  *
18485498Stimh  * outputs:
18495498Stimh  * zc_nvlist_dst	zpl property nvlist
18505498Stimh  * zc_nvlist_dst_size	size of zpl property nvlist
18515498Stimh  */
18525498Stimh static int
zfs_ioc_objset_zplprops(zfs_cmd_t * zc)18535498Stimh zfs_ioc_objset_zplprops(zfs_cmd_t *zc)
18545498Stimh {
18555498Stimh 	objset_t *os;
18565498Stimh 	int err;
18575498Stimh 
185810298SMatthew.Ahrens@Sun.COM 	/* XXX reading without owning */
185910298SMatthew.Ahrens@Sun.COM 	if (err = dmu_objset_hold(zc->zc_name, FTAG, &os))
18605498Stimh 		return (err);
18615498Stimh 
18625498Stimh 	dmu_objset_fast_stat(os, &zc->zc_objset_stats);
18635498Stimh 
18645498Stimh 	/*
18655498Stimh 	 * NB: nvl_add_zplprop() will read the objset contents,
18666689Smaybee 	 * which we aren't supposed to do with a DS_MODE_USER
18676689Smaybee 	 * hold, because it could be inconsistent.
18685498Stimh 	 */
18695498Stimh 	if (zc->zc_nvlist_dst != NULL &&
18705498Stimh 	    !zc->zc_objset_stats.dds_inconsistent &&
18715498Stimh 	    dmu_objset_type(os) == DMU_OST_ZFS) {
18725498Stimh 		nvlist_t *nv;
18735498Stimh 
18745498Stimh 		VERIFY(nvlist_alloc(&nv, NV_UNIQUE_NAME, KM_SLEEP) == 0);
18755498Stimh 		if ((err = nvl_add_zplprop(os, nv, ZFS_PROP_VERSION)) == 0 &&
18765498Stimh 		    (err = nvl_add_zplprop(os, nv, ZFS_PROP_NORMALIZE)) == 0 &&
18775498Stimh 		    (err = nvl_add_zplprop(os, nv, ZFS_PROP_UTF8ONLY)) == 0 &&
18785498Stimh 		    (err = nvl_add_zplprop(os, nv, ZFS_PROP_CASE)) == 0)
18795498Stimh 			err = put_nvlist(zc, nv);
18805498Stimh 		nvlist_free(nv);
18815498Stimh 	} else {
18825498Stimh 		err = ENOENT;
18835498Stimh 	}
188410298SMatthew.Ahrens@Sun.COM 	dmu_objset_rele(os, FTAG);
18855498Stimh 	return (err);
18865498Stimh }
18875498Stimh 
18889396SMatthew.Ahrens@Sun.COM static boolean_t
dataset_name_hidden(const char * name)18899396SMatthew.Ahrens@Sun.COM dataset_name_hidden(const char *name)
18909396SMatthew.Ahrens@Sun.COM {
18919396SMatthew.Ahrens@Sun.COM 	/*
18929396SMatthew.Ahrens@Sun.COM 	 * Skip over datasets that are not visible in this zone,
18939396SMatthew.Ahrens@Sun.COM 	 * internal datasets (which have a $ in their name), and
18949396SMatthew.Ahrens@Sun.COM 	 * temporary datasets (which have a % in their name).
18959396SMatthew.Ahrens@Sun.COM 	 */
18969396SMatthew.Ahrens@Sun.COM 	if (strchr(name, '$') != NULL)
18979396SMatthew.Ahrens@Sun.COM 		return (B_TRUE);
18989396SMatthew.Ahrens@Sun.COM 	if (strchr(name, '%') != NULL)
18999396SMatthew.Ahrens@Sun.COM 		return (B_TRUE);
19009396SMatthew.Ahrens@Sun.COM 	if (!INGLOBALZONE(curproc) && !zone_dataset_visible(name, NULL))
19019396SMatthew.Ahrens@Sun.COM 		return (B_TRUE);
19029396SMatthew.Ahrens@Sun.COM 	return (B_FALSE);
19039396SMatthew.Ahrens@Sun.COM }
19049396SMatthew.Ahrens@Sun.COM 
19055367Sahrens /*
19065367Sahrens  * inputs:
19075367Sahrens  * zc_name		name of filesystem
19085367Sahrens  * zc_cookie		zap cursor
19095367Sahrens  * zc_nvlist_dst_size	size of buffer for property nvlist
19105367Sahrens  *
19115367Sahrens  * outputs:
19125367Sahrens  * zc_name		name of next filesystem
19139396SMatthew.Ahrens@Sun.COM  * zc_cookie		zap cursor
19145367Sahrens  * zc_objset_stats	stats
19155367Sahrens  * zc_nvlist_dst	property nvlist
19165367Sahrens  * zc_nvlist_dst_size	size of property nvlist
19175367Sahrens  */
1918789Sahrens static int
zfs_ioc_dataset_list_next(zfs_cmd_t * zc)1919789Sahrens zfs_ioc_dataset_list_next(zfs_cmd_t *zc)
1920789Sahrens {
1921885Sahrens 	objset_t *os;
1922789Sahrens 	int error;
1923789Sahrens 	char *p;
192411546SChris.Kirby@sun.com 	size_t orig_len = strlen(zc->zc_name);
192511546SChris.Kirby@sun.com 
192611546SChris.Kirby@sun.com top:
192710298SMatthew.Ahrens@Sun.COM 	if (error = dmu_objset_hold(zc->zc_name, FTAG, &os)) {
1928885Sahrens 		if (error == ENOENT)
1929885Sahrens 			error = ESRCH;
1930885Sahrens 		return (error);
1931789Sahrens 	}
1932789Sahrens 
1933789Sahrens 	p = strrchr(zc->zc_name, '/');
1934789Sahrens 	if (p == NULL || p[1] != '\0')
1935789Sahrens 		(void) strlcat(zc->zc_name, "/", sizeof (zc->zc_name));
1936789Sahrens 	p = zc->zc_name + strlen(zc->zc_name);
1937789Sahrens 
19388697SRichard.Morris@Sun.COM 	/*
19398697SRichard.Morris@Sun.COM 	 * Pre-fetch the datasets.  dmu_objset_prefetch() always returns 0
19408697SRichard.Morris@Sun.COM 	 * but is not declared void because its called by dmu_objset_find().
19418697SRichard.Morris@Sun.COM 	 */
19428415SRichard.Morris@Sun.COM 	if (zc->zc_cookie == 0) {
19438415SRichard.Morris@Sun.COM 		uint64_t cookie = 0;
19448415SRichard.Morris@Sun.COM 		int len = sizeof (zc->zc_name) - (p - zc->zc_name);
19458415SRichard.Morris@Sun.COM 
19468415SRichard.Morris@Sun.COM 		while (dmu_dir_list_next(os, len, p, NULL, &cookie) == 0)
19478697SRichard.Morris@Sun.COM 			(void) dmu_objset_prefetch(p, NULL);
19488415SRichard.Morris@Sun.COM 	}
19498415SRichard.Morris@Sun.COM 
1950789Sahrens 	do {
1951885Sahrens 		error = dmu_dir_list_next(os,
1952885Sahrens 		    sizeof (zc->zc_name) - (p - zc->zc_name), p,
1953885Sahrens 		    NULL, &zc->zc_cookie);
1954789Sahrens 		if (error == ENOENT)
1955789Sahrens 			error = ESRCH;
195610588SEric.Taylor@Sun.COM 	} while (error == 0 && dataset_name_hidden(zc->zc_name) &&
195710588SEric.Taylor@Sun.COM 	    !(zc->zc_iflags & FKIOCTL));
195810298SMatthew.Ahrens@Sun.COM 	dmu_objset_rele(os, FTAG);
1959789Sahrens 
196010588SEric.Taylor@Sun.COM 	/*
196110588SEric.Taylor@Sun.COM 	 * If it's an internal dataset (ie. with a '$' in its name),
196210588SEric.Taylor@Sun.COM 	 * don't try to get stats for it, otherwise we'll return ENOENT.
196310588SEric.Taylor@Sun.COM 	 */
196411546SChris.Kirby@sun.com 	if (error == 0 && strchr(zc->zc_name, '$') == NULL) {
1965885Sahrens 		error = zfs_ioc_objset_stats(zc); /* fill in the stats */
196611546SChris.Kirby@sun.com 		if (error == ENOENT) {
196711546SChris.Kirby@sun.com 			/* We lost a race with destroy, get the next one. */
196811546SChris.Kirby@sun.com 			zc->zc_name[orig_len] = '\0';
196911546SChris.Kirby@sun.com 			goto top;
197011546SChris.Kirby@sun.com 		}
197111546SChris.Kirby@sun.com 	}
1972789Sahrens 	return (error);
1973789Sahrens }
1974789Sahrens 
19755367Sahrens /*
19765367Sahrens  * inputs:
19775367Sahrens  * zc_name		name of filesystem
19785367Sahrens  * zc_cookie		zap cursor
19795367Sahrens  * zc_nvlist_dst_size	size of buffer for property nvlist
19805367Sahrens  *
19815367Sahrens  * outputs:
19825367Sahrens  * zc_name		name of next snapshot
19835367Sahrens  * zc_objset_stats	stats
19845367Sahrens  * zc_nvlist_dst	property nvlist
19855367Sahrens  * zc_nvlist_dst_size	size of property nvlist
19865367Sahrens  */
1987789Sahrens static int
zfs_ioc_snapshot_list_next(zfs_cmd_t * zc)1988789Sahrens zfs_ioc_snapshot_list_next(zfs_cmd_t *zc)
1989789Sahrens {
1990885Sahrens 	objset_t *os;
1991789Sahrens 	int error;
1992789Sahrens 
199311546SChris.Kirby@sun.com top:
199410474SRichard.Morris@Sun.COM 	if (zc->zc_cookie == 0)
199510474SRichard.Morris@Sun.COM 		(void) dmu_objset_find(zc->zc_name, dmu_objset_prefetch,
199610474SRichard.Morris@Sun.COM 		    NULL, DS_FIND_SNAPSHOTS);
199710474SRichard.Morris@Sun.COM 
199810298SMatthew.Ahrens@Sun.COM 	error = dmu_objset_hold(zc->zc_name, FTAG, &os);
19996689Smaybee 	if (error)
20006689Smaybee 		return (error == ENOENT ? ESRCH : error);
2001789Sahrens 
20021003Slling 	/*
20031003Slling 	 * A dataset name of maximum length cannot have any snapshots,
20041003Slling 	 * so exit immediately.
20051003Slling 	 */
20061003Slling 	if (strlcat(zc->zc_name, "@", sizeof (zc->zc_name)) >= MAXNAMELEN) {
200710298SMatthew.Ahrens@Sun.COM 		dmu_objset_rele(os, FTAG);
20081003Slling 		return (ESRCH);
2009789Sahrens 	}
2010789Sahrens 
2011885Sahrens 	error = dmu_snapshot_list_next(os,
2012885Sahrens 	    sizeof (zc->zc_name) - strlen(zc->zc_name),
201312786SChris.Kirby@oracle.com 	    zc->zc_name + strlen(zc->zc_name), &zc->zc_obj, &zc->zc_cookie,
201412786SChris.Kirby@oracle.com 	    NULL);
201512786SChris.Kirby@oracle.com 
201611546SChris.Kirby@sun.com 	if (error == 0) {
201712786SChris.Kirby@oracle.com 		dsl_dataset_t *ds;
201812786SChris.Kirby@oracle.com 		dsl_pool_t *dp = os->os_dsl_dataset->ds_dir->dd_pool;
201912786SChris.Kirby@oracle.com 
202012786SChris.Kirby@oracle.com 		/*
202112786SChris.Kirby@oracle.com 		 * Since we probably don't have a hold on this snapshot,
202212786SChris.Kirby@oracle.com 		 * it's possible that the objsetid could have been destroyed
202312786SChris.Kirby@oracle.com 		 * and reused for a new objset. It's OK if this happens during
202412786SChris.Kirby@oracle.com 		 * a zfs send operation, since the new createtxg will be
202512786SChris.Kirby@oracle.com 		 * beyond the range we're interested in.
202612786SChris.Kirby@oracle.com 		 */
202712786SChris.Kirby@oracle.com 		rw_enter(&dp->dp_config_rwlock, RW_READER);
202812786SChris.Kirby@oracle.com 		error = dsl_dataset_hold_obj(dp, zc->zc_obj, FTAG, &ds);
202912786SChris.Kirby@oracle.com 		rw_exit(&dp->dp_config_rwlock);
203012786SChris.Kirby@oracle.com 		if (error) {
203112786SChris.Kirby@oracle.com 			if (error == ENOENT) {
203212786SChris.Kirby@oracle.com 				/* Racing with destroy, get the next one. */
203312786SChris.Kirby@oracle.com 				*strchr(zc->zc_name, '@') = '\0';
203412786SChris.Kirby@oracle.com 				dmu_objset_rele(os, FTAG);
203512786SChris.Kirby@oracle.com 				goto top;
203612786SChris.Kirby@oracle.com 			}
203712786SChris.Kirby@oracle.com 		} else {
203812786SChris.Kirby@oracle.com 			objset_t *ossnap;
203912786SChris.Kirby@oracle.com 
204012786SChris.Kirby@oracle.com 			error = dmu_objset_from_ds(ds, &ossnap);
204112786SChris.Kirby@oracle.com 			if (error == 0)
204212786SChris.Kirby@oracle.com 				error = zfs_ioc_objset_stats_impl(zc, ossnap);
204312786SChris.Kirby@oracle.com 			dsl_dataset_rele(ds, FTAG);
204411546SChris.Kirby@sun.com 		}
204511546SChris.Kirby@sun.com 	} else if (error == ENOENT) {
20466689Smaybee 		error = ESRCH;
204711546SChris.Kirby@sun.com 	}
2048789Sahrens 
204912786SChris.Kirby@oracle.com 	dmu_objset_rele(os, FTAG);
20505367Sahrens 	/* if we failed, undo the @ that we tacked on to zc_name */
20516689Smaybee 	if (error)
20525367Sahrens 		*strchr(zc->zc_name, '@') = '\0';
2053789Sahrens 	return (error);
2054789Sahrens }
2055789Sahrens 
205611022STom.Erickson@Sun.COM static int
zfs_prop_set_userquota(const char * dsname,nvpair_t * pair)205711022STom.Erickson@Sun.COM zfs_prop_set_userquota(const char *dsname, nvpair_t *pair)
205811022STom.Erickson@Sun.COM {
205911022STom.Erickson@Sun.COM 	const char *propname = nvpair_name(pair);
206011022STom.Erickson@Sun.COM 	uint64_t *valary;
206111022STom.Erickson@Sun.COM 	unsigned int vallen;
206211022STom.Erickson@Sun.COM 	const char *domain;
206311933STim.Haley@Sun.COM 	char *dash;
206411022STom.Erickson@Sun.COM 	zfs_userquota_prop_t type;
206511022STom.Erickson@Sun.COM 	uint64_t rid;
206611022STom.Erickson@Sun.COM 	uint64_t quota;
206711022STom.Erickson@Sun.COM 	zfsvfs_t *zfsvfs;
206811022STom.Erickson@Sun.COM 	int err;
206911022STom.Erickson@Sun.COM 
207011022STom.Erickson@Sun.COM 	if (nvpair_type(pair) == DATA_TYPE_NVLIST) {
207111022STom.Erickson@Sun.COM 		nvlist_t *attrs;
207211022STom.Erickson@Sun.COM 		VERIFY(nvpair_value_nvlist(pair, &attrs) == 0);
207311933STim.Haley@Sun.COM 		if (nvlist_lookup_nvpair(attrs, ZPROP_VALUE,
207411933STim.Haley@Sun.COM 		    &pair) != 0)
207511933STim.Haley@Sun.COM 			return (EINVAL);
207611022STom.Erickson@Sun.COM 	}
207711022STom.Erickson@Sun.COM 
207811933STim.Haley@Sun.COM 	/*
207911933STim.Haley@Sun.COM 	 * A correctly constructed propname is encoded as
208011933STim.Haley@Sun.COM 	 * userquota@<rid>-<domain>.
208111933STim.Haley@Sun.COM 	 */
208211933STim.Haley@Sun.COM 	if ((dash = strchr(propname, '-')) == NULL ||
208311933STim.Haley@Sun.COM 	    nvpair_value_uint64_array(pair, &valary, &vallen) != 0 ||
208411933STim.Haley@Sun.COM 	    vallen != 3)
208511933STim.Haley@Sun.COM 		return (EINVAL);
208611933STim.Haley@Sun.COM 
208711933STim.Haley@Sun.COM 	domain = dash + 1;
208811022STom.Erickson@Sun.COM 	type = valary[0];
208911022STom.Erickson@Sun.COM 	rid = valary[1];
209011022STom.Erickson@Sun.COM 	quota = valary[2];
209111022STom.Erickson@Sun.COM 
209212620SMark.Shellenbaum@Oracle.COM 	err = zfsvfs_hold(dsname, FTAG, &zfsvfs, B_FALSE);
209311022STom.Erickson@Sun.COM 	if (err == 0) {
209411022STom.Erickson@Sun.COM 		err = zfs_set_userquota(zfsvfs, type, domain, rid, quota);
209511022STom.Erickson@Sun.COM 		zfsvfs_rele(zfsvfs, FTAG);
209611022STom.Erickson@Sun.COM 	}
209711022STom.Erickson@Sun.COM 
209811022STom.Erickson@Sun.COM 	return (err);
209911022STom.Erickson@Sun.COM }
210011022STom.Erickson@Sun.COM 
210111022STom.Erickson@Sun.COM /*
210211022STom.Erickson@Sun.COM  * If the named property is one that has a special function to set its value,
210311022STom.Erickson@Sun.COM  * return 0 on success and a positive error code on failure; otherwise if it is
210411022STom.Erickson@Sun.COM  * not one of the special properties handled by this function, return -1.
210511022STom.Erickson@Sun.COM  *
210611933STim.Haley@Sun.COM  * XXX: It would be better for callers of the property interface if we handled
210711022STom.Erickson@Sun.COM  * these special cases in dsl_prop.c (in the dsl layer).
210811022STom.Erickson@Sun.COM  */
210911022STom.Erickson@Sun.COM static int
zfs_prop_set_special(const char * dsname,zprop_source_t source,nvpair_t * pair)211011022STom.Erickson@Sun.COM zfs_prop_set_special(const char *dsname, zprop_source_t source,
211111022STom.Erickson@Sun.COM     nvpair_t *pair)
2112789Sahrens {
211311022STom.Erickson@Sun.COM 	const char *propname = nvpair_name(pair);
211411022STom.Erickson@Sun.COM 	zfs_prop_t prop = zfs_name_to_prop(propname);
211511022STom.Erickson@Sun.COM 	uint64_t intval;
211611022STom.Erickson@Sun.COM 	int err;
211711022STom.Erickson@Sun.COM 
211811022STom.Erickson@Sun.COM 	if (prop == ZPROP_INVAL) {
211911022STom.Erickson@Sun.COM 		if (zfs_prop_userquota(propname))
212011022STom.Erickson@Sun.COM 			return (zfs_prop_set_userquota(dsname, pair));
212111022STom.Erickson@Sun.COM 		return (-1);
212211022STom.Erickson@Sun.COM 	}
212311022STom.Erickson@Sun.COM 
212411022STom.Erickson@Sun.COM 	if (nvpair_type(pair) == DATA_TYPE_NVLIST) {
212511022STom.Erickson@Sun.COM 		nvlist_t *attrs;
212611022STom.Erickson@Sun.COM 		VERIFY(nvpair_value_nvlist(pair, &attrs) == 0);
212711022STom.Erickson@Sun.COM 		VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE,
212811022STom.Erickson@Sun.COM 		    &pair) == 0);
212911022STom.Erickson@Sun.COM 	}
213011022STom.Erickson@Sun.COM 
213111022STom.Erickson@Sun.COM 	if (zfs_prop_get_type(prop) == PROP_TYPE_STRING)
213211022STom.Erickson@Sun.COM 		return (-1);
213311022STom.Erickson@Sun.COM 
213411022STom.Erickson@Sun.COM 	VERIFY(0 == nvpair_value_uint64(pair, &intval));
213511022STom.Erickson@Sun.COM 
213611022STom.Erickson@Sun.COM 	switch (prop) {
213711022STom.Erickson@Sun.COM 	case ZFS_PROP_QUOTA:
213811022STom.Erickson@Sun.COM 		err = dsl_dir_set_quota(dsname, source, intval);
213911022STom.Erickson@Sun.COM 		break;
214011022STom.Erickson@Sun.COM 	case ZFS_PROP_REFQUOTA:
214111022STom.Erickson@Sun.COM 		err = dsl_dataset_set_quota(dsname, source, intval);
214211022STom.Erickson@Sun.COM 		break;
214311022STom.Erickson@Sun.COM 	case ZFS_PROP_RESERVATION:
214411022STom.Erickson@Sun.COM 		err = dsl_dir_set_reservation(dsname, source, intval);
214511022STom.Erickson@Sun.COM 		break;
214611022STom.Erickson@Sun.COM 	case ZFS_PROP_REFRESERVATION:
214711022STom.Erickson@Sun.COM 		err = dsl_dataset_set_reservation(dsname, source, intval);
214811022STom.Erickson@Sun.COM 		break;
214911022STom.Erickson@Sun.COM 	case ZFS_PROP_VOLSIZE:
215011022STom.Erickson@Sun.COM 		err = zvol_set_volsize(dsname, ddi_driver_major(zfs_dip),
215111022STom.Erickson@Sun.COM 		    intval);
215211022STom.Erickson@Sun.COM 		break;
215311022STom.Erickson@Sun.COM 	case ZFS_PROP_VERSION:
215411022STom.Erickson@Sun.COM 	{
215511022STom.Erickson@Sun.COM 		zfsvfs_t *zfsvfs;
215611022STom.Erickson@Sun.COM 
215712620SMark.Shellenbaum@Oracle.COM 		if ((err = zfsvfs_hold(dsname, FTAG, &zfsvfs, B_TRUE)) != 0)
215811022STom.Erickson@Sun.COM 			break;
215911022STom.Erickson@Sun.COM 
216011022STom.Erickson@Sun.COM 		err = zfs_set_version(zfsvfs, intval);
216111022STom.Erickson@Sun.COM 		zfsvfs_rele(zfsvfs, FTAG);
216211022STom.Erickson@Sun.COM 
216311022STom.Erickson@Sun.COM 		if (err == 0 && intval >= ZPL_VERSION_USERSPACE) {
216411147SGeorge.Wilson@Sun.COM 			zfs_cmd_t *zc;
216511147SGeorge.Wilson@Sun.COM 
216611147SGeorge.Wilson@Sun.COM 			zc = kmem_zalloc(sizeof (zfs_cmd_t), KM_SLEEP);
216711147SGeorge.Wilson@Sun.COM 			(void) strcpy(zc->zc_name, dsname);
216811147SGeorge.Wilson@Sun.COM 			(void) zfs_ioc_userspace_upgrade(zc);
216911147SGeorge.Wilson@Sun.COM 			kmem_free(zc, sizeof (zfs_cmd_t));
217011022STom.Erickson@Sun.COM 		}
217111022STom.Erickson@Sun.COM 		break;
217211022STom.Erickson@Sun.COM 	}
217311022STom.Erickson@Sun.COM 
217411022STom.Erickson@Sun.COM 	default:
217511022STom.Erickson@Sun.COM 		err = -1;
217611022STom.Erickson@Sun.COM 	}
217711022STom.Erickson@Sun.COM 
217811022STom.Erickson@Sun.COM 	return (err);
217911022STom.Erickson@Sun.COM }
218011022STom.Erickson@Sun.COM 
218111022STom.Erickson@Sun.COM /*
218211022STom.Erickson@Sun.COM  * This function is best effort. If it fails to set any of the given properties,
218311022STom.Erickson@Sun.COM  * it continues to set as many as it can and returns the first error
218411022STom.Erickson@Sun.COM  * encountered. If the caller provides a non-NULL errlist, it also gives the
218511022STom.Erickson@Sun.COM  * complete list of names of all the properties it failed to set along with the
218611022STom.Erickson@Sun.COM  * corresponding error numbers. The caller is responsible for freeing the
218711022STom.Erickson@Sun.COM  * returned errlist.
218811022STom.Erickson@Sun.COM  *
218911022STom.Erickson@Sun.COM  * If every property is set successfully, zero is returned and the list pointed
219011022STom.Erickson@Sun.COM  * at by errlist is NULL.
219111022STom.Erickson@Sun.COM  */
219211022STom.Erickson@Sun.COM int
zfs_set_prop_nvlist(const char * dsname,zprop_source_t source,nvlist_t * nvl,nvlist_t ** errlist)219311022STom.Erickson@Sun.COM zfs_set_prop_nvlist(const char *dsname, zprop_source_t source, nvlist_t *nvl,
219411022STom.Erickson@Sun.COM     nvlist_t **errlist)
219511022STom.Erickson@Sun.COM {
219611022STom.Erickson@Sun.COM 	nvpair_t *pair;
219711022STom.Erickson@Sun.COM 	nvpair_t *propval;
219811045STom.Erickson@Sun.COM 	int rv = 0;
21992676Seschrock 	uint64_t intval;
22002676Seschrock 	char *strval;
22018697SRichard.Morris@Sun.COM 	nvlist_t *genericnvl;
220211022STom.Erickson@Sun.COM 	nvlist_t *errors;
220311022STom.Erickson@Sun.COM 	nvlist_t *retrynvl;
22044543Smarks 
22058697SRichard.Morris@Sun.COM 	VERIFY(nvlist_alloc(&genericnvl, NV_UNIQUE_NAME, KM_SLEEP) == 0);
220611022STom.Erickson@Sun.COM 	VERIFY(nvlist_alloc(&errors, NV_UNIQUE_NAME, KM_SLEEP) == 0);
220711022STom.Erickson@Sun.COM 	VERIFY(nvlist_alloc(&retrynvl, NV_UNIQUE_NAME, KM_SLEEP) == 0);
220811022STom.Erickson@Sun.COM 
220911022STom.Erickson@Sun.COM retry:
221011022STom.Erickson@Sun.COM 	pair = NULL;
221111022STom.Erickson@Sun.COM 	while ((pair = nvlist_next_nvpair(nvl, pair)) != NULL) {
221211022STom.Erickson@Sun.COM 		const char *propname = nvpair_name(pair);
22134670Sahrens 		zfs_prop_t prop = zfs_name_to_prop(propname);
221411181STom.Erickson@Sun.COM 		int err = 0;
22154543Smarks 
221611022STom.Erickson@Sun.COM 		/* decode the property value */
221711022STom.Erickson@Sun.COM 		propval = pair;
221811022STom.Erickson@Sun.COM 		if (nvpair_type(pair) == DATA_TYPE_NVLIST) {
221911022STom.Erickson@Sun.COM 			nvlist_t *attrs;
222011022STom.Erickson@Sun.COM 			VERIFY(nvpair_value_nvlist(pair, &attrs) == 0);
222111933STim.Haley@Sun.COM 			if (nvlist_lookup_nvpair(attrs, ZPROP_VALUE,
222211933STim.Haley@Sun.COM 			    &propval) != 0)
222311933STim.Haley@Sun.COM 				err = EINVAL;
22244543Smarks 		}
22252676Seschrock 
222611022STom.Erickson@Sun.COM 		/* Validate value type */
222711933STim.Haley@Sun.COM 		if (err == 0 && prop == ZPROP_INVAL) {
222811022STom.Erickson@Sun.COM 			if (zfs_prop_user(propname)) {
222911022STom.Erickson@Sun.COM 				if (nvpair_type(propval) != DATA_TYPE_STRING)
223011022STom.Erickson@Sun.COM 					err = EINVAL;
223111022STom.Erickson@Sun.COM 			} else if (zfs_prop_userquota(propname)) {
223211022STom.Erickson@Sun.COM 				if (nvpair_type(propval) !=
223311022STom.Erickson@Sun.COM 				    DATA_TYPE_UINT64_ARRAY)
223411022STom.Erickson@Sun.COM 					err = EINVAL;
22359396SMatthew.Ahrens@Sun.COM 			}
223611933STim.Haley@Sun.COM 		} else if (err == 0) {
223711022STom.Erickson@Sun.COM 			if (nvpair_type(propval) == DATA_TYPE_STRING) {
223811022STom.Erickson@Sun.COM 				if (zfs_prop_get_type(prop) != PROP_TYPE_STRING)
223911022STom.Erickson@Sun.COM 					err = EINVAL;
224011022STom.Erickson@Sun.COM 			} else if (nvpair_type(propval) == DATA_TYPE_UINT64) {
22412885Sahrens 				const char *unused;
22422885Sahrens 
224311022STom.Erickson@Sun.COM 				VERIFY(nvpair_value_uint64(propval,
224411022STom.Erickson@Sun.COM 				    &intval) == 0);
22452676Seschrock 
22462676Seschrock 				switch (zfs_prop_get_type(prop)) {
22474787Sahrens 				case PROP_TYPE_NUMBER:
22482676Seschrock 					break;
22494787Sahrens 				case PROP_TYPE_STRING:
225011022STom.Erickson@Sun.COM 					err = EINVAL;
225111022STom.Erickson@Sun.COM 					break;
22524787Sahrens 				case PROP_TYPE_INDEX:
22532717Seschrock 					if (zfs_prop_index_to_string(prop,
225411022STom.Erickson@Sun.COM 					    intval, &unused) != 0)
225511022STom.Erickson@Sun.COM 						err = EINVAL;
22562676Seschrock 					break;
22572676Seschrock 				default:
22584577Sahrens 					cmn_err(CE_PANIC,
22594577Sahrens 					    "unknown property type");
22602676Seschrock 				}
22612676Seschrock 			} else {
226211022STom.Erickson@Sun.COM 				err = EINVAL;
226311022STom.Erickson@Sun.COM 			}
226411022STom.Erickson@Sun.COM 		}
226511022STom.Erickson@Sun.COM 
226611022STom.Erickson@Sun.COM 		/* Validate permissions */
226711022STom.Erickson@Sun.COM 		if (err == 0)
226811022STom.Erickson@Sun.COM 			err = zfs_check_settable(dsname, pair, CRED());
226911022STom.Erickson@Sun.COM 
227011022STom.Erickson@Sun.COM 		if (err == 0) {
227111022STom.Erickson@Sun.COM 			err = zfs_prop_set_special(dsname, source, pair);
227211022STom.Erickson@Sun.COM 			if (err == -1) {
227311022STom.Erickson@Sun.COM 				/*
227411022STom.Erickson@Sun.COM 				 * For better performance we build up a list of
227511022STom.Erickson@Sun.COM 				 * properties to set in a single transaction.
227611022STom.Erickson@Sun.COM 				 */
227711022STom.Erickson@Sun.COM 				err = nvlist_add_nvpair(genericnvl, pair);
227811022STom.Erickson@Sun.COM 			} else if (err != 0 && nvl != retrynvl) {
227911022STom.Erickson@Sun.COM 				/*
228011022STom.Erickson@Sun.COM 				 * This may be a spurious error caused by
228111022STom.Erickson@Sun.COM 				 * receiving quota and reservation out of order.
228211022STom.Erickson@Sun.COM 				 * Try again in a second pass.
228311022STom.Erickson@Sun.COM 				 */
228411022STom.Erickson@Sun.COM 				err = nvlist_add_nvpair(retrynvl, pair);
22852676Seschrock 			}
228611022STom.Erickson@Sun.COM 		}
228711022STom.Erickson@Sun.COM 
228811022STom.Erickson@Sun.COM 		if (err != 0)
228911022STom.Erickson@Sun.COM 			VERIFY(nvlist_add_int32(errors, propname, err) == 0);
229011022STom.Erickson@Sun.COM 	}
229111022STom.Erickson@Sun.COM 
229211022STom.Erickson@Sun.COM 	if (nvl != retrynvl && !nvlist_empty(retrynvl)) {
229311022STom.Erickson@Sun.COM 		nvl = retrynvl;
229411022STom.Erickson@Sun.COM 		goto retry;
229511022STom.Erickson@Sun.COM 	}
229611022STom.Erickson@Sun.COM 
229711022STom.Erickson@Sun.COM 	if (!nvlist_empty(genericnvl) &&
229811022STom.Erickson@Sun.COM 	    dsl_props_set(dsname, source, genericnvl) != 0) {
229911022STom.Erickson@Sun.COM 		/*
230011022STom.Erickson@Sun.COM 		 * If this fails, we still want to set as many properties as we
230111022STom.Erickson@Sun.COM 		 * can, so try setting them individually.
230211022STom.Erickson@Sun.COM 		 */
230311022STom.Erickson@Sun.COM 		pair = NULL;
230411022STom.Erickson@Sun.COM 		while ((pair = nvlist_next_nvpair(genericnvl, pair)) != NULL) {
230511022STom.Erickson@Sun.COM 			const char *propname = nvpair_name(pair);
230611181STom.Erickson@Sun.COM 			int err = 0;
230711022STom.Erickson@Sun.COM 
230811022STom.Erickson@Sun.COM 			propval = pair;
230911022STom.Erickson@Sun.COM 			if (nvpair_type(pair) == DATA_TYPE_NVLIST) {
231011022STom.Erickson@Sun.COM 				nvlist_t *attrs;
231111022STom.Erickson@Sun.COM 				VERIFY(nvpair_value_nvlist(pair, &attrs) == 0);
231211022STom.Erickson@Sun.COM 				VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE,
231311022STom.Erickson@Sun.COM 				    &propval) == 0);
231411022STom.Erickson@Sun.COM 			}
231511022STom.Erickson@Sun.COM 
231611022STom.Erickson@Sun.COM 			if (nvpair_type(propval) == DATA_TYPE_STRING) {
231711022STom.Erickson@Sun.COM 				VERIFY(nvpair_value_string(propval,
231811022STom.Erickson@Sun.COM 				    &strval) == 0);
231911022STom.Erickson@Sun.COM 				err = dsl_prop_set(dsname, propname, source, 1,
232011022STom.Erickson@Sun.COM 				    strlen(strval) + 1, strval);
232111022STom.Erickson@Sun.COM 			} else {
232211022STom.Erickson@Sun.COM 				VERIFY(nvpair_value_uint64(propval,
232311022STom.Erickson@Sun.COM 				    &intval) == 0);
232411022STom.Erickson@Sun.COM 				err = dsl_prop_set(dsname, propname, source, 8,
232511022STom.Erickson@Sun.COM 				    1, &intval);
232611022STom.Erickson@Sun.COM 			}
232711022STom.Erickson@Sun.COM 
232811022STom.Erickson@Sun.COM 			if (err != 0) {
232911022STom.Erickson@Sun.COM 				VERIFY(nvlist_add_int32(errors, propname,
233011022STom.Erickson@Sun.COM 				    err) == 0);
233111022STom.Erickson@Sun.COM 			}
23322676Seschrock 		}
23332676Seschrock 	}
233411022STom.Erickson@Sun.COM 	nvlist_free(genericnvl);
233511022STom.Erickson@Sun.COM 	nvlist_free(retrynvl);
233611022STom.Erickson@Sun.COM 
233711022STom.Erickson@Sun.COM 	if ((pair = nvlist_next_nvpair(errors, NULL)) == NULL) {
233811022STom.Erickson@Sun.COM 		nvlist_free(errors);
233911022STom.Erickson@Sun.COM 		errors = NULL;
234011022STom.Erickson@Sun.COM 	} else {
234111022STom.Erickson@Sun.COM 		VERIFY(nvpair_value_int32(pair, &rv) == 0);
23428697SRichard.Morris@Sun.COM 	}
234311022STom.Erickson@Sun.COM 
234411022STom.Erickson@Sun.COM 	if (errlist == NULL)
234511022STom.Erickson@Sun.COM 		nvlist_free(errors);
234611022STom.Erickson@Sun.COM 	else
234711022STom.Erickson@Sun.COM 		*errlist = errors;
234811022STom.Erickson@Sun.COM 
234911022STom.Erickson@Sun.COM 	return (rv);
2350789Sahrens }
2351789Sahrens 
23525367Sahrens /*
23539355SMatthew.Ahrens@Sun.COM  * Check that all the properties are valid user properties.
23549355SMatthew.Ahrens@Sun.COM  */
23559355SMatthew.Ahrens@Sun.COM static int
zfs_check_userprops(char * fsname,nvlist_t * nvl)23569355SMatthew.Ahrens@Sun.COM zfs_check_userprops(char *fsname, nvlist_t *nvl)
23579355SMatthew.Ahrens@Sun.COM {
235811022STom.Erickson@Sun.COM 	nvpair_t *pair = NULL;
23599355SMatthew.Ahrens@Sun.COM 	int error = 0;
23609355SMatthew.Ahrens@Sun.COM 
236111022STom.Erickson@Sun.COM 	while ((pair = nvlist_next_nvpair(nvl, pair)) != NULL) {
236211022STom.Erickson@Sun.COM 		const char *propname = nvpair_name(pair);
23639355SMatthew.Ahrens@Sun.COM 		char *valstr;
23649355SMatthew.Ahrens@Sun.COM 
23659355SMatthew.Ahrens@Sun.COM 		if (!zfs_prop_user(propname) ||
236611022STom.Erickson@Sun.COM 		    nvpair_type(pair) != DATA_TYPE_STRING)
23679355SMatthew.Ahrens@Sun.COM 			return (EINVAL);
23689355SMatthew.Ahrens@Sun.COM 
23699355SMatthew.Ahrens@Sun.COM 		if (error = zfs_secpolicy_write_perms(fsname,
23709355SMatthew.Ahrens@Sun.COM 		    ZFS_DELEG_PERM_USERPROP, CRED()))
23719355SMatthew.Ahrens@Sun.COM 			return (error);
23729355SMatthew.Ahrens@Sun.COM 
23739355SMatthew.Ahrens@Sun.COM 		if (strlen(propname) >= ZAP_MAXNAMELEN)
23749355SMatthew.Ahrens@Sun.COM 			return (ENAMETOOLONG);
23759355SMatthew.Ahrens@Sun.COM 
237611022STom.Erickson@Sun.COM 		VERIFY(nvpair_value_string(pair, &valstr) == 0);
23779355SMatthew.Ahrens@Sun.COM 		if (strlen(valstr) >= ZAP_MAXVALUELEN)
23789355SMatthew.Ahrens@Sun.COM 			return (E2BIG);
23799355SMatthew.Ahrens@Sun.COM 	}
23809355SMatthew.Ahrens@Sun.COM 	return (0);
23819355SMatthew.Ahrens@Sun.COM }
23829355SMatthew.Ahrens@Sun.COM 
238311022STom.Erickson@Sun.COM static void
props_skip(nvlist_t * props,nvlist_t * skipped,nvlist_t ** newprops)238411022STom.Erickson@Sun.COM props_skip(nvlist_t *props, nvlist_t *skipped, nvlist_t **newprops)
238511022STom.Erickson@Sun.COM {
238611022STom.Erickson@Sun.COM 	nvpair_t *pair;
238711022STom.Erickson@Sun.COM 
238811022STom.Erickson@Sun.COM 	VERIFY(nvlist_alloc(newprops, NV_UNIQUE_NAME, KM_SLEEP) == 0);
238911022STom.Erickson@Sun.COM 
239011022STom.Erickson@Sun.COM 	pair = NULL;
239111022STom.Erickson@Sun.COM 	while ((pair = nvlist_next_nvpair(props, pair)) != NULL) {
239211022STom.Erickson@Sun.COM 		if (nvlist_exists(skipped, nvpair_name(pair)))
239311022STom.Erickson@Sun.COM 			continue;
239411022STom.Erickson@Sun.COM 
239511022STom.Erickson@Sun.COM 		VERIFY(nvlist_add_nvpair(*newprops, pair) == 0);
239611022STom.Erickson@Sun.COM 	}
239711022STom.Erickson@Sun.COM }
239811022STom.Erickson@Sun.COM 
239911022STom.Erickson@Sun.COM static int
clear_received_props(objset_t * os,const char * fs,nvlist_t * props,nvlist_t * skipped)240011022STom.Erickson@Sun.COM clear_received_props(objset_t *os, const char *fs, nvlist_t *props,
240111022STom.Erickson@Sun.COM     nvlist_t *skipped)
240211022STom.Erickson@Sun.COM {
240311022STom.Erickson@Sun.COM 	int err = 0;
240411022STom.Erickson@Sun.COM 	nvlist_t *cleared_props = NULL;
240511022STom.Erickson@Sun.COM 	props_skip(props, skipped, &cleared_props);
240611022STom.Erickson@Sun.COM 	if (!nvlist_empty(cleared_props)) {
240711022STom.Erickson@Sun.COM 		/*
240811022STom.Erickson@Sun.COM 		 * Acts on local properties until the dataset has received
240911022STom.Erickson@Sun.COM 		 * properties at least once on or after SPA_VERSION_RECVD_PROPS.
241011022STom.Erickson@Sun.COM 		 */
241111022STom.Erickson@Sun.COM 		zprop_source_t flags = (ZPROP_SRC_NONE |
241211022STom.Erickson@Sun.COM 		    (dsl_prop_get_hasrecvd(os) ? ZPROP_SRC_RECEIVED : 0));
241311022STom.Erickson@Sun.COM 		err = zfs_set_prop_nvlist(fs, flags, cleared_props, NULL);
241411022STom.Erickson@Sun.COM 	}
241511022STom.Erickson@Sun.COM 	nvlist_free(cleared_props);
241611022STom.Erickson@Sun.COM 	return (err);
241711022STom.Erickson@Sun.COM }
241811022STom.Erickson@Sun.COM 
24199355SMatthew.Ahrens@Sun.COM /*
24205367Sahrens  * inputs:
24215367Sahrens  * zc_name		name of filesystem
24228697SRichard.Morris@Sun.COM  * zc_value		name of property to set
24235367Sahrens  * zc_nvlist_src{_size}	nvlist of properties to apply
242411022STom.Erickson@Sun.COM  * zc_cookie		received properties flag
24255367Sahrens  *
242611022STom.Erickson@Sun.COM  * outputs:
242711022STom.Erickson@Sun.COM  * zc_nvlist_dst{_size} error for each unapplied received property
24285367Sahrens  */
2429789Sahrens static int
zfs_ioc_set_prop(zfs_cmd_t * zc)24302676Seschrock zfs_ioc_set_prop(zfs_cmd_t *zc)
2431789Sahrens {
24322676Seschrock 	nvlist_t *nvl;
243311022STom.Erickson@Sun.COM 	boolean_t received = zc->zc_cookie;
243411022STom.Erickson@Sun.COM 	zprop_source_t source = (received ? ZPROP_SRC_RECEIVED :
243511022STom.Erickson@Sun.COM 	    ZPROP_SRC_LOCAL);
243611022STom.Erickson@Sun.COM 	nvlist_t *errors = NULL;
24372676Seschrock 	int error;
2438789Sahrens 
24395094Slling 	if ((error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
24409643SEric.Taylor@Sun.COM 	    zc->zc_iflags, &nvl)) != 0)
24412676Seschrock 		return (error);
24422676Seschrock 
244311022STom.Erickson@Sun.COM 	if (received) {
24447265Sahrens 		nvlist_t *origprops;
24457265Sahrens 		objset_t *os;
24467265Sahrens 
244710298SMatthew.Ahrens@Sun.COM 		if (dmu_objset_hold(zc->zc_name, FTAG, &os) == 0) {
244811022STom.Erickson@Sun.COM 			if (dsl_prop_get_received(os, &origprops) == 0) {
244911022STom.Erickson@Sun.COM 				(void) clear_received_props(os,
245011022STom.Erickson@Sun.COM 				    zc->zc_name, origprops, nvl);
24517265Sahrens 				nvlist_free(origprops);
24527265Sahrens 			}
245311022STom.Erickson@Sun.COM 
245411022STom.Erickson@Sun.COM 			dsl_prop_set_hasrecvd(os);
245510298SMatthew.Ahrens@Sun.COM 			dmu_objset_rele(os, FTAG);
24567265Sahrens 		}
24577265Sahrens 	}
24587265Sahrens 
245911022STom.Erickson@Sun.COM 	error = zfs_set_prop_nvlist(zc->zc_name, source, nvl, &errors);
246011022STom.Erickson@Sun.COM 
246111022STom.Erickson@Sun.COM 	if (zc->zc_nvlist_dst != NULL && errors != NULL) {
246211022STom.Erickson@Sun.COM 		(void) put_nvlist(zc, errors);
246311022STom.Erickson@Sun.COM 	}
246411022STom.Erickson@Sun.COM 
246511022STom.Erickson@Sun.COM 	nvlist_free(errors);
24662676Seschrock 	nvlist_free(nvl);
24672676Seschrock 	return (error);
2468789Sahrens }
2469789Sahrens 
24705367Sahrens /*
24715367Sahrens  * inputs:
24725367Sahrens  * zc_name		name of filesystem
24735367Sahrens  * zc_value		name of property to inherit
247411022STom.Erickson@Sun.COM  * zc_cookie		revert to received value if TRUE
24755367Sahrens  *
24765367Sahrens  * outputs:		none
24775367Sahrens  */
2478789Sahrens static int
zfs_ioc_inherit_prop(zfs_cmd_t * zc)24794849Sahrens zfs_ioc_inherit_prop(zfs_cmd_t *zc)
24804849Sahrens {
248111022STom.Erickson@Sun.COM 	const char *propname = zc->zc_value;
248211022STom.Erickson@Sun.COM 	zfs_prop_t prop = zfs_name_to_prop(propname);
248311022STom.Erickson@Sun.COM 	boolean_t received = zc->zc_cookie;
248411022STom.Erickson@Sun.COM 	zprop_source_t source = (received
248511022STom.Erickson@Sun.COM 	    ? ZPROP_SRC_NONE		/* revert to received value, if any */
248611022STom.Erickson@Sun.COM 	    : ZPROP_SRC_INHERITED);	/* explicitly inherit */
248711022STom.Erickson@Sun.COM 
248811022STom.Erickson@Sun.COM 	if (received) {
248911022STom.Erickson@Sun.COM 		nvlist_t *dummy;
249011022STom.Erickson@Sun.COM 		nvpair_t *pair;
249111022STom.Erickson@Sun.COM 		zprop_type_t type;
249211022STom.Erickson@Sun.COM 		int err;
249311022STom.Erickson@Sun.COM 
249411022STom.Erickson@Sun.COM 		/*
249511022STom.Erickson@Sun.COM 		 * zfs_prop_set_special() expects properties in the form of an
249611022STom.Erickson@Sun.COM 		 * nvpair with type info.
249711022STom.Erickson@Sun.COM 		 */
249811022STom.Erickson@Sun.COM 		if (prop == ZPROP_INVAL) {
249911022STom.Erickson@Sun.COM 			if (!zfs_prop_user(propname))
250011022STom.Erickson@Sun.COM 				return (EINVAL);
250111022STom.Erickson@Sun.COM 
250211022STom.Erickson@Sun.COM 			type = PROP_TYPE_STRING;
250311515STom.Erickson@Sun.COM 		} else if (prop == ZFS_PROP_VOLSIZE ||
250411515STom.Erickson@Sun.COM 		    prop == ZFS_PROP_VERSION) {
250511515STom.Erickson@Sun.COM 			return (EINVAL);
250611022STom.Erickson@Sun.COM 		} else {
250711022STom.Erickson@Sun.COM 			type = zfs_prop_get_type(prop);
250811022STom.Erickson@Sun.COM 		}
250911022STom.Erickson@Sun.COM 
251011022STom.Erickson@Sun.COM 		VERIFY(nvlist_alloc(&dummy, NV_UNIQUE_NAME, KM_SLEEP) == 0);
251111022STom.Erickson@Sun.COM 
251211022STom.Erickson@Sun.COM 		switch (type) {
251311022STom.Erickson@Sun.COM 		case PROP_TYPE_STRING:
251411022STom.Erickson@Sun.COM 			VERIFY(0 == nvlist_add_string(dummy, propname, ""));
251511022STom.Erickson@Sun.COM 			break;
251611022STom.Erickson@Sun.COM 		case PROP_TYPE_NUMBER:
251711022STom.Erickson@Sun.COM 		case PROP_TYPE_INDEX:
251811022STom.Erickson@Sun.COM 			VERIFY(0 == nvlist_add_uint64(dummy, propname, 0));
251911022STom.Erickson@Sun.COM 			break;
252011022STom.Erickson@Sun.COM 		default:
252111022STom.Erickson@Sun.COM 			nvlist_free(dummy);
252211022STom.Erickson@Sun.COM 			return (EINVAL);
252311022STom.Erickson@Sun.COM 		}
252411022STom.Erickson@Sun.COM 
252511022STom.Erickson@Sun.COM 		pair = nvlist_next_nvpair(dummy, NULL);
252611022STom.Erickson@Sun.COM 		err = zfs_prop_set_special(zc->zc_name, source, pair);
252711022STom.Erickson@Sun.COM 		nvlist_free(dummy);
252811022STom.Erickson@Sun.COM 		if (err != -1)
252911022STom.Erickson@Sun.COM 			return (err); /* special property already handled */
253011022STom.Erickson@Sun.COM 	} else {
253111022STom.Erickson@Sun.COM 		/*
253211022STom.Erickson@Sun.COM 		 * Only check this in the non-received case. We want to allow
253311022STom.Erickson@Sun.COM 		 * 'inherit -S' to revert non-inheritable properties like quota
253411022STom.Erickson@Sun.COM 		 * and reservation to the received or default values even though
253511022STom.Erickson@Sun.COM 		 * they are not considered inheritable.
253611022STom.Erickson@Sun.COM 		 */
253711022STom.Erickson@Sun.COM 		if (prop != ZPROP_INVAL && !zfs_prop_inheritable(prop))
253811022STom.Erickson@Sun.COM 			return (EINVAL);
253911022STom.Erickson@Sun.COM 	}
254011022STom.Erickson@Sun.COM 
25414849Sahrens 	/* the property name has been validated by zfs_secpolicy_inherit() */
254211022STom.Erickson@Sun.COM 	return (dsl_prop_set(zc->zc_name, zc->zc_value, source, 0, 0, NULL));
25434849Sahrens }
25444849Sahrens 
25454849Sahrens static int
zfs_ioc_pool_set_props(zfs_cmd_t * zc)25464098Slling zfs_ioc_pool_set_props(zfs_cmd_t *zc)
25473912Slling {
25485094Slling 	nvlist_t *props;
25493912Slling 	spa_t *spa;
25505094Slling 	int error;
255111022STom.Erickson@Sun.COM 	nvpair_t *pair;
255211022STom.Erickson@Sun.COM 
255311022STom.Erickson@Sun.COM 	if (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
255411022STom.Erickson@Sun.COM 	    zc->zc_iflags, &props))
25553912Slling 		return (error);
25563912Slling 
25578525SEric.Schrock@Sun.COM 	/*
25588525SEric.Schrock@Sun.COM 	 * If the only property is the configfile, then just do a spa_lookup()
25598525SEric.Schrock@Sun.COM 	 * to handle the faulted case.
25608525SEric.Schrock@Sun.COM 	 */
256111022STom.Erickson@Sun.COM 	pair = nvlist_next_nvpair(props, NULL);
256211022STom.Erickson@Sun.COM 	if (pair != NULL && strcmp(nvpair_name(pair),
25638525SEric.Schrock@Sun.COM 	    zpool_prop_to_name(ZPOOL_PROP_CACHEFILE)) == 0 &&
256411022STom.Erickson@Sun.COM 	    nvlist_next_nvpair(props, pair) == NULL) {
25658525SEric.Schrock@Sun.COM 		mutex_enter(&spa_namespace_lock);
25668525SEric.Schrock@Sun.COM 		if ((spa = spa_lookup(zc->zc_name)) != NULL) {
25678525SEric.Schrock@Sun.COM 			spa_configfile_set(spa, props, B_FALSE);
25688525SEric.Schrock@Sun.COM 			spa_config_sync(spa, B_FALSE, B_TRUE);
25698525SEric.Schrock@Sun.COM 		}
25708525SEric.Schrock@Sun.COM 		mutex_exit(&spa_namespace_lock);
257110672SEric.Schrock@Sun.COM 		if (spa != NULL) {
257210672SEric.Schrock@Sun.COM 			nvlist_free(props);
25738525SEric.Schrock@Sun.COM 			return (0);
257410672SEric.Schrock@Sun.COM 		}
25758525SEric.Schrock@Sun.COM 	}
25768525SEric.Schrock@Sun.COM 
25773912Slling 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) {
25785094Slling 		nvlist_free(props);
25793912Slling 		return (error);
25803912Slling 	}
25813912Slling 
25825094Slling 	error = spa_prop_set(spa, props);
25833912Slling 
25845094Slling 	nvlist_free(props);
25853912Slling 	spa_close(spa, FTAG);
25863912Slling 
25873912Slling 	return (error);
25883912Slling }
25893912Slling 
25903912Slling static int
zfs_ioc_pool_get_props(zfs_cmd_t * zc)25914098Slling zfs_ioc_pool_get_props(zfs_cmd_t *zc)
25923912Slling {
25933912Slling 	spa_t *spa;
25943912Slling 	int error;
25953912Slling 	nvlist_t *nvp = NULL;
25963912Slling 
25978525SEric.Schrock@Sun.COM 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) {
25988525SEric.Schrock@Sun.COM 		/*
25998525SEric.Schrock@Sun.COM 		 * If the pool is faulted, there may be properties we can still
26008525SEric.Schrock@Sun.COM 		 * get (such as altroot and cachefile), so attempt to get them
26018525SEric.Schrock@Sun.COM 		 * anyway.
26028525SEric.Schrock@Sun.COM 		 */
26038525SEric.Schrock@Sun.COM 		mutex_enter(&spa_namespace_lock);
26048525SEric.Schrock@Sun.COM 		if ((spa = spa_lookup(zc->zc_name)) != NULL)
26058525SEric.Schrock@Sun.COM 			error = spa_prop_get(spa, &nvp);
26068525SEric.Schrock@Sun.COM 		mutex_exit(&spa_namespace_lock);
26078525SEric.Schrock@Sun.COM 	} else {
26088525SEric.Schrock@Sun.COM 		error = spa_prop_get(spa, &nvp);
26098525SEric.Schrock@Sun.COM 		spa_close(spa, FTAG);
26108525SEric.Schrock@Sun.COM 	}
26113912Slling 
26123912Slling 	if (error == 0 && zc->zc_nvlist_dst != NULL)
26133912Slling 		error = put_nvlist(zc, nvp);
26143912Slling 	else
26153912Slling 		error = EFAULT;
26163912Slling 
26178525SEric.Schrock@Sun.COM 	nvlist_free(nvp);
26183912Slling 	return (error);
26193912Slling }
26203912Slling 
26215367Sahrens /*
26225367Sahrens  * inputs:
26235367Sahrens  * zc_name		name of filesystem
26245367Sahrens  * zc_nvlist_src{_size}	nvlist of delegated permissions
26255367Sahrens  * zc_perm_action	allow/unallow flag
26265367Sahrens  *
26275367Sahrens  * outputs:		none
26285367Sahrens  */
26294543Smarks static int
zfs_ioc_set_fsacl(zfs_cmd_t * zc)26304543Smarks zfs_ioc_set_fsacl(zfs_cmd_t *zc)
26314543Smarks {
26324543Smarks 	int error;
26334543Smarks 	nvlist_t *fsaclnv = NULL;
26344543Smarks 
26355094Slling 	if ((error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
26369643SEric.Taylor@Sun.COM 	    zc->zc_iflags, &fsaclnv)) != 0)
26374543Smarks 		return (error);
26384543Smarks 
26394543Smarks 	/*
26404543Smarks 	 * Verify nvlist is constructed correctly
26414543Smarks 	 */
26424543Smarks 	if ((error = zfs_deleg_verify_nvlist(fsaclnv)) != 0) {
26434543Smarks 		nvlist_free(fsaclnv);
26444543Smarks 		return (EINVAL);
26454543Smarks 	}
26464543Smarks 
26474543Smarks 	/*
26484543Smarks 	 * If we don't have PRIV_SYS_MOUNT, then validate
26494543Smarks 	 * that user is allowed to hand out each permission in
26504543Smarks 	 * the nvlist(s)
26514543Smarks 	 */
26524543Smarks 
26534787Sahrens 	error = secpolicy_zfs(CRED());
26544543Smarks 	if (error) {
26554787Sahrens 		if (zc->zc_perm_action == B_FALSE) {
26564787Sahrens 			error = dsl_deleg_can_allow(zc->zc_name,
26574787Sahrens 			    fsaclnv, CRED());
26584787Sahrens 		} else {
26594787Sahrens 			error = dsl_deleg_can_unallow(zc->zc_name,
26604787Sahrens 			    fsaclnv, CRED());
26614787Sahrens 		}
26624543Smarks 	}
26634543Smarks 
26644543Smarks 	if (error == 0)
26654543Smarks 		error = dsl_deleg_set(zc->zc_name, fsaclnv, zc->zc_perm_action);
26664543Smarks 
26674543Smarks 	nvlist_free(fsaclnv);
26684543Smarks 	return (error);
26694543Smarks }
26704543Smarks 
26715367Sahrens /*
26725367Sahrens  * inputs:
26735367Sahrens  * zc_name		name of filesystem
26745367Sahrens  *
26755367Sahrens  * outputs:
26765367Sahrens  * zc_nvlist_src{_size}	nvlist of delegated permissions
26775367Sahrens  */
26784543Smarks static int
zfs_ioc_get_fsacl(zfs_cmd_t * zc)26794543Smarks zfs_ioc_get_fsacl(zfs_cmd_t *zc)
26804543Smarks {
26814543Smarks 	nvlist_t *nvp;
26824543Smarks 	int error;
26834543Smarks 
26844543Smarks 	if ((error = dsl_deleg_get(zc->zc_name, &nvp)) == 0) {
26854543Smarks 		error = put_nvlist(zc, nvp);
26864543Smarks 		nvlist_free(nvp);
26874543Smarks 	}
26884543Smarks 
26894543Smarks 	return (error);
26904543Smarks }
26914543Smarks 
26925367Sahrens /*
2693789Sahrens  * Search the vfs list for a specified resource.  Returns a pointer to it
2694789Sahrens  * or NULL if no suitable entry is found. The caller of this routine
2695789Sahrens  * is responsible for releasing the returned vfs pointer.
2696789Sahrens  */
2697789Sahrens static vfs_t *
zfs_get_vfs(const char * resource)2698789Sahrens zfs_get_vfs(const char *resource)
2699789Sahrens {
2700789Sahrens 	struct vfs *vfsp;
2701789Sahrens 	struct vfs *vfs_found = NULL;
2702789Sahrens 
2703789Sahrens 	vfs_list_read_lock();
2704789Sahrens 	vfsp = rootvfs;
2705789Sahrens 	do {
2706789Sahrens 		if (strcmp(refstr_value(vfsp->vfs_resource), resource) == 0) {
2707789Sahrens 			VFS_HOLD(vfsp);
2708789Sahrens 			vfs_found = vfsp;
2709789Sahrens 			break;
2710789Sahrens 		}
2711789Sahrens 		vfsp = vfsp->vfs_next;
2712789Sahrens 	} while (vfsp != rootvfs);
2713789Sahrens 	vfs_list_unlock();
2714789Sahrens 	return (vfs_found);
2715789Sahrens }
2716789Sahrens 
27174543Smarks /* ARGSUSED */
2718789Sahrens static void
zfs_create_cb(objset_t * os,void * arg,cred_t * cr,dmu_tx_t * tx)27194543Smarks zfs_create_cb(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx)
2720789Sahrens {
27215331Samw 	zfs_creat_t *zct = arg;
27225498Stimh 
27235498Stimh 	zfs_create_fs(os, cr, zct->zct_zplprops, tx);
27245331Samw }
27255331Samw 
27265498Stimh #define	ZFS_PROP_UNDEFINED	((uint64_t)-1)
27275498Stimh 
27285331Samw /*
27295498Stimh  * inputs:
27307184Stimh  * createprops		list of properties requested by creator
27317184Stimh  * default_zplver	zpl version to use if unspecified in createprops
27327184Stimh  * fuids_ok		fuids allowed in this version of the spa?
27337184Stimh  * os			parent objset pointer (NULL if root fs)
27345331Samw  *
27355498Stimh  * outputs:
27365498Stimh  * zplprops	values for the zplprops we attach to the master node object
27377184Stimh  * is_ci	true if requested file system will be purely case-insensitive
27385331Samw  *
27395498Stimh  * Determine the settings for utf8only, normalization and
27405498Stimh  * casesensitivity.  Specific values may have been requested by the
27415498Stimh  * creator and/or we can inherit values from the parent dataset.  If
27425498Stimh  * the file system is of too early a vintage, a creator can not
27435498Stimh  * request settings for these properties, even if the requested
27445498Stimh  * setting is the default value.  We don't actually want to create dsl
27455498Stimh  * properties for these, so remove them from the source nvlist after
27465498Stimh  * processing.
27475331Samw  */
27485331Samw static int
zfs_fill_zplprops_impl(objset_t * os,uint64_t zplver,boolean_t fuids_ok,boolean_t sa_ok,nvlist_t * createprops,nvlist_t * zplprops,boolean_t * is_ci)27499396SMatthew.Ahrens@Sun.COM zfs_fill_zplprops_impl(objset_t *os, uint64_t zplver,
275011935SMark.Shellenbaum@Sun.COM     boolean_t fuids_ok, boolean_t sa_ok, nvlist_t *createprops,
275111935SMark.Shellenbaum@Sun.COM     nvlist_t *zplprops, boolean_t *is_ci)
27525331Samw {
27535498Stimh 	uint64_t sense = ZFS_PROP_UNDEFINED;
27545498Stimh 	uint64_t norm = ZFS_PROP_UNDEFINED;
27555498Stimh 	uint64_t u8 = ZFS_PROP_UNDEFINED;
27565498Stimh 
27575498Stimh 	ASSERT(zplprops != NULL);
27585498Stimh 
27595375Stimh 	/*
27605498Stimh 	 * Pull out creator prop choices, if any.
27615375Stimh 	 */
27625498Stimh 	if (createprops) {
27635498Stimh 		(void) nvlist_lookup_uint64(createprops,
27647184Stimh 		    zfs_prop_to_name(ZFS_PROP_VERSION), &zplver);
27657184Stimh 		(void) nvlist_lookup_uint64(createprops,
27665498Stimh 		    zfs_prop_to_name(ZFS_PROP_NORMALIZE), &norm);
27675498Stimh 		(void) nvlist_remove_all(createprops,
27685498Stimh 		    zfs_prop_to_name(ZFS_PROP_NORMALIZE));
27695498Stimh 		(void) nvlist_lookup_uint64(createprops,
27705498Stimh 		    zfs_prop_to_name(ZFS_PROP_UTF8ONLY), &u8);
27715498Stimh 		(void) nvlist_remove_all(createprops,
27725498Stimh 		    zfs_prop_to_name(ZFS_PROP_UTF8ONLY));
27735498Stimh 		(void) nvlist_lookup_uint64(createprops,
27745498Stimh 		    zfs_prop_to_name(ZFS_PROP_CASE), &sense);
27755498Stimh 		(void) nvlist_remove_all(createprops,
27765498Stimh 		    zfs_prop_to_name(ZFS_PROP_CASE));
27775331Samw 	}
27785331Samw 
27795375Stimh 	/*
27807184Stimh 	 * If the zpl version requested is whacky or the file system
27817184Stimh 	 * or pool is version is too "young" to support normalization
27827184Stimh 	 * and the creator tried to set a value for one of the props,
27837184Stimh 	 * error out.
27845498Stimh 	 */
27857184Stimh 	if ((zplver < ZPL_VERSION_INITIAL || zplver > ZPL_VERSION) ||
27867184Stimh 	    (zplver >= ZPL_VERSION_FUID && !fuids_ok) ||
278711935SMark.Shellenbaum@Sun.COM 	    (zplver >= ZPL_VERSION_SA && !sa_ok) ||
27887184Stimh 	    (zplver < ZPL_VERSION_NORMALIZATION &&
27895498Stimh 	    (norm != ZFS_PROP_UNDEFINED || u8 != ZFS_PROP_UNDEFINED ||
27907184Stimh 	    sense != ZFS_PROP_UNDEFINED)))
27915498Stimh 		return (ENOTSUP);
27925498Stimh 
27935498Stimh 	/*
27945498Stimh 	 * Put the version in the zplprops
27955498Stimh 	 */
27965498Stimh 	VERIFY(nvlist_add_uint64(zplprops,
27975498Stimh 	    zfs_prop_to_name(ZFS_PROP_VERSION), zplver) == 0);
27985498Stimh 
27995498Stimh 	if (norm == ZFS_PROP_UNDEFINED)
28005498Stimh 		VERIFY(zfs_get_zplprop(os, ZFS_PROP_NORMALIZE, &norm) == 0);
28015498Stimh 	VERIFY(nvlist_add_uint64(zplprops,
28025498Stimh 	    zfs_prop_to_name(ZFS_PROP_NORMALIZE), norm) == 0);
28035498Stimh 
28045498Stimh 	/*
28055498Stimh 	 * If we're normalizing, names must always be valid UTF-8 strings.
28065498Stimh 	 */
28075498Stimh 	if (norm)
28085498Stimh 		u8 = 1;
28095498Stimh 	if (u8 == ZFS_PROP_UNDEFINED)
28105498Stimh 		VERIFY(zfs_get_zplprop(os, ZFS_PROP_UTF8ONLY, &u8) == 0);
28115498Stimh 	VERIFY(nvlist_add_uint64(zplprops,
28125498Stimh 	    zfs_prop_to_name(ZFS_PROP_UTF8ONLY), u8) == 0);
28135498Stimh 
28145498Stimh 	if (sense == ZFS_PROP_UNDEFINED)
28155498Stimh 		VERIFY(zfs_get_zplprop(os, ZFS_PROP_CASE, &sense) == 0);
28165498Stimh 	VERIFY(nvlist_add_uint64(zplprops,
28175498Stimh 	    zfs_prop_to_name(ZFS_PROP_CASE), sense) == 0);
28185498Stimh 
28196492Stimh 	if (is_ci)
28206492Stimh 		*is_ci = (sense == ZFS_CASE_INSENSITIVE);
28216492Stimh 
28227184Stimh 	return (0);
28237184Stimh }
28247184Stimh 
28257184Stimh static int
zfs_fill_zplprops(const char * dataset,nvlist_t * createprops,nvlist_t * zplprops,boolean_t * is_ci)28267184Stimh zfs_fill_zplprops(const char *dataset, nvlist_t *createprops,
28277184Stimh     nvlist_t *zplprops, boolean_t *is_ci)
28287184Stimh {
282911935SMark.Shellenbaum@Sun.COM 	boolean_t fuids_ok, sa_ok;
28307184Stimh 	uint64_t zplver = ZPL_VERSION;
28317184Stimh 	objset_t *os = NULL;
28327184Stimh 	char parentname[MAXNAMELEN];
28337184Stimh 	char *cp;
283411935SMark.Shellenbaum@Sun.COM 	spa_t *spa;
283511935SMark.Shellenbaum@Sun.COM 	uint64_t spa_vers;
28367184Stimh 	int error;
28377184Stimh 
28387184Stimh 	(void) strlcpy(parentname, dataset, sizeof (parentname));
28397184Stimh 	cp = strrchr(parentname, '/');
28407184Stimh 	ASSERT(cp != NULL);
28417184Stimh 	cp[0] = '\0';
28427184Stimh 
284311935SMark.Shellenbaum@Sun.COM 	if ((error = spa_open(dataset, &spa, FTAG)) != 0)
284411935SMark.Shellenbaum@Sun.COM 		return (error);
284511935SMark.Shellenbaum@Sun.COM 
284611935SMark.Shellenbaum@Sun.COM 	spa_vers = spa_version(spa);
284711935SMark.Shellenbaum@Sun.COM 	spa_close(spa, FTAG);
284811935SMark.Shellenbaum@Sun.COM 
284911935SMark.Shellenbaum@Sun.COM 	zplver = zfs_zpl_version_map(spa_vers);
285011935SMark.Shellenbaum@Sun.COM 	fuids_ok = (zplver >= ZPL_VERSION_FUID);
285111935SMark.Shellenbaum@Sun.COM 	sa_ok = (zplver >= ZPL_VERSION_SA);
28527184Stimh 
28537184Stimh 	/*
28547184Stimh 	 * Open parent object set so we can inherit zplprop values.
28557184Stimh 	 */
285610298SMatthew.Ahrens@Sun.COM 	if ((error = dmu_objset_hold(parentname, FTAG, &os)) != 0)
28577184Stimh 		return (error);
28587184Stimh 
285911935SMark.Shellenbaum@Sun.COM 	error = zfs_fill_zplprops_impl(os, zplver, fuids_ok, sa_ok, createprops,
28607184Stimh 	    zplprops, is_ci);
286110298SMatthew.Ahrens@Sun.COM 	dmu_objset_rele(os, FTAG);
28627184Stimh 	return (error);
28637184Stimh }
28647184Stimh 
28657184Stimh static int
zfs_fill_zplprops_root(uint64_t spa_vers,nvlist_t * createprops,nvlist_t * zplprops,boolean_t * is_ci)28667184Stimh zfs_fill_zplprops_root(uint64_t spa_vers, nvlist_t *createprops,
28677184Stimh     nvlist_t *zplprops, boolean_t *is_ci)
28687184Stimh {
286911935SMark.Shellenbaum@Sun.COM 	boolean_t fuids_ok;
287011935SMark.Shellenbaum@Sun.COM 	boolean_t sa_ok;
28717184Stimh 	uint64_t zplver = ZPL_VERSION;
28727184Stimh 	int error;
28737184Stimh 
287411935SMark.Shellenbaum@Sun.COM 	zplver = zfs_zpl_version_map(spa_vers);
287511935SMark.Shellenbaum@Sun.COM 	fuids_ok = (zplver >= ZPL_VERSION_FUID);
287611935SMark.Shellenbaum@Sun.COM 	sa_ok = (zplver >= ZPL_VERSION_SA);
287711935SMark.Shellenbaum@Sun.COM 
287811935SMark.Shellenbaum@Sun.COM 	error = zfs_fill_zplprops_impl(NULL, zplver, fuids_ok, sa_ok,
287911935SMark.Shellenbaum@Sun.COM 	    createprops, zplprops, is_ci);
28807184Stimh 	return (error);
2881789Sahrens }
2882789Sahrens 
28835367Sahrens /*
28845367Sahrens  * inputs:
28855367Sahrens  * zc_objset_type	type of objset to create (fs vs zvol)
28865367Sahrens  * zc_name		name of new objset
28875367Sahrens  * zc_value		name of snapshot to clone from (may be empty)
28885367Sahrens  * zc_nvlist_src{_size}	nvlist of properties to apply
28895367Sahrens  *
28905498Stimh  * outputs: none
28915367Sahrens  */
2892789Sahrens static int
zfs_ioc_create(zfs_cmd_t * zc)2893789Sahrens zfs_ioc_create(zfs_cmd_t *zc)
2894789Sahrens {
2895789Sahrens 	objset_t *clone;
2896789Sahrens 	int error = 0;
28975331Samw 	zfs_creat_t zct;
28984543Smarks 	nvlist_t *nvprops = NULL;
28994543Smarks 	void (*cbfunc)(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx);
2900789Sahrens 	dmu_objset_type_t type = zc->zc_objset_type;
2901789Sahrens 
2902789Sahrens 	switch (type) {
2903789Sahrens 
2904789Sahrens 	case DMU_OST_ZFS:
2905789Sahrens 		cbfunc = zfs_create_cb;
2906789Sahrens 		break;
2907789Sahrens 
2908789Sahrens 	case DMU_OST_ZVOL:
2909789Sahrens 		cbfunc = zvol_create_cb;
2910789Sahrens 		break;
2911789Sahrens 
2912789Sahrens 	default:
29132199Sahrens 		cbfunc = NULL;
29146423Sgw25295 		break;
29152199Sahrens 	}
29165326Sek110237 	if (strchr(zc->zc_name, '@') ||
29175326Sek110237 	    strchr(zc->zc_name, '%'))
2918789Sahrens 		return (EINVAL);
2919789Sahrens 
29202676Seschrock 	if (zc->zc_nvlist_src != NULL &&
29215094Slling 	    (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
29229643SEric.Taylor@Sun.COM 	    zc->zc_iflags, &nvprops)) != 0)
29232676Seschrock 		return (error);
29242676Seschrock 
29255498Stimh 	zct.zct_zplprops = NULL;
29265331Samw 	zct.zct_props = nvprops;
29275331Samw 
29282676Seschrock 	if (zc->zc_value[0] != '\0') {
2929789Sahrens 		/*
2930789Sahrens 		 * We're creating a clone of an existing snapshot.
2931789Sahrens 		 */
29322676Seschrock 		zc->zc_value[sizeof (zc->zc_value) - 1] = '\0';
29332676Seschrock 		if (dataset_namecheck(zc->zc_value, NULL, NULL) != 0) {
29344543Smarks 			nvlist_free(nvprops);
2935789Sahrens 			return (EINVAL);
29362676Seschrock 		}
2937789Sahrens 
293810298SMatthew.Ahrens@Sun.COM 		error = dmu_objset_hold(zc->zc_value, FTAG, &clone);
29392676Seschrock 		if (error) {
29404543Smarks 			nvlist_free(nvprops);
2941789Sahrens 			return (error);
29422676Seschrock 		}
29436492Stimh 
294410272SMatthew.Ahrens@Sun.COM 		error = dmu_objset_clone(zc->zc_name, dmu_objset_ds(clone), 0);
294510298SMatthew.Ahrens@Sun.COM 		dmu_objset_rele(clone, FTAG);
29465331Samw 		if (error) {
29475331Samw 			nvlist_free(nvprops);
29485331Samw 			return (error);
29495331Samw 		}
2950789Sahrens 	} else {
29516492Stimh 		boolean_t is_insensitive = B_FALSE;
29526492Stimh 
29532676Seschrock 		if (cbfunc == NULL) {
29544543Smarks 			nvlist_free(nvprops);
29552199Sahrens 			return (EINVAL);
29562676Seschrock 		}
29572676Seschrock 
2958789Sahrens 		if (type == DMU_OST_ZVOL) {
29592676Seschrock 			uint64_t volsize, volblocksize;
29602676Seschrock 
29614543Smarks 			if (nvprops == NULL ||
29624543Smarks 			    nvlist_lookup_uint64(nvprops,
29632676Seschrock 			    zfs_prop_to_name(ZFS_PROP_VOLSIZE),
29642676Seschrock 			    &volsize) != 0) {
29654543Smarks 				nvlist_free(nvprops);
29662676Seschrock 				return (EINVAL);
29672676Seschrock 			}
29682676Seschrock 
29694543Smarks 			if ((error = nvlist_lookup_uint64(nvprops,
29702676Seschrock 			    zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE),
29712676Seschrock 			    &volblocksize)) != 0 && error != ENOENT) {
29724543Smarks 				nvlist_free(nvprops);
29732676Seschrock 				return (EINVAL);
29742676Seschrock 			}
29751133Seschrock 
29762676Seschrock 			if (error != 0)
29772676Seschrock 				volblocksize = zfs_prop_default_numeric(
29782676Seschrock 				    ZFS_PROP_VOLBLOCKSIZE);
29792676Seschrock 
29802676Seschrock 			if ((error = zvol_check_volblocksize(
29812676Seschrock 			    volblocksize)) != 0 ||
29822676Seschrock 			    (error = zvol_check_volsize(volsize,
29832676Seschrock 			    volblocksize)) != 0) {
29844543Smarks 				nvlist_free(nvprops);
2985789Sahrens 				return (error);
29862676Seschrock 			}
29874577Sahrens 		} else if (type == DMU_OST_ZFS) {
29885331Samw 			int error;
29895331Samw 
29905498Stimh 			/*
29915331Samw 			 * We have to have normalization and
29925331Samw 			 * case-folding flags correct when we do the
29935331Samw 			 * file system creation, so go figure them out
29945498Stimh 			 * now.
29955331Samw 			 */
29965498Stimh 			VERIFY(nvlist_alloc(&zct.zct_zplprops,
29975498Stimh 			    NV_UNIQUE_NAME, KM_SLEEP) == 0);
29985498Stimh 			error = zfs_fill_zplprops(zc->zc_name, nvprops,
29997184Stimh 			    zct.zct_zplprops, &is_insensitive);
30005331Samw 			if (error != 0) {
30015331Samw 				nvlist_free(nvprops);
30025498Stimh 				nvlist_free(zct.zct_zplprops);
30035331Samw 				return (error);
30044577Sahrens 			}
30052676Seschrock 		}
300610272SMatthew.Ahrens@Sun.COM 		error = dmu_objset_create(zc->zc_name, type,
30076492Stimh 		    is_insensitive ? DS_FLAG_CI_DATASET : 0, cbfunc, &zct);
30085498Stimh 		nvlist_free(zct.zct_zplprops);
3009789Sahrens 	}
30102676Seschrock 
30112676Seschrock 	/*
30122676Seschrock 	 * It would be nice to do this atomically.
30132676Seschrock 	 */
30142676Seschrock 	if (error == 0) {
301511022STom.Erickson@Sun.COM 		error = zfs_set_prop_nvlist(zc->zc_name, ZPROP_SRC_LOCAL,
301611022STom.Erickson@Sun.COM 		    nvprops, NULL);
301711022STom.Erickson@Sun.COM 		if (error != 0)
301810242Schris.kirby@sun.com 			(void) dmu_objset_destroy(zc->zc_name, B_FALSE);
30192676Seschrock 	}
30204543Smarks 	nvlist_free(nvprops);
3021789Sahrens 	return (error);
3022789Sahrens }
3023789Sahrens 
30245367Sahrens /*
30255367Sahrens  * inputs:
30265367Sahrens  * zc_name	name of filesystem
30275367Sahrens  * zc_value	short name of snapshot
30285367Sahrens  * zc_cookie	recursive flag
30299396SMatthew.Ahrens@Sun.COM  * zc_nvlist_src[_size] property list
30305367Sahrens  *
303110588SEric.Taylor@Sun.COM  * outputs:
303210588SEric.Taylor@Sun.COM  * zc_value	short snapname (i.e. part after the '@')
30335367Sahrens  */
3034789Sahrens static int
zfs_ioc_snapshot(zfs_cmd_t * zc)30352199Sahrens zfs_ioc_snapshot(zfs_cmd_t *zc)
30362199Sahrens {
30377265Sahrens 	nvlist_t *nvprops = NULL;
30387265Sahrens 	int error;
30397265Sahrens 	boolean_t recursive = zc->zc_cookie;
30407265Sahrens 
30412676Seschrock 	if (snapshot_namecheck(zc->zc_value, NULL, NULL) != 0)
30422199Sahrens 		return (EINVAL);
30437265Sahrens 
30447265Sahrens 	if (zc->zc_nvlist_src != NULL &&
30457265Sahrens 	    (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
30469643SEric.Taylor@Sun.COM 	    zc->zc_iflags, &nvprops)) != 0)
30477265Sahrens 		return (error);
30487265Sahrens 
30499355SMatthew.Ahrens@Sun.COM 	error = zfs_check_userprops(zc->zc_name, nvprops);
30509355SMatthew.Ahrens@Sun.COM 	if (error)
30519355SMatthew.Ahrens@Sun.COM 		goto out;
30529355SMatthew.Ahrens@Sun.COM 
305311022STom.Erickson@Sun.COM 	if (!nvlist_empty(nvprops) &&
30549355SMatthew.Ahrens@Sun.COM 	    zfs_earlier_version(zc->zc_name, SPA_VERSION_SNAP_PROPS)) {
30559355SMatthew.Ahrens@Sun.COM 		error = ENOTSUP;
30569355SMatthew.Ahrens@Sun.COM 		goto out;
30577265Sahrens 	}
30589355SMatthew.Ahrens@Sun.COM 
305913043STim.Haley@Sun.COM 	error = dmu_objset_snapshot(zc->zc_name, zc->zc_value, NULL,
306013043STim.Haley@Sun.COM 	    nvprops, recursive, B_FALSE, -1);
30619355SMatthew.Ahrens@Sun.COM 
30629355SMatthew.Ahrens@Sun.COM out:
30637265Sahrens 	nvlist_free(nvprops);
30647265Sahrens 	return (error);
30652199Sahrens }
30662199Sahrens 
30674007Smmusante int
zfs_unmount_snap(const char * name,void * arg)306811209SMatthew.Ahrens@Sun.COM zfs_unmount_snap(const char *name, void *arg)
3069789Sahrens {
30702417Sahrens 	vfs_t *vfsp = NULL;
30712199Sahrens 
30726689Smaybee 	if (arg) {
30736689Smaybee 		char *snapname = arg;
307411209SMatthew.Ahrens@Sun.COM 		char *fullname = kmem_asprintf("%s@%s", name, snapname);
307511209SMatthew.Ahrens@Sun.COM 		vfsp = zfs_get_vfs(fullname);
307611209SMatthew.Ahrens@Sun.COM 		strfree(fullname);
30772417Sahrens 	} else if (strchr(name, '@')) {
30782199Sahrens 		vfsp = zfs_get_vfs(name);
30792199Sahrens 	}
30802199Sahrens 
30812199Sahrens 	if (vfsp) {
30822199Sahrens 		/*
30832199Sahrens 		 * Always force the unmount for snapshots.
30842199Sahrens 		 */
30852199Sahrens 		int flag = MS_FORCE;
3086789Sahrens 		int err;
3087789Sahrens 
30882199Sahrens 		if ((err = vn_vfswlock(vfsp->vfs_vnodecovered)) != 0) {
30892199Sahrens 			VFS_RELE(vfsp);
30902199Sahrens 			return (err);
30912199Sahrens 		}
30922199Sahrens 		VFS_RELE(vfsp);
30932199Sahrens 		if ((err = dounmount(vfsp, flag, kcred)) != 0)
30942199Sahrens 			return (err);
30952199Sahrens 	}
30962199Sahrens 	return (0);
30972199Sahrens }
30982199Sahrens 
30995367Sahrens /*
31005367Sahrens  * inputs:
310110242Schris.kirby@sun.com  * zc_name		name of filesystem
310210242Schris.kirby@sun.com  * zc_value		short name of snapshot
310310242Schris.kirby@sun.com  * zc_defer_destroy	mark for deferred destroy
31045367Sahrens  *
31055367Sahrens  * outputs:	none
31065367Sahrens  */
31072199Sahrens static int
zfs_ioc_destroy_snaps(zfs_cmd_t * zc)31082199Sahrens zfs_ioc_destroy_snaps(zfs_cmd_t *zc)
31092199Sahrens {
31102199Sahrens 	int err;
3111789Sahrens 
31122676Seschrock 	if (snapshot_namecheck(zc->zc_value, NULL, NULL) != 0)
31132199Sahrens 		return (EINVAL);
31142199Sahrens 	err = dmu_objset_find(zc->zc_name,
31152676Seschrock 	    zfs_unmount_snap, zc->zc_value, DS_FIND_CHILDREN);
31162199Sahrens 	if (err)
31172199Sahrens 		return (err);
311810242Schris.kirby@sun.com 	return (dmu_snapshots_destroy(zc->zc_name, zc->zc_value,
311910242Schris.kirby@sun.com 	    zc->zc_defer_destroy));
31202199Sahrens }
31212199Sahrens 
31225367Sahrens /*
31235367Sahrens  * inputs:
31245367Sahrens  * zc_name		name of dataset to destroy
31255367Sahrens  * zc_objset_type	type of objset
312610242Schris.kirby@sun.com  * zc_defer_destroy	mark for deferred destroy
31275367Sahrens  *
31285367Sahrens  * outputs:		none
31295367Sahrens  */
31302199Sahrens static int
zfs_ioc_destroy(zfs_cmd_t * zc)31312199Sahrens zfs_ioc_destroy(zfs_cmd_t *zc)
31322199Sahrens {
313310588SEric.Taylor@Sun.COM 	int err;
31342199Sahrens 	if (strchr(zc->zc_name, '@') && zc->zc_objset_type == DMU_OST_ZFS) {
313510588SEric.Taylor@Sun.COM 		err = zfs_unmount_snap(zc->zc_name, NULL);
31362199Sahrens 		if (err)
31372199Sahrens 			return (err);
3138789Sahrens 	}
3139789Sahrens 
314010588SEric.Taylor@Sun.COM 	err = dmu_objset_destroy(zc->zc_name, zc->zc_defer_destroy);
314110588SEric.Taylor@Sun.COM 	if (zc->zc_objset_type == DMU_OST_ZVOL && err == 0)
314210693Schris.kirby@sun.com 		(void) zvol_remove_minor(zc->zc_name);
314310588SEric.Taylor@Sun.COM 	return (err);
3144789Sahrens }
3145789Sahrens 
31465367Sahrens /*
31475367Sahrens  * inputs:
31485446Sahrens  * zc_name	name of dataset to rollback (to most recent snapshot)
31495367Sahrens  *
31505367Sahrens  * outputs:	none
31515367Sahrens  */
3152789Sahrens static int
zfs_ioc_rollback(zfs_cmd_t * zc)3153789Sahrens zfs_ioc_rollback(zfs_cmd_t *zc)
3154789Sahrens {
315510272SMatthew.Ahrens@Sun.COM 	dsl_dataset_t *ds, *clone;
31565446Sahrens 	int error;
315710272SMatthew.Ahrens@Sun.COM 	zfsvfs_t *zfsvfs;
315810272SMatthew.Ahrens@Sun.COM 	char *clone_name;
315910272SMatthew.Ahrens@Sun.COM 
316010272SMatthew.Ahrens@Sun.COM 	error = dsl_dataset_hold(zc->zc_name, FTAG, &ds);
316110272SMatthew.Ahrens@Sun.COM 	if (error)
316210272SMatthew.Ahrens@Sun.COM 		return (error);
316310272SMatthew.Ahrens@Sun.COM 
316410272SMatthew.Ahrens@Sun.COM 	/* must not be a snapshot */
316510272SMatthew.Ahrens@Sun.COM 	if (dsl_dataset_is_snapshot(ds)) {
316610272SMatthew.Ahrens@Sun.COM 		dsl_dataset_rele(ds, FTAG);
316710272SMatthew.Ahrens@Sun.COM 		return (EINVAL);
316810272SMatthew.Ahrens@Sun.COM 	}
316910272SMatthew.Ahrens@Sun.COM 
317010272SMatthew.Ahrens@Sun.COM 	/* must have a most recent snapshot */
317110272SMatthew.Ahrens@Sun.COM 	if (ds->ds_phys->ds_prev_snap_txg < TXG_INITIAL) {
317210272SMatthew.Ahrens@Sun.COM 		dsl_dataset_rele(ds, FTAG);
317310272SMatthew.Ahrens@Sun.COM 		return (EINVAL);
317410272SMatthew.Ahrens@Sun.COM 	}
31755446Sahrens 
31765446Sahrens 	/*
317710272SMatthew.Ahrens@Sun.COM 	 * Create clone of most recent snapshot.
31785446Sahrens 	 */
317910272SMatthew.Ahrens@Sun.COM 	clone_name = kmem_asprintf("%s/%%rollback", zc->zc_name);
318010272SMatthew.Ahrens@Sun.COM 	error = dmu_objset_clone(clone_name, ds->ds_prev, DS_FLAG_INCONSISTENT);
31815446Sahrens 	if (error)
318210272SMatthew.Ahrens@Sun.COM 		goto out;
318310272SMatthew.Ahrens@Sun.COM 
318410298SMatthew.Ahrens@Sun.COM 	error = dsl_dataset_own(clone_name, B_TRUE, FTAG, &clone);
318510272SMatthew.Ahrens@Sun.COM 	if (error)
318610272SMatthew.Ahrens@Sun.COM 		goto out;
318710272SMatthew.Ahrens@Sun.COM 
318810272SMatthew.Ahrens@Sun.COM 	/*
318910272SMatthew.Ahrens@Sun.COM 	 * Do clone swap.
319010272SMatthew.Ahrens@Sun.COM 	 */
31919396SMatthew.Ahrens@Sun.COM 	if (getzfsvfs(zc->zc_name, &zfsvfs) == 0) {
319210298SMatthew.Ahrens@Sun.COM 		error = zfs_suspend_fs(zfsvfs);
31936083Sek110237 		if (error == 0) {
31946083Sek110237 			int resume_err;
31956083Sek110237 
319610272SMatthew.Ahrens@Sun.COM 			if (dsl_dataset_tryown(ds, B_FALSE, FTAG)) {
319710272SMatthew.Ahrens@Sun.COM 				error = dsl_dataset_clone_swap(clone, ds,
319810272SMatthew.Ahrens@Sun.COM 				    B_TRUE);
319910272SMatthew.Ahrens@Sun.COM 				dsl_dataset_disown(ds, FTAG);
320010272SMatthew.Ahrens@Sun.COM 				ds = NULL;
320110272SMatthew.Ahrens@Sun.COM 			} else {
320210272SMatthew.Ahrens@Sun.COM 				error = EBUSY;
320310272SMatthew.Ahrens@Sun.COM 			}
320410298SMatthew.Ahrens@Sun.COM 			resume_err = zfs_resume_fs(zfsvfs, zc->zc_name);
32056083Sek110237 			error = error ? error : resume_err;
32066083Sek110237 		}
32075446Sahrens 		VFS_RELE(zfsvfs->z_vfs);
32085446Sahrens 	} else {
320910272SMatthew.Ahrens@Sun.COM 		if (dsl_dataset_tryown(ds, B_FALSE, FTAG)) {
321010272SMatthew.Ahrens@Sun.COM 			error = dsl_dataset_clone_swap(clone, ds, B_TRUE);
321110272SMatthew.Ahrens@Sun.COM 			dsl_dataset_disown(ds, FTAG);
321210272SMatthew.Ahrens@Sun.COM 			ds = NULL;
321310272SMatthew.Ahrens@Sun.COM 		} else {
321410272SMatthew.Ahrens@Sun.COM 			error = EBUSY;
321510272SMatthew.Ahrens@Sun.COM 		}
32165446Sahrens 	}
321710272SMatthew.Ahrens@Sun.COM 
321810272SMatthew.Ahrens@Sun.COM 	/*
321910272SMatthew.Ahrens@Sun.COM 	 * Destroy clone (which also closes it).
322010272SMatthew.Ahrens@Sun.COM 	 */
322110272SMatthew.Ahrens@Sun.COM 	(void) dsl_dataset_destroy(clone, FTAG, B_FALSE);
322210272SMatthew.Ahrens@Sun.COM 
322310272SMatthew.Ahrens@Sun.COM out:
322410272SMatthew.Ahrens@Sun.COM 	strfree(clone_name);
322510272SMatthew.Ahrens@Sun.COM 	if (ds)
322610272SMatthew.Ahrens@Sun.COM 		dsl_dataset_rele(ds, FTAG);
32275446Sahrens 	return (error);
3228789Sahrens }
3229789Sahrens 
32305367Sahrens /*
32315367Sahrens  * inputs:
32325367Sahrens  * zc_name	old name of dataset
32335367Sahrens  * zc_value	new name of dataset
32345367Sahrens  * zc_cookie	recursive flag (only valid for snapshots)
32355367Sahrens  *
32365367Sahrens  * outputs:	none
32375367Sahrens  */
3238789Sahrens static int
zfs_ioc_rename(zfs_cmd_t * zc)3239789Sahrens zfs_ioc_rename(zfs_cmd_t *zc)
3240789Sahrens {
32414490Svb160487 	boolean_t recursive = zc->zc_cookie & 1;
32424007Smmusante 
32432676Seschrock 	zc->zc_value[sizeof (zc->zc_value) - 1] = '\0';
32445326Sek110237 	if (dataset_namecheck(zc->zc_value, NULL, NULL) != 0 ||
32455326Sek110237 	    strchr(zc->zc_value, '%'))
3246789Sahrens 		return (EINVAL);
3247789Sahrens 
32484007Smmusante 	/*
32494007Smmusante 	 * Unmount snapshot unless we're doing a recursive rename,
32504007Smmusante 	 * in which case the dataset code figures out which snapshots
32514007Smmusante 	 * to unmount.
32524007Smmusante 	 */
32534007Smmusante 	if (!recursive && strchr(zc->zc_name, '@') != NULL &&
3254789Sahrens 	    zc->zc_objset_type == DMU_OST_ZFS) {
32552199Sahrens 		int err = zfs_unmount_snap(zc->zc_name, NULL);
32562199Sahrens 		if (err)
32572199Sahrens 			return (err);
3258789Sahrens 	}
325910588SEric.Taylor@Sun.COM 	if (zc->zc_objset_type == DMU_OST_ZVOL)
326010588SEric.Taylor@Sun.COM 		(void) zvol_remove_minor(zc->zc_name);
32614007Smmusante 	return (dmu_objset_rename(zc->zc_name, zc->zc_value, recursive));
3262789Sahrens }
3263789Sahrens 
326411022STom.Erickson@Sun.COM static int
zfs_check_settable(const char * dsname,nvpair_t * pair,cred_t * cr)326511022STom.Erickson@Sun.COM zfs_check_settable(const char *dsname, nvpair_t *pair, cred_t *cr)
326611022STom.Erickson@Sun.COM {
326711022STom.Erickson@Sun.COM 	const char *propname = nvpair_name(pair);
326811022STom.Erickson@Sun.COM 	boolean_t issnap = (strchr(dsname, '@') != NULL);
326911022STom.Erickson@Sun.COM 	zfs_prop_t prop = zfs_name_to_prop(propname);
327011022STom.Erickson@Sun.COM 	uint64_t intval;
327111022STom.Erickson@Sun.COM 	int err;
327211022STom.Erickson@Sun.COM 
327311022STom.Erickson@Sun.COM 	if (prop == ZPROP_INVAL) {
327411022STom.Erickson@Sun.COM 		if (zfs_prop_user(propname)) {
327511022STom.Erickson@Sun.COM 			if (err = zfs_secpolicy_write_perms(dsname,
327611022STom.Erickson@Sun.COM 			    ZFS_DELEG_PERM_USERPROP, cr))
327711022STom.Erickson@Sun.COM 				return (err);
327811022STom.Erickson@Sun.COM 			return (0);
327911022STom.Erickson@Sun.COM 		}
328011022STom.Erickson@Sun.COM 
328111022STom.Erickson@Sun.COM 		if (!issnap && zfs_prop_userquota(propname)) {
328211022STom.Erickson@Sun.COM 			const char *perm = NULL;
328311022STom.Erickson@Sun.COM 			const char *uq_prefix =
328411022STom.Erickson@Sun.COM 			    zfs_userquota_prop_prefixes[ZFS_PROP_USERQUOTA];
328511022STom.Erickson@Sun.COM 			const char *gq_prefix =
328611022STom.Erickson@Sun.COM 			    zfs_userquota_prop_prefixes[ZFS_PROP_GROUPQUOTA];
328711022STom.Erickson@Sun.COM 
328811022STom.Erickson@Sun.COM 			if (strncmp(propname, uq_prefix,
328911022STom.Erickson@Sun.COM 			    strlen(uq_prefix)) == 0) {
329011022STom.Erickson@Sun.COM 				perm = ZFS_DELEG_PERM_USERQUOTA;
329111022STom.Erickson@Sun.COM 			} else if (strncmp(propname, gq_prefix,
329211022STom.Erickson@Sun.COM 			    strlen(gq_prefix)) == 0) {
329311022STom.Erickson@Sun.COM 				perm = ZFS_DELEG_PERM_GROUPQUOTA;
329411022STom.Erickson@Sun.COM 			} else {
329511022STom.Erickson@Sun.COM 				/* USERUSED and GROUPUSED are read-only */
329611022STom.Erickson@Sun.COM 				return (EINVAL);
329711022STom.Erickson@Sun.COM 			}
329811022STom.Erickson@Sun.COM 
329911022STom.Erickson@Sun.COM 			if (err = zfs_secpolicy_write_perms(dsname, perm, cr))
330011022STom.Erickson@Sun.COM 				return (err);
330111022STom.Erickson@Sun.COM 			return (0);
330211022STom.Erickson@Sun.COM 		}
330311022STom.Erickson@Sun.COM 
330411022STom.Erickson@Sun.COM 		return (EINVAL);
330511022STom.Erickson@Sun.COM 	}
330611022STom.Erickson@Sun.COM 
330711022STom.Erickson@Sun.COM 	if (issnap)
330811022STom.Erickson@Sun.COM 		return (EINVAL);
330911022STom.Erickson@Sun.COM 
331011022STom.Erickson@Sun.COM 	if (nvpair_type(pair) == DATA_TYPE_NVLIST) {
331111022STom.Erickson@Sun.COM 		/*
331211022STom.Erickson@Sun.COM 		 * dsl_prop_get_all_impl() returns properties in this
331311022STom.Erickson@Sun.COM 		 * format.
331411022STom.Erickson@Sun.COM 		 */
331511022STom.Erickson@Sun.COM 		nvlist_t *attrs;
331611022STom.Erickson@Sun.COM 		VERIFY(nvpair_value_nvlist(pair, &attrs) == 0);
331711022STom.Erickson@Sun.COM 		VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE,
331811022STom.Erickson@Sun.COM 		    &pair) == 0);
331911022STom.Erickson@Sun.COM 	}
332011022STom.Erickson@Sun.COM 
332111022STom.Erickson@Sun.COM 	/*
332211022STom.Erickson@Sun.COM 	 * Check that this value is valid for this pool version
332311022STom.Erickson@Sun.COM 	 */
332411022STom.Erickson@Sun.COM 	switch (prop) {
332511022STom.Erickson@Sun.COM 	case ZFS_PROP_COMPRESSION:
332611022STom.Erickson@Sun.COM 		/*
332711022STom.Erickson@Sun.COM 		 * If the user specified gzip compression, make sure
332811022STom.Erickson@Sun.COM 		 * the SPA supports it. We ignore any errors here since
332911022STom.Erickson@Sun.COM 		 * we'll catch them later.
333011022STom.Erickson@Sun.COM 		 */
333111022STom.Erickson@Sun.COM 		if (nvpair_type(pair) == DATA_TYPE_UINT64 &&
333211022STom.Erickson@Sun.COM 		    nvpair_value_uint64(pair, &intval) == 0) {
333311022STom.Erickson@Sun.COM 			if (intval >= ZIO_COMPRESS_GZIP_1 &&
333411022STom.Erickson@Sun.COM 			    intval <= ZIO_COMPRESS_GZIP_9 &&
333511022STom.Erickson@Sun.COM 			    zfs_earlier_version(dsname,
333611022STom.Erickson@Sun.COM 			    SPA_VERSION_GZIP_COMPRESSION)) {
333711022STom.Erickson@Sun.COM 				return (ENOTSUP);
333811022STom.Erickson@Sun.COM 			}
333911022STom.Erickson@Sun.COM 
334011022STom.Erickson@Sun.COM 			if (intval == ZIO_COMPRESS_ZLE &&
334111022STom.Erickson@Sun.COM 			    zfs_earlier_version(dsname,
334211022STom.Erickson@Sun.COM 			    SPA_VERSION_ZLE_COMPRESSION))
334311022STom.Erickson@Sun.COM 				return (ENOTSUP);
334411022STom.Erickson@Sun.COM 
334511022STom.Erickson@Sun.COM 			/*
334611022STom.Erickson@Sun.COM 			 * If this is a bootable dataset then
334711022STom.Erickson@Sun.COM 			 * verify that the compression algorithm
334811022STom.Erickson@Sun.COM 			 * is supported for booting. We must return
334911022STom.Erickson@Sun.COM 			 * something other than ENOTSUP since it
335011022STom.Erickson@Sun.COM 			 * implies a downrev pool version.
335111022STom.Erickson@Sun.COM 			 */
335211022STom.Erickson@Sun.COM 			if (zfs_is_bootfs(dsname) &&
335311022STom.Erickson@Sun.COM 			    !BOOTFS_COMPRESS_VALID(intval)) {
335411022STom.Erickson@Sun.COM 				return (ERANGE);
335511022STom.Erickson@Sun.COM 			}
335611022STom.Erickson@Sun.COM 		}
335711022STom.Erickson@Sun.COM 		break;
335811022STom.Erickson@Sun.COM 
335911022STom.Erickson@Sun.COM 	case ZFS_PROP_COPIES:
336011022STom.Erickson@Sun.COM 		if (zfs_earlier_version(dsname, SPA_VERSION_DITTO_BLOCKS))
336111022STom.Erickson@Sun.COM 			return (ENOTSUP);
336211022STom.Erickson@Sun.COM 		break;
336311022STom.Erickson@Sun.COM 
336411022STom.Erickson@Sun.COM 	case ZFS_PROP_DEDUP:
336511022STom.Erickson@Sun.COM 		if (zfs_earlier_version(dsname, SPA_VERSION_DEDUP))
336611022STom.Erickson@Sun.COM 			return (ENOTSUP);
336711022STom.Erickson@Sun.COM 		break;
336811022STom.Erickson@Sun.COM 
336911022STom.Erickson@Sun.COM 	case ZFS_PROP_SHARESMB:
337011022STom.Erickson@Sun.COM 		if (zpl_earlier_version(dsname, ZPL_VERSION_FUID))
337111022STom.Erickson@Sun.COM 			return (ENOTSUP);
337211022STom.Erickson@Sun.COM 		break;
337311022STom.Erickson@Sun.COM 
337411022STom.Erickson@Sun.COM 	case ZFS_PROP_ACLINHERIT:
337511022STom.Erickson@Sun.COM 		if (nvpair_type(pair) == DATA_TYPE_UINT64 &&
337611022STom.Erickson@Sun.COM 		    nvpair_value_uint64(pair, &intval) == 0) {
337711022STom.Erickson@Sun.COM 			if (intval == ZFS_ACL_PASSTHROUGH_X &&
337811022STom.Erickson@Sun.COM 			    zfs_earlier_version(dsname,
337911022STom.Erickson@Sun.COM 			    SPA_VERSION_PASSTHROUGH_X))
338011022STom.Erickson@Sun.COM 				return (ENOTSUP);
338111022STom.Erickson@Sun.COM 		}
338211022STom.Erickson@Sun.COM 		break;
338311022STom.Erickson@Sun.COM 	}
338411022STom.Erickson@Sun.COM 
338511022STom.Erickson@Sun.COM 	return (zfs_secpolicy_setprop(dsname, prop, pair, CRED()));
338611022STom.Erickson@Sun.COM }
338711022STom.Erickson@Sun.COM 
338811022STom.Erickson@Sun.COM /*
338911022STom.Erickson@Sun.COM  * Removes properties from the given props list that fail permission checks
339011022STom.Erickson@Sun.COM  * needed to clear them and to restore them in case of a receive error. For each
339111022STom.Erickson@Sun.COM  * property, make sure we have both set and inherit permissions.
339211022STom.Erickson@Sun.COM  *
339311022STom.Erickson@Sun.COM  * Returns the first error encountered if any permission checks fail. If the
339411022STom.Erickson@Sun.COM  * caller provides a non-NULL errlist, it also gives the complete list of names
339511022STom.Erickson@Sun.COM  * of all the properties that failed a permission check along with the
339611022STom.Erickson@Sun.COM  * corresponding error numbers. The caller is responsible for freeing the
339711022STom.Erickson@Sun.COM  * returned errlist.
339811022STom.Erickson@Sun.COM  *
339911022STom.Erickson@Sun.COM  * If every property checks out successfully, zero is returned and the list
340011022STom.Erickson@Sun.COM  * pointed at by errlist is NULL.
340111022STom.Erickson@Sun.COM  */
340211022STom.Erickson@Sun.COM static int
zfs_check_clearable(char * dataset,nvlist_t * props,nvlist_t ** errlist)340311022STom.Erickson@Sun.COM zfs_check_clearable(char *dataset, nvlist_t *props, nvlist_t **errlist)
34046689Smaybee {
34056689Smaybee 	zfs_cmd_t *zc;
340611022STom.Erickson@Sun.COM 	nvpair_t *pair, *next_pair;
340711022STom.Erickson@Sun.COM 	nvlist_t *errors;
340811022STom.Erickson@Sun.COM 	int err, rv = 0;
34096689Smaybee 
34106689Smaybee 	if (props == NULL)
341111022STom.Erickson@Sun.COM 		return (0);
341211022STom.Erickson@Sun.COM 
341311022STom.Erickson@Sun.COM 	VERIFY(nvlist_alloc(&errors, NV_UNIQUE_NAME, KM_SLEEP) == 0);
341411022STom.Erickson@Sun.COM 
34156689Smaybee 	zc = kmem_alloc(sizeof (zfs_cmd_t), KM_SLEEP);
34166689Smaybee 	(void) strcpy(zc->zc_name, dataset);
341711022STom.Erickson@Sun.COM 	pair = nvlist_next_nvpair(props, NULL);
341811022STom.Erickson@Sun.COM 	while (pair != NULL) {
341911022STom.Erickson@Sun.COM 		next_pair = nvlist_next_nvpair(props, pair);
342011022STom.Erickson@Sun.COM 
342111022STom.Erickson@Sun.COM 		(void) strcpy(zc->zc_value, nvpair_name(pair));
342211022STom.Erickson@Sun.COM 		if ((err = zfs_check_settable(dataset, pair, CRED())) != 0 ||
342311022STom.Erickson@Sun.COM 		    (err = zfs_secpolicy_inherit(zc, CRED())) != 0) {
342411022STom.Erickson@Sun.COM 			VERIFY(nvlist_remove_nvpair(props, pair) == 0);
342511022STom.Erickson@Sun.COM 			VERIFY(nvlist_add_int32(errors,
342611022STom.Erickson@Sun.COM 			    zc->zc_value, err) == 0);
342711022STom.Erickson@Sun.COM 		}
342811022STom.Erickson@Sun.COM 		pair = next_pair;
34296689Smaybee 	}
34306689Smaybee 	kmem_free(zc, sizeof (zfs_cmd_t));
343111022STom.Erickson@Sun.COM 
343211022STom.Erickson@Sun.COM 	if ((pair = nvlist_next_nvpair(errors, NULL)) == NULL) {
343311022STom.Erickson@Sun.COM 		nvlist_free(errors);
343411022STom.Erickson@Sun.COM 		errors = NULL;
343511022STom.Erickson@Sun.COM 	} else {
343611022STom.Erickson@Sun.COM 		VERIFY(nvpair_value_int32(pair, &rv) == 0);
343711022STom.Erickson@Sun.COM 	}
343811022STom.Erickson@Sun.COM 
343911022STom.Erickson@Sun.COM 	if (errlist == NULL)
344011022STom.Erickson@Sun.COM 		nvlist_free(errors);
344111022STom.Erickson@Sun.COM 	else
344211022STom.Erickson@Sun.COM 		*errlist = errors;
344311022STom.Erickson@Sun.COM 
344411022STom.Erickson@Sun.COM 	return (rv);
34456689Smaybee }
34466689Smaybee 
344711022STom.Erickson@Sun.COM static boolean_t
propval_equals(nvpair_t * p1,nvpair_t * p2)344811022STom.Erickson@Sun.COM propval_equals(nvpair_t *p1, nvpair_t *p2)
344911022STom.Erickson@Sun.COM {
345011022STom.Erickson@Sun.COM 	if (nvpair_type(p1) == DATA_TYPE_NVLIST) {
345111022STom.Erickson@Sun.COM 		/* dsl_prop_get_all_impl() format */
345211022STom.Erickson@Sun.COM 		nvlist_t *attrs;
345311022STom.Erickson@Sun.COM 		VERIFY(nvpair_value_nvlist(p1, &attrs) == 0);
345411022STom.Erickson@Sun.COM 		VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE,
345511022STom.Erickson@Sun.COM 		    &p1) == 0);
345611022STom.Erickson@Sun.COM 	}
345711022STom.Erickson@Sun.COM 
345811022STom.Erickson@Sun.COM 	if (nvpair_type(p2) == DATA_TYPE_NVLIST) {
345911022STom.Erickson@Sun.COM 		nvlist_t *attrs;
346011022STom.Erickson@Sun.COM 		VERIFY(nvpair_value_nvlist(p2, &attrs) == 0);
346111022STom.Erickson@Sun.COM 		VERIFY(nvlist_lookup_nvpair(attrs, ZPROP_VALUE,
346211022STom.Erickson@Sun.COM 		    &p2) == 0);
346311022STom.Erickson@Sun.COM 	}
346411022STom.Erickson@Sun.COM 
346511022STom.Erickson@Sun.COM 	if (nvpair_type(p1) != nvpair_type(p2))
346611022STom.Erickson@Sun.COM 		return (B_FALSE);
346711022STom.Erickson@Sun.COM 
346811022STom.Erickson@Sun.COM 	if (nvpair_type(p1) == DATA_TYPE_STRING) {
346911022STom.Erickson@Sun.COM 		char *valstr1, *valstr2;
347011022STom.Erickson@Sun.COM 
347111022STom.Erickson@Sun.COM 		VERIFY(nvpair_value_string(p1, (char **)&valstr1) == 0);
347211022STom.Erickson@Sun.COM 		VERIFY(nvpair_value_string(p2, (char **)&valstr2) == 0);
347311022STom.Erickson@Sun.COM 		return (strcmp(valstr1, valstr2) == 0);
347411022STom.Erickson@Sun.COM 	} else {
347511022STom.Erickson@Sun.COM 		uint64_t intval1, intval2;
347611022STom.Erickson@Sun.COM 
347711022STom.Erickson@Sun.COM 		VERIFY(nvpair_value_uint64(p1, &intval1) == 0);
347811022STom.Erickson@Sun.COM 		VERIFY(nvpair_value_uint64(p2, &intval2) == 0);
347911022STom.Erickson@Sun.COM 		return (intval1 == intval2);
348011022STom.Erickson@Sun.COM 	}
348111022STom.Erickson@Sun.COM }
348211022STom.Erickson@Sun.COM 
348311022STom.Erickson@Sun.COM /*
348411022STom.Erickson@Sun.COM  * Remove properties from props if they are not going to change (as determined
348511022STom.Erickson@Sun.COM  * by comparison with origprops). Remove them from origprops as well, since we
348611022STom.Erickson@Sun.COM  * do not need to clear or restore properties that won't change.
348711022STom.Erickson@Sun.COM  */
348811022STom.Erickson@Sun.COM static void
props_reduce(nvlist_t * props,nvlist_t * origprops)348911022STom.Erickson@Sun.COM props_reduce(nvlist_t *props, nvlist_t *origprops)
349011022STom.Erickson@Sun.COM {
349111022STom.Erickson@Sun.COM 	nvpair_t *pair, *next_pair;
349211022STom.Erickson@Sun.COM 
349311022STom.Erickson@Sun.COM 	if (origprops == NULL)
349411022STom.Erickson@Sun.COM 		return; /* all props need to be received */
349511022STom.Erickson@Sun.COM 
349611022STom.Erickson@Sun.COM 	pair = nvlist_next_nvpair(props, NULL);
349711022STom.Erickson@Sun.COM 	while (pair != NULL) {
349811022STom.Erickson@Sun.COM 		const char *propname = nvpair_name(pair);
349911022STom.Erickson@Sun.COM 		nvpair_t *match;
350011022STom.Erickson@Sun.COM 
350111022STom.Erickson@Sun.COM 		next_pair = nvlist_next_nvpair(props, pair);
350211022STom.Erickson@Sun.COM 
350311022STom.Erickson@Sun.COM 		if ((nvlist_lookup_nvpair(origprops, propname,
350411022STom.Erickson@Sun.COM 		    &match) != 0) || !propval_equals(pair, match))
350511022STom.Erickson@Sun.COM 			goto next; /* need to set received value */
350611022STom.Erickson@Sun.COM 
350711022STom.Erickson@Sun.COM 		/* don't clear the existing received value */
350811022STom.Erickson@Sun.COM 		(void) nvlist_remove_nvpair(origprops, match);
350911022STom.Erickson@Sun.COM 		/* don't bother receiving the property */
351011022STom.Erickson@Sun.COM 		(void) nvlist_remove_nvpair(props, pair);
351111022STom.Erickson@Sun.COM next:
351211022STom.Erickson@Sun.COM 		pair = next_pair;
351311022STom.Erickson@Sun.COM 	}
351411022STom.Erickson@Sun.COM }
351511022STom.Erickson@Sun.COM 
351611022STom.Erickson@Sun.COM #ifdef	DEBUG
351711022STom.Erickson@Sun.COM static boolean_t zfs_ioc_recv_inject_err;
351811022STom.Erickson@Sun.COM #endif
351911022STom.Erickson@Sun.COM 
35205367Sahrens /*
35215367Sahrens  * inputs:
35225367Sahrens  * zc_name		name of containing filesystem
35235367Sahrens  * zc_nvlist_src{_size}	nvlist of properties to apply
35245367Sahrens  * zc_value		name of snapshot to create
35255367Sahrens  * zc_string		name of clone origin (if DRR_FLAG_CLONE)
35265367Sahrens  * zc_cookie		file descriptor to recv from
35275367Sahrens  * zc_begin_record	the BEGIN record of the stream (not byteswapped)
35285367Sahrens  * zc_guid		force flag
352912527SChris.Kirby@oracle.com  * zc_cleanup_fd	cleanup-on-exit file descriptor
353012527SChris.Kirby@oracle.com  * zc_action_handle	handle for this guid/ds mapping (or zero on first call)
35315367Sahrens  *
35325367Sahrens  * outputs:
35335367Sahrens  * zc_cookie		number of bytes read
353411022STom.Erickson@Sun.COM  * zc_nvlist_dst{_size} error for each unapplied received property
353511022STom.Erickson@Sun.COM  * zc_obj		zprop_errflags_t
353612527SChris.Kirby@oracle.com  * zc_action_handle	handle for this guid/ds mapping
35375367Sahrens  */
3538789Sahrens static int
zfs_ioc_recv(zfs_cmd_t * zc)35395367Sahrens zfs_ioc_recv(zfs_cmd_t *zc)
3540789Sahrens {
3541789Sahrens 	file_t *fp;
35425326Sek110237 	objset_t *os;
35435367Sahrens 	dmu_recv_cookie_t drc;
35445326Sek110237 	boolean_t force = (boolean_t)zc->zc_guid;
354511022STom.Erickson@Sun.COM 	int fd;
354611022STom.Erickson@Sun.COM 	int error = 0;
354711022STom.Erickson@Sun.COM 	int props_error = 0;
354811022STom.Erickson@Sun.COM 	nvlist_t *errors;
35495367Sahrens 	offset_t off;
355011022STom.Erickson@Sun.COM 	nvlist_t *props = NULL; /* sent properties */
355111022STom.Erickson@Sun.COM 	nvlist_t *origprops = NULL; /* existing properties */
35525367Sahrens 	objset_t *origin = NULL;
35535367Sahrens 	char *tosnap;
35545367Sahrens 	char tofs[ZFS_MAXNAMELEN];
355511022STom.Erickson@Sun.COM 	boolean_t first_recvd_props = B_FALSE;
3556789Sahrens 
35573265Sahrens 	if (dataset_namecheck(zc->zc_value, NULL, NULL) != 0 ||
35585326Sek110237 	    strchr(zc->zc_value, '@') == NULL ||
35595326Sek110237 	    strchr(zc->zc_value, '%'))
35603265Sahrens 		return (EINVAL);
35613265Sahrens 
35625367Sahrens 	(void) strcpy(tofs, zc->zc_value);
35635367Sahrens 	tosnap = strchr(tofs, '@');
356411022STom.Erickson@Sun.COM 	*tosnap++ = '\0';
35655367Sahrens 
35665367Sahrens 	if (zc->zc_nvlist_src != NULL &&
35675367Sahrens 	    (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
35689643SEric.Taylor@Sun.COM 	    zc->zc_iflags, &props)) != 0)
35695367Sahrens 		return (error);
35705367Sahrens 
3571789Sahrens 	fd = zc->zc_cookie;
3572789Sahrens 	fp = getf(fd);
35735367Sahrens 	if (fp == NULL) {
35745367Sahrens 		nvlist_free(props);
3575789Sahrens 		return (EBADF);
35765367Sahrens 	}
35775326Sek110237 
357811022STom.Erickson@Sun.COM 	VERIFY(nvlist_alloc(&errors, NV_UNIQUE_NAME, KM_SLEEP) == 0);
357911022STom.Erickson@Sun.COM 
358010298SMatthew.Ahrens@Sun.COM 	if (props && dmu_objset_hold(tofs, FTAG, &os) == 0) {
358111022STom.Erickson@Sun.COM 		if ((spa_version(os->os_spa) >= SPA_VERSION_RECVD_PROPS) &&
358211022STom.Erickson@Sun.COM 		    !dsl_prop_get_hasrecvd(os)) {
358311022STom.Erickson@Sun.COM 			first_recvd_props = B_TRUE;
358411022STom.Erickson@Sun.COM 		}
358511022STom.Erickson@Sun.COM 
35866689Smaybee 		/*
358711022STom.Erickson@Sun.COM 		 * If new received properties are supplied, they are to
358811022STom.Erickson@Sun.COM 		 * completely replace the existing received properties, so stash
358911022STom.Erickson@Sun.COM 		 * away the existing ones.
35906689Smaybee 		 */
359111022STom.Erickson@Sun.COM 		if (dsl_prop_get_received(os, &origprops) == 0) {
359211022STom.Erickson@Sun.COM 			nvlist_t *errlist = NULL;
359311022STom.Erickson@Sun.COM 			/*
359411022STom.Erickson@Sun.COM 			 * Don't bother writing a property if its value won't
359511022STom.Erickson@Sun.COM 			 * change (and avoid the unnecessary security checks).
359611022STom.Erickson@Sun.COM 			 *
359711022STom.Erickson@Sun.COM 			 * The first receive after SPA_VERSION_RECVD_PROPS is a
359811022STom.Erickson@Sun.COM 			 * special case where we blow away all local properties
359911022STom.Erickson@Sun.COM 			 * regardless.
360011022STom.Erickson@Sun.COM 			 */
360111022STom.Erickson@Sun.COM 			if (!first_recvd_props)
360211022STom.Erickson@Sun.COM 				props_reduce(props, origprops);
360311022STom.Erickson@Sun.COM 			if (zfs_check_clearable(tofs, origprops,
360411022STom.Erickson@Sun.COM 			    &errlist) != 0)
360511022STom.Erickson@Sun.COM 				(void) nvlist_merge(errors, errlist, 0);
360611022STom.Erickson@Sun.COM 			nvlist_free(errlist);
360711022STom.Erickson@Sun.COM 		}
360810298SMatthew.Ahrens@Sun.COM 
360910298SMatthew.Ahrens@Sun.COM 		dmu_objset_rele(os, FTAG);
36105326Sek110237 	}
36115326Sek110237 
36125367Sahrens 	if (zc->zc_string[0]) {
361310298SMatthew.Ahrens@Sun.COM 		error = dmu_objset_hold(zc->zc_string, FTAG, &origin);
36146689Smaybee 		if (error)
36156689Smaybee 			goto out;
36165367Sahrens 	}
36175367Sahrens 
361811007SLori.Alt@Sun.COM 	error = dmu_recv_begin(tofs, tosnap, zc->zc_top_ds,
361911007SLori.Alt@Sun.COM 	    &zc->zc_begin_record, force, origin, &drc);
36205367Sahrens 	if (origin)
362110298SMatthew.Ahrens@Sun.COM 		dmu_objset_rele(origin, FTAG);
36226689Smaybee 	if (error)
36236689Smaybee 		goto out;
36245326Sek110237 
36255326Sek110237 	/*
362611022STom.Erickson@Sun.COM 	 * Set properties before we receive the stream so that they are applied
362711022STom.Erickson@Sun.COM 	 * to the new data. Note that we must call dmu_recv_stream() if
362811022STom.Erickson@Sun.COM 	 * dmu_recv_begin() succeeds.
36295326Sek110237 	 */
36305367Sahrens 	if (props) {
363111022STom.Erickson@Sun.COM 		nvlist_t *errlist;
363211022STom.Erickson@Sun.COM 
363311022STom.Erickson@Sun.COM 		if (dmu_objset_from_ds(drc.drc_logical_ds, &os) == 0) {
363411022STom.Erickson@Sun.COM 			if (drc.drc_newfs) {
363511022STom.Erickson@Sun.COM 				if (spa_version(os->os_spa) >=
363611022STom.Erickson@Sun.COM 				    SPA_VERSION_RECVD_PROPS)
363711022STom.Erickson@Sun.COM 					first_recvd_props = B_TRUE;
363811022STom.Erickson@Sun.COM 			} else if (origprops != NULL) {
363911022STom.Erickson@Sun.COM 				if (clear_received_props(os, tofs, origprops,
364011022STom.Erickson@Sun.COM 				    first_recvd_props ? NULL : props) != 0)
364111022STom.Erickson@Sun.COM 					zc->zc_obj |= ZPROP_ERR_NOCLEAR;
364211022STom.Erickson@Sun.COM 			} else {
364311022STom.Erickson@Sun.COM 				zc->zc_obj |= ZPROP_ERR_NOCLEAR;
364411022STom.Erickson@Sun.COM 			}
364511022STom.Erickson@Sun.COM 			dsl_prop_set_hasrecvd(os);
364611022STom.Erickson@Sun.COM 		} else if (!drc.drc_newfs) {
364711022STom.Erickson@Sun.COM 			zc->zc_obj |= ZPROP_ERR_NOCLEAR;
364811022STom.Erickson@Sun.COM 		}
364911022STom.Erickson@Sun.COM 
365011022STom.Erickson@Sun.COM 		(void) zfs_set_prop_nvlist(tofs, ZPROP_SRC_RECEIVED,
365111022STom.Erickson@Sun.COM 		    props, &errlist);
365211022STom.Erickson@Sun.COM 		(void) nvlist_merge(errors, errlist, 0);
365311022STom.Erickson@Sun.COM 		nvlist_free(errlist);
365411022STom.Erickson@Sun.COM 	}
365511022STom.Erickson@Sun.COM 
365611022STom.Erickson@Sun.COM 	if (fit_error_list(zc, &errors) != 0 || put_nvlist(zc, errors) != 0) {
36576689Smaybee 		/*
365811022STom.Erickson@Sun.COM 		 * Caller made zc->zc_nvlist_dst less than the minimum expected
365911022STom.Erickson@Sun.COM 		 * size or supplied an invalid address.
36606689Smaybee 		 */
366111022STom.Erickson@Sun.COM 		props_error = EINVAL;
36625367Sahrens 	}
36635367Sahrens 
36645367Sahrens 	off = fp->f_offset;
366512527SChris.Kirby@oracle.com 	error = dmu_recv_stream(&drc, fp->f_vnode, &off, zc->zc_cleanup_fd,
366612527SChris.Kirby@oracle.com 	    &zc->zc_action_handle);
36675367Sahrens 
366810204SMatthew.Ahrens@Sun.COM 	if (error == 0) {
366910204SMatthew.Ahrens@Sun.COM 		zfsvfs_t *zfsvfs = NULL;
367010204SMatthew.Ahrens@Sun.COM 
367110204SMatthew.Ahrens@Sun.COM 		if (getzfsvfs(tofs, &zfsvfs) == 0) {
367210204SMatthew.Ahrens@Sun.COM 			/* online recv */
367310204SMatthew.Ahrens@Sun.COM 			int end_err;
367410298SMatthew.Ahrens@Sun.COM 
367510298SMatthew.Ahrens@Sun.COM 			error = zfs_suspend_fs(zfsvfs);
367610204SMatthew.Ahrens@Sun.COM 			/*
367710204SMatthew.Ahrens@Sun.COM 			 * If the suspend fails, then the recv_end will
367810204SMatthew.Ahrens@Sun.COM 			 * likely also fail, and clean up after itself.
367910204SMatthew.Ahrens@Sun.COM 			 */
368010204SMatthew.Ahrens@Sun.COM 			end_err = dmu_recv_end(&drc);
368111812SGeorge.Wilson@Sun.COM 			if (error == 0)
368211812SGeorge.Wilson@Sun.COM 				error = zfs_resume_fs(zfsvfs, tofs);
368310204SMatthew.Ahrens@Sun.COM 			error = error ? error : end_err;
368410204SMatthew.Ahrens@Sun.COM 			VFS_RELE(zfsvfs->z_vfs);
368510204SMatthew.Ahrens@Sun.COM 		} else {
36866689Smaybee 			error = dmu_recv_end(&drc);
36875367Sahrens 		}
36886083Sek110237 	}
36895367Sahrens 
36905367Sahrens 	zc->zc_cookie = off - fp->f_offset;
36915367Sahrens 	if (VOP_SEEK(fp->f_vnode, fp->f_offset, &off, NULL) == 0)
36925367Sahrens 		fp->f_offset = off;
36932885Sahrens 
369411022STom.Erickson@Sun.COM #ifdef	DEBUG
369511022STom.Erickson@Sun.COM 	if (zfs_ioc_recv_inject_err) {
369611022STom.Erickson@Sun.COM 		zfs_ioc_recv_inject_err = B_FALSE;
369711022STom.Erickson@Sun.COM 		error = 1;
369811022STom.Erickson@Sun.COM 	}
369911022STom.Erickson@Sun.COM #endif
37006689Smaybee 	/*
37016689Smaybee 	 * On error, restore the original props.
37026689Smaybee 	 */
37036689Smaybee 	if (error && props) {
370411022STom.Erickson@Sun.COM 		if (dmu_objset_hold(tofs, FTAG, &os) == 0) {
370511022STom.Erickson@Sun.COM 			if (clear_received_props(os, tofs, props, NULL) != 0) {
370611022STom.Erickson@Sun.COM 				/*
370711022STom.Erickson@Sun.COM 				 * We failed to clear the received properties.
370811022STom.Erickson@Sun.COM 				 * Since we may have left a $recvd value on the
370911022STom.Erickson@Sun.COM 				 * system, we can't clear the $hasrecvd flag.
371011022STom.Erickson@Sun.COM 				 */
371111022STom.Erickson@Sun.COM 				zc->zc_obj |= ZPROP_ERR_NORESTORE;
371211022STom.Erickson@Sun.COM 			} else if (first_recvd_props) {
371311022STom.Erickson@Sun.COM 				dsl_prop_unset_hasrecvd(os);
371411022STom.Erickson@Sun.COM 			}
371511022STom.Erickson@Sun.COM 			dmu_objset_rele(os, FTAG);
371611022STom.Erickson@Sun.COM 		} else if (!drc.drc_newfs) {
371711022STom.Erickson@Sun.COM 			/* We failed to clear the received properties. */
371811022STom.Erickson@Sun.COM 			zc->zc_obj |= ZPROP_ERR_NORESTORE;
371911022STom.Erickson@Sun.COM 		}
372011022STom.Erickson@Sun.COM 
372111022STom.Erickson@Sun.COM 		if (origprops == NULL && !drc.drc_newfs) {
372211022STom.Erickson@Sun.COM 			/* We failed to stash the original properties. */
372311022STom.Erickson@Sun.COM 			zc->zc_obj |= ZPROP_ERR_NORESTORE;
372411022STom.Erickson@Sun.COM 		}
372511022STom.Erickson@Sun.COM 
372611022STom.Erickson@Sun.COM 		/*
372711022STom.Erickson@Sun.COM 		 * dsl_props_set() will not convert RECEIVED to LOCAL on or
372811022STom.Erickson@Sun.COM 		 * after SPA_VERSION_RECVD_PROPS, so we need to specify LOCAL
372911022STom.Erickson@Sun.COM 		 * explictly if we're restoring local properties cleared in the
373011022STom.Erickson@Sun.COM 		 * first new-style receive.
373111022STom.Erickson@Sun.COM 		 */
373211022STom.Erickson@Sun.COM 		if (origprops != NULL &&
373311022STom.Erickson@Sun.COM 		    zfs_set_prop_nvlist(tofs, (first_recvd_props ?
373411022STom.Erickson@Sun.COM 		    ZPROP_SRC_LOCAL : ZPROP_SRC_RECEIVED),
373511022STom.Erickson@Sun.COM 		    origprops, NULL) != 0) {
373611022STom.Erickson@Sun.COM 			/*
373711022STom.Erickson@Sun.COM 			 * We stashed the original properties but failed to
373811022STom.Erickson@Sun.COM 			 * restore them.
373911022STom.Erickson@Sun.COM 			 */
374011022STom.Erickson@Sun.COM 			zc->zc_obj |= ZPROP_ERR_NORESTORE;
374111022STom.Erickson@Sun.COM 		}
37426689Smaybee 	}
37436689Smaybee out:
37446689Smaybee 	nvlist_free(props);
37456689Smaybee 	nvlist_free(origprops);
374611022STom.Erickson@Sun.COM 	nvlist_free(errors);
3747789Sahrens 	releasef(fd);
374811022STom.Erickson@Sun.COM 
374911022STom.Erickson@Sun.COM 	if (error == 0)
375011022STom.Erickson@Sun.COM 		error = props_error;
375111022STom.Erickson@Sun.COM 
3752789Sahrens 	return (error);
3753789Sahrens }
3754789Sahrens 
37555367Sahrens /*
37565367Sahrens  * inputs:
37575367Sahrens  * zc_name	name of snapshot to send
37585367Sahrens  * zc_cookie	file descriptor to send stream to
375912786SChris.Kirby@oracle.com  * zc_obj	fromorigin flag (mutually exclusive with zc_fromobj)
376012786SChris.Kirby@oracle.com  * zc_sendobj	objsetid of snapshot to send
376112786SChris.Kirby@oracle.com  * zc_fromobj	objsetid of incremental fromsnap (may be zero)
37625367Sahrens  *
37635367Sahrens  * outputs: none
37645367Sahrens  */
3765789Sahrens static int
zfs_ioc_send(zfs_cmd_t * zc)37665367Sahrens zfs_ioc_send(zfs_cmd_t *zc)
3767789Sahrens {
3768789Sahrens 	objset_t *fromsnap = NULL;
3769789Sahrens 	objset_t *tosnap;
3770789Sahrens 	file_t *fp;
3771789Sahrens 	int error;
37725367Sahrens 	offset_t off;
377312786SChris.Kirby@oracle.com 	dsl_dataset_t *ds;
377412786SChris.Kirby@oracle.com 	dsl_dataset_t *dsfrom = NULL;
377512786SChris.Kirby@oracle.com 	spa_t *spa;
377612786SChris.Kirby@oracle.com 	dsl_pool_t *dp;
377712786SChris.Kirby@oracle.com 
377812786SChris.Kirby@oracle.com 	error = spa_open(zc->zc_name, &spa, FTAG);
3779789Sahrens 	if (error)
3780789Sahrens 		return (error);
3781789Sahrens 
378212786SChris.Kirby@oracle.com 	dp = spa_get_dsl(spa);
378312786SChris.Kirby@oracle.com 	rw_enter(&dp->dp_config_rwlock, RW_READER);
378412786SChris.Kirby@oracle.com 	error = dsl_dataset_hold_obj(dp, zc->zc_sendobj, FTAG, &ds);
378512786SChris.Kirby@oracle.com 	rw_exit(&dp->dp_config_rwlock);
378612786SChris.Kirby@oracle.com 	if (error) {
378712786SChris.Kirby@oracle.com 		spa_close(spa, FTAG);
378812786SChris.Kirby@oracle.com 		return (error);
378912786SChris.Kirby@oracle.com 	}
379012786SChris.Kirby@oracle.com 
379112786SChris.Kirby@oracle.com 	error = dmu_objset_from_ds(ds, &tosnap);
379212786SChris.Kirby@oracle.com 	if (error) {
379312786SChris.Kirby@oracle.com 		dsl_dataset_rele(ds, FTAG);
379412786SChris.Kirby@oracle.com 		spa_close(spa, FTAG);
379512786SChris.Kirby@oracle.com 		return (error);
379612786SChris.Kirby@oracle.com 	}
379712786SChris.Kirby@oracle.com 
379812786SChris.Kirby@oracle.com 	if (zc->zc_fromobj != 0) {
379912786SChris.Kirby@oracle.com 		rw_enter(&dp->dp_config_rwlock, RW_READER);
380012786SChris.Kirby@oracle.com 		error = dsl_dataset_hold_obj(dp, zc->zc_fromobj, FTAG, &dsfrom);
380112786SChris.Kirby@oracle.com 		rw_exit(&dp->dp_config_rwlock);
380212786SChris.Kirby@oracle.com 		spa_close(spa, FTAG);
3803789Sahrens 		if (error) {
380412786SChris.Kirby@oracle.com 			dsl_dataset_rele(ds, FTAG);
3805789Sahrens 			return (error);
3806789Sahrens 		}
380712786SChris.Kirby@oracle.com 		error = dmu_objset_from_ds(dsfrom, &fromsnap);
380812786SChris.Kirby@oracle.com 		if (error) {
380912786SChris.Kirby@oracle.com 			dsl_dataset_rele(dsfrom, FTAG);
381012786SChris.Kirby@oracle.com 			dsl_dataset_rele(ds, FTAG);
381112786SChris.Kirby@oracle.com 			return (error);
381212786SChris.Kirby@oracle.com 		}
381312786SChris.Kirby@oracle.com 	} else {
381412786SChris.Kirby@oracle.com 		spa_close(spa, FTAG);
3815789Sahrens 	}
3816789Sahrens 
3817789Sahrens 	fp = getf(zc->zc_cookie);
3818789Sahrens 	if (fp == NULL) {
381912786SChris.Kirby@oracle.com 		dsl_dataset_rele(ds, FTAG);
382012786SChris.Kirby@oracle.com 		if (dsfrom)
382112786SChris.Kirby@oracle.com 			dsl_dataset_rele(dsfrom, FTAG);
3822789Sahrens 		return (EBADF);
3823789Sahrens 	}
3824789Sahrens 
38255367Sahrens 	off = fp->f_offset;
38265367Sahrens 	error = dmu_sendbackup(tosnap, fromsnap, zc->zc_obj, fp->f_vnode, &off);
38275367Sahrens 
38285367Sahrens 	if (VOP_SEEK(fp->f_vnode, fp->f_offset, &off, NULL) == 0)
38295367Sahrens 		fp->f_offset = off;
3830789Sahrens 	releasef(zc->zc_cookie);
383112786SChris.Kirby@oracle.com 	if (dsfrom)
383212786SChris.Kirby@oracle.com 		dsl_dataset_rele(dsfrom, FTAG);
383312786SChris.Kirby@oracle.com 	dsl_dataset_rele(ds, FTAG);
3834789Sahrens 	return (error);
3835789Sahrens }
3836789Sahrens 
38371544Seschrock static int
zfs_ioc_inject_fault(zfs_cmd_t * zc)38381544Seschrock zfs_ioc_inject_fault(zfs_cmd_t *zc)
38391544Seschrock {
38401544Seschrock 	int id, error;
38411544Seschrock 
38421544Seschrock 	error = zio_inject_fault(zc->zc_name, (int)zc->zc_guid, &id,
38431544Seschrock 	    &zc->zc_inject_record);
38441544Seschrock 
38451544Seschrock 	if (error == 0)
38461544Seschrock 		zc->zc_guid = (uint64_t)id;
38471544Seschrock 
38481544Seschrock 	return (error);
38491544Seschrock }
38501544Seschrock 
38511544Seschrock static int
zfs_ioc_clear_fault(zfs_cmd_t * zc)38521544Seschrock zfs_ioc_clear_fault(zfs_cmd_t *zc)
38531544Seschrock {
38541544Seschrock 	return (zio_clear_fault((int)zc->zc_guid));
38551544Seschrock }
38561544Seschrock 
38571544Seschrock static int
zfs_ioc_inject_list_next(zfs_cmd_t * zc)38581544Seschrock zfs_ioc_inject_list_next(zfs_cmd_t *zc)
38591544Seschrock {
38601544Seschrock 	int id = (int)zc->zc_guid;
38611544Seschrock 	int error;
38621544Seschrock 
38631544Seschrock 	error = zio_inject_list_next(&id, zc->zc_name, sizeof (zc->zc_name),
38641544Seschrock 	    &zc->zc_inject_record);
38651544Seschrock 
38661544Seschrock 	zc->zc_guid = id;
38671544Seschrock 
38681544Seschrock 	return (error);
38691544Seschrock }
38701544Seschrock 
38711544Seschrock static int
zfs_ioc_error_log(zfs_cmd_t * zc)38721544Seschrock zfs_ioc_error_log(zfs_cmd_t *zc)
38731544Seschrock {
38741544Seschrock 	spa_t *spa;
38751544Seschrock 	int error;
38762676Seschrock 	size_t count = (size_t)zc->zc_nvlist_dst_size;
38771544Seschrock 
38781544Seschrock 	if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0)
38791544Seschrock 		return (error);
38801544Seschrock 
38812676Seschrock 	error = spa_get_errlog(spa, (void *)(uintptr_t)zc->zc_nvlist_dst,
38821544Seschrock 	    &count);
38831544Seschrock 	if (error == 0)
38842676Seschrock 		zc->zc_nvlist_dst_size = count;
38851544Seschrock 	else
38862676Seschrock 		zc->zc_nvlist_dst_size = spa_get_errlog_size(spa);
38871544Seschrock 
38881544Seschrock 	spa_close(spa, FTAG);
38891544Seschrock 
38901544Seschrock 	return (error);
38911544Seschrock }
38921544Seschrock 
38931544Seschrock static int
zfs_ioc_clear(zfs_cmd_t * zc)38941544Seschrock zfs_ioc_clear(zfs_cmd_t *zc)
38951544Seschrock {
38961544Seschrock 	spa_t *spa;
38971544Seschrock 	vdev_t *vd;
38981544Seschrock 	int error;
38991544Seschrock 
39007294Sperrin 	/*
39017294Sperrin 	 * On zpool clear we also fix up missing slogs
39027294Sperrin 	 */
39037294Sperrin 	mutex_enter(&spa_namespace_lock);
39047294Sperrin 	spa = spa_lookup(zc->zc_name);
39057294Sperrin 	if (spa == NULL) {
39067294Sperrin 		mutex_exit(&spa_namespace_lock);
39077294Sperrin 		return (EIO);
39087294Sperrin 	}
390910922SJeff.Bonwick@Sun.COM 	if (spa_get_log_state(spa) == SPA_LOG_MISSING) {
39107294Sperrin 		/* we need to let spa_open/spa_load clear the chains */
391110922SJeff.Bonwick@Sun.COM 		spa_set_log_state(spa, SPA_LOG_CLEAR);
39127294Sperrin 	}
391310921STim.Haley@Sun.COM 	spa->spa_last_open_failed = 0;
39147294Sperrin 	mutex_exit(&spa_namespace_lock);
39157294Sperrin 
391611727SVictor.Latushkin@Sun.COM 	if (zc->zc_cookie & ZPOOL_NO_REWIND) {
391710921STim.Haley@Sun.COM 		error = spa_open(zc->zc_name, &spa, FTAG);
391810921STim.Haley@Sun.COM 	} else {
391910921STim.Haley@Sun.COM 		nvlist_t *policy;
392010921STim.Haley@Sun.COM 		nvlist_t *config = NULL;
392110921STim.Haley@Sun.COM 
392210921STim.Haley@Sun.COM 		if (zc->zc_nvlist_src == NULL)
392310921STim.Haley@Sun.COM 			return (EINVAL);
392410921STim.Haley@Sun.COM 
392510921STim.Haley@Sun.COM 		if ((error = get_nvlist(zc->zc_nvlist_src,
392610921STim.Haley@Sun.COM 		    zc->zc_nvlist_src_size, zc->zc_iflags, &policy)) == 0) {
392710921STim.Haley@Sun.COM 			error = spa_open_rewind(zc->zc_name, &spa, FTAG,
392810921STim.Haley@Sun.COM 			    policy, &config);
392910921STim.Haley@Sun.COM 			if (config != NULL) {
393012949SGeorge.Wilson@Sun.COM 				int err;
393112949SGeorge.Wilson@Sun.COM 
393212949SGeorge.Wilson@Sun.COM 				if ((err = put_nvlist(zc, config)) != 0)
393312949SGeorge.Wilson@Sun.COM 					error = err;
393410921STim.Haley@Sun.COM 				nvlist_free(config);
393510921STim.Haley@Sun.COM 			}
393610921STim.Haley@Sun.COM 			nvlist_free(policy);
393710921STim.Haley@Sun.COM 		}
393810921STim.Haley@Sun.COM 	}
393910921STim.Haley@Sun.COM 
394010921STim.Haley@Sun.COM 	if (error)
39411544Seschrock 		return (error);
39421544Seschrock 
394310685SGeorge.Wilson@Sun.COM 	spa_vdev_state_enter(spa, SCL_NONE);
39441544Seschrock 
39452676Seschrock 	if (zc->zc_guid == 0) {
39461544Seschrock 		vd = NULL;
39476643Seschrock 	} else {
39486643Seschrock 		vd = spa_lookup_by_guid(spa, zc->zc_guid, B_TRUE);
39495450Sbrendan 		if (vd == NULL) {
39507754SJeff.Bonwick@Sun.COM 			(void) spa_vdev_state_exit(spa, NULL, ENODEV);
39515450Sbrendan 			spa_close(spa, FTAG);
39525450Sbrendan 			return (ENODEV);
39535450Sbrendan 		}
39541544Seschrock 	}
39551544Seschrock 
39567754SJeff.Bonwick@Sun.COM 	vdev_clear(spa, vd);
39577754SJeff.Bonwick@Sun.COM 
39587754SJeff.Bonwick@Sun.COM 	(void) spa_vdev_state_exit(spa, NULL, 0);
39597754SJeff.Bonwick@Sun.COM 
39607754SJeff.Bonwick@Sun.COM 	/*
39617754SJeff.Bonwick@Sun.COM 	 * Resume any suspended I/Os.
39627754SJeff.Bonwick@Sun.COM 	 */
39639234SGeorge.Wilson@Sun.COM 	if (zio_resume(spa) != 0)
39649234SGeorge.Wilson@Sun.COM 		error = EIO;
39651544Seschrock 
39661544Seschrock 	spa_close(spa, FTAG);
39671544Seschrock 
39689234SGeorge.Wilson@Sun.COM 	return (error);
39691544Seschrock }
39701544Seschrock 
39715367Sahrens /*
39725367Sahrens  * inputs:
39735367Sahrens  * zc_name	name of filesystem
39745367Sahrens  * zc_value	name of origin snapshot
39755367Sahrens  *
397610588SEric.Taylor@Sun.COM  * outputs:
397710588SEric.Taylor@Sun.COM  * zc_string	name of conflicting snapshot, if there is one
39785367Sahrens  */
39791544Seschrock static int
zfs_ioc_promote(zfs_cmd_t * zc)39802082Seschrock zfs_ioc_promote(zfs_cmd_t *zc)
39812082Seschrock {
39822417Sahrens 	char *cp;
39832417Sahrens 
39842417Sahrens 	/*
39852417Sahrens 	 * We don't need to unmount *all* the origin fs's snapshots, but
39862417Sahrens 	 * it's easier.
39872417Sahrens 	 */
39882676Seschrock 	cp = strchr(zc->zc_value, '@');
39892417Sahrens 	if (cp)
39902417Sahrens 		*cp = '\0';
39912676Seschrock 	(void) dmu_objset_find(zc->zc_value,
39922417Sahrens 	    zfs_unmount_snap, NULL, DS_FIND_SNAPSHOTS);
399310588SEric.Taylor@Sun.COM 	return (dsl_dataset_promote(zc->zc_name, zc->zc_string));
39942082Seschrock }
39952082Seschrock 
39964543Smarks /*
39979396SMatthew.Ahrens@Sun.COM  * Retrieve a single {user|group}{used|quota}@... property.
39989396SMatthew.Ahrens@Sun.COM  *
39999396SMatthew.Ahrens@Sun.COM  * inputs:
40009396SMatthew.Ahrens@Sun.COM  * zc_name	name of filesystem
40019396SMatthew.Ahrens@Sun.COM  * zc_objset_type zfs_userquota_prop_t
40029396SMatthew.Ahrens@Sun.COM  * zc_value	domain name (eg. "S-1-234-567-89")
40039396SMatthew.Ahrens@Sun.COM  * zc_guid	RID/UID/GID
40049396SMatthew.Ahrens@Sun.COM  *
40059396SMatthew.Ahrens@Sun.COM  * outputs:
40069396SMatthew.Ahrens@Sun.COM  * zc_cookie	property value
40079396SMatthew.Ahrens@Sun.COM  */
40089396SMatthew.Ahrens@Sun.COM static int
zfs_ioc_userspace_one(zfs_cmd_t * zc)40099396SMatthew.Ahrens@Sun.COM zfs_ioc_userspace_one(zfs_cmd_t *zc)
40109396SMatthew.Ahrens@Sun.COM {
40119396SMatthew.Ahrens@Sun.COM 	zfsvfs_t *zfsvfs;
40129396SMatthew.Ahrens@Sun.COM 	int error;
40139396SMatthew.Ahrens@Sun.COM 
40149396SMatthew.Ahrens@Sun.COM 	if (zc->zc_objset_type >= ZFS_NUM_USERQUOTA_PROPS)
40159396SMatthew.Ahrens@Sun.COM 		return (EINVAL);
40169396SMatthew.Ahrens@Sun.COM 
401712620SMark.Shellenbaum@Oracle.COM 	error = zfsvfs_hold(zc->zc_name, FTAG, &zfsvfs, B_FALSE);
40189396SMatthew.Ahrens@Sun.COM 	if (error)
40199396SMatthew.Ahrens@Sun.COM 		return (error);
40209396SMatthew.Ahrens@Sun.COM 
40219396SMatthew.Ahrens@Sun.COM 	error = zfs_userspace_one(zfsvfs,
40229396SMatthew.Ahrens@Sun.COM 	    zc->zc_objset_type, zc->zc_value, zc->zc_guid, &zc->zc_cookie);
40239396SMatthew.Ahrens@Sun.COM 	zfsvfs_rele(zfsvfs, FTAG);
40249396SMatthew.Ahrens@Sun.COM 
40259396SMatthew.Ahrens@Sun.COM 	return (error);
40269396SMatthew.Ahrens@Sun.COM }
40279396SMatthew.Ahrens@Sun.COM 
40289396SMatthew.Ahrens@Sun.COM /*
40299396SMatthew.Ahrens@Sun.COM  * inputs:
40309396SMatthew.Ahrens@Sun.COM  * zc_name		name of filesystem
40319396SMatthew.Ahrens@Sun.COM  * zc_cookie		zap cursor
40329396SMatthew.Ahrens@Sun.COM  * zc_objset_type	zfs_userquota_prop_t
40339396SMatthew.Ahrens@Sun.COM  * zc_nvlist_dst[_size] buffer to fill (not really an nvlist)
40349396SMatthew.Ahrens@Sun.COM  *
40359396SMatthew.Ahrens@Sun.COM  * outputs:
40369396SMatthew.Ahrens@Sun.COM  * zc_nvlist_dst[_size]	data buffer (array of zfs_useracct_t)
40379396SMatthew.Ahrens@Sun.COM  * zc_cookie	zap cursor
40389396SMatthew.Ahrens@Sun.COM  */
40399396SMatthew.Ahrens@Sun.COM static int
zfs_ioc_userspace_many(zfs_cmd_t * zc)40409396SMatthew.Ahrens@Sun.COM zfs_ioc_userspace_many(zfs_cmd_t *zc)
40419396SMatthew.Ahrens@Sun.COM {
40429396SMatthew.Ahrens@Sun.COM 	zfsvfs_t *zfsvfs;
404311933STim.Haley@Sun.COM 	int bufsize = zc->zc_nvlist_dst_size;
404411933STim.Haley@Sun.COM 
404511933STim.Haley@Sun.COM 	if (bufsize <= 0)
404611933STim.Haley@Sun.COM 		return (ENOMEM);
404711933STim.Haley@Sun.COM 
404812620SMark.Shellenbaum@Oracle.COM 	int error = zfsvfs_hold(zc->zc_name, FTAG, &zfsvfs, B_FALSE);
40499396SMatthew.Ahrens@Sun.COM 	if (error)
40509396SMatthew.Ahrens@Sun.COM 		return (error);
40519396SMatthew.Ahrens@Sun.COM 
40529396SMatthew.Ahrens@Sun.COM 	void *buf = kmem_alloc(bufsize, KM_SLEEP);
40539396SMatthew.Ahrens@Sun.COM 
40549396SMatthew.Ahrens@Sun.COM 	error = zfs_userspace_many(zfsvfs, zc->zc_objset_type, &zc->zc_cookie,
40559396SMatthew.Ahrens@Sun.COM 	    buf, &zc->zc_nvlist_dst_size);
40569396SMatthew.Ahrens@Sun.COM 
40579396SMatthew.Ahrens@Sun.COM 	if (error == 0) {
40589396SMatthew.Ahrens@Sun.COM 		error = xcopyout(buf,
40599396SMatthew.Ahrens@Sun.COM 		    (void *)(uintptr_t)zc->zc_nvlist_dst,
40609396SMatthew.Ahrens@Sun.COM 		    zc->zc_nvlist_dst_size);
40619396SMatthew.Ahrens@Sun.COM 	}
40629396SMatthew.Ahrens@Sun.COM 	kmem_free(buf, bufsize);
40639396SMatthew.Ahrens@Sun.COM 	zfsvfs_rele(zfsvfs, FTAG);
40649396SMatthew.Ahrens@Sun.COM 
40659396SMatthew.Ahrens@Sun.COM 	return (error);
40669396SMatthew.Ahrens@Sun.COM }
40679396SMatthew.Ahrens@Sun.COM 
40689396SMatthew.Ahrens@Sun.COM /*
40699396SMatthew.Ahrens@Sun.COM  * inputs:
40709396SMatthew.Ahrens@Sun.COM  * zc_name		name of filesystem
40719396SMatthew.Ahrens@Sun.COM  *
40729396SMatthew.Ahrens@Sun.COM  * outputs:
40739396SMatthew.Ahrens@Sun.COM  * none
40749396SMatthew.Ahrens@Sun.COM  */
40759396SMatthew.Ahrens@Sun.COM static int
zfs_ioc_userspace_upgrade(zfs_cmd_t * zc)40769396SMatthew.Ahrens@Sun.COM zfs_ioc_userspace_upgrade(zfs_cmd_t *zc)
40779396SMatthew.Ahrens@Sun.COM {
40789396SMatthew.Ahrens@Sun.COM 	objset_t *os;
407911422SMark.Musante@Sun.COM 	int error = 0;
40809396SMatthew.Ahrens@Sun.COM 	zfsvfs_t *zfsvfs;
40819396SMatthew.Ahrens@Sun.COM 
40829396SMatthew.Ahrens@Sun.COM 	if (getzfsvfs(zc->zc_name, &zfsvfs) == 0) {
408310298SMatthew.Ahrens@Sun.COM 		if (!dmu_objset_userused_enabled(zfsvfs->z_os)) {
40849396SMatthew.Ahrens@Sun.COM 			/*
40859396SMatthew.Ahrens@Sun.COM 			 * If userused is not enabled, it may be because the
40869396SMatthew.Ahrens@Sun.COM 			 * objset needs to be closed & reopened (to grow the
40879396SMatthew.Ahrens@Sun.COM 			 * objset_phys_t).  Suspend/resume the fs will do that.
40889396SMatthew.Ahrens@Sun.COM 			 */
408910298SMatthew.Ahrens@Sun.COM 			error = zfs_suspend_fs(zfsvfs);
409010298SMatthew.Ahrens@Sun.COM 			if (error == 0)
409110298SMatthew.Ahrens@Sun.COM 				error = zfs_resume_fs(zfsvfs, zc->zc_name);
40929396SMatthew.Ahrens@Sun.COM 		}
40939396SMatthew.Ahrens@Sun.COM 		if (error == 0)
40949396SMatthew.Ahrens@Sun.COM 			error = dmu_objset_userspace_upgrade(zfsvfs->z_os);
40959396SMatthew.Ahrens@Sun.COM 		VFS_RELE(zfsvfs->z_vfs);
40969396SMatthew.Ahrens@Sun.COM 	} else {
409710298SMatthew.Ahrens@Sun.COM 		/* XXX kind of reading contents without owning */
409810298SMatthew.Ahrens@Sun.COM 		error = dmu_objset_hold(zc->zc_name, FTAG, &os);
40999396SMatthew.Ahrens@Sun.COM 		if (error)
41009396SMatthew.Ahrens@Sun.COM 			return (error);
41019396SMatthew.Ahrens@Sun.COM 
41029396SMatthew.Ahrens@Sun.COM 		error = dmu_objset_userspace_upgrade(os);
410310298SMatthew.Ahrens@Sun.COM 		dmu_objset_rele(os, FTAG);
41049396SMatthew.Ahrens@Sun.COM 	}
41059396SMatthew.Ahrens@Sun.COM 
41069396SMatthew.Ahrens@Sun.COM 	return (error);
41079396SMatthew.Ahrens@Sun.COM }
41089396SMatthew.Ahrens@Sun.COM 
41099396SMatthew.Ahrens@Sun.COM /*
41104543Smarks  * We don't want to have a hard dependency
41114543Smarks  * against some special symbols in sharefs
41125331Samw  * nfs, and smbsrv.  Determine them if needed when
41134543Smarks  * the first file system is shared.
41145331Samw  * Neither sharefs, nfs or smbsrv are unloadable modules.
41154543Smarks  */
41165331Samw int (*znfsexport_fs)(void *arg);
41174543Smarks int (*zshare_fs)(enum sharefs_sys_op, share_t *, uint32_t);
41185331Samw int (*zsmbexport_fs)(void *arg, boolean_t add_share);
41195331Samw 
41205331Samw int zfs_nfsshare_inited;
41215331Samw int zfs_smbshare_inited;
41225331Samw 
41234543Smarks ddi_modhandle_t nfs_mod;
41244543Smarks ddi_modhandle_t sharefs_mod;
41255331Samw ddi_modhandle_t smbsrv_mod;
41264543Smarks kmutex_t zfs_share_lock;
41274543Smarks 
41284543Smarks static int
zfs_init_sharefs()41295331Samw zfs_init_sharefs()
41305331Samw {
41315331Samw 	int error;
41325331Samw 
41335331Samw 	ASSERT(MUTEX_HELD(&zfs_share_lock));
41345331Samw 	/* Both NFS and SMB shares also require sharetab support. */
41355331Samw 	if (sharefs_mod == NULL && ((sharefs_mod =
41365331Samw 	    ddi_modopen("fs/sharefs",
41375331Samw 	    KRTLD_MODE_FIRST, &error)) == NULL)) {
41385331Samw 		return (ENOSYS);
41395331Samw 	}
41405331Samw 	if (zshare_fs == NULL && ((zshare_fs =
41415331Samw 	    (int (*)(enum sharefs_sys_op, share_t *, uint32_t))
41425331Samw 	    ddi_modsym(sharefs_mod, "sharefs_impl", &error)) == NULL)) {
41435331Samw 		return (ENOSYS);
41445331Samw 	}
41455331Samw 	return (0);
41465331Samw }
41475331Samw 
41485331Samw static int
zfs_ioc_share(zfs_cmd_t * zc)41494543Smarks zfs_ioc_share(zfs_cmd_t *zc)
41504543Smarks {
41514543Smarks 	int error;
41524543Smarks 	int opcode;
41534543Smarks 
41545331Samw 	switch (zc->zc_share.z_sharetype) {
41555331Samw 	case ZFS_SHARE_NFS:
41565331Samw 	case ZFS_UNSHARE_NFS:
41575331Samw 		if (zfs_nfsshare_inited == 0) {
41585331Samw 			mutex_enter(&zfs_share_lock);
41595331Samw 			if (nfs_mod == NULL && ((nfs_mod = ddi_modopen("fs/nfs",
41605331Samw 			    KRTLD_MODE_FIRST, &error)) == NULL)) {
41615331Samw 				mutex_exit(&zfs_share_lock);
41625331Samw 				return (ENOSYS);
41635331Samw 			}
41645331Samw 			if (znfsexport_fs == NULL &&
41655331Samw 			    ((znfsexport_fs = (int (*)(void *))
41665331Samw 			    ddi_modsym(nfs_mod,
41675331Samw 			    "nfs_export", &error)) == NULL)) {
41685331Samw 				mutex_exit(&zfs_share_lock);
41695331Samw 				return (ENOSYS);
41705331Samw 			}
41715331Samw 			error = zfs_init_sharefs();
41725331Samw 			if (error) {
41735331Samw 				mutex_exit(&zfs_share_lock);
41745331Samw 				return (ENOSYS);
41755331Samw 			}
41765331Samw 			zfs_nfsshare_inited = 1;
41774543Smarks 			mutex_exit(&zfs_share_lock);
41784543Smarks 		}
41795331Samw 		break;
41805331Samw 	case ZFS_SHARE_SMB:
41815331Samw 	case ZFS_UNSHARE_SMB:
41825331Samw 		if (zfs_smbshare_inited == 0) {
41835331Samw 			mutex_enter(&zfs_share_lock);
41845331Samw 			if (smbsrv_mod == NULL && ((smbsrv_mod =
41855331Samw 			    ddi_modopen("drv/smbsrv",
41865331Samw 			    KRTLD_MODE_FIRST, &error)) == NULL)) {
41875331Samw 				mutex_exit(&zfs_share_lock);
41885331Samw 				return (ENOSYS);
41895331Samw 			}
41905331Samw 			if (zsmbexport_fs == NULL && ((zsmbexport_fs =
41915331Samw 			    (int (*)(void *, boolean_t))ddi_modsym(smbsrv_mod,
41926139Sjb150015 			    "smb_server_share", &error)) == NULL)) {
41935331Samw 				mutex_exit(&zfs_share_lock);
41945331Samw 				return (ENOSYS);
41955331Samw 			}
41965331Samw 			error = zfs_init_sharefs();
41975331Samw 			if (error) {
41985331Samw 				mutex_exit(&zfs_share_lock);
41995331Samw 				return (ENOSYS);
42005331Samw 			}
42015331Samw 			zfs_smbshare_inited = 1;
42024543Smarks 			mutex_exit(&zfs_share_lock);
42034543Smarks 		}
42045331Samw 		break;
42055331Samw 	default:
42065331Samw 		return (EINVAL);
42074543Smarks 	}
42084543Smarks 
42095331Samw 	switch (zc->zc_share.z_sharetype) {
42105331Samw 	case ZFS_SHARE_NFS:
42115331Samw 	case ZFS_UNSHARE_NFS:
42125331Samw 		if (error =
42135331Samw 		    znfsexport_fs((void *)
42145331Samw 		    (uintptr_t)zc->zc_share.z_exportdata))
42155331Samw 			return (error);
42165331Samw 		break;
42175331Samw 	case ZFS_SHARE_SMB:
42185331Samw 	case ZFS_UNSHARE_SMB:
42195331Samw 		if (error = zsmbexport_fs((void *)
42205331Samw 		    (uintptr_t)zc->zc_share.z_exportdata,
42215331Samw 		    zc->zc_share.z_sharetype == ZFS_SHARE_SMB ?
42228845Samw@Sun.COM 		    B_TRUE: B_FALSE)) {
42235331Samw 			return (error);
42245331Samw 		}
42255331Samw 		break;
42265331Samw 	}
42275331Samw 
42285331Samw 	opcode = (zc->zc_share.z_sharetype == ZFS_SHARE_NFS ||
42295331Samw 	    zc->zc_share.z_sharetype == ZFS_SHARE_SMB) ?
42304543Smarks 	    SHAREFS_ADD : SHAREFS_REMOVE;
42314543Smarks 
42325331Samw 	/*
42335331Samw 	 * Add or remove share from sharetab
42345331Samw 	 */
42354543Smarks 	error = zshare_fs(opcode,
42364543Smarks 	    (void *)(uintptr_t)zc->zc_share.z_sharedata,
42374543Smarks 	    zc->zc_share.z_sharemax);
42384543Smarks 
42394543Smarks 	return (error);
42404543Smarks 
42414543Smarks }
42424543Smarks 
42438845Samw@Sun.COM ace_t full_access[] = {
42448845Samw@Sun.COM 	{(uid_t)-1, ACE_ALL_PERMS, ACE_EVERYONE, 0}
42458845Samw@Sun.COM };
42468845Samw@Sun.COM 
42478845Samw@Sun.COM /*
424813043STim.Haley@Sun.COM  * inputs:
424913043STim.Haley@Sun.COM  * zc_name		name of containing filesystem
425013043STim.Haley@Sun.COM  * zc_obj		object # beyond which we want next in-use object #
425113043STim.Haley@Sun.COM  *
425213043STim.Haley@Sun.COM  * outputs:
425313043STim.Haley@Sun.COM  * zc_obj		next in-use object #
425413043STim.Haley@Sun.COM  */
425513043STim.Haley@Sun.COM static int
zfs_ioc_next_obj(zfs_cmd_t * zc)425613043STim.Haley@Sun.COM zfs_ioc_next_obj(zfs_cmd_t *zc)
425713043STim.Haley@Sun.COM {
425813043STim.Haley@Sun.COM 	objset_t *os = NULL;
425913043STim.Haley@Sun.COM 	int error;
426013043STim.Haley@Sun.COM 
426113043STim.Haley@Sun.COM 	error = dmu_objset_hold(zc->zc_name, FTAG, &os);
426213043STim.Haley@Sun.COM 	if (error)
426313043STim.Haley@Sun.COM 		return (error);
426413043STim.Haley@Sun.COM 
426513043STim.Haley@Sun.COM 	error = dmu_object_next(os, &zc->zc_obj, B_FALSE,
426613043STim.Haley@Sun.COM 	    os->os_dsl_dataset->ds_phys->ds_prev_snap_txg);
426713043STim.Haley@Sun.COM 
426813043STim.Haley@Sun.COM 	dmu_objset_rele(os, FTAG);
426913043STim.Haley@Sun.COM 	return (error);
427013043STim.Haley@Sun.COM }
427113043STim.Haley@Sun.COM 
427213043STim.Haley@Sun.COM /*
427313043STim.Haley@Sun.COM  * inputs:
427413043STim.Haley@Sun.COM  * zc_name		name of filesystem
427513043STim.Haley@Sun.COM  * zc_value		prefix name for snapshot
427613043STim.Haley@Sun.COM  * zc_cleanup_fd	cleanup-on-exit file descriptor for calling process
427713043STim.Haley@Sun.COM  *
427813043STim.Haley@Sun.COM  * outputs:
427913043STim.Haley@Sun.COM  */
428013043STim.Haley@Sun.COM static int
zfs_ioc_tmp_snapshot(zfs_cmd_t * zc)428113043STim.Haley@Sun.COM zfs_ioc_tmp_snapshot(zfs_cmd_t *zc)
428213043STim.Haley@Sun.COM {
428313043STim.Haley@Sun.COM 	char *snap_name;
428413043STim.Haley@Sun.COM 	int error;
428513043STim.Haley@Sun.COM 
428613043STim.Haley@Sun.COM 	snap_name = kmem_asprintf("%s-%016llx", zc->zc_value,
428713043STim.Haley@Sun.COM 	    (u_longlong_t)ddi_get_lbolt64());
428813043STim.Haley@Sun.COM 
428913043STim.Haley@Sun.COM 	if (strlen(snap_name) >= MAXNAMELEN) {
429013043STim.Haley@Sun.COM 		strfree(snap_name);
429113043STim.Haley@Sun.COM 		return (E2BIG);
429213043STim.Haley@Sun.COM 	}
429313043STim.Haley@Sun.COM 
429413043STim.Haley@Sun.COM 	error = dmu_objset_snapshot(zc->zc_name, snap_name, snap_name,
429513043STim.Haley@Sun.COM 	    NULL, B_FALSE, B_TRUE, zc->zc_cleanup_fd);
429613043STim.Haley@Sun.COM 	if (error != 0) {
429713043STim.Haley@Sun.COM 		strfree(snap_name);
429813043STim.Haley@Sun.COM 		return (error);
429913043STim.Haley@Sun.COM 	}
430013043STim.Haley@Sun.COM 
430113043STim.Haley@Sun.COM 	(void) strcpy(zc->zc_value, snap_name);
430213043STim.Haley@Sun.COM 	strfree(snap_name);
430313043STim.Haley@Sun.COM 	return (0);
430413043STim.Haley@Sun.COM }
430513043STim.Haley@Sun.COM 
430613043STim.Haley@Sun.COM /*
430713043STim.Haley@Sun.COM  * inputs:
430813043STim.Haley@Sun.COM  * zc_name		name of "to" snapshot
430913043STim.Haley@Sun.COM  * zc_value		name of "from" snapshot
431013043STim.Haley@Sun.COM  * zc_cookie		file descriptor to write diff data on
431113043STim.Haley@Sun.COM  *
431213043STim.Haley@Sun.COM  * outputs:
431313043STim.Haley@Sun.COM  * dmu_diff_record_t's to the file descriptor
431413043STim.Haley@Sun.COM  */
431513043STim.Haley@Sun.COM static int
zfs_ioc_diff(zfs_cmd_t * zc)431613043STim.Haley@Sun.COM zfs_ioc_diff(zfs_cmd_t *zc)
431713043STim.Haley@Sun.COM {
431813043STim.Haley@Sun.COM 	objset_t *fromsnap;
431913043STim.Haley@Sun.COM 	objset_t *tosnap;
432013043STim.Haley@Sun.COM 	file_t *fp;
432113043STim.Haley@Sun.COM 	offset_t off;
432213043STim.Haley@Sun.COM 	int error;
432313043STim.Haley@Sun.COM 
432413043STim.Haley@Sun.COM 	error = dmu_objset_hold(zc->zc_name, FTAG, &tosnap);
432513043STim.Haley@Sun.COM 	if (error)
432613043STim.Haley@Sun.COM 		return (error);
432713043STim.Haley@Sun.COM 
432813043STim.Haley@Sun.COM 	error = dmu_objset_hold(zc->zc_value, FTAG, &fromsnap);
432913043STim.Haley@Sun.COM 	if (error) {
433013043STim.Haley@Sun.COM 		dmu_objset_rele(tosnap, FTAG);
433113043STim.Haley@Sun.COM 		return (error);
433213043STim.Haley@Sun.COM 	}
433313043STim.Haley@Sun.COM 
433413043STim.Haley@Sun.COM 	fp = getf(zc->zc_cookie);
433513043STim.Haley@Sun.COM 	if (fp == NULL) {
433613043STim.Haley@Sun.COM 		dmu_objset_rele(fromsnap, FTAG);
433713043STim.Haley@Sun.COM 		dmu_objset_rele(tosnap, FTAG);
433813043STim.Haley@Sun.COM 		return (EBADF);
433913043STim.Haley@Sun.COM 	}
434013043STim.Haley@Sun.COM 
434113043STim.Haley@Sun.COM 	off = fp->f_offset;
434213043STim.Haley@Sun.COM 
434313043STim.Haley@Sun.COM 	error = dmu_diff(tosnap, fromsnap, fp->f_vnode, &off);
434413043STim.Haley@Sun.COM 
434513043STim.Haley@Sun.COM 	if (VOP_SEEK(fp->f_vnode, fp->f_offset, &off, NULL) == 0)
434613043STim.Haley@Sun.COM 		fp->f_offset = off;
434713043STim.Haley@Sun.COM 	releasef(zc->zc_cookie);
434813043STim.Haley@Sun.COM 
434913043STim.Haley@Sun.COM 	dmu_objset_rele(fromsnap, FTAG);
435013043STim.Haley@Sun.COM 	dmu_objset_rele(tosnap, FTAG);
435113043STim.Haley@Sun.COM 	return (error);
435213043STim.Haley@Sun.COM }
435313043STim.Haley@Sun.COM 
435413043STim.Haley@Sun.COM /*
43558845Samw@Sun.COM  * Remove all ACL files in shares dir
43568845Samw@Sun.COM  */
43578845Samw@Sun.COM static int
zfs_smb_acl_purge(znode_t * dzp)43588845Samw@Sun.COM zfs_smb_acl_purge(znode_t *dzp)
43598845Samw@Sun.COM {
43608845Samw@Sun.COM 	zap_cursor_t	zc;
43618845Samw@Sun.COM 	zap_attribute_t	zap;
43628845Samw@Sun.COM 	zfsvfs_t *zfsvfs = dzp->z_zfsvfs;
43638845Samw@Sun.COM 	int error;
43648845Samw@Sun.COM 
43658845Samw@Sun.COM 	for (zap_cursor_init(&zc, zfsvfs->z_os, dzp->z_id);
43668845Samw@Sun.COM 	    (error = zap_cursor_retrieve(&zc, &zap)) == 0;
43678845Samw@Sun.COM 	    zap_cursor_advance(&zc)) {
43688845Samw@Sun.COM 		if ((error = VOP_REMOVE(ZTOV(dzp), zap.za_name, kcred,
43698845Samw@Sun.COM 		    NULL, 0)) != 0)
43708845Samw@Sun.COM 			break;
43718845Samw@Sun.COM 	}
43728845Samw@Sun.COM 	zap_cursor_fini(&zc);
43738845Samw@Sun.COM 	return (error);
43748845Samw@Sun.COM }
43758845Samw@Sun.COM 
43768845Samw@Sun.COM static int
zfs_ioc_smb_acl(zfs_cmd_t * zc)43778845Samw@Sun.COM zfs_ioc_smb_acl(zfs_cmd_t *zc)
43788845Samw@Sun.COM {
43798845Samw@Sun.COM 	vnode_t *vp;
43808845Samw@Sun.COM 	znode_t *dzp;
43818845Samw@Sun.COM 	vnode_t *resourcevp = NULL;
43828845Samw@Sun.COM 	znode_t *sharedir;
43838845Samw@Sun.COM 	zfsvfs_t *zfsvfs;
43848845Samw@Sun.COM 	nvlist_t *nvlist;
43858845Samw@Sun.COM 	char *src, *target;
43868845Samw@Sun.COM 	vattr_t vattr;
43878845Samw@Sun.COM 	vsecattr_t vsec;
43888845Samw@Sun.COM 	int error = 0;
43898845Samw@Sun.COM 
43908845Samw@Sun.COM 	if ((error = lookupname(zc->zc_value, UIO_SYSSPACE,
43918845Samw@Sun.COM 	    NO_FOLLOW, NULL, &vp)) != 0)
43928845Samw@Sun.COM 		return (error);
43938845Samw@Sun.COM 
43948845Samw@Sun.COM 	/* Now make sure mntpnt and dataset are ZFS */
43958845Samw@Sun.COM 
43968845Samw@Sun.COM 	if (vp->v_vfsp->vfs_fstype != zfsfstype ||
43978845Samw@Sun.COM 	    (strcmp((char *)refstr_value(vp->v_vfsp->vfs_resource),
43988845Samw@Sun.COM 	    zc->zc_name) != 0)) {
43998845Samw@Sun.COM 		VN_RELE(vp);
44008845Samw@Sun.COM 		return (EINVAL);
44018845Samw@Sun.COM 	}
44028845Samw@Sun.COM 
44038845Samw@Sun.COM 	dzp = VTOZ(vp);
44048845Samw@Sun.COM 	zfsvfs = dzp->z_zfsvfs;
44058845Samw@Sun.COM 	ZFS_ENTER(zfsvfs);
44068845Samw@Sun.COM 
44079030SMark.Shellenbaum@Sun.COM 	/*
44089030SMark.Shellenbaum@Sun.COM 	 * Create share dir if its missing.
44099030SMark.Shellenbaum@Sun.COM 	 */
44109030SMark.Shellenbaum@Sun.COM 	mutex_enter(&zfsvfs->z_lock);
44119030SMark.Shellenbaum@Sun.COM 	if (zfsvfs->z_shares_dir == 0) {
44129030SMark.Shellenbaum@Sun.COM 		dmu_tx_t *tx;
44139030SMark.Shellenbaum@Sun.COM 
44149030SMark.Shellenbaum@Sun.COM 		tx = dmu_tx_create(zfsvfs->z_os);
44159030SMark.Shellenbaum@Sun.COM 		dmu_tx_hold_zap(tx, MASTER_NODE_OBJ, TRUE,
44169030SMark.Shellenbaum@Sun.COM 		    ZFS_SHARES_DIR);
44179030SMark.Shellenbaum@Sun.COM 		dmu_tx_hold_zap(tx, DMU_NEW_OBJECT, FALSE, NULL);
44189030SMark.Shellenbaum@Sun.COM 		error = dmu_tx_assign(tx, TXG_WAIT);
44199030SMark.Shellenbaum@Sun.COM 		if (error) {
44209030SMark.Shellenbaum@Sun.COM 			dmu_tx_abort(tx);
44219030SMark.Shellenbaum@Sun.COM 		} else {
44229030SMark.Shellenbaum@Sun.COM 			error = zfs_create_share_dir(zfsvfs, tx);
44239030SMark.Shellenbaum@Sun.COM 			dmu_tx_commit(tx);
44249030SMark.Shellenbaum@Sun.COM 		}
44259030SMark.Shellenbaum@Sun.COM 		if (error) {
44269030SMark.Shellenbaum@Sun.COM 			mutex_exit(&zfsvfs->z_lock);
44279030SMark.Shellenbaum@Sun.COM 			VN_RELE(vp);
44289030SMark.Shellenbaum@Sun.COM 			ZFS_EXIT(zfsvfs);
44299030SMark.Shellenbaum@Sun.COM 			return (error);
44309030SMark.Shellenbaum@Sun.COM 		}
44319030SMark.Shellenbaum@Sun.COM 	}
44329030SMark.Shellenbaum@Sun.COM 	mutex_exit(&zfsvfs->z_lock);
44339030SMark.Shellenbaum@Sun.COM 
44349030SMark.Shellenbaum@Sun.COM 	ASSERT(zfsvfs->z_shares_dir);
44358845Samw@Sun.COM 	if ((error = zfs_zget(zfsvfs, zfsvfs->z_shares_dir, &sharedir)) != 0) {
44369030SMark.Shellenbaum@Sun.COM 		VN_RELE(vp);
44378845Samw@Sun.COM 		ZFS_EXIT(zfsvfs);
44388845Samw@Sun.COM 		return (error);
44398845Samw@Sun.COM 	}
44408845Samw@Sun.COM 
44418845Samw@Sun.COM 	switch (zc->zc_cookie) {
44428845Samw@Sun.COM 	case ZFS_SMB_ACL_ADD:
44438845Samw@Sun.COM 		vattr.va_mask = AT_MODE|AT_UID|AT_GID|AT_TYPE;
44448845Samw@Sun.COM 		vattr.va_type = VREG;
44458845Samw@Sun.COM 		vattr.va_mode = S_IFREG|0777;
44468845Samw@Sun.COM 		vattr.va_uid = 0;
44478845Samw@Sun.COM 		vattr.va_gid = 0;
44488845Samw@Sun.COM 
44498845Samw@Sun.COM 		vsec.vsa_mask = VSA_ACE;
44508845Samw@Sun.COM 		vsec.vsa_aclentp = &full_access;
44518845Samw@Sun.COM 		vsec.vsa_aclentsz = sizeof (full_access);
44528845Samw@Sun.COM 		vsec.vsa_aclcnt = 1;
44538845Samw@Sun.COM 
44548845Samw@Sun.COM 		error = VOP_CREATE(ZTOV(sharedir), zc->zc_string,
44558845Samw@Sun.COM 		    &vattr, EXCL, 0, &resourcevp, kcred, 0, NULL, &vsec);
44568845Samw@Sun.COM 		if (resourcevp)
44578845Samw@Sun.COM 			VN_RELE(resourcevp);
44588845Samw@Sun.COM 		break;
44598845Samw@Sun.COM 
44608845Samw@Sun.COM 	case ZFS_SMB_ACL_REMOVE:
44618845Samw@Sun.COM 		error = VOP_REMOVE(ZTOV(sharedir), zc->zc_string, kcred,
44628845Samw@Sun.COM 		    NULL, 0);
44638845Samw@Sun.COM 		break;
44648845Samw@Sun.COM 
44658845Samw@Sun.COM 	case ZFS_SMB_ACL_RENAME:
44668845Samw@Sun.COM 		if ((error = get_nvlist(zc->zc_nvlist_src,
44679643SEric.Taylor@Sun.COM 		    zc->zc_nvlist_src_size, zc->zc_iflags, &nvlist)) != 0) {
44688845Samw@Sun.COM 			VN_RELE(vp);
44698845Samw@Sun.COM 			ZFS_EXIT(zfsvfs);
44708845Samw@Sun.COM 			return (error);
44718845Samw@Sun.COM 		}
44728845Samw@Sun.COM 		if (nvlist_lookup_string(nvlist, ZFS_SMB_ACL_SRC, &src) ||
44738845Samw@Sun.COM 		    nvlist_lookup_string(nvlist, ZFS_SMB_ACL_TARGET,
44748845Samw@Sun.COM 		    &target)) {
44758845Samw@Sun.COM 			VN_RELE(vp);
44769179SMark.Shellenbaum@Sun.COM 			VN_RELE(ZTOV(sharedir));
44778845Samw@Sun.COM 			ZFS_EXIT(zfsvfs);
447811422SMark.Musante@Sun.COM 			nvlist_free(nvlist);
44798845Samw@Sun.COM 			return (error);
44808845Samw@Sun.COM 		}
44818845Samw@Sun.COM 		error = VOP_RENAME(ZTOV(sharedir), src, ZTOV(sharedir), target,
44828845Samw@Sun.COM 		    kcred, NULL, 0);
44838845Samw@Sun.COM 		nvlist_free(nvlist);
44848845Samw@Sun.COM 		break;
44858845Samw@Sun.COM 
44868845Samw@Sun.COM 	case ZFS_SMB_ACL_PURGE:
44878845Samw@Sun.COM 		error = zfs_smb_acl_purge(sharedir);
44888845Samw@Sun.COM 		break;
44898845Samw@Sun.COM 
44908845Samw@Sun.COM 	default:
44918845Samw@Sun.COM 		error = EINVAL;
44928845Samw@Sun.COM 		break;
44938845Samw@Sun.COM 	}
44948845Samw@Sun.COM 
44958845Samw@Sun.COM 	VN_RELE(vp);
44968845Samw@Sun.COM 	VN_RELE(ZTOV(sharedir));
44978845Samw@Sun.COM 
44988845Samw@Sun.COM 	ZFS_EXIT(zfsvfs);
44998845Samw@Sun.COM 
45008845Samw@Sun.COM 	return (error);
45018845Samw@Sun.COM }
45028845Samw@Sun.COM 
45034543Smarks /*
450410242Schris.kirby@sun.com  * inputs:
450512527SChris.Kirby@oracle.com  * zc_name		name of filesystem
450612527SChris.Kirby@oracle.com  * zc_value		short name of snap
450712527SChris.Kirby@oracle.com  * zc_string		user-supplied tag for this hold
450812527SChris.Kirby@oracle.com  * zc_cookie		recursive flag
450912527SChris.Kirby@oracle.com  * zc_temphold		set if hold is temporary
451012527SChris.Kirby@oracle.com  * zc_cleanup_fd	cleanup-on-exit file descriptor for calling process
451112786SChris.Kirby@oracle.com  * zc_sendobj		if non-zero, the objid for zc_name@zc_value
451212786SChris.Kirby@oracle.com  * zc_createtxg		if zc_sendobj is non-zero, snap must have zc_createtxg
451310242Schris.kirby@sun.com  *
451410242Schris.kirby@sun.com  * outputs:		none
451510242Schris.kirby@sun.com  */
451610242Schris.kirby@sun.com static int
zfs_ioc_hold(zfs_cmd_t * zc)451710242Schris.kirby@sun.com zfs_ioc_hold(zfs_cmd_t *zc)
451810242Schris.kirby@sun.com {
451910242Schris.kirby@sun.com 	boolean_t recursive = zc->zc_cookie;
452012786SChris.Kirby@oracle.com 	spa_t *spa;
452112786SChris.Kirby@oracle.com 	dsl_pool_t *dp;
452212786SChris.Kirby@oracle.com 	dsl_dataset_t *ds;
452312786SChris.Kirby@oracle.com 	int error;
452412786SChris.Kirby@oracle.com 	minor_t minor = 0;
452510242Schris.kirby@sun.com 
452610242Schris.kirby@sun.com 	if (snapshot_namecheck(zc->zc_value, NULL, NULL) != 0)
452710242Schris.kirby@sun.com 		return (EINVAL);
452810242Schris.kirby@sun.com 
452912786SChris.Kirby@oracle.com 	if (zc->zc_sendobj == 0) {
453012786SChris.Kirby@oracle.com 		return (dsl_dataset_user_hold(zc->zc_name, zc->zc_value,
453112786SChris.Kirby@oracle.com 		    zc->zc_string, recursive, zc->zc_temphold,
453212786SChris.Kirby@oracle.com 		    zc->zc_cleanup_fd));
453312786SChris.Kirby@oracle.com 	}
453412786SChris.Kirby@oracle.com 
453512786SChris.Kirby@oracle.com 	if (recursive)
453612786SChris.Kirby@oracle.com 		return (EINVAL);
453712786SChris.Kirby@oracle.com 
453812786SChris.Kirby@oracle.com 	error = spa_open(zc->zc_name, &spa, FTAG);
453912786SChris.Kirby@oracle.com 	if (error)
454012786SChris.Kirby@oracle.com 		return (error);
454112786SChris.Kirby@oracle.com 
454212786SChris.Kirby@oracle.com 	dp = spa_get_dsl(spa);
454312786SChris.Kirby@oracle.com 	rw_enter(&dp->dp_config_rwlock, RW_READER);
454412786SChris.Kirby@oracle.com 	error = dsl_dataset_hold_obj(dp, zc->zc_sendobj, FTAG, &ds);
454512786SChris.Kirby@oracle.com 	rw_exit(&dp->dp_config_rwlock);
454612786SChris.Kirby@oracle.com 	spa_close(spa, FTAG);
454712786SChris.Kirby@oracle.com 	if (error)
454812786SChris.Kirby@oracle.com 		return (error);
454912786SChris.Kirby@oracle.com 
455012786SChris.Kirby@oracle.com 	/*
455112786SChris.Kirby@oracle.com 	 * Until we have a hold on this snapshot, it's possible that
455212786SChris.Kirby@oracle.com 	 * zc_sendobj could've been destroyed and reused as part
455312786SChris.Kirby@oracle.com 	 * of a later txg.  Make sure we're looking at the right object.
455412786SChris.Kirby@oracle.com 	 */
455512786SChris.Kirby@oracle.com 	if (zc->zc_createtxg != ds->ds_phys->ds_creation_txg) {
455612786SChris.Kirby@oracle.com 		dsl_dataset_rele(ds, FTAG);
455712786SChris.Kirby@oracle.com 		return (ENOENT);
455812786SChris.Kirby@oracle.com 	}
455912786SChris.Kirby@oracle.com 
456012786SChris.Kirby@oracle.com 	if (zc->zc_cleanup_fd != -1 && zc->zc_temphold) {
456112786SChris.Kirby@oracle.com 		error = zfs_onexit_fd_hold(zc->zc_cleanup_fd, &minor);
456212786SChris.Kirby@oracle.com 		if (error) {
456312786SChris.Kirby@oracle.com 			dsl_dataset_rele(ds, FTAG);
456412786SChris.Kirby@oracle.com 			return (error);
456512786SChris.Kirby@oracle.com 		}
456612786SChris.Kirby@oracle.com 	}
456712786SChris.Kirby@oracle.com 
456812786SChris.Kirby@oracle.com 	error = dsl_dataset_user_hold_for_send(ds, zc->zc_string,
456912786SChris.Kirby@oracle.com 	    zc->zc_temphold);
457012786SChris.Kirby@oracle.com 	if (minor != 0) {
457112786SChris.Kirby@oracle.com 		if (error == 0) {
457212786SChris.Kirby@oracle.com 			dsl_register_onexit_hold_cleanup(ds, zc->zc_string,
457312786SChris.Kirby@oracle.com 			    minor);
457412786SChris.Kirby@oracle.com 		}
457512786SChris.Kirby@oracle.com 		zfs_onexit_fd_rele(zc->zc_cleanup_fd);
457612786SChris.Kirby@oracle.com 	}
457712786SChris.Kirby@oracle.com 	dsl_dataset_rele(ds, FTAG);
457812786SChris.Kirby@oracle.com 
457912786SChris.Kirby@oracle.com 	return (error);
458010242Schris.kirby@sun.com }
458110242Schris.kirby@sun.com 
458210242Schris.kirby@sun.com /*
458310242Schris.kirby@sun.com  * inputs:
458412527SChris.Kirby@oracle.com  * zc_name	name of dataset from which we're releasing a user hold
458510242Schris.kirby@sun.com  * zc_value	short name of snap
458612527SChris.Kirby@oracle.com  * zc_string	user-supplied tag for this hold
458710242Schris.kirby@sun.com  * zc_cookie	recursive flag
458810242Schris.kirby@sun.com  *
458912527SChris.Kirby@oracle.com  * outputs:	none
459010242Schris.kirby@sun.com  */
459110242Schris.kirby@sun.com static int
zfs_ioc_release(zfs_cmd_t * zc)459210242Schris.kirby@sun.com zfs_ioc_release(zfs_cmd_t *zc)
459310242Schris.kirby@sun.com {
459410242Schris.kirby@sun.com 	boolean_t recursive = zc->zc_cookie;
459510242Schris.kirby@sun.com 
459610242Schris.kirby@sun.com 	if (snapshot_namecheck(zc->zc_value, NULL, NULL) != 0)
459710242Schris.kirby@sun.com 		return (EINVAL);
459810242Schris.kirby@sun.com 
459910242Schris.kirby@sun.com 	return (dsl_dataset_user_release(zc->zc_name, zc->zc_value,
460010242Schris.kirby@sun.com 	    zc->zc_string, recursive));
460110242Schris.kirby@sun.com }
460210242Schris.kirby@sun.com 
460310242Schris.kirby@sun.com /*
460410242Schris.kirby@sun.com  * inputs:
460510242Schris.kirby@sun.com  * zc_name		name of filesystem
460610242Schris.kirby@sun.com  *
460710242Schris.kirby@sun.com  * outputs:
460810242Schris.kirby@sun.com  * zc_nvlist_src{_size}	nvlist of snapshot holds
460910242Schris.kirby@sun.com  */
461010242Schris.kirby@sun.com static int
zfs_ioc_get_holds(zfs_cmd_t * zc)461110242Schris.kirby@sun.com zfs_ioc_get_holds(zfs_cmd_t *zc)
461210242Schris.kirby@sun.com {
461310242Schris.kirby@sun.com 	nvlist_t *nvp;
461410242Schris.kirby@sun.com 	int error;
461510242Schris.kirby@sun.com 
461610242Schris.kirby@sun.com 	if ((error = dsl_dataset_get_holds(zc->zc_name, &nvp)) == 0) {
461710242Schris.kirby@sun.com 		error = put_nvlist(zc, nvp);
461810242Schris.kirby@sun.com 		nvlist_free(nvp);
461910242Schris.kirby@sun.com 	}
462010242Schris.kirby@sun.com 
462110242Schris.kirby@sun.com 	return (error);
462210242Schris.kirby@sun.com }
462310242Schris.kirby@sun.com 
462410242Schris.kirby@sun.com /*
46254988Sek110237  * pool create, destroy, and export don't log the history as part of
46264988Sek110237  * zfsdev_ioctl, but rather zfs_ioc_pool_create, and zfs_ioc_pool_export
46274988Sek110237  * do the logging of those commands.
46284543Smarks  */
4629789Sahrens static zfs_ioc_vec_t zfs_ioc_vec[] = {
46309234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_pool_create, zfs_secpolicy_config, POOL_NAME, B_FALSE,
4631*13049SGeorge.Wilson@Sun.COM 	    POOL_CHECK_NONE },
46329234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_pool_destroy,	zfs_secpolicy_config, POOL_NAME, B_FALSE,
4633*13049SGeorge.Wilson@Sun.COM 	    POOL_CHECK_NONE },
46349234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_pool_import, zfs_secpolicy_config, POOL_NAME, B_TRUE,
4635*13049SGeorge.Wilson@Sun.COM 	    POOL_CHECK_NONE },
46369234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_pool_export, zfs_secpolicy_config, POOL_NAME, B_FALSE,
4637*13049SGeorge.Wilson@Sun.COM 	    POOL_CHECK_NONE },
46389234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_pool_configs,	zfs_secpolicy_none, NO_NAME, B_FALSE,
4639*13049SGeorge.Wilson@Sun.COM 	    POOL_CHECK_NONE },
46409234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_pool_stats, zfs_secpolicy_read, POOL_NAME, B_FALSE,
4641*13049SGeorge.Wilson@Sun.COM 	    POOL_CHECK_NONE },
46429234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_pool_tryimport, zfs_secpolicy_config, NO_NAME, B_FALSE,
4643*13049SGeorge.Wilson@Sun.COM 	    POOL_CHECK_NONE },
464412296SLin.Ling@Sun.COM 	{ zfs_ioc_pool_scan, zfs_secpolicy_config, POOL_NAME, B_TRUE,
4645*13049SGeorge.Wilson@Sun.COM 	    POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY },
46469234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_pool_freeze, zfs_secpolicy_config, NO_NAME, B_FALSE,
4647*13049SGeorge.Wilson@Sun.COM 	    POOL_CHECK_READONLY },
46489234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_pool_upgrade,	zfs_secpolicy_config, POOL_NAME, B_TRUE,
4649*13049SGeorge.Wilson@Sun.COM 	    POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY },
46509234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_pool_get_history, zfs_secpolicy_config, POOL_NAME, B_FALSE,
4651*13049SGeorge.Wilson@Sun.COM 	    POOL_CHECK_NONE },
46529234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_vdev_add, zfs_secpolicy_config, POOL_NAME, B_TRUE,
4653*13049SGeorge.Wilson@Sun.COM 	    POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY },
46549234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_vdev_remove, zfs_secpolicy_config, POOL_NAME, B_TRUE,
4655*13049SGeorge.Wilson@Sun.COM 	    POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY },
46569234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_vdev_set_state, zfs_secpolicy_config,	POOL_NAME, B_TRUE,
4657*13049SGeorge.Wilson@Sun.COM 	    POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY },
46589234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_vdev_attach, zfs_secpolicy_config, POOL_NAME, B_TRUE,
4659*13049SGeorge.Wilson@Sun.COM 	    POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY },
46609234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_vdev_detach, zfs_secpolicy_config, POOL_NAME, B_TRUE,
4661*13049SGeorge.Wilson@Sun.COM 	    POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY },
46629234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_vdev_setpath,	zfs_secpolicy_config, POOL_NAME, B_FALSE,
4663*13049SGeorge.Wilson@Sun.COM 	    POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY },
46649425SEric.Schrock@Sun.COM 	{ zfs_ioc_vdev_setfru,	zfs_secpolicy_config, POOL_NAME, B_FALSE,
4665*13049SGeorge.Wilson@Sun.COM 	    POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY },
46669234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_objset_stats,	zfs_secpolicy_read, DATASET_NAME, B_FALSE,
4667*13049SGeorge.Wilson@Sun.COM 	    POOL_CHECK_SUSPENDED },
46689234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_objset_zplprops, zfs_secpolicy_read, DATASET_NAME, B_FALSE,
4669*13049SGeorge.Wilson@Sun.COM 	    POOL_CHECK_NONE },
46709234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_dataset_list_next, zfs_secpolicy_read, DATASET_NAME, B_FALSE,
4671*13049SGeorge.Wilson@Sun.COM 	    POOL_CHECK_SUSPENDED },
46729234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_snapshot_list_next, zfs_secpolicy_read, DATASET_NAME, B_FALSE,
4673*13049SGeorge.Wilson@Sun.COM 	    POOL_CHECK_SUSPENDED },
4674*13049SGeorge.Wilson@Sun.COM 	{ zfs_ioc_set_prop, zfs_secpolicy_none, DATASET_NAME, B_TRUE,
4675*13049SGeorge.Wilson@Sun.COM 	    POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY },
4676*13049SGeorge.Wilson@Sun.COM 	{ zfs_ioc_create, zfs_secpolicy_create, DATASET_NAME, B_TRUE,
4677*13049SGeorge.Wilson@Sun.COM 	    POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY },
46789234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_destroy, zfs_secpolicy_destroy, DATASET_NAME, B_TRUE,
4679*13049SGeorge.Wilson@Sun.COM 	    POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY },
46809234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_rollback, zfs_secpolicy_rollback, DATASET_NAME, B_TRUE,
4681*13049SGeorge.Wilson@Sun.COM 	    POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY },
4682*13049SGeorge.Wilson@Sun.COM 	{ zfs_ioc_rename, zfs_secpolicy_rename,	DATASET_NAME, B_TRUE,
4683*13049SGeorge.Wilson@Sun.COM 	    POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY },
4684*13049SGeorge.Wilson@Sun.COM 	{ zfs_ioc_recv, zfs_secpolicy_receive, DATASET_NAME, B_TRUE,
4685*13049SGeorge.Wilson@Sun.COM 	    POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY },
4686*13049SGeorge.Wilson@Sun.COM 	{ zfs_ioc_send, zfs_secpolicy_send, DATASET_NAME, B_TRUE,
4687*13049SGeorge.Wilson@Sun.COM 	    POOL_CHECK_NONE },
46889234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_inject_fault,	zfs_secpolicy_inject, NO_NAME, B_FALSE,
4689*13049SGeorge.Wilson@Sun.COM 	    POOL_CHECK_NONE },
46909234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_clear_fault, zfs_secpolicy_inject, NO_NAME, B_FALSE,
4691*13049SGeorge.Wilson@Sun.COM 	    POOL_CHECK_NONE },
46929234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_inject_list_next, zfs_secpolicy_inject, NO_NAME, B_FALSE,
4693*13049SGeorge.Wilson@Sun.COM 	    POOL_CHECK_NONE },
46949234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_error_log, zfs_secpolicy_inject, POOL_NAME, B_FALSE,
4695*13049SGeorge.Wilson@Sun.COM 	    POOL_CHECK_NONE },
4696*13049SGeorge.Wilson@Sun.COM 	{ zfs_ioc_clear, zfs_secpolicy_config, POOL_NAME, B_TRUE,
4697*13049SGeorge.Wilson@Sun.COM 	    POOL_CHECK_NONE },
46989234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_promote, zfs_secpolicy_promote, DATASET_NAME, B_TRUE,
4699*13049SGeorge.Wilson@Sun.COM 	    POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY },
470011314Swilliam.gorrell@sun.com 	{ zfs_ioc_destroy_snaps, zfs_secpolicy_destroy_snaps, DATASET_NAME,
4701*13049SGeorge.Wilson@Sun.COM 	    B_TRUE, POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY },
47029234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_snapshot, zfs_secpolicy_snapshot, DATASET_NAME, B_TRUE,
4703*13049SGeorge.Wilson@Sun.COM 	    POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY },
470413043STim.Haley@Sun.COM 	{ zfs_ioc_dsobj_to_dsname, zfs_secpolicy_diff, POOL_NAME, B_FALSE,
4705*13049SGeorge.Wilson@Sun.COM 	    POOL_CHECK_NONE },
470613043STim.Haley@Sun.COM 	{ zfs_ioc_obj_to_path, zfs_secpolicy_diff, DATASET_NAME, B_FALSE,
4707*13049SGeorge.Wilson@Sun.COM 	    POOL_CHECK_SUSPENDED },
47089234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_pool_set_props, zfs_secpolicy_config,	POOL_NAME, B_TRUE,
4709*13049SGeorge.Wilson@Sun.COM 	    POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY },
47109234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_pool_get_props, zfs_secpolicy_read, POOL_NAME, B_FALSE,
4711*13049SGeorge.Wilson@Sun.COM 	    POOL_CHECK_NONE },
47129234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_set_fsacl, zfs_secpolicy_fsacl, DATASET_NAME, B_TRUE,
4713*13049SGeorge.Wilson@Sun.COM 	    POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY },
47149234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_get_fsacl, zfs_secpolicy_read, DATASET_NAME, B_FALSE,
4715*13049SGeorge.Wilson@Sun.COM 	    POOL_CHECK_NONE },
4716*13049SGeorge.Wilson@Sun.COM 	{ zfs_ioc_share, zfs_secpolicy_share, DATASET_NAME, B_FALSE,
4717*13049SGeorge.Wilson@Sun.COM 	    POOL_CHECK_NONE },
47189234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_inherit_prop, zfs_secpolicy_inherit, DATASET_NAME, B_TRUE,
4719*13049SGeorge.Wilson@Sun.COM 	    POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY },
47209234SGeorge.Wilson@Sun.COM 	{ zfs_ioc_smb_acl, zfs_secpolicy_smb_acl, DATASET_NAME, B_FALSE,
4721*13049SGeorge.Wilson@Sun.COM 	    POOL_CHECK_NONE },
4722*13049SGeorge.Wilson@Sun.COM 	{ zfs_ioc_userspace_one, zfs_secpolicy_userspace_one, DATASET_NAME,
4723*13049SGeorge.Wilson@Sun.COM 	    B_FALSE, POOL_CHECK_NONE },
4724*13049SGeorge.Wilson@Sun.COM 	{ zfs_ioc_userspace_many, zfs_secpolicy_userspace_many, DATASET_NAME,
4725*13049SGeorge.Wilson@Sun.COM 	    B_FALSE, POOL_CHECK_NONE },
47269396SMatthew.Ahrens@Sun.COM 	{ zfs_ioc_userspace_upgrade, zfs_secpolicy_userspace_upgrade,
4727*13049SGeorge.Wilson@Sun.COM 	    DATASET_NAME, B_FALSE, POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY },
4728*13049SGeorge.Wilson@Sun.COM 	{ zfs_ioc_hold, zfs_secpolicy_hold, DATASET_NAME, B_TRUE,
4729*13049SGeorge.Wilson@Sun.COM 	    POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY },
473010242Schris.kirby@sun.com 	{ zfs_ioc_release, zfs_secpolicy_release, DATASET_NAME, B_TRUE,
4731*13049SGeorge.Wilson@Sun.COM 	    POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY },
473210242Schris.kirby@sun.com 	{ zfs_ioc_get_holds, zfs_secpolicy_read, DATASET_NAME, B_FALSE,
4733*13049SGeorge.Wilson@Sun.COM 	    POOL_CHECK_SUSPENDED },
473411022STom.Erickson@Sun.COM 	{ zfs_ioc_objset_recvd_props, zfs_secpolicy_read, DATASET_NAME, B_FALSE,
4735*13049SGeorge.Wilson@Sun.COM 	    POOL_CHECK_NONE },
473611422SMark.Musante@Sun.COM 	{ zfs_ioc_vdev_split, zfs_secpolicy_config, POOL_NAME, B_TRUE,
4737*13049SGeorge.Wilson@Sun.COM 	    POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY },
473813043STim.Haley@Sun.COM 	{ zfs_ioc_next_obj, zfs_secpolicy_read, DATASET_NAME, B_FALSE,
4739*13049SGeorge.Wilson@Sun.COM 	    POOL_CHECK_NONE },
4740*13049SGeorge.Wilson@Sun.COM 	{ zfs_ioc_diff, zfs_secpolicy_diff, DATASET_NAME, B_FALSE,
4741*13049SGeorge.Wilson@Sun.COM 	    POOL_CHECK_NONE },
474213043STim.Haley@Sun.COM 	{ zfs_ioc_tmp_snapshot, zfs_secpolicy_tmp_snapshot, DATASET_NAME,
4743*13049SGeorge.Wilson@Sun.COM 	    B_FALSE, POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY },
474413043STim.Haley@Sun.COM 	{ zfs_ioc_obj_to_stats, zfs_secpolicy_diff, DATASET_NAME, B_FALSE,
4745*13049SGeorge.Wilson@Sun.COM 	    POOL_CHECK_SUSPENDED }
4746789Sahrens };
4747789Sahrens 
47489234SGeorge.Wilson@Sun.COM int
pool_status_check(const char * name,zfs_ioc_namecheck_t type,zfs_ioc_poolcheck_t check)4749*13049SGeorge.Wilson@Sun.COM pool_status_check(const char *name, zfs_ioc_namecheck_t type,
4750*13049SGeorge.Wilson@Sun.COM     zfs_ioc_poolcheck_t check)
47519234SGeorge.Wilson@Sun.COM {
47529234SGeorge.Wilson@Sun.COM 	spa_t *spa;
47539234SGeorge.Wilson@Sun.COM 	int error;
47549234SGeorge.Wilson@Sun.COM 
47559234SGeorge.Wilson@Sun.COM 	ASSERT(type == POOL_NAME || type == DATASET_NAME);
47569234SGeorge.Wilson@Sun.COM 
4757*13049SGeorge.Wilson@Sun.COM 	if (check & POOL_CHECK_NONE)
4758*13049SGeorge.Wilson@Sun.COM 		return (0);
4759*13049SGeorge.Wilson@Sun.COM 
47609396SMatthew.Ahrens@Sun.COM 	error = spa_open(name, &spa, FTAG);
47619234SGeorge.Wilson@Sun.COM 	if (error == 0) {
4762*13049SGeorge.Wilson@Sun.COM 		if ((check & POOL_CHECK_SUSPENDED) && spa_suspended(spa))
47639234SGeorge.Wilson@Sun.COM 			error = EAGAIN;
4764*13049SGeorge.Wilson@Sun.COM 		else if ((check & POOL_CHECK_READONLY) && !spa_writeable(spa))
4765*13049SGeorge.Wilson@Sun.COM 			error = EROFS;
47669234SGeorge.Wilson@Sun.COM 		spa_close(spa, FTAG);
47679234SGeorge.Wilson@Sun.COM 	}
47689234SGeorge.Wilson@Sun.COM 	return (error);
47699234SGeorge.Wilson@Sun.COM }
47709234SGeorge.Wilson@Sun.COM 
477112527SChris.Kirby@oracle.com /*
477212527SChris.Kirby@oracle.com  * Find a free minor number.
477312527SChris.Kirby@oracle.com  */
477412527SChris.Kirby@oracle.com minor_t
zfsdev_minor_alloc(void)477512527SChris.Kirby@oracle.com zfsdev_minor_alloc(void)
477612527SChris.Kirby@oracle.com {
477712527SChris.Kirby@oracle.com 	static minor_t last_minor;
477812527SChris.Kirby@oracle.com 	minor_t m;
477912527SChris.Kirby@oracle.com 
478012527SChris.Kirby@oracle.com 	ASSERT(MUTEX_HELD(&zfsdev_state_lock));
478112527SChris.Kirby@oracle.com 
478212527SChris.Kirby@oracle.com 	for (m = last_minor + 1; m != last_minor; m++) {
478312527SChris.Kirby@oracle.com 		if (m > ZFSDEV_MAX_MINOR)
478412527SChris.Kirby@oracle.com 			m = 1;
478512527SChris.Kirby@oracle.com 		if (ddi_get_soft_state(zfsdev_state, m) == NULL) {
478612527SChris.Kirby@oracle.com 			last_minor = m;
478712527SChris.Kirby@oracle.com 			return (m);
478812527SChris.Kirby@oracle.com 		}
478912527SChris.Kirby@oracle.com 	}
479012527SChris.Kirby@oracle.com 
479112527SChris.Kirby@oracle.com 	return (0);
479212527SChris.Kirby@oracle.com }
479312527SChris.Kirby@oracle.com 
479412527SChris.Kirby@oracle.com static int
zfs_ctldev_init(dev_t * devp)479512527SChris.Kirby@oracle.com zfs_ctldev_init(dev_t *devp)
479612527SChris.Kirby@oracle.com {
479712527SChris.Kirby@oracle.com 	minor_t minor;
479812527SChris.Kirby@oracle.com 	zfs_soft_state_t *zs;
479912527SChris.Kirby@oracle.com 
480012527SChris.Kirby@oracle.com 	ASSERT(MUTEX_HELD(&zfsdev_state_lock));
480112527SChris.Kirby@oracle.com 	ASSERT(getminor(*devp) == 0);
480212527SChris.Kirby@oracle.com 
480312527SChris.Kirby@oracle.com 	minor = zfsdev_minor_alloc();
480412527SChris.Kirby@oracle.com 	if (minor == 0)
480512527SChris.Kirby@oracle.com 		return (ENXIO);
480612527SChris.Kirby@oracle.com 
480712527SChris.Kirby@oracle.com 	if (ddi_soft_state_zalloc(zfsdev_state, minor) != DDI_SUCCESS)
480812527SChris.Kirby@oracle.com 		return (EAGAIN);
480912527SChris.Kirby@oracle.com 
481012527SChris.Kirby@oracle.com 	*devp = makedevice(getemajor(*devp), minor);
481112527SChris.Kirby@oracle.com 
481212527SChris.Kirby@oracle.com 	zs = ddi_get_soft_state(zfsdev_state, minor);
481312527SChris.Kirby@oracle.com 	zs->zss_type = ZSST_CTLDEV;
481412527SChris.Kirby@oracle.com 	zfs_onexit_init((zfs_onexit_t **)&zs->zss_data);
481512527SChris.Kirby@oracle.com 
481612527SChris.Kirby@oracle.com 	return (0);
481712527SChris.Kirby@oracle.com }
481812527SChris.Kirby@oracle.com 
481912527SChris.Kirby@oracle.com static void
zfs_ctldev_destroy(zfs_onexit_t * zo,minor_t minor)482012527SChris.Kirby@oracle.com zfs_ctldev_destroy(zfs_onexit_t *zo, minor_t minor)
482112527SChris.Kirby@oracle.com {
482212527SChris.Kirby@oracle.com 	ASSERT(MUTEX_HELD(&zfsdev_state_lock));
482312527SChris.Kirby@oracle.com 
482412527SChris.Kirby@oracle.com 	zfs_onexit_destroy(zo);
482512527SChris.Kirby@oracle.com 	ddi_soft_state_free(zfsdev_state, minor);
482612527SChris.Kirby@oracle.com }
482712527SChris.Kirby@oracle.com 
482812527SChris.Kirby@oracle.com void *
zfsdev_get_soft_state(minor_t minor,enum zfs_soft_state_type which)482912527SChris.Kirby@oracle.com zfsdev_get_soft_state(minor_t minor, enum zfs_soft_state_type which)
483012527SChris.Kirby@oracle.com {
483112527SChris.Kirby@oracle.com 	zfs_soft_state_t *zp;
483212527SChris.Kirby@oracle.com 
483312527SChris.Kirby@oracle.com 	zp = ddi_get_soft_state(zfsdev_state, minor);
483412527SChris.Kirby@oracle.com 	if (zp == NULL || zp->zss_type != which)
483512527SChris.Kirby@oracle.com 		return (NULL);
483612527SChris.Kirby@oracle.com 
483712527SChris.Kirby@oracle.com 	return (zp->zss_data);
483812527SChris.Kirby@oracle.com }
483912527SChris.Kirby@oracle.com 
484012527SChris.Kirby@oracle.com static int
zfsdev_open(dev_t * devp,int flag,int otyp,cred_t * cr)484112527SChris.Kirby@oracle.com zfsdev_open(dev_t *devp, int flag, int otyp, cred_t *cr)
484212527SChris.Kirby@oracle.com {
484312527SChris.Kirby@oracle.com 	int error = 0;
484412527SChris.Kirby@oracle.com 
484512527SChris.Kirby@oracle.com 	if (getminor(*devp) != 0)
484612527SChris.Kirby@oracle.com 		return (zvol_open(devp, flag, otyp, cr));
484712527SChris.Kirby@oracle.com 
484812527SChris.Kirby@oracle.com 	/* This is the control device. Allocate a new minor if requested. */
484912527SChris.Kirby@oracle.com 	if (flag & FEXCL) {
485012527SChris.Kirby@oracle.com 		mutex_enter(&zfsdev_state_lock);
485112527SChris.Kirby@oracle.com 		error = zfs_ctldev_init(devp);
485212527SChris.Kirby@oracle.com 		mutex_exit(&zfsdev_state_lock);
485312527SChris.Kirby@oracle.com 	}
485412527SChris.Kirby@oracle.com 
485512527SChris.Kirby@oracle.com 	return (error);
485612527SChris.Kirby@oracle.com }
485712527SChris.Kirby@oracle.com 
485812527SChris.Kirby@oracle.com static int
zfsdev_close(dev_t dev,int flag,int otyp,cred_t * cr)485912527SChris.Kirby@oracle.com zfsdev_close(dev_t dev, int flag, int otyp, cred_t *cr)
486012527SChris.Kirby@oracle.com {
486112527SChris.Kirby@oracle.com 	zfs_onexit_t *zo;
486212527SChris.Kirby@oracle.com 	minor_t minor = getminor(dev);
486312527SChris.Kirby@oracle.com 
486412527SChris.Kirby@oracle.com 	if (minor == 0)
486512527SChris.Kirby@oracle.com 		return (0);
486612527SChris.Kirby@oracle.com 
486712527SChris.Kirby@oracle.com 	mutex_enter(&zfsdev_state_lock);
486812527SChris.Kirby@oracle.com 	zo = zfsdev_get_soft_state(minor, ZSST_CTLDEV);
486912527SChris.Kirby@oracle.com 	if (zo == NULL) {
487012527SChris.Kirby@oracle.com 		mutex_exit(&zfsdev_state_lock);
487112527SChris.Kirby@oracle.com 		return (zvol_close(dev, flag, otyp, cr));
487212527SChris.Kirby@oracle.com 	}
487312527SChris.Kirby@oracle.com 	zfs_ctldev_destroy(zo, minor);
487412527SChris.Kirby@oracle.com 	mutex_exit(&zfsdev_state_lock);
487512527SChris.Kirby@oracle.com 
487612527SChris.Kirby@oracle.com 	return (0);
487712527SChris.Kirby@oracle.com }
487812527SChris.Kirby@oracle.com 
4879789Sahrens static int
zfsdev_ioctl(dev_t dev,int cmd,intptr_t arg,int flag,cred_t * cr,int * rvalp)4880789Sahrens zfsdev_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *cr, int *rvalp)
4881789Sahrens {
4882789Sahrens 	zfs_cmd_t *zc;
4883789Sahrens 	uint_t vec;
48842199Sahrens 	int error, rc;
488512527SChris.Kirby@oracle.com 	minor_t minor = getminor(dev);
488612527SChris.Kirby@oracle.com 
488712527SChris.Kirby@oracle.com 	if (minor != 0 &&
488812527SChris.Kirby@oracle.com 	    zfsdev_get_soft_state(minor, ZSST_CTLDEV) == NULL)
4889789Sahrens 		return (zvol_ioctl(dev, cmd, arg, flag, cr, rvalp));
4890789Sahrens 
4891789Sahrens 	vec = cmd - ZFS_IOC;
48924787Sahrens 	ASSERT3U(getmajor(dev), ==, ddi_driver_major(zfs_dip));
4893789Sahrens 
4894789Sahrens 	if (vec >= sizeof (zfs_ioc_vec) / sizeof (zfs_ioc_vec[0]))
4895789Sahrens 		return (EINVAL);
4896789Sahrens 
4897789Sahrens 	zc = kmem_zalloc(sizeof (zfs_cmd_t), KM_SLEEP);
4898789Sahrens 
48999643SEric.Taylor@Sun.COM 	error = ddi_copyin((void *)arg, zc, sizeof (zfs_cmd_t), flag);
490011807SSam.Falkner@Sun.COM 	if (error != 0)
490111807SSam.Falkner@Sun.COM 		error = EFAULT;
4902789Sahrens 
490310588SEric.Taylor@Sun.COM 	if ((error == 0) && !(flag & FKIOCTL))
49044543Smarks 		error = zfs_ioc_vec[vec].zvec_secpolicy(zc, cr);
4905789Sahrens 
4906789Sahrens 	/*
4907789Sahrens 	 * Ensure that all pool/dataset names are valid before we pass down to
4908789Sahrens 	 * the lower layers.
4909789Sahrens 	 */
4910789Sahrens 	if (error == 0) {
4911789Sahrens 		zc->zc_name[sizeof (zc->zc_name) - 1] = '\0';
49129643SEric.Taylor@Sun.COM 		zc->zc_iflags = flag & FKIOCTL;
4913789Sahrens 		switch (zfs_ioc_vec[vec].zvec_namecheck) {
49144577Sahrens 		case POOL_NAME:
4915789Sahrens 			if (pool_namecheck(zc->zc_name, NULL, NULL) != 0)
4916789Sahrens 				error = EINVAL;
4917*13049SGeorge.Wilson@Sun.COM 			error = pool_status_check(zc->zc_name,
4918*13049SGeorge.Wilson@Sun.COM 			    zfs_ioc_vec[vec].zvec_namecheck,
4919*13049SGeorge.Wilson@Sun.COM 			    zfs_ioc_vec[vec].zvec_pool_check);
4920789Sahrens 			break;
4921789Sahrens 
49224577Sahrens 		case DATASET_NAME:
4923789Sahrens 			if (dataset_namecheck(zc->zc_name, NULL, NULL) != 0)
4924789Sahrens 				error = EINVAL;
4925*13049SGeorge.Wilson@Sun.COM 			error = pool_status_check(zc->zc_name,
4926*13049SGeorge.Wilson@Sun.COM 			    zfs_ioc_vec[vec].zvec_namecheck,
4927*13049SGeorge.Wilson@Sun.COM 			    zfs_ioc_vec[vec].zvec_pool_check);
4928789Sahrens 			break;
49292856Snd150628 
49304577Sahrens 		case NO_NAME:
49312856Snd150628 			break;
4932789Sahrens 		}
4933789Sahrens 	}
4934789Sahrens 
4935789Sahrens 	if (error == 0)
4936789Sahrens 		error = zfs_ioc_vec[vec].zvec_func(zc);
4937789Sahrens 
49389643SEric.Taylor@Sun.COM 	rc = ddi_copyout(zc, (void *)arg, sizeof (zfs_cmd_t), flag);
49394543Smarks 	if (error == 0) {
494011807SSam.Falkner@Sun.COM 		if (rc != 0)
494111807SSam.Falkner@Sun.COM 			error = EFAULT;
49429396SMatthew.Ahrens@Sun.COM 		if (zfs_ioc_vec[vec].zvec_his_log)
49434543Smarks 			zfs_log_history(zc);
49444543Smarks 	}
4945789Sahrens 
4946789Sahrens 	kmem_free(zc, sizeof (zfs_cmd_t));
4947789Sahrens 	return (error);
4948789Sahrens }
4949789Sahrens 
4950789Sahrens static int
zfs_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)4951789Sahrens zfs_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
4952789Sahrens {
4953789Sahrens 	if (cmd != DDI_ATTACH)
4954789Sahrens 		return (DDI_FAILURE);
4955789Sahrens 
4956789Sahrens 	if (ddi_create_minor_node(dip, "zfs", S_IFCHR, 0,
4957789Sahrens 	    DDI_PSEUDO, 0) == DDI_FAILURE)
4958789Sahrens 		return (DDI_FAILURE);
4959789Sahrens 
4960789Sahrens 	zfs_dip = dip;
4961789Sahrens 
4962789Sahrens 	ddi_report_dev(dip);
4963789Sahrens 
4964789Sahrens 	return (DDI_SUCCESS);
4965789Sahrens }
4966789Sahrens 
4967789Sahrens static int
zfs_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)4968789Sahrens zfs_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
4969789Sahrens {
4970789Sahrens 	if (spa_busy() || zfs_busy() || zvol_busy())
4971789Sahrens 		return (DDI_FAILURE);
4972789Sahrens 
4973789Sahrens 	if (cmd != DDI_DETACH)
4974789Sahrens 		return (DDI_FAILURE);
4975789Sahrens 
4976789Sahrens 	zfs_dip = NULL;
4977789Sahrens 
4978789Sahrens 	ddi_prop_remove_all(dip);
4979789Sahrens 	ddi_remove_minor_node(dip, NULL);
4980789Sahrens 
4981789Sahrens 	return (DDI_SUCCESS);
4982789Sahrens }
4983789Sahrens 
4984789Sahrens /*ARGSUSED*/
4985789Sahrens static int
zfs_info(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)4986789Sahrens zfs_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
4987789Sahrens {
4988789Sahrens 	switch (infocmd) {
4989789Sahrens 	case DDI_INFO_DEVT2DEVINFO:
4990789Sahrens 		*result = zfs_dip;
4991789Sahrens 		return (DDI_SUCCESS);
4992789Sahrens 
4993789Sahrens 	case DDI_INFO_DEVT2INSTANCE:
4994849Sbonwick 		*result = (void *)0;
4995789Sahrens 		return (DDI_SUCCESS);
4996789Sahrens 	}
4997789Sahrens 
4998789Sahrens 	return (DDI_FAILURE);
4999789Sahrens }
5000789Sahrens 
5001789Sahrens /*
5002789Sahrens  * OK, so this is a little weird.
5003789Sahrens  *
5004789Sahrens  * /dev/zfs is the control node, i.e. minor 0.
5005789Sahrens  * /dev/zvol/[r]dsk/pool/dataset are the zvols, minor > 0.
5006789Sahrens  *
5007789Sahrens  * /dev/zfs has basically nothing to do except serve up ioctls,
5008789Sahrens  * so most of the standard driver entry points are in zvol.c.
5009789Sahrens  */
5010789Sahrens static struct cb_ops zfs_cb_ops = {
501112527SChris.Kirby@oracle.com 	zfsdev_open,	/* open */
501212527SChris.Kirby@oracle.com 	zfsdev_close,	/* close */
5013789Sahrens 	zvol_strategy,	/* strategy */
5014789Sahrens 	nodev,		/* print */
50156423Sgw25295 	zvol_dump,	/* dump */
5016789Sahrens 	zvol_read,	/* read */
5017789Sahrens 	zvol_write,	/* write */
5018789Sahrens 	zfsdev_ioctl,	/* ioctl */
5019789Sahrens 	nodev,		/* devmap */
5020789Sahrens 	nodev,		/* mmap */
5021789Sahrens 	nodev,		/* segmap */
5022789Sahrens 	nochpoll,	/* poll */
5023789Sahrens 	ddi_prop_op,	/* prop_op */
5024789Sahrens 	NULL,		/* streamtab */
5025789Sahrens 	D_NEW | D_MP | D_64BIT,		/* Driver compatibility flag */
5026789Sahrens 	CB_REV,		/* version */
50273638Sbillm 	nodev,		/* async read */
50283638Sbillm 	nodev,		/* async write */
5029789Sahrens };
5030789Sahrens 
5031789Sahrens static struct dev_ops zfs_dev_ops = {
5032789Sahrens 	DEVO_REV,	/* version */
5033789Sahrens 	0,		/* refcnt */
5034789Sahrens 	zfs_info,	/* info */
5035789Sahrens 	nulldev,	/* identify */
5036789Sahrens 	nulldev,	/* probe */
5037789Sahrens 	zfs_attach,	/* attach */
5038789Sahrens 	zfs_detach,	/* detach */
5039789Sahrens 	nodev,		/* reset */
5040789Sahrens 	&zfs_cb_ops,	/* driver operations */
50417656SSherry.Moore@Sun.COM 	NULL,		/* no bus operations */
50427656SSherry.Moore@Sun.COM 	NULL,		/* power */
50437656SSherry.Moore@Sun.COM 	ddi_quiesce_not_needed,	/* quiesce */
5044789Sahrens };
5045789Sahrens 
5046789Sahrens static struct modldrv zfs_modldrv = {
50477656SSherry.Moore@Sun.COM 	&mod_driverops,
50487656SSherry.Moore@Sun.COM 	"ZFS storage pool",
50497656SSherry.Moore@Sun.COM 	&zfs_dev_ops
5050789Sahrens };
5051789Sahrens 
5052789Sahrens static struct modlinkage modlinkage = {
5053789Sahrens 	MODREV_1,
5054789Sahrens 	(void *)&zfs_modlfs,
5055789Sahrens 	(void *)&zfs_modldrv,
5056789Sahrens 	NULL
5057789Sahrens };
5058789Sahrens 
50594720Sfr157268 
50604720Sfr157268 uint_t zfs_fsyncer_key;
50615326Sek110237 extern uint_t rrw_tsd_key;
50624720Sfr157268 
5063789Sahrens int
_init(void)5064789Sahrens _init(void)
5065789Sahrens {
5066789Sahrens 	int error;
5067789Sahrens 
5068849Sbonwick 	spa_init(FREAD | FWRITE);
5069849Sbonwick 	zfs_init();
5070849Sbonwick 	zvol_init();
5071849Sbonwick 
5072849Sbonwick 	if ((error = mod_install(&modlinkage)) != 0) {
5073849Sbonwick 		zvol_fini();
5074849Sbonwick 		zfs_fini();
5075849Sbonwick 		spa_fini();
5076789Sahrens 		return (error);
5077849Sbonwick 	}
5078789Sahrens 
50794720Sfr157268 	tsd_create(&zfs_fsyncer_key, NULL);
50805326Sek110237 	tsd_create(&rrw_tsd_key, NULL);
50814720Sfr157268 
5082789Sahrens 	error = ldi_ident_from_mod(&modlinkage, &zfs_li);
5083789Sahrens 	ASSERT(error == 0);
50844543Smarks 	mutex_init(&zfs_share_lock, NULL, MUTEX_DEFAULT, NULL);
5085789Sahrens 
5086789Sahrens 	return (0);
5087789Sahrens }
5088789Sahrens 
5089789Sahrens int
_fini(void)5090789Sahrens _fini(void)
5091789Sahrens {
5092789Sahrens 	int error;
5093789Sahrens 
50941544Seschrock 	if (spa_busy() || zfs_busy() || zvol_busy() || zio_injection_enabled)
5095789Sahrens 		return (EBUSY);
5096789Sahrens 
5097789Sahrens 	if ((error = mod_remove(&modlinkage)) != 0)
5098789Sahrens 		return (error);
5099789Sahrens 
5100789Sahrens 	zvol_fini();
5101789Sahrens 	zfs_fini();
5102789Sahrens 	spa_fini();
51035331Samw 	if (zfs_nfsshare_inited)
51044543Smarks 		(void) ddi_modclose(nfs_mod);
51055331Samw 	if (zfs_smbshare_inited)
51065331Samw 		(void) ddi_modclose(smbsrv_mod);
51075331Samw 	if (zfs_nfsshare_inited || zfs_smbshare_inited)
51084543Smarks 		(void) ddi_modclose(sharefs_mod);
5109789Sahrens 
51104720Sfr157268 	tsd_destroy(&zfs_fsyncer_key);
5111789Sahrens 	ldi_ident_release(zfs_li);
5112789Sahrens 	zfs_li = NULL;
51134543Smarks 	mutex_destroy(&zfs_share_lock);
5114789Sahrens 
5115789Sahrens 	return (error);
5116789Sahrens }
5117789Sahrens 
5118789Sahrens int
_info(struct modinfo * modinfop)5119789Sahrens _info(struct modinfo *modinfop)
5120789Sahrens {
5121789Sahrens 	return (mod_info(&modlinkage, modinfop));
5122789Sahrens }
5123