xref: /titanic_51/usr/src/uts/common/fs/zfs/dsl_deleg.c (revision 40a5c998e5462ed47916e2edfcc5016073cf8851)
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.
23*40a5c998SMatthew 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
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
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;
154ecd6cf80Smarks 
1553b2aab18SMatthew Ahrens static void
1563b2aab18SMatthew Ahrens dsl_deleg_set_sync(void *arg, dmu_tx_t *tx)
1573b2aab18SMatthew Ahrens {
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;
1623b2aab18SMatthew Ahrens 	nvpair_t *whopair = NULL;
1633b2aab18SMatthew Ahrens 	uint64_t zapobj;
1643b2aab18SMatthew Ahrens 
1653b2aab18SMatthew Ahrens 	VERIFY0(dsl_dir_hold(dp, dda->dda_name, FTAG, &dd, NULL));
1663b2aab18SMatthew Ahrens 
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
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)
235ecd6cf80Smarks 			continue;
236ecd6cf80Smarks 
237ecd6cf80Smarks 		while (permpair = nvlist_next_nvpair(perms, permpair)) {
238ecd6cf80Smarks 			const char *perm = nvpair_name(permpair);
239ecd6cf80Smarks 			uint64_t n = 0;
240ecd6cf80Smarks 
241ecd6cf80Smarks 			(void) zap_remove(mos, jumpobj, perm, tx);
2423cb34c60Sahrens 			if (zap_count(mos, jumpobj, &n) == 0 && n == 0) {
243ecd6cf80Smarks 				(void) zap_remove(mos, zapobj,
244ecd6cf80Smarks 				    whokey, tx);
245ecd6cf80Smarks 				VERIFY(0 == zap_destroy(mos,
246ecd6cf80Smarks 				    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);
2533b2aab18SMatthew Ahrens }
2543b2aab18SMatthew Ahrens 
2553b2aab18SMatthew Ahrens static int
2563b2aab18SMatthew Ahrens dsl_deleg_check(void *arg, dmu_tx_t *tx)
2573b2aab18SMatthew Ahrens {
2583b2aab18SMatthew Ahrens 	dsl_deleg_arg_t *dda = arg;
2593b2aab18SMatthew Ahrens 	dsl_dir_t *dd;
2603b2aab18SMatthew Ahrens 	int error;
2613b2aab18SMatthew Ahrens 
2623b2aab18SMatthew Ahrens 	if (spa_version(dmu_tx_pool(tx)->dp_spa) <
2633b2aab18SMatthew Ahrens 	    SPA_VERSION_DELEGATED_PERMS) {
264be6fd75aSMatthew Ahrens 		return (SET_ERROR(ENOTSUP));
2653b2aab18SMatthew Ahrens 	}
2663b2aab18SMatthew Ahrens 
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);
271ecd6cf80Smarks }
272ecd6cf80Smarks 
273ecd6cf80Smarks int
274ecd6cf80Smarks dsl_deleg_set(const char *ddname, nvlist_t *nvp, boolean_t unset)
275ecd6cf80Smarks {
2763b2aab18SMatthew Ahrens 	dsl_deleg_arg_t dda;
277ecd6cf80Smarks 
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,
2843cb34c60Sahrens 	    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
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)
315ecd6cf80Smarks 		return (error);
316ecd6cf80Smarks 
3173b2aab18SMatthew Ahrens 	error = dsl_dir_hold(dp, ddname, FTAG, &startdd, NULL);
3183b2aab18SMatthew Ahrens 	if (error != 0) {
3193b2aab18SMatthew Ahrens 		dsl_pool_rele(dp, FTAG);
3203b2aab18SMatthew Ahrens 		return (error);
3213b2aab18SMatthew Ahrens 	}
3223b2aab18SMatthew Ahrens 
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;
333*40a5c998SMatthew 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
385ecd6cf80Smarks perm_set_compare(const void *arg1, const void *arg2)
386ecd6cf80Smarks {
387ecd6cf80Smarks 	const perm_set_t *node1 = arg1;
388ecd6cf80Smarks 	const perm_set_t *node2 = arg2;
389ecd6cf80Smarks 	int val;
390ecd6cf80Smarks 
391ecd6cf80Smarks 	val = strcmp(node1->p_setname, node2->p_setname);
392ecd6cf80Smarks 	if (val == 0)
393ecd6cf80Smarks 		return (0);
394ecd6cf80Smarks 	return (val > 0 ? 1 : -1);
395ecd6cf80Smarks }
396ecd6cf80Smarks 
397ecd6cf80Smarks /*
398ecd6cf80Smarks  * Determine whether a specified permission exists.
399ecd6cf80Smarks  *
400ecd6cf80Smarks  * First the base attribute has to be retrieved.  i.e. ul$12
401ecd6cf80Smarks  * Once the base object has been retrieved the actual permission
402ecd6cf80Smarks  * is lookup up in the zap object the base object points to.
403ecd6cf80Smarks  *
404ecd6cf80Smarks  * Return 0 if permission exists, ENOENT if there is no whokey, EPERM if
405ecd6cf80Smarks  * there is no perm in that jumpobj.
406ecd6cf80Smarks  */
407ecd6cf80Smarks static int
408ecd6cf80Smarks dsl_check_access(objset_t *mos, uint64_t zapobj,
409ecd6cf80Smarks     char type, char checkflag, void *valp, const char *perm)
410ecd6cf80Smarks {
411ecd6cf80Smarks 	int error;
412ecd6cf80Smarks 	uint64_t jumpobj, zero;
413ecd6cf80Smarks 	char whokey[ZFS_MAX_DELEG_NAME];
414ecd6cf80Smarks 
415ecd6cf80Smarks 	zfs_deleg_whokey(whokey, type, checkflag, valp);
416ecd6cf80Smarks 	error = zap_lookup(mos, zapobj, whokey, 8, 1, &jumpobj);
417ecd6cf80Smarks 	if (error == 0) {
418ecd6cf80Smarks 		error = zap_lookup(mos, jumpobj, perm, 8, 1, &zero);
419ecd6cf80Smarks 		if (error == ENOENT)
420be6fd75aSMatthew Ahrens 			error = SET_ERROR(EPERM);
421ecd6cf80Smarks 	}
422ecd6cf80Smarks 	return (error);
423ecd6cf80Smarks }
424ecd6cf80Smarks 
425ecd6cf80Smarks /*
426ecd6cf80Smarks  * check a specified user/group for a requested permission
427ecd6cf80Smarks  */
428ecd6cf80Smarks static int
42991ebeef5Sahrens dsl_check_user_access(objset_t *mos, uint64_t zapobj, const char *perm,
430ecd6cf80Smarks     int checkflag, cred_t *cr)
431ecd6cf80Smarks {
432ecd6cf80Smarks 	const	gid_t *gids;
433ecd6cf80Smarks 	int	ngids;
434ecd6cf80Smarks 	int	i;
435ecd6cf80Smarks 	uint64_t id;
436ecd6cf80Smarks 
437ecd6cf80Smarks 	/* check for user */
438ecd6cf80Smarks 	id = crgetuid(cr);
43991ebeef5Sahrens 	if (dsl_check_access(mos, zapobj,
440ecd6cf80Smarks 	    ZFS_DELEG_USER, checkflag, &id, perm) == 0)
441ecd6cf80Smarks 		return (0);
442ecd6cf80Smarks 
443ecd6cf80Smarks 	/* check for users primary group */
444ecd6cf80Smarks 	id = crgetgid(cr);
44591ebeef5Sahrens 	if (dsl_check_access(mos, zapobj,
446ecd6cf80Smarks 	    ZFS_DELEG_GROUP, checkflag, &id, perm) == 0)
447ecd6cf80Smarks 		return (0);
448ecd6cf80Smarks 
449ecd6cf80Smarks 	/* check for everyone entry */
450ecd6cf80Smarks 	id = -1;
45191ebeef5Sahrens 	if (dsl_check_access(mos, zapobj,
452ecd6cf80Smarks 	    ZFS_DELEG_EVERYONE, checkflag, &id, perm) == 0)
453ecd6cf80Smarks 		return (0);
454ecd6cf80Smarks 
455ecd6cf80Smarks 	/* check each supplemental group user is a member of */
456ecd6cf80Smarks 	ngids = crgetngroups(cr);
457ecd6cf80Smarks 	gids = crgetgroups(cr);
458ecd6cf80Smarks 	for (i = 0; i != ngids; i++) {
459ecd6cf80Smarks 		id = gids[i];
46091ebeef5Sahrens 		if (dsl_check_access(mos, zapobj,
461ecd6cf80Smarks 		    ZFS_DELEG_GROUP, checkflag, &id, perm) == 0)
462ecd6cf80Smarks 			return (0);
463ecd6cf80Smarks 	}
464ecd6cf80Smarks 
465be6fd75aSMatthew Ahrens 	return (SET_ERROR(EPERM));
466ecd6cf80Smarks }
467ecd6cf80Smarks 
468ecd6cf80Smarks /*
469ecd6cf80Smarks  * Iterate over the sets specified in the specified zapobj
470ecd6cf80Smarks  * and load them into the permsets avl tree.
471ecd6cf80Smarks  */
472ecd6cf80Smarks static int
473ecd6cf80Smarks dsl_load_sets(objset_t *mos, uint64_t zapobj,
474ecd6cf80Smarks     char type, char checkflag, void *valp, avl_tree_t *avl)
475ecd6cf80Smarks {
476ecd6cf80Smarks 	zap_cursor_t zc;
477ecd6cf80Smarks 	zap_attribute_t za;
478ecd6cf80Smarks 	perm_set_t *permnode;
479ecd6cf80Smarks 	avl_index_t idx;
480ecd6cf80Smarks 	uint64_t jumpobj;
481ecd6cf80Smarks 	int error;
482ecd6cf80Smarks 	char whokey[ZFS_MAX_DELEG_NAME];
483ecd6cf80Smarks 
484ecd6cf80Smarks 	zfs_deleg_whokey(whokey, type, checkflag, valp);
485ecd6cf80Smarks 
486ecd6cf80Smarks 	error = zap_lookup(mos, zapobj, whokey, 8, 1, &jumpobj);
487ecd6cf80Smarks 	if (error != 0)
488ecd6cf80Smarks 		return (error);
489ecd6cf80Smarks 
490ecd6cf80Smarks 	for (zap_cursor_init(&zc, mos, jumpobj);
491ecd6cf80Smarks 	    zap_cursor_retrieve(&zc, &za) == 0;
492ecd6cf80Smarks 	    zap_cursor_advance(&zc)) {
493ecd6cf80Smarks 		permnode = kmem_alloc(sizeof (perm_set_t), KM_SLEEP);
494ecd6cf80Smarks 		(void) strlcpy(permnode->p_setname, za.za_name,
495ecd6cf80Smarks 		    sizeof (permnode->p_setname));
496ecd6cf80Smarks 		permnode->p_matched = B_FALSE;
497ecd6cf80Smarks 
498ecd6cf80Smarks 		if (avl_find(avl, permnode, &idx) == NULL) {
499ecd6cf80Smarks 			avl_insert(avl, permnode, idx);
500ecd6cf80Smarks 		} else {
501ecd6cf80Smarks 			kmem_free(permnode, sizeof (perm_set_t));
502ecd6cf80Smarks 		}
503ecd6cf80Smarks 	}
504ecd6cf80Smarks 	zap_cursor_fini(&zc);
505ecd6cf80Smarks 	return (0);
506ecd6cf80Smarks }
507ecd6cf80Smarks 
508ecd6cf80Smarks /*
509ecd6cf80Smarks  * Load all permissions user based on cred belongs to.
510ecd6cf80Smarks  */
511ecd6cf80Smarks static void
512ecd6cf80Smarks dsl_load_user_sets(objset_t *mos, uint64_t zapobj, avl_tree_t *avl,
513ecd6cf80Smarks     char checkflag, cred_t *cr)
514ecd6cf80Smarks {
515ecd6cf80Smarks 	const	gid_t *gids;
516ecd6cf80Smarks 	int	ngids, i;
517ecd6cf80Smarks 	uint64_t id;
518ecd6cf80Smarks 
519ecd6cf80Smarks 	id = crgetuid(cr);
520ecd6cf80Smarks 	(void) dsl_load_sets(mos, zapobj,
521ecd6cf80Smarks 	    ZFS_DELEG_USER_SETS, checkflag, &id, avl);
522ecd6cf80Smarks 
523ecd6cf80Smarks 	id = crgetgid(cr);
524ecd6cf80Smarks 	(void) dsl_load_sets(mos, zapobj,
525ecd6cf80Smarks 	    ZFS_DELEG_GROUP_SETS, checkflag, &id, avl);
526ecd6cf80Smarks 
527ecd6cf80Smarks 	(void) dsl_load_sets(mos, zapobj,
528ecd6cf80Smarks 	    ZFS_DELEG_EVERYONE_SETS, checkflag, NULL, avl);
529ecd6cf80Smarks 
530ecd6cf80Smarks 	ngids = crgetngroups(cr);
531ecd6cf80Smarks 	gids = crgetgroups(cr);
532ecd6cf80Smarks 	for (i = 0; i != ngids; i++) {
533ecd6cf80Smarks 		id = gids[i];
534ecd6cf80Smarks 		(void) dsl_load_sets(mos, zapobj,
535ecd6cf80Smarks 		    ZFS_DELEG_GROUP_SETS, checkflag, &id, avl);
536ecd6cf80Smarks 	}
537ecd6cf80Smarks }
538ecd6cf80Smarks 
539ecd6cf80Smarks /*
5404445fffbSMatthew Ahrens  * Check if user has requested permission.
541ecd6cf80Smarks  */
542ecd6cf80Smarks int
5434445fffbSMatthew Ahrens dsl_deleg_access_impl(dsl_dataset_t *ds, const char *perm, cred_t *cr)
544ecd6cf80Smarks {
545bb0ade09Sahrens 	dsl_dir_t *dd;
546ecd6cf80Smarks 	dsl_pool_t *dp;
547ecd6cf80Smarks 	void *cookie;
548ecd6cf80Smarks 	int	error;
5498b33e213SWilliam Gorrell 	char	checkflag;
550ecd6cf80Smarks 	objset_t *mos;
551ecd6cf80Smarks 	avl_tree_t permsets;
552ecd6cf80Smarks 	perm_set_t *setnode;
553ecd6cf80Smarks 
554bb0ade09Sahrens 	dp = ds->ds_dir->dd_pool;
555ecd6cf80Smarks 	mos = dp->dp_meta_objset;
556ecd6cf80Smarks 
557a7f53a56SChris Kirby 	if (dsl_delegation_on(mos) == B_FALSE)
558be6fd75aSMatthew Ahrens 		return (SET_ERROR(ECANCELED));
559ecd6cf80Smarks 
560ecd6cf80Smarks 	if (spa_version(dmu_objset_spa(dp->dp_meta_objset)) <
561a7f53a56SChris Kirby 	    SPA_VERSION_DELEGATED_PERMS)
562be6fd75aSMatthew Ahrens 		return (SET_ERROR(EPERM));
563ecd6cf80Smarks 
564bc9014e6SJustin Gibbs 	if (ds->ds_is_snapshot) {
5658b33e213SWilliam Gorrell 		/*
5668b33e213SWilliam Gorrell 		 * Snapshots are treated as descendents only,
5678b33e213SWilliam Gorrell 		 * local permissions do not apply.
5688b33e213SWilliam Gorrell 		 */
5698b33e213SWilliam Gorrell 		checkflag = ZFS_DELEG_DESCENDENT;
5708b33e213SWilliam Gorrell 	} else {
5718b33e213SWilliam Gorrell 		checkflag = ZFS_DELEG_LOCAL;
5728b33e213SWilliam Gorrell 	}
5738b33e213SWilliam Gorrell 
574ecd6cf80Smarks 	avl_create(&permsets, perm_set_compare, sizeof (perm_set_t),
575ecd6cf80Smarks 	    offsetof(perm_set_t, p_node));
576ecd6cf80Smarks 
5773b2aab18SMatthew Ahrens 	ASSERT(dsl_pool_config_held(dp));
578bb0ade09Sahrens 	for (dd = ds->ds_dir; dd != NULL; dd = dd->dd_parent,
579ecd6cf80Smarks 	    checkflag = ZFS_DELEG_DESCENDENT) {
580ecd6cf80Smarks 		uint64_t zapobj;
581ecd6cf80Smarks 		boolean_t expanded;
582ecd6cf80Smarks 
583ecd6cf80Smarks 		/*
584ecd6cf80Smarks 		 * If not in global zone then make sure
585ecd6cf80Smarks 		 * the zoned property is set
586ecd6cf80Smarks 		 */
587ecd6cf80Smarks 		if (!INGLOBALZONE(curproc)) {
588ecd6cf80Smarks 			uint64_t zoned;
589ecd6cf80Smarks 
590bb0ade09Sahrens 			if (dsl_prop_get_dd(dd,
591ecd6cf80Smarks 			    zfs_prop_to_name(ZFS_PROP_ZONED),
59292241e0bSTom Erickson 			    8, 1, &zoned, NULL, B_FALSE) != 0)
593ecd6cf80Smarks 				break;
594ecd6cf80Smarks 			if (!zoned)
595ecd6cf80Smarks 				break;
596ecd6cf80Smarks 		}
597c1379625SJustin T. Gibbs 		zapobj = dsl_dir_phys(dd)->dd_deleg_zapobj;
598ecd6cf80Smarks 
599ecd6cf80Smarks 		if (zapobj == 0)
600ecd6cf80Smarks 			continue;
601ecd6cf80Smarks 
602ecd6cf80Smarks 		dsl_load_user_sets(mos, zapobj, &permsets, checkflag, cr);
603ecd6cf80Smarks again:
604ecd6cf80Smarks 		expanded = B_FALSE;
605ecd6cf80Smarks 		for (setnode = avl_first(&permsets); setnode;
606ecd6cf80Smarks 		    setnode = AVL_NEXT(&permsets, setnode)) {
607ecd6cf80Smarks 			if (setnode->p_matched == B_TRUE)
608ecd6cf80Smarks 				continue;
609ecd6cf80Smarks 
610ecd6cf80Smarks 			/* See if this set directly grants this permission */
611ecd6cf80Smarks 			error = dsl_check_access(mos, zapobj,
612ecd6cf80Smarks 			    ZFS_DELEG_NAMED_SET, 0, setnode->p_setname, perm);
613ecd6cf80Smarks 			if (error == 0)
614ecd6cf80Smarks 				goto success;
615ecd6cf80Smarks 			if (error == EPERM)
616ecd6cf80Smarks 				setnode->p_matched = B_TRUE;
617ecd6cf80Smarks 
618ecd6cf80Smarks 			/* See if this set includes other sets */
619ecd6cf80Smarks 			error = dsl_load_sets(mos, zapobj,
620ecd6cf80Smarks 			    ZFS_DELEG_NAMED_SET_SETS, 0,
621ecd6cf80Smarks 			    setnode->p_setname, &permsets);
622ecd6cf80Smarks 			if (error == 0)
623ecd6cf80Smarks 				setnode->p_matched = expanded = B_TRUE;
624ecd6cf80Smarks 		}
625ecd6cf80Smarks 		/*
626ecd6cf80Smarks 		 * If we expanded any sets, that will define more sets,
627ecd6cf80Smarks 		 * which we need to check.
628ecd6cf80Smarks 		 */
629ecd6cf80Smarks 		if (expanded)
630ecd6cf80Smarks 			goto again;
631ecd6cf80Smarks 
632ecd6cf80Smarks 		error = dsl_check_user_access(mos, zapobj, perm, checkflag, cr);
633ecd6cf80Smarks 		if (error == 0)
634ecd6cf80Smarks 			goto success;
635ecd6cf80Smarks 	}
636be6fd75aSMatthew Ahrens 	error = SET_ERROR(EPERM);
637ecd6cf80Smarks success:
638ecd6cf80Smarks 
639ecd6cf80Smarks 	cookie = NULL;
64091ebeef5Sahrens 	while ((setnode = avl_destroy_nodes(&permsets, &cookie)) != NULL)
641ecd6cf80Smarks 		kmem_free(setnode, sizeof (perm_set_t));
642ecd6cf80Smarks 
643ecd6cf80Smarks 	return (error);
644ecd6cf80Smarks }
645ecd6cf80Smarks 
646a7f53a56SChris Kirby int
647a7f53a56SChris Kirby dsl_deleg_access(const char *dsname, const char *perm, cred_t *cr)
648a7f53a56SChris Kirby {
6493b2aab18SMatthew Ahrens 	dsl_pool_t *dp;
650a7f53a56SChris Kirby 	dsl_dataset_t *ds;
651a7f53a56SChris Kirby 	int error;
652a7f53a56SChris Kirby 
6533b2aab18SMatthew Ahrens 	error = dsl_pool_hold(dsname, FTAG, &dp);
6543b2aab18SMatthew Ahrens 	if (error != 0)
655a7f53a56SChris Kirby 		return (error);
6563b2aab18SMatthew Ahrens 	error = dsl_dataset_hold(dp, dsname, FTAG, &ds);
6573b2aab18SMatthew Ahrens 	if (error == 0) {
6584445fffbSMatthew Ahrens 		error = dsl_deleg_access_impl(ds, perm, cr);
659a7f53a56SChris Kirby 		dsl_dataset_rele(ds, FTAG);
6603b2aab18SMatthew Ahrens 	}
6613b2aab18SMatthew Ahrens 	dsl_pool_rele(dp, FTAG);
662a7f53a56SChris Kirby 
663a7f53a56SChris Kirby 	return (error);
664a7f53a56SChris Kirby }
665a7f53a56SChris Kirby 
666ecd6cf80Smarks /*
667ecd6cf80Smarks  * Other routines.
668ecd6cf80Smarks  */
669ecd6cf80Smarks 
670ecd6cf80Smarks static void
67191ebeef5Sahrens copy_create_perms(dsl_dir_t *dd, uint64_t pzapobj,
672ecd6cf80Smarks     boolean_t dosets, uint64_t uid, dmu_tx_t *tx)
673ecd6cf80Smarks {
67491ebeef5Sahrens 	objset_t *mos = dd->dd_pool->dp_meta_objset;
675ecd6cf80Smarks 	uint64_t jumpobj, pjumpobj;
676c1379625SJustin T. Gibbs 	uint64_t zapobj = dsl_dir_phys(dd)->dd_deleg_zapobj;
677ecd6cf80Smarks 	zap_cursor_t zc;
678ecd6cf80Smarks 	zap_attribute_t za;
679ecd6cf80Smarks 	char whokey[ZFS_MAX_DELEG_NAME];
680ecd6cf80Smarks 
681ecd6cf80Smarks 	zfs_deleg_whokey(whokey,
682ecd6cf80Smarks 	    dosets ? ZFS_DELEG_CREATE_SETS : ZFS_DELEG_CREATE,
683ecd6cf80Smarks 	    ZFS_DELEG_LOCAL, NULL);
68491ebeef5Sahrens 	if (zap_lookup(mos, pzapobj, whokey, 8, 1, &pjumpobj) != 0)
685ecd6cf80Smarks 		return;
686ecd6cf80Smarks 
687ecd6cf80Smarks 	if (zapobj == 0) {
688ecd6cf80Smarks 		dmu_buf_will_dirty(dd->dd_dbuf, tx);
689c1379625SJustin T. Gibbs 		zapobj = dsl_dir_phys(dd)->dd_deleg_zapobj = zap_create(mos,
690ecd6cf80Smarks 		    DMU_OT_DSL_PERMS, DMU_OT_NONE, 0, tx);
691ecd6cf80Smarks 	}
692ecd6cf80Smarks 
69391ebeef5Sahrens 	zfs_deleg_whokey(whokey,
69491ebeef5Sahrens 	    dosets ? ZFS_DELEG_USER_SETS : ZFS_DELEG_USER,
69591ebeef5Sahrens 	    ZFS_DELEG_LOCAL, &uid);
696ecd6cf80Smarks 	if (zap_lookup(mos, zapobj, whokey, 8, 1, &jumpobj) == ENOENT) {
697ecd6cf80Smarks 		jumpobj = zap_create(mos, DMU_OT_DSL_PERMS, DMU_OT_NONE, 0, tx);
698ecd6cf80Smarks 		VERIFY(zap_add(mos, zapobj, whokey, 8, 1, &jumpobj, tx) == 0);
699ecd6cf80Smarks 	}
700ecd6cf80Smarks 
701ecd6cf80Smarks 	for (zap_cursor_init(&zc, mos, pjumpobj);
702ecd6cf80Smarks 	    zap_cursor_retrieve(&zc, &za) == 0;
703ecd6cf80Smarks 	    zap_cursor_advance(&zc)) {
70491ebeef5Sahrens 		uint64_t zero = 0;
705ecd6cf80Smarks 		ASSERT(za.za_integer_length == 8 && za.za_num_integers == 1);
706ecd6cf80Smarks 
707ecd6cf80Smarks 		VERIFY(zap_update(mos, jumpobj, za.za_name,
708ecd6cf80Smarks 		    8, 1, &zero, tx) == 0);
709ecd6cf80Smarks 	}
710ecd6cf80Smarks 	zap_cursor_fini(&zc);
711ecd6cf80Smarks }
712ecd6cf80Smarks 
713ecd6cf80Smarks /*
714ecd6cf80Smarks  * set all create time permission on new dataset.
715ecd6cf80Smarks  */
716ecd6cf80Smarks void
717ecd6cf80Smarks dsl_deleg_set_create_perms(dsl_dir_t *sdd, dmu_tx_t *tx, cred_t *cr)
718ecd6cf80Smarks {
719ecd6cf80Smarks 	dsl_dir_t *dd;
72091ebeef5Sahrens 	uint64_t uid = crgetuid(cr);
721ecd6cf80Smarks 
722ecd6cf80Smarks 	if (spa_version(dmu_objset_spa(sdd->dd_pool->dp_meta_objset)) <
723990b4856Slling 	    SPA_VERSION_DELEGATED_PERMS)
724ecd6cf80Smarks 		return;
725ecd6cf80Smarks 
726ecd6cf80Smarks 	for (dd = sdd->dd_parent; dd != NULL; dd = dd->dd_parent) {
727c1379625SJustin T. Gibbs 		uint64_t pzapobj = dsl_dir_phys(dd)->dd_deleg_zapobj;
728ecd6cf80Smarks 
72991ebeef5Sahrens 		if (pzapobj == 0)
730ecd6cf80Smarks 			continue;
731ecd6cf80Smarks 
73291ebeef5Sahrens 		copy_create_perms(sdd, pzapobj, B_FALSE, uid, tx);
73391ebeef5Sahrens 		copy_create_perms(sdd, pzapobj, B_TRUE, uid, tx);
734ecd6cf80Smarks 	}
735ecd6cf80Smarks }
736ecd6cf80Smarks 
737ecd6cf80Smarks int
738ecd6cf80Smarks dsl_deleg_destroy(objset_t *mos, uint64_t zapobj, dmu_tx_t *tx)
739ecd6cf80Smarks {
740ecd6cf80Smarks 	zap_cursor_t zc;
741ecd6cf80Smarks 	zap_attribute_t za;
742ecd6cf80Smarks 
743ecd6cf80Smarks 	if (zapobj == 0)
744ecd6cf80Smarks 		return (0);
745ecd6cf80Smarks 
746ecd6cf80Smarks 	for (zap_cursor_init(&zc, mos, zapobj);
747ecd6cf80Smarks 	    zap_cursor_retrieve(&zc, &za) == 0;
748ecd6cf80Smarks 	    zap_cursor_advance(&zc)) {
749ecd6cf80Smarks 		ASSERT(za.za_integer_length == 8 && za.za_num_integers == 1);
750ecd6cf80Smarks 		VERIFY(0 == zap_destroy(mos, za.za_first_integer, tx));
751ecd6cf80Smarks 	}
752ecd6cf80Smarks 	zap_cursor_fini(&zc);
753ecd6cf80Smarks 	VERIFY(0 == zap_destroy(mos, zapobj, tx));
754ecd6cf80Smarks 	return (0);
755ecd6cf80Smarks }
756ecd6cf80Smarks 
757ecd6cf80Smarks boolean_t
758ecd6cf80Smarks dsl_delegation_on(objset_t *os)
759ecd6cf80Smarks {
760b24ab676SJeff Bonwick 	return (!!spa_delegation(os->os_spa));
761ecd6cf80Smarks }
762