xref: /illumos-gate/usr/src/uts/common/fs/zfs/dsl_deleg.c (revision 4d7988d6)
1ecd6cf80Smarks /*
2ecd6cf80Smarks  * CDDL HEADER START
3ecd6cf80Smarks  *
4ecd6cf80Smarks  * The contents of this file are subject to the terms of the
5ecd6cf80Smarks  * Common Development and Distribution License (the "License").
6ecd6cf80Smarks  * You may not use this file except in compliance with the License.
7ecd6cf80Smarks  *
8ecd6cf80Smarks  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9ecd6cf80Smarks  * or http://www.opensolaris.org/os/licensing.
10ecd6cf80Smarks  * See the License for the specific language governing permissions
11ecd6cf80Smarks  * and limitations under the License.
12ecd6cf80Smarks  *
13ecd6cf80Smarks  * When distributing Covered Code, include this CDDL HEADER in each
14ecd6cf80Smarks  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15ecd6cf80Smarks  * If applicable, add the following below this CDDL HEADER, with the
16ecd6cf80Smarks  * fields enclosed by brackets "[]" replaced with your own identifying
17ecd6cf80Smarks  * information: Portions Copyright [yyyy] [name of copyright owner]
18ecd6cf80Smarks  *
19ecd6cf80Smarks  * CDDL HEADER END
20ecd6cf80Smarks  */
21ecd6cf80Smarks /*
2299d5e173STim Haley  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
239adfa60dSMatthew Ahrens  * Copyright (c) 2011, 2015 by Delphix. All rights reserved.
24ecd6cf80Smarks  */
25ecd6cf80Smarks 
26ecd6cf80Smarks /*
27ecd6cf80Smarks  * DSL permissions are stored in a two level zap attribute
28ecd6cf80Smarks  * mechanism.   The first level identifies the "class" of
29ecd6cf80Smarks  * entry.  The class is identified by the first 2 letters of
30ecd6cf80Smarks  * the attribute.  The second letter "l" or "d" identifies whether
31ecd6cf80Smarks  * it is a local or descendent permission.  The first letter
32ecd6cf80Smarks  * identifies the type of entry.
33ecd6cf80Smarks  *
34da6c28aaSamw  * ul$<id>    identifies permissions granted locally for this userid.
35ecd6cf80Smarks  * ud$<id>    identifies permissions granted on descendent datasets for
36ecd6cf80Smarks  *            this userid.
37ecd6cf80Smarks  * Ul$<id>    identifies permission sets granted locally for this userid.
38ecd6cf80Smarks  * Ud$<id>    identifies permission sets granted on descendent datasets for
39ecd6cf80Smarks  *            this userid.
40ecd6cf80Smarks  * gl$<id>    identifies permissions granted locally for this groupid.
41ecd6cf80Smarks  * gd$<id>    identifies permissions granted on descendent datasets for
42ecd6cf80Smarks  *            this groupid.
43ecd6cf80Smarks  * Gl$<id>    identifies permission sets granted locally for this groupid.
44ecd6cf80Smarks  * Gd$<id>    identifies permission sets granted on descendent datasets for
45ecd6cf80Smarks  *            this groupid.
46ecd6cf80Smarks  * el$        identifies permissions granted locally for everyone.
47ecd6cf80Smarks  * ed$        identifies permissions granted on descendent datasets
48ecd6cf80Smarks  *            for everyone.
49ecd6cf80Smarks  * El$        identifies permission sets granted locally for everyone.
50ecd6cf80Smarks  * Ed$        identifies permission sets granted to descendent datasets for
51ecd6cf80Smarks  *            everyone.
52ecd6cf80Smarks  * c-$        identifies permission to create at dataset creation time.
53ecd6cf80Smarks  * C-$        identifies permission sets to grant locally at dataset creation
54ecd6cf80Smarks  *            time.
55ecd6cf80Smarks  * s-$@<name> permissions defined in specified set @<name>
56ecd6cf80Smarks  * S-$@<name> Sets defined in named set @<name>
57ecd6cf80Smarks  *
58da6c28aaSamw  * Each of the above entities points to another zap attribute that contains one
59ecd6cf80Smarks  * attribute for each allowed permission, such as create, destroy,...
60ecd6cf80Smarks  * All of the "upper" case class types will specify permission set names
61ecd6cf80Smarks  * rather than permissions.
62ecd6cf80Smarks  *
63ecd6cf80Smarks  * Basically it looks something like this:
64ecd6cf80Smarks  * ul$12 -> ZAP OBJ -> permissions...
65ecd6cf80Smarks  *
66ecd6cf80Smarks  * The ZAP OBJ is referred to as the jump object.
67ecd6cf80Smarks  */
68ecd6cf80Smarks 
69ecd6cf80Smarks #include <sys/dmu.h>
70ecd6cf80Smarks #include <sys/dmu_objset.h>
71ecd6cf80Smarks #include <sys/dmu_tx.h>
72ecd6cf80Smarks #include <sys/dsl_dataset.h>
73ecd6cf80Smarks #include <sys/dsl_dir.h>
74ecd6cf80Smarks #include <sys/dsl_prop.h>
75ecd6cf80Smarks #include <sys/dsl_synctask.h>
76ecd6cf80Smarks #include <sys/dsl_deleg.h>
77ecd6cf80Smarks #include <sys/spa.h>
78ecd6cf80Smarks #include <sys/zap.h>
79ecd6cf80Smarks #include <sys/fs/zfs.h>
80ecd6cf80Smarks #include <sys/cred.h>
81ecd6cf80Smarks #include <sys/sunddi.h>
82ecd6cf80Smarks 
83ecd6cf80Smarks #include "zfs_deleg.h"
84ecd6cf80Smarks 
85ecd6cf80Smarks /*
86ecd6cf80Smarks  * Validate that user is allowed to delegate specified permissions.
87ecd6cf80Smarks  *
8891ebeef5Sahrens  * In order to delegate "create" you must have "create"
89ecd6cf80Smarks  * and "allow".
90ecd6cf80Smarks  */
91ecd6cf80Smarks int
dsl_deleg_can_allow(char * ddname,nvlist_t * nvp,cred_t * cr)92ecd6cf80Smarks dsl_deleg_can_allow(char *ddname, nvlist_t *nvp, cred_t *cr)
93ecd6cf80Smarks {
94ecd6cf80Smarks 	nvpair_t *whopair = NULL;
9591ebeef5Sahrens 	int error;
96ecd6cf80Smarks 
9791ebeef5Sahrens 	if ((error = dsl_deleg_access(ddname, ZFS_DELEG_PERM_ALLOW, cr)) != 0)
98ecd6cf80Smarks 		return (error);
99ecd6cf80Smarks 
100ecd6cf80Smarks 	while (whopair = nvlist_next_nvpair(nvp, whopair)) {
101ecd6cf80Smarks 		nvlist_t *perms;
102ecd6cf80Smarks 		nvpair_t *permpair = NULL;
103ecd6cf80Smarks 
104ecd6cf80Smarks 		VERIFY(nvpair_value_nvlist(whopair, &perms) == 0);
105ecd6cf80Smarks 
106ecd6cf80Smarks 		while (permpair = nvlist_next_nvpair(perms, permpair)) {
107ecd6cf80Smarks 			const char *perm = nvpair_name(permpair);
108ecd6cf80Smarks 
109ecd6cf80Smarks 			if (strcmp(perm, ZFS_DELEG_PERM_ALLOW) == 0)
110be6fd75aSMatthew Ahrens 				return (SET_ERROR(EPERM));
111ecd6cf80Smarks 
11291ebeef5Sahrens 			if ((error = dsl_deleg_access(ddname, perm, cr)) != 0)
113ecd6cf80Smarks 				return (error);
114ecd6cf80Smarks 		}
115ecd6cf80Smarks 	}
11691ebeef5Sahrens 	return (0);
117ecd6cf80Smarks }
118ecd6cf80Smarks 
119ecd6cf80Smarks /*
120ecd6cf80Smarks  * Validate that user is allowed to unallow specified permissions.  They
121ecd6cf80Smarks  * must have the 'allow' permission, and even then can only unallow
122ecd6cf80Smarks  * perms for their uid.
123ecd6cf80Smarks  */
124ecd6cf80Smarks int
dsl_deleg_can_unallow(char * ddname,nvlist_t * nvp,cred_t * cr)125ecd6cf80Smarks dsl_deleg_can_unallow(char *ddname, nvlist_t *nvp, cred_t *cr)
126ecd6cf80Smarks {
127ecd6cf80Smarks 	nvpair_t *whopair = NULL;
128ecd6cf80Smarks 	int error;
12991ebeef5Sahrens 	char idstr[32];
130ecd6cf80Smarks 
13191ebeef5Sahrens 	if ((error = dsl_deleg_access(ddname, ZFS_DELEG_PERM_ALLOW, cr)) != 0)
132ecd6cf80Smarks 		return (error);
133ecd6cf80Smarks 
13491ebeef5Sahrens 	(void) snprintf(idstr, sizeof (idstr), "%lld",
13591ebeef5Sahrens 	    (longlong_t)crgetuid(cr));
13691ebeef5Sahrens 
137ecd6cf80Smarks 	while (whopair = nvlist_next_nvpair(nvp, whopair)) {
138ecd6cf80Smarks 		zfs_deleg_who_type_t type = nvpair_name(whopair)[0];
139ecd6cf80Smarks 
140ecd6cf80Smarks 		if (type != ZFS_DELEG_USER &&
141ecd6cf80Smarks 		    type != ZFS_DELEG_USER_SETS)
142be6fd75aSMatthew Ahrens 			return (SET_ERROR(EPERM));
143ecd6cf80Smarks 
144ecd6cf80Smarks 		if (strcmp(idstr, &nvpair_name(whopair)[3]) != 0)
145be6fd75aSMatthew Ahrens 			return (SET_ERROR(EPERM));
146ecd6cf80Smarks 	}
147ecd6cf80Smarks 	return (0);
148ecd6cf80Smarks }
149ecd6cf80Smarks 
1503b2aab18SMatthew Ahrens typedef struct dsl_deleg_arg {
1513b2aab18SMatthew Ahrens 	const char *dda_name;
1523b2aab18SMatthew Ahrens 	nvlist_t *dda_nvlist;
1533b2aab18SMatthew Ahrens } dsl_deleg_arg_t;
1543b2aab18SMatthew Ahrens 
155ecd6cf80Smarks static void
dsl_deleg_set_sync(void * arg,dmu_tx_t * tx)1563b2aab18SMatthew Ahrens dsl_deleg_set_sync(void *arg, dmu_tx_t *tx)
157ecd6cf80Smarks {
1583b2aab18SMatthew Ahrens 	dsl_deleg_arg_t *dda = arg;
1593b2aab18SMatthew Ahrens 	dsl_dir_t *dd;
1603b2aab18SMatthew Ahrens 	dsl_pool_t *dp = dmu_tx_pool(tx);
1613b2aab18SMatthew Ahrens 	objset_t *mos = dp->dp_meta_objset;
162ecd6cf80Smarks 	nvpair_t *whopair = NULL;
1633b2aab18SMatthew Ahrens 	uint64_t zapobj;
1643b2aab18SMatthew Ahrens 
1653b2aab18SMatthew Ahrens 	VERIFY0(dsl_dir_hold(dp, dda->dda_name, FTAG, &dd, NULL));
166ecd6cf80Smarks 
167c1379625SJustin T. Gibbs 	zapobj = dsl_dir_phys(dd)->dd_deleg_zapobj;
168ecd6cf80Smarks 	if (zapobj == 0) {
169ecd6cf80Smarks 		dmu_buf_will_dirty(dd->dd_dbuf, tx);
170c1379625SJustin T. Gibbs 		zapobj = dsl_dir_phys(dd)->dd_deleg_zapobj = zap_create(mos,
171ecd6cf80Smarks 		    DMU_OT_DSL_PERMS, DMU_OT_NONE, 0, tx);
172ecd6cf80Smarks 	}
173ecd6cf80Smarks 
1743b2aab18SMatthew Ahrens 	while (whopair = nvlist_next_nvpair(dda->dda_nvlist, whopair)) {
1753cb34c60Sahrens 		const char *whokey = nvpair_name(whopair);
1763cb34c60Sahrens 		nvlist_t *perms;
1773cb34c60Sahrens 		nvpair_t *permpair = NULL;
1783cb34c60Sahrens 		uint64_t jumpobj;
1793cb34c60Sahrens 
1803b2aab18SMatthew Ahrens 		perms = fnvpair_value_nvlist(whopair);
1813cb34c60Sahrens 
1823cb34c60Sahrens 		if (zap_lookup(mos, zapobj, whokey, 8, 1, &jumpobj) != 0) {
183ad135b5dSChristopher Siden 			jumpobj = zap_create_link(mos, DMU_OT_DSL_PERMS,
184ad135b5dSChristopher Siden 			    zapobj, whokey, tx);
1853cb34c60Sahrens 		}
1863cb34c60Sahrens 
1873cb34c60Sahrens 		while (permpair = nvlist_next_nvpair(perms, permpair)) {
1883cb34c60Sahrens 			const char *perm = nvpair_name(permpair);
1893cb34c60Sahrens 			uint64_t n = 0;
1903cb34c60Sahrens 
1913cb34c60Sahrens 			VERIFY(zap_update(mos, jumpobj,
1923cb34c60Sahrens 			    perm, 8, 1, &n, tx) == 0);
1934445fffbSMatthew Ahrens 			spa_history_log_internal_dd(dd, "permission update", tx,
1944445fffbSMatthew Ahrens 			    "%s %s", whokey, perm);
1953cb34c60Sahrens 		}
1963cb34c60Sahrens 	}
1973b2aab18SMatthew Ahrens 	dsl_dir_rele(dd, FTAG);
1983cb34c60Sahrens }
1993cb34c60Sahrens 
2003cb34c60Sahrens static void
dsl_deleg_unset_sync(void * arg,dmu_tx_t * tx)2013b2aab18SMatthew Ahrens dsl_deleg_unset_sync(void *arg, dmu_tx_t *tx)
2023cb34c60Sahrens {
2033b2aab18SMatthew Ahrens 	dsl_deleg_arg_t *dda = arg;
2043b2aab18SMatthew Ahrens 	dsl_dir_t *dd;
2053b2aab18SMatthew Ahrens 	dsl_pool_t *dp = dmu_tx_pool(tx);
2063b2aab18SMatthew Ahrens 	objset_t *mos = dp->dp_meta_objset;
2073cb34c60Sahrens 	nvpair_t *whopair = NULL;
2083b2aab18SMatthew Ahrens 	uint64_t zapobj;
2093cb34c60Sahrens 
2103b2aab18SMatthew Ahrens 	VERIFY0(dsl_dir_hold(dp, dda->dda_name, FTAG, &dd, NULL));
211c1379625SJustin T. Gibbs 	zapobj = dsl_dir_phys(dd)->dd_deleg_zapobj;
2123b2aab18SMatthew Ahrens 	if (zapobj == 0) {
2133b2aab18SMatthew Ahrens 		dsl_dir_rele(dd, FTAG);
2143cb34c60Sahrens 		return;
2153b2aab18SMatthew Ahrens 	}
2163cb34c60Sahrens 
2173b2aab18SMatthew Ahrens 	while (whopair = nvlist_next_nvpair(dda->dda_nvlist, whopair)) {
218ecd6cf80Smarks 		const char *whokey = nvpair_name(whopair);
219ecd6cf80Smarks 		nvlist_t *perms;
220ecd6cf80Smarks 		nvpair_t *permpair = NULL;
221ecd6cf80Smarks 		uint64_t jumpobj;
222ecd6cf80Smarks 
223ecd6cf80Smarks 		if (nvpair_value_nvlist(whopair, &perms) != 0) {
224ecd6cf80Smarks 			if (zap_lookup(mos, zapobj, whokey, 8,
225ecd6cf80Smarks 			    1, &jumpobj) == 0) {
226ecd6cf80Smarks 				(void) zap_remove(mos, zapobj, whokey, tx);
227ecd6cf80Smarks 				VERIFY(0 == zap_destroy(mos, jumpobj, tx));
228ecd6cf80Smarks 			}
2294445fffbSMatthew Ahrens 			spa_history_log_internal_dd(dd, "permission who remove",
2304445fffbSMatthew Ahrens 			    tx, "%s", whokey);
231ecd6cf80Smarks 			continue;
232ecd6cf80Smarks 		}
233ecd6cf80Smarks 
2343cb34c60Sahrens 		if (zap_lookup(mos, zapobj, whokey, 8, 1, &jumpobj) != 0)
2353cb34c60Sahrens 			continue;
236ecd6cf80Smarks 
237ecd6cf80Smarks 		while (permpair = nvlist_next_nvpair(perms, permpair)) {
238ecd6cf80Smarks 			const char *perm = nvpair_name(permpair);
239ecd6cf80Smarks 			uint64_t n = 0;
240ecd6cf80Smarks 
2413cb34c60Sahrens 			(void) zap_remove(mos, jumpobj, perm, tx);
2423cb34c60Sahrens 			if (zap_count(mos, jumpobj, &n) == 0 && n == 0) {
2433cb34c60Sahrens 				(void) zap_remove(mos, zapobj,
2443cb34c60Sahrens 				    whokey, tx);
2453cb34c60Sahrens 				VERIFY(0 == zap_destroy(mos,
2463cb34c60Sahrens 				    jumpobj, tx));
247ecd6cf80Smarks 			}
2484445fffbSMatthew Ahrens 			spa_history_log_internal_dd(dd, "permission remove", tx,
2494445fffbSMatthew Ahrens 			    "%s %s", whokey, perm);
250ecd6cf80Smarks 		}
251ecd6cf80Smarks 	}
2523b2aab18SMatthew Ahrens 	dsl_dir_rele(dd, FTAG);
253ecd6cf80Smarks }
254ecd6cf80Smarks 
2553b2aab18SMatthew Ahrens static int
dsl_deleg_check(void * arg,dmu_tx_t * tx)2563b2aab18SMatthew Ahrens dsl_deleg_check(void *arg, dmu_tx_t *tx)
257ecd6cf80Smarks {
2583b2aab18SMatthew Ahrens 	dsl_deleg_arg_t *dda = arg;
259ecd6cf80Smarks 	dsl_dir_t *dd;
260ecd6cf80Smarks 	int error;
261ecd6cf80Smarks 
2623b2aab18SMatthew Ahrens 	if (spa_version(dmu_tx_pool(tx)->dp_spa) <
263990b4856Slling 	    SPA_VERSION_DELEGATED_PERMS) {
264be6fd75aSMatthew Ahrens 		return (SET_ERROR(ENOTSUP));
265ecd6cf80Smarks 	}
266ecd6cf80Smarks 
2673b2aab18SMatthew Ahrens 	error = dsl_dir_hold(dmu_tx_pool(tx), dda->dda_name, FTAG, &dd, NULL);
2683b2aab18SMatthew Ahrens 	if (error == 0)
2693b2aab18SMatthew Ahrens 		dsl_dir_rele(dd, FTAG);
2703b2aab18SMatthew Ahrens 	return (error);
2713b2aab18SMatthew Ahrens }
2723b2aab18SMatthew Ahrens 
2733b2aab18SMatthew Ahrens int
dsl_deleg_set(const char * ddname,nvlist_t * nvp,boolean_t unset)2743b2aab18SMatthew Ahrens dsl_deleg_set(const char *ddname, nvlist_t *nvp, boolean_t unset)
2753b2aab18SMatthew Ahrens {
2763b2aab18SMatthew Ahrens 	dsl_deleg_arg_t dda;
2773b2aab18SMatthew Ahrens 
2783b2aab18SMatthew Ahrens 	/* nvp must already have been verified to be valid */
279ecd6cf80Smarks 
2803b2aab18SMatthew Ahrens 	dda.dda_name = ddname;
2813b2aab18SMatthew Ahrens 	dda.dda_nvlist = nvp;
282ecd6cf80Smarks 
2833b2aab18SMatthew Ahrens 	return (dsl_sync_task(ddname, dsl_deleg_check,
2843b2aab18SMatthew Ahrens 	    unset ? dsl_deleg_unset_sync : dsl_deleg_set_sync,
2857d46dc6cSMatthew Ahrens 	    &dda, fnvlist_num_pairs(nvp), ZFS_SPACE_CHECK_RESERVED));
286ecd6cf80Smarks }
287ecd6cf80Smarks 
288ecd6cf80Smarks /*
289ecd6cf80Smarks  * Find all 'allow' permissions from a given point and then continue
290ecd6cf80Smarks  * traversing up to the root.
291ecd6cf80Smarks  *
292ecd6cf80Smarks  * This function constructs an nvlist of nvlists.
293ecd6cf80Smarks  * each setpoint is an nvlist composed of an nvlist of an nvlist
294ecd6cf80Smarks  * of the individual * users/groups/everyone/create
295ecd6cf80Smarks  * permissions.
296ecd6cf80Smarks  *
297ecd6cf80Smarks  * The nvlist will look like this.
298ecd6cf80Smarks  *
299ecd6cf80Smarks  * { source fsname -> { whokeys { permissions,...}, ...}}
300ecd6cf80Smarks  *
301ecd6cf80Smarks  * The fsname nvpairs will be arranged in a bottom up order.  For example,
302ecd6cf80Smarks  * if we have the following structure a/b/c then the nvpairs for the fsnames
303ecd6cf80Smarks  * will be ordered a/b/c, a/b, a.
304ecd6cf80Smarks  */
305ecd6cf80Smarks int
dsl_deleg_get(const char * ddname,nvlist_t ** nvp)306ecd6cf80Smarks dsl_deleg_get(const char *ddname, nvlist_t **nvp)
307ecd6cf80Smarks {
308ecd6cf80Smarks 	dsl_dir_t *dd, *startdd;
309ecd6cf80Smarks 	dsl_pool_t *dp;
310ecd6cf80Smarks 	int error;
311ecd6cf80Smarks 	objset_t *mos;
312ecd6cf80Smarks 
3133b2aab18SMatthew Ahrens 	error = dsl_pool_hold(ddname, FTAG, &dp);
3143b2aab18SMatthew Ahrens 	if (error != 0)
3153b2aab18SMatthew Ahrens 		return (error);
3163b2aab18SMatthew Ahrens 
3173b2aab18SMatthew Ahrens 	error = dsl_dir_hold(dp, ddname, FTAG, &startdd, NULL);
3183b2aab18SMatthew Ahrens 	if (error != 0) {
3193b2aab18SMatthew Ahrens 		dsl_pool_rele(dp, FTAG);
320ecd6cf80Smarks 		return (error);
3213b2aab18SMatthew Ahrens 	}
322ecd6cf80Smarks 
323ecd6cf80Smarks 	dp = startdd->dd_pool;
324ecd6cf80Smarks 	mos = dp->dp_meta_objset;
325ecd6cf80Smarks 
326ecd6cf80Smarks 	VERIFY(nvlist_alloc(nvp, NV_UNIQUE_NAME, KM_SLEEP) == 0);
327ecd6cf80Smarks 
328ecd6cf80Smarks 	for (dd = startdd; dd != NULL; dd = dd->dd_parent) {
329ecd6cf80Smarks 		zap_cursor_t basezc;
330ecd6cf80Smarks 		zap_attribute_t baseza;
331ecd6cf80Smarks 		nvlist_t *sp_nvp;
332ecd6cf80Smarks 		uint64_t n;
3339adfa60dSMatthew Ahrens 		char source[ZFS_MAX_DATASET_NAME_LEN];
334ecd6cf80Smarks 
335c1379625SJustin T. Gibbs 		if (dsl_dir_phys(dd)->dd_deleg_zapobj == 0 ||
336c1379625SJustin T. Gibbs 		    zap_count(mos,
337c1379625SJustin T. Gibbs 		    dsl_dir_phys(dd)->dd_deleg_zapobj, &n) != 0 || n == 0)
338ecd6cf80Smarks 			continue;
339ecd6cf80Smarks 
3403b2aab18SMatthew Ahrens 		sp_nvp = fnvlist_alloc();
341ecd6cf80Smarks 		for (zap_cursor_init(&basezc, mos,
342c1379625SJustin T. Gibbs 		    dsl_dir_phys(dd)->dd_deleg_zapobj);
343ecd6cf80Smarks 		    zap_cursor_retrieve(&basezc, &baseza) == 0;
344ecd6cf80Smarks 		    zap_cursor_advance(&basezc)) {
345ecd6cf80Smarks 			zap_cursor_t zc;
346ecd6cf80Smarks 			zap_attribute_t za;
347ecd6cf80Smarks 			nvlist_t *perms_nvp;
348ecd6cf80Smarks 
349ecd6cf80Smarks 			ASSERT(baseza.za_integer_length == 8);
350ecd6cf80Smarks 			ASSERT(baseza.za_num_integers == 1);
351ecd6cf80Smarks 
3523b2aab18SMatthew Ahrens 			perms_nvp = fnvlist_alloc();
353ecd6cf80Smarks 			for (zap_cursor_init(&zc, mos, baseza.za_first_integer);
354ecd6cf80Smarks 			    zap_cursor_retrieve(&zc, &za) == 0;
355ecd6cf80Smarks 			    zap_cursor_advance(&zc)) {
3563b2aab18SMatthew Ahrens 				fnvlist_add_boolean(perms_nvp, za.za_name);
357ecd6cf80Smarks 			}
358ecd6cf80Smarks 			zap_cursor_fini(&zc);
3593b2aab18SMatthew Ahrens 			fnvlist_add_nvlist(sp_nvp, baseza.za_name, perms_nvp);
3603b2aab18SMatthew Ahrens 			fnvlist_free(perms_nvp);
361ecd6cf80Smarks 		}
362ecd6cf80Smarks 
363ecd6cf80Smarks 		zap_cursor_fini(&basezc);
364ecd6cf80Smarks 
365ecd6cf80Smarks 		dsl_dir_name(dd, source);
3663b2aab18SMatthew Ahrens 		fnvlist_add_nvlist(*nvp, source, sp_nvp);
367ecd6cf80Smarks 		nvlist_free(sp_nvp);
368ecd6cf80Smarks 	}
369ecd6cf80Smarks 
3703b2aab18SMatthew Ahrens 	dsl_dir_rele(startdd, FTAG);
3713b2aab18SMatthew Ahrens 	dsl_pool_rele(dp, FTAG);
372ecd6cf80Smarks 	return (0);
373ecd6cf80Smarks }
374ecd6cf80Smarks 
375ecd6cf80Smarks /*
376ecd6cf80Smarks  * Routines for dsl_deleg_access() -- access checking.
377ecd6cf80Smarks  */
378ecd6cf80Smarks typedef struct perm_set {
379ecd6cf80Smarks 	avl_node_t	p_node;
380ecd6cf80Smarks 	boolean_t	p_matched;
38191ebeef5Sahrens 	char		p_setname[ZFS_MAX_DELEG_NAME];
382ecd6cf80Smarks } perm_set_t;
383ecd6cf80Smarks 
384ecd6cf80Smarks static int
perm_set_compare(const void * arg1,const void * arg2)385ecd6cf80Smarks perm_set_compare(const void *arg1, const void *arg2)
386ecd6cf80Smarks {
387c4ab0d3fSGvozden Neskovic 	const perm_set_t *node1 = (const perm_set_t *)arg1;
388c4ab0d3fSGvozden Neskovic 	const perm_set_t *node2 = (const perm_set_t *)arg2;
389ecd6cf80Smarks 	int val;
390ecd6cf80Smarks 
391ecd6cf80Smarks 	val = strcmp(node1->p_setname, node2->p_setname);
392c4ab0d3fSGvozden Neskovic 
393*4d7988d6SPaul Dagnelie 	return (TREE_ISIGN(val));
394ecd6cf80Smarks }
395ecd6cf80Smarks 
396ecd6cf80Smarks /*
397ecd6cf80Smarks  * Determine whether a specified permission exists.
398ecd6cf80Smarks  *
399ecd6cf80Smarks  * First the base attribute has to be retrieved.  i.e. ul$12
400ecd6cf80Smarks  * Once the base object has been retrieved the actual permission
401ecd6cf80Smarks  * is lookup up in the zap object the base object points to.
402ecd6cf80Smarks  *
403ecd6cf80Smarks  * Return 0 if permission exists, ENOENT if there is no whokey, EPERM if
404ecd6cf80Smarks  * there is no perm in that jumpobj.
405ecd6cf80Smarks  */
406ecd6cf80Smarks static int
dsl_check_access(objset_t * mos,uint64_t zapobj,char type,char checkflag,void * valp,const char * perm)407ecd6cf80Smarks dsl_check_access(objset_t *mos, uint64_t zapobj,
408ecd6cf80Smarks     char type, char checkflag, void *valp, const char *perm)
409ecd6cf80Smarks {
410ecd6cf80Smarks 	int error;
411ecd6cf80Smarks 	uint64_t jumpobj, zero;
412ecd6cf80Smarks 	char whokey[ZFS_MAX_DELEG_NAME];
413ecd6cf80Smarks 
414ecd6cf80Smarks 	zfs_deleg_whokey(whokey, type, checkflag, valp);
415ecd6cf80Smarks 	error = zap_lookup(mos, zapobj, whokey, 8, 1, &jumpobj);
416ecd6cf80Smarks 	if (error == 0) {
417ecd6cf80Smarks 		error = zap_lookup(mos, jumpobj, perm, 8, 1, &zero);
418ecd6cf80Smarks 		if (error == ENOENT)
419be6fd75aSMatthew Ahrens 			error = SET_ERROR(EPERM);
420ecd6cf80Smarks 	}
421ecd6cf80Smarks 	return (error);
422ecd6cf80Smarks }
423ecd6cf80Smarks 
424ecd6cf80Smarks /*
425ecd6cf80Smarks  * check a specified user/group for a requested permission
426ecd6cf80Smarks  */
427ecd6cf80Smarks static int
dsl_check_user_access(objset_t * mos,uint64_t zapobj,const char * perm,int checkflag,cred_t * cr)42891ebeef5Sahrens dsl_check_user_access(objset_t *mos, uint64_t zapobj, const char *perm,
429ecd6cf80Smarks     int checkflag, cred_t *cr)
430ecd6cf80Smarks {
431ecd6cf80Smarks 	const	gid_t *gids;
432ecd6cf80Smarks 	int	ngids;
433ecd6cf80Smarks 	int	i;
434ecd6cf80Smarks 	uint64_t id;
435ecd6cf80Smarks 
436ecd6cf80Smarks 	/* check for user */
437ecd6cf80Smarks 	id = crgetuid(cr);
43891ebeef5Sahrens 	if (dsl_check_access(mos, zapobj,
439ecd6cf80Smarks 	    ZFS_DELEG_USER, checkflag, &id, perm) == 0)
440ecd6cf80Smarks 		return (0);
441ecd6cf80Smarks 
442ecd6cf80Smarks 	/* check for users primary group */
443ecd6cf80Smarks 	id = crgetgid(cr);
44491ebeef5Sahrens 	if (dsl_check_access(mos, zapobj,
445ecd6cf80Smarks 	    ZFS_DELEG_GROUP, checkflag, &id, perm) == 0)
446ecd6cf80Smarks 		return (0);
447ecd6cf80Smarks 
448ecd6cf80Smarks 	/* check for everyone entry */
449ecd6cf80Smarks 	id = -1;
45091ebeef5Sahrens 	if (dsl_check_access(mos, zapobj,
451ecd6cf80Smarks 	    ZFS_DELEG_EVERYONE, checkflag, &id, perm) == 0)
452ecd6cf80Smarks 		return (0);
453ecd6cf80Smarks 
454ecd6cf80Smarks 	/* check each supplemental group user is a member of */
455ecd6cf80Smarks 	ngids = crgetngroups(cr);
456ecd6cf80Smarks 	gids = crgetgroups(cr);
457ecd6cf80Smarks 	for (i = 0; i != ngids; i++) {
458ecd6cf80Smarks 		id = gids[i];
45991ebeef5Sahrens 		if (dsl_check_access(mos, zapobj,
460ecd6cf80Smarks 		    ZFS_DELEG_GROUP, checkflag, &id, perm) == 0)
461ecd6cf80Smarks 			return (0);
462ecd6cf80Smarks 	}
463ecd6cf80Smarks 
464be6fd75aSMatthew Ahrens 	return (SET_ERROR(EPERM));
465ecd6cf80Smarks }
466ecd6cf80Smarks 
467ecd6cf80Smarks /*
468ecd6cf80Smarks  * Iterate over the sets specified in the specified zapobj
469ecd6cf80Smarks  * and load them into the permsets avl tree.
470ecd6cf80Smarks  */
471ecd6cf80Smarks static int
dsl_load_sets(objset_t * mos,uint64_t zapobj,char type,char checkflag,void * valp,avl_tree_t * avl)472ecd6cf80Smarks dsl_load_sets(objset_t *mos, uint64_t zapobj,
473ecd6cf80Smarks     char type, char checkflag, void *valp, avl_tree_t *avl)
474ecd6cf80Smarks {
475ecd6cf80Smarks 	zap_cursor_t zc;
476ecd6cf80Smarks 	zap_attribute_t za;
477ecd6cf80Smarks 	perm_set_t *permnode;
478ecd6cf80Smarks 	avl_index_t idx;
479ecd6cf80Smarks 	uint64_t jumpobj;
480ecd6cf80Smarks 	int error;
481ecd6cf80Smarks 	char whokey[ZFS_MAX_DELEG_NAME];
482ecd6cf80Smarks 
483ecd6cf80Smarks 	zfs_deleg_whokey(whokey, type, checkflag, valp);
484ecd6cf80Smarks 
485ecd6cf80Smarks 	error = zap_lookup(mos, zapobj, whokey, 8, 1, &jumpobj);
486ecd6cf80Smarks 	if (error != 0)
487ecd6cf80Smarks 		return (error);
488ecd6cf80Smarks 
489ecd6cf80Smarks 	for (zap_cursor_init(&zc, mos, jumpobj);
490ecd6cf80Smarks 	    zap_cursor_retrieve(&zc, &za) == 0;
491ecd6cf80Smarks 	    zap_cursor_advance(&zc)) {
492ecd6cf80Smarks 		permnode = kmem_alloc(sizeof (perm_set_t), KM_SLEEP);
493ecd6cf80Smarks 		(void) strlcpy(permnode->p_setname, za.za_name,
494ecd6cf80Smarks 		    sizeof (permnode->p_setname));
495ecd6cf80Smarks 		permnode->p_matched = B_FALSE;
496ecd6cf80Smarks 
497ecd6cf80Smarks 		if (avl_find(avl, permnode, &idx) == NULL) {
498ecd6cf80Smarks 			avl_insert(avl, permnode, idx);
499ecd6cf80Smarks 		} else {
500ecd6cf80Smarks 			kmem_free(permnode, sizeof (perm_set_t));
501ecd6cf80Smarks 		}
502ecd6cf80Smarks 	}
503ecd6cf80Smarks 	zap_cursor_fini(&zc);
504ecd6cf80Smarks 	return (0);
505ecd6cf80Smarks }
506ecd6cf80Smarks 
507ecd6cf80Smarks /*
508ecd6cf80Smarks  * Load all permissions user based on cred belongs to.
509ecd6cf80Smarks  */
510ecd6cf80Smarks static void
dsl_load_user_sets(objset_t * mos,uint64_t zapobj,avl_tree_t * avl,char checkflag,cred_t * cr)511ecd6cf80Smarks dsl_load_user_sets(objset_t *mos, uint64_t zapobj, avl_tree_t *avl,
512ecd6cf80Smarks     char checkflag, cred_t *cr)
513ecd6cf80Smarks {
514ecd6cf80Smarks 	const	gid_t *gids;
515ecd6cf80Smarks 	int	ngids, i;
516ecd6cf80Smarks 	uint64_t id;
517ecd6cf80Smarks 
518ecd6cf80Smarks 	id = crgetuid(cr);
519ecd6cf80Smarks 	(void) dsl_load_sets(mos, zapobj,
520ecd6cf80Smarks 	    ZFS_DELEG_USER_SETS, checkflag, &id, avl);
521ecd6cf80Smarks 
522ecd6cf80Smarks 	id = crgetgid(cr);
523ecd6cf80Smarks 	(void) dsl_load_sets(mos, zapobj,
524ecd6cf80Smarks 	    ZFS_DELEG_GROUP_SETS, checkflag, &id, avl);
525ecd6cf80Smarks 
526ecd6cf80Smarks 	(void) dsl_load_sets(mos, zapobj,
527ecd6cf80Smarks 	    ZFS_DELEG_EVERYONE_SETS, checkflag, NULL, avl);
528ecd6cf80Smarks 
529ecd6cf80Smarks 	ngids = crgetngroups(cr);
530ecd6cf80Smarks 	gids = crgetgroups(cr);
531ecd6cf80Smarks 	for (i = 0; i != ngids; i++) {
532ecd6cf80Smarks 		id = gids[i];
533ecd6cf80Smarks 		(void) dsl_load_sets(mos, zapobj,
534ecd6cf80Smarks 		    ZFS_DELEG_GROUP_SETS, checkflag, &id, avl);
535ecd6cf80Smarks 	}
536ecd6cf80Smarks }
537ecd6cf80Smarks 
538ecd6cf80Smarks /*
5394445fffbSMatthew Ahrens  * Check if user has requested permission.
540ecd6cf80Smarks  */
541ecd6cf80Smarks int
dsl_deleg_access_impl(dsl_dataset_t * ds,const char * perm,cred_t * cr)5424445fffbSMatthew Ahrens dsl_deleg_access_impl(dsl_dataset_t *ds, const char *perm, cred_t *cr)
543ecd6cf80Smarks {
544bb0ade09Sahrens 	dsl_dir_t *dd;
545ecd6cf80Smarks 	dsl_pool_t *dp;
546ecd6cf80Smarks 	void *cookie;
547ecd6cf80Smarks 	int	error;
5488b33e213SWilliam Gorrell 	char	checkflag;
549ecd6cf80Smarks 	objset_t *mos;
550ecd6cf80Smarks 	avl_tree_t permsets;
551ecd6cf80Smarks 	perm_set_t *setnode;
552ecd6cf80Smarks 
553bb0ade09Sahrens 	dp = ds->ds_dir->dd_pool;
554ecd6cf80Smarks 	mos = dp->dp_meta_objset;
555ecd6cf80Smarks 
556a7f53a56SChris Kirby 	if (dsl_delegation_on(mos) == B_FALSE)
557be6fd75aSMatthew Ahrens 		return (SET_ERROR(ECANCELED));
558ecd6cf80Smarks 
559ecd6cf80Smarks 	if (spa_version(dmu_objset_spa(dp->dp_meta_objset)) <
560a7f53a56SChris Kirby 	    SPA_VERSION_DELEGATED_PERMS)
561be6fd75aSMatthew Ahrens 		return (SET_ERROR(EPERM));
562ecd6cf80Smarks 
563bc9014e6SJustin Gibbs 	if (ds->ds_is_snapshot) {
5648b33e213SWilliam Gorrell 		/*
5658b33e213SWilliam Gorrell 		 * Snapshots are treated as descendents only,
5668b33e213SWilliam Gorrell 		 * local permissions do not apply.
5678b33e213SWilliam Gorrell 		 */
5688b33e213SWilliam Gorrell 		checkflag = ZFS_DELEG_DESCENDENT;
5698b33e213SWilliam Gorrell 	} else {
5708b33e213SWilliam Gorrell 		checkflag = ZFS_DELEG_LOCAL;
5718b33e213SWilliam Gorrell 	}
5728b33e213SWilliam Gorrell 
573ecd6cf80Smarks 	avl_create(&permsets, perm_set_compare, sizeof (perm_set_t),
574ecd6cf80Smarks 	    offsetof(perm_set_t, p_node));
575ecd6cf80Smarks 
5763b2aab18SMatthew Ahrens 	ASSERT(dsl_pool_config_held(dp));
577bb0ade09Sahrens 	for (dd = ds->ds_dir; dd != NULL; dd = dd->dd_parent,
578ecd6cf80Smarks 	    checkflag = ZFS_DELEG_DESCENDENT) {
579ecd6cf80Smarks 		uint64_t zapobj;
580ecd6cf80Smarks 		boolean_t expanded;
581ecd6cf80Smarks 
582ecd6cf80Smarks 		/*
583ecd6cf80Smarks 		 * If not in global zone then make sure
584ecd6cf80Smarks 		 * the zoned property is set
585ecd6cf80Smarks 		 */
586ecd6cf80Smarks 		if (!INGLOBALZONE(curproc)) {
587ecd6cf80Smarks 			uint64_t zoned;
588ecd6cf80Smarks 
589bb0ade09Sahrens 			if (dsl_prop_get_dd(dd,
590ecd6cf80Smarks 			    zfs_prop_to_name(ZFS_PROP_ZONED),
59192241e0bSTom Erickson 			    8, 1, &zoned, NULL, B_FALSE) != 0)
592ecd6cf80Smarks 				break;
593ecd6cf80Smarks 			if (!zoned)
594ecd6cf80Smarks 				break;
595ecd6cf80Smarks 		}
596c1379625SJustin T. Gibbs 		zapobj = dsl_dir_phys(dd)->dd_deleg_zapobj;
597ecd6cf80Smarks 
598ecd6cf80Smarks 		if (zapobj == 0)
599ecd6cf80Smarks 			continue;
600ecd6cf80Smarks 
601ecd6cf80Smarks 		dsl_load_user_sets(mos, zapobj, &permsets, checkflag, cr);
602ecd6cf80Smarks again:
603ecd6cf80Smarks 		expanded = B_FALSE;
604ecd6cf80Smarks 		for (setnode = avl_first(&permsets); setnode;
605ecd6cf80Smarks 		    setnode = AVL_NEXT(&permsets, setnode)) {
606ecd6cf80Smarks 			if (setnode->p_matched == B_TRUE)
607ecd6cf80Smarks 				continue;
608ecd6cf80Smarks 
609ecd6cf80Smarks 			/* See if this set directly grants this permission */
610ecd6cf80Smarks 			error = dsl_check_access(mos, zapobj,
611ecd6cf80Smarks 			    ZFS_DELEG_NAMED_SET, 0, setnode->p_setname, perm);
612ecd6cf80Smarks 			if (error == 0)
613ecd6cf80Smarks 				goto success;
614ecd6cf80Smarks 			if (error == EPERM)
615ecd6cf80Smarks 				setnode->p_matched = B_TRUE;
616ecd6cf80Smarks 
617ecd6cf80Smarks 			/* See if this set includes other sets */
618ecd6cf80Smarks 			error = dsl_load_sets(mos, zapobj,
619ecd6cf80Smarks 			    ZFS_DELEG_NAMED_SET_SETS, 0,
620ecd6cf80Smarks 			    setnode->p_setname, &permsets);
621ecd6cf80Smarks 			if (error == 0)
622ecd6cf80Smarks 				setnode->p_matched = expanded = B_TRUE;
623ecd6cf80Smarks 		}
624ecd6cf80Smarks 		/*
625ecd6cf80Smarks 		 * If we expanded any sets, that will define more sets,
626ecd6cf80Smarks 		 * which we need to check.
627ecd6cf80Smarks 		 */
628ecd6cf80Smarks 		if (expanded)
629ecd6cf80Smarks 			goto again;
630ecd6cf80Smarks 
631ecd6cf80Smarks 		error = dsl_check_user_access(mos, zapobj, perm, checkflag, cr);
632ecd6cf80Smarks 		if (error == 0)
633ecd6cf80Smarks 			goto success;
634ecd6cf80Smarks 	}
635be6fd75aSMatthew Ahrens 	error = SET_ERROR(EPERM);
636ecd6cf80Smarks success:
637ecd6cf80Smarks 
638ecd6cf80Smarks 	cookie = NULL;
63991ebeef5Sahrens 	while ((setnode = avl_destroy_nodes(&permsets, &cookie)) != NULL)
640ecd6cf80Smarks 		kmem_free(setnode, sizeof (perm_set_t));
641ecd6cf80Smarks 
642ecd6cf80Smarks 	return (error);
643ecd6cf80Smarks }
644ecd6cf80Smarks 
645a7f53a56SChris Kirby int
dsl_deleg_access(const char * dsname,const char * perm,cred_t * cr)646a7f53a56SChris Kirby dsl_deleg_access(const char *dsname, const char *perm, cred_t *cr)
647a7f53a56SChris Kirby {
6483b2aab18SMatthew Ahrens 	dsl_pool_t *dp;
649a7f53a56SChris Kirby 	dsl_dataset_t *ds;
650a7f53a56SChris Kirby 	int error;
651a7f53a56SChris Kirby 
6523b2aab18SMatthew Ahrens 	error = dsl_pool_hold(dsname, FTAG, &dp);
6533b2aab18SMatthew Ahrens 	if (error != 0)
654a7f53a56SChris Kirby 		return (error);
6553b2aab18SMatthew Ahrens 	error = dsl_dataset_hold(dp, dsname, FTAG, &ds);
6563b2aab18SMatthew Ahrens 	if (error == 0) {
6573b2aab18SMatthew Ahrens 		error = dsl_deleg_access_impl(ds, perm, cr);
6583b2aab18SMatthew Ahrens 		dsl_dataset_rele(ds, FTAG);
6593b2aab18SMatthew Ahrens 	}
6603b2aab18SMatthew Ahrens 	dsl_pool_rele(dp, FTAG);
661a7f53a56SChris Kirby 
662a7f53a56SChris Kirby 	return (error);
663a7f53a56SChris Kirby }
664a7f53a56SChris Kirby 
665ecd6cf80Smarks /*
666ecd6cf80Smarks  * Other routines.
667ecd6cf80Smarks  */
668ecd6cf80Smarks 
669ecd6cf80Smarks static void
copy_create_perms(dsl_dir_t * dd,uint64_t pzapobj,boolean_t dosets,uint64_t uid,dmu_tx_t * tx)67091ebeef5Sahrens copy_create_perms(dsl_dir_t *dd, uint64_t pzapobj,
671ecd6cf80Smarks     boolean_t dosets, uint64_t uid, dmu_tx_t *tx)
672ecd6cf80Smarks {
67391ebeef5Sahrens 	objset_t *mos = dd->dd_pool->dp_meta_objset;
674ecd6cf80Smarks 	uint64_t jumpobj, pjumpobj;
675c1379625SJustin T. Gibbs 	uint64_t zapobj = dsl_dir_phys(dd)->dd_deleg_zapobj;
676ecd6cf80Smarks 	zap_cursor_t zc;
677ecd6cf80Smarks 	zap_attribute_t za;
678ecd6cf80Smarks 	char whokey[ZFS_MAX_DELEG_NAME];
679ecd6cf80Smarks 
680ecd6cf80Smarks 	zfs_deleg_whokey(whokey,
681ecd6cf80Smarks 	    dosets ? ZFS_DELEG_CREATE_SETS : ZFS_DELEG_CREATE,
682ecd6cf80Smarks 	    ZFS_DELEG_LOCAL, NULL);
68391ebeef5Sahrens 	if (zap_lookup(mos, pzapobj, whokey, 8, 1, &pjumpobj) != 0)
684ecd6cf80Smarks 		return;
685ecd6cf80Smarks 
686ecd6cf80Smarks 	if (zapobj == 0) {
687ecd6cf80Smarks 		dmu_buf_will_dirty(dd->dd_dbuf, tx);
688c1379625SJustin T. Gibbs 		zapobj = dsl_dir_phys(dd)->dd_deleg_zapobj = zap_create(mos,
689ecd6cf80Smarks 		    DMU_OT_DSL_PERMS, DMU_OT_NONE, 0, tx);
690ecd6cf80Smarks 	}
691ecd6cf80Smarks 
69291ebeef5Sahrens 	zfs_deleg_whokey(whokey,
69391ebeef5Sahrens 	    dosets ? ZFS_DELEG_USER_SETS : ZFS_DELEG_USER,
69491ebeef5Sahrens 	    ZFS_DELEG_LOCAL, &uid);
695ecd6cf80Smarks 	if (zap_lookup(mos, zapobj, whokey, 8, 1, &jumpobj) == ENOENT) {
696ecd6cf80Smarks 		jumpobj = zap_create(mos, DMU_OT_DSL_PERMS, DMU_OT_NONE, 0, tx);
697ecd6cf80Smarks 		VERIFY(zap_add(mos, zapobj, whokey, 8, 1, &jumpobj, tx) == 0);
698ecd6cf80Smarks 	}
699ecd6cf80Smarks 
700ecd6cf80Smarks 	for (zap_cursor_init(&zc, mos, pjumpobj);
701ecd6cf80Smarks 	    zap_cursor_retrieve(&zc, &za) == 0;
702ecd6cf80Smarks 	    zap_cursor_advance(&zc)) {
70391ebeef5Sahrens 		uint64_t zero = 0;
704ecd6cf80Smarks 		ASSERT(za.za_integer_length == 8 && za.za_num_integers == 1);
705ecd6cf80Smarks 
706ecd6cf80Smarks 		VERIFY(zap_update(mos, jumpobj, za.za_name,
707ecd6cf80Smarks 		    8, 1, &zero, tx) == 0);
708ecd6cf80Smarks 	}
709ecd6cf80Smarks 	zap_cursor_fini(&zc);
710ecd6cf80Smarks }
711ecd6cf80Smarks 
712ecd6cf80Smarks /*
713ecd6cf80Smarks  * set all create time permission on new dataset.
714ecd6cf80Smarks  */
715ecd6cf80Smarks void
dsl_deleg_set_create_perms(dsl_dir_t * sdd,dmu_tx_t * tx,cred_t * cr)716ecd6cf80Smarks dsl_deleg_set_create_perms(dsl_dir_t *sdd, dmu_tx_t *tx, cred_t *cr)
717ecd6cf80Smarks {
718ecd6cf80Smarks 	dsl_dir_t *dd;
71991ebeef5Sahrens 	uint64_t uid = crgetuid(cr);
720ecd6cf80Smarks 
721ecd6cf80Smarks 	if (spa_version(dmu_objset_spa(sdd->dd_pool->dp_meta_objset)) <
722990b4856Slling 	    SPA_VERSION_DELEGATED_PERMS)
723ecd6cf80Smarks 		return;
724ecd6cf80Smarks 
725ecd6cf80Smarks 	for (dd = sdd->dd_parent; dd != NULL; dd = dd->dd_parent) {
726c1379625SJustin T. Gibbs 		uint64_t pzapobj = dsl_dir_phys(dd)->dd_deleg_zapobj;
727ecd6cf80Smarks 
72891ebeef5Sahrens 		if (pzapobj == 0)
729ecd6cf80Smarks 			continue;
730ecd6cf80Smarks 
73191ebeef5Sahrens 		copy_create_perms(sdd, pzapobj, B_FALSE, uid, tx);
73291ebeef5Sahrens 		copy_create_perms(sdd, pzapobj, B_TRUE, uid, tx);
733ecd6cf80Smarks 	}
734ecd6cf80Smarks }
735ecd6cf80Smarks 
736ecd6cf80Smarks int
dsl_deleg_destroy(objset_t * mos,uint64_t zapobj,dmu_tx_t * tx)737ecd6cf80Smarks dsl_deleg_destroy(objset_t *mos, uint64_t zapobj, dmu_tx_t *tx)
738ecd6cf80Smarks {
739ecd6cf80Smarks 	zap_cursor_t zc;
740ecd6cf80Smarks 	zap_attribute_t za;
741ecd6cf80Smarks 
742ecd6cf80Smarks 	if (zapobj == 0)
743ecd6cf80Smarks 		return (0);
744ecd6cf80Smarks 
745ecd6cf80Smarks 	for (zap_cursor_init(&zc, mos, zapobj);
746ecd6cf80Smarks 	    zap_cursor_retrieve(&zc, &za) == 0;
747ecd6cf80Smarks 	    zap_cursor_advance(&zc)) {
748ecd6cf80Smarks 		ASSERT(za.za_integer_length == 8 && za.za_num_integers == 1);
749ecd6cf80Smarks 		VERIFY(0 == zap_destroy(mos, za.za_first_integer, tx));
750ecd6cf80Smarks 	}
751ecd6cf80Smarks 	zap_cursor_fini(&zc);
752ecd6cf80Smarks 	VERIFY(0 == zap_destroy(mos, zapobj, tx));
753ecd6cf80Smarks 	return (0);
754ecd6cf80Smarks }
755ecd6cf80Smarks 
756ecd6cf80Smarks boolean_t
dsl_delegation_on(objset_t * os)757ecd6cf80Smarks dsl_delegation_on(objset_t *os)
758ecd6cf80Smarks {
759b24ab676SJeff Bonwick 	return (!!spa_delegation(os->os_spa));
760ecd6cf80Smarks }
761