4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or https://opensource.org/licenses/CDDL-1.0.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
23 * Copyright (c) 2011, 2015 by Delphix. All rights reserved.
27 * DSL permissions are stored in a two level zap attribute
28 * mechanism. The first level identifies the "class" of
29 * entry. The class is identified by the first 2 letters of
30 * the attribute. The second letter "l" or "d" identifies whether
31 * it is a local or descendent permission. The first letter
32 * identifies the type of entry.
34 * ul$<id> identifies permissions granted locally for this userid.
35 * ud$<id> identifies permissions granted on descendent datasets for
37 * Ul$<id> identifies permission sets granted locally for this userid.
38 * Ud$<id> identifies permission sets granted on descendent datasets for
40 * gl$<id> identifies permissions granted locally for this groupid.
41 * gd$<id> identifies permissions granted on descendent datasets for
43 * Gl$<id> identifies permission sets granted locally for this groupid.
44 * Gd$<id> identifies permission sets granted on descendent datasets for
46 * el$ identifies permissions granted locally for everyone.
47 * ed$ identifies permissions granted on descendent datasets
49 * El$ identifies permission sets granted locally for everyone.
50 * Ed$ identifies permission sets granted to descendent datasets for
52 * c-$ identifies permission to create at dataset creation time.
53 * C-$ identifies permission sets to grant locally at dataset creation
55 * s-$@<name> permissions defined in specified set @<name>
56 * S-$@<name> Sets defined in named set @<name>
58 * Each of the above entities points to another zap attribute that contains one
59 * attribute for each allowed permission, such as create, destroy,...
60 * All of the "upper" case class types will specify permission set names
61 * rather than permissions.
63 * Basically it looks something like this:
64 * ul$12 -> ZAP OBJ -> permissions...
66 * The ZAP OBJ is referred to as the jump object.
70 #include <sys/dmu_objset.h>
71 #include <sys/dmu_tx.h>
72 #include <sys/dsl_dataset.h>
73 #include <sys/dsl_dir.h>
74 #include <sys/dsl_prop.h>
75 #include <sys/dsl_synctask.h>
76 #include <sys/dsl_deleg.h>
79 #include <sys/fs/zfs.h>
81 #include <sys/sunddi.h>
83 #include "zfs_deleg.h"
86 * Validate that user is allowed to delegate specified permissions.
88 * In order to delegate "create" you must have "create"
92 dsl_deleg_can_allow(char *ddname
, nvlist_t
*nvp
, cred_t
*cr
)
94 nvpair_t
*whopair
= NULL
;
97 if ((error
= dsl_deleg_access(ddname
, ZFS_DELEG_PERM_ALLOW
, cr
)) != 0)
100 while ((whopair
= nvlist_next_nvpair(nvp
, whopair
))) {
102 nvpair_t
*permpair
= NULL
;
104 VERIFY(nvpair_value_nvlist(whopair
, &perms
) == 0);
106 while ((permpair
= nvlist_next_nvpair(perms
, permpair
))) {
107 const char *perm
= nvpair_name(permpair
);
109 if (strcmp(perm
, ZFS_DELEG_PERM_ALLOW
) == 0)
110 return (SET_ERROR(EPERM
));
112 if ((error
= dsl_deleg_access(ddname
, perm
, cr
)) != 0)
120 * Validate that user is allowed to unallow specified permissions. They
121 * must have the 'allow' permission, and even then can only unallow
122 * perms for their uid.
125 dsl_deleg_can_unallow(char *ddname
, nvlist_t
*nvp
, cred_t
*cr
)
127 nvpair_t
*whopair
= NULL
;
131 if ((error
= dsl_deleg_access(ddname
, ZFS_DELEG_PERM_ALLOW
, cr
)) != 0)
134 (void) snprintf(idstr
, sizeof (idstr
), "%lld",
135 (longlong_t
)crgetuid(cr
));
137 while ((whopair
= nvlist_next_nvpair(nvp
, whopair
))) {
138 zfs_deleg_who_type_t type
= nvpair_name(whopair
)[0];
140 if (type
!= ZFS_DELEG_USER
&&
141 type
!= ZFS_DELEG_USER_SETS
)
142 return (SET_ERROR(EPERM
));
144 if (strcmp(idstr
, &nvpair_name(whopair
)[3]) != 0)
145 return (SET_ERROR(EPERM
));
150 typedef struct dsl_deleg_arg
{
151 const char *dda_name
;
152 nvlist_t
*dda_nvlist
;
156 dsl_deleg_set_sync(void *arg
, dmu_tx_t
*tx
)
158 dsl_deleg_arg_t
*dda
= arg
;
160 dsl_pool_t
*dp
= dmu_tx_pool(tx
);
161 objset_t
*mos
= dp
->dp_meta_objset
;
162 nvpair_t
*whopair
= NULL
;
165 VERIFY0(dsl_dir_hold(dp
, dda
->dda_name
, FTAG
, &dd
, NULL
));
167 zapobj
= dsl_dir_phys(dd
)->dd_deleg_zapobj
;
169 dmu_buf_will_dirty(dd
->dd_dbuf
, tx
);
170 zapobj
= dsl_dir_phys(dd
)->dd_deleg_zapobj
= zap_create(mos
,
171 DMU_OT_DSL_PERMS
, DMU_OT_NONE
, 0, tx
);
174 while ((whopair
= nvlist_next_nvpair(dda
->dda_nvlist
, whopair
))) {
175 const char *whokey
= nvpair_name(whopair
);
177 nvpair_t
*permpair
= NULL
;
180 perms
= fnvpair_value_nvlist(whopair
);
182 if (zap_lookup(mos
, zapobj
, whokey
, 8, 1, &jumpobj
) != 0) {
183 jumpobj
= zap_create_link(mos
, DMU_OT_DSL_PERMS
,
187 while ((permpair
= nvlist_next_nvpair(perms
, permpair
))) {
188 const char *perm
= nvpair_name(permpair
);
191 VERIFY(zap_update(mos
, jumpobj
,
192 perm
, 8, 1, &n
, tx
) == 0);
193 spa_history_log_internal_dd(dd
, "permission update", tx
,
194 "%s %s", whokey
, perm
);
197 dsl_dir_rele(dd
, FTAG
);
201 dsl_deleg_unset_sync(void *arg
, dmu_tx_t
*tx
)
203 dsl_deleg_arg_t
*dda
= arg
;
205 dsl_pool_t
*dp
= dmu_tx_pool(tx
);
206 objset_t
*mos
= dp
->dp_meta_objset
;
207 nvpair_t
*whopair
= NULL
;
210 VERIFY0(dsl_dir_hold(dp
, dda
->dda_name
, FTAG
, &dd
, NULL
));
211 zapobj
= dsl_dir_phys(dd
)->dd_deleg_zapobj
;
213 dsl_dir_rele(dd
, FTAG
);
217 while ((whopair
= nvlist_next_nvpair(dda
->dda_nvlist
, whopair
))) {
218 const char *whokey
= nvpair_name(whopair
);
220 nvpair_t
*permpair
= NULL
;
223 if (nvpair_value_nvlist(whopair
, &perms
) != 0) {
224 if (zap_lookup(mos
, zapobj
, whokey
, 8,
226 (void) zap_remove(mos
, zapobj
, whokey
, tx
);
227 VERIFY(0 == zap_destroy(mos
, jumpobj
, tx
));
229 spa_history_log_internal_dd(dd
, "permission who remove",
234 if (zap_lookup(mos
, zapobj
, whokey
, 8, 1, &jumpobj
) != 0)
237 while ((permpair
= nvlist_next_nvpair(perms
, permpair
))) {
238 const char *perm
= nvpair_name(permpair
);
241 (void) zap_remove(mos
, jumpobj
, perm
, tx
);
242 if (zap_count(mos
, jumpobj
, &n
) == 0 && n
== 0) {
243 (void) zap_remove(mos
, zapobj
,
245 VERIFY(0 == zap_destroy(mos
,
248 spa_history_log_internal_dd(dd
, "permission remove", tx
,
249 "%s %s", whokey
, perm
);
252 dsl_dir_rele(dd
, FTAG
);
256 dsl_deleg_check(void *arg
, dmu_tx_t
*tx
)
258 dsl_deleg_arg_t
*dda
= arg
;
262 if (spa_version(dmu_tx_pool(tx
)->dp_spa
) <
263 SPA_VERSION_DELEGATED_PERMS
) {
264 return (SET_ERROR(ENOTSUP
));
267 error
= dsl_dir_hold(dmu_tx_pool(tx
), dda
->dda_name
, FTAG
, &dd
, NULL
);
269 dsl_dir_rele(dd
, FTAG
);
274 dsl_deleg_set(const char *ddname
, nvlist_t
*nvp
, boolean_t unset
)
278 /* nvp must already have been verified to be valid */
280 dda
.dda_name
= ddname
;
281 dda
.dda_nvlist
= nvp
;
283 return (dsl_sync_task(ddname
, dsl_deleg_check
,
284 unset
? dsl_deleg_unset_sync
: dsl_deleg_set_sync
,
285 &dda
, fnvlist_num_pairs(nvp
), ZFS_SPACE_CHECK_RESERVED
));
289 * Find all 'allow' permissions from a given point and then continue
290 * traversing up to the root.
292 * This function constructs an nvlist of nvlists.
293 * each setpoint is an nvlist composed of an nvlist of an nvlist
294 * of the individual * users/groups/everyone/create
297 * The nvlist will look like this.
299 * { source fsname -> { whokeys { permissions,...}, ...}}
301 * The fsname nvpairs will be arranged in a bottom up order. For example,
302 * if we have the following structure a/b/c then the nvpairs for the fsnames
303 * will be ordered a/b/c, a/b, a.
306 dsl_deleg_get(const char *ddname
, nvlist_t
**nvp
)
308 dsl_dir_t
*dd
, *startdd
;
312 zap_cursor_t
*basezc
, *zc
;
313 zap_attribute_t
*baseza
, *za
;
316 error
= dsl_pool_hold(ddname
, FTAG
, &dp
);
320 error
= dsl_dir_hold(dp
, ddname
, FTAG
, &startdd
, NULL
);
322 dsl_pool_rele(dp
, FTAG
);
326 dp
= startdd
->dd_pool
;
327 mos
= dp
->dp_meta_objset
;
329 zc
= kmem_alloc(sizeof (zap_cursor_t
), KM_SLEEP
);
330 za
= kmem_alloc(sizeof (zap_attribute_t
), KM_SLEEP
);
331 basezc
= kmem_alloc(sizeof (zap_cursor_t
), KM_SLEEP
);
332 baseza
= kmem_alloc(sizeof (zap_attribute_t
), KM_SLEEP
);
333 source
= kmem_alloc(ZFS_MAX_DATASET_NAME_LEN
, KM_SLEEP
);
334 VERIFY(nvlist_alloc(nvp
, NV_UNIQUE_NAME
, KM_SLEEP
) == 0);
336 for (dd
= startdd
; dd
!= NULL
; dd
= dd
->dd_parent
) {
340 if (dsl_dir_phys(dd
)->dd_deleg_zapobj
== 0 ||
342 dsl_dir_phys(dd
)->dd_deleg_zapobj
, &n
) != 0 || n
== 0)
345 sp_nvp
= fnvlist_alloc();
346 for (zap_cursor_init(basezc
, mos
,
347 dsl_dir_phys(dd
)->dd_deleg_zapobj
);
348 zap_cursor_retrieve(basezc
, baseza
) == 0;
349 zap_cursor_advance(basezc
)) {
352 ASSERT(baseza
->za_integer_length
== 8);
353 ASSERT(baseza
->za_num_integers
== 1);
355 perms_nvp
= fnvlist_alloc();
356 for (zap_cursor_init(zc
, mos
, baseza
->za_first_integer
);
357 zap_cursor_retrieve(zc
, za
) == 0;
358 zap_cursor_advance(zc
)) {
359 fnvlist_add_boolean(perms_nvp
, za
->za_name
);
362 fnvlist_add_nvlist(sp_nvp
, baseza
->za_name
, perms_nvp
);
363 fnvlist_free(perms_nvp
);
366 zap_cursor_fini(basezc
);
368 dsl_dir_name(dd
, source
);
369 fnvlist_add_nvlist(*nvp
, source
, sp_nvp
);
373 kmem_free(source
, ZFS_MAX_DATASET_NAME_LEN
);
374 kmem_free(baseza
, sizeof (zap_attribute_t
));
375 kmem_free(basezc
, sizeof (zap_cursor_t
));
376 kmem_free(za
, sizeof (zap_attribute_t
));
377 kmem_free(zc
, sizeof (zap_cursor_t
));
379 dsl_dir_rele(startdd
, FTAG
);
380 dsl_pool_rele(dp
, FTAG
);
385 * Routines for dsl_deleg_access() -- access checking.
387 typedef struct perm_set
{
390 char p_setname
[ZFS_MAX_DELEG_NAME
];
394 perm_set_compare(const void *arg1
, const void *arg2
)
396 const perm_set_t
*node1
= (const perm_set_t
*)arg1
;
397 const perm_set_t
*node2
= (const perm_set_t
*)arg2
;
400 val
= strcmp(node1
->p_setname
, node2
->p_setname
);
402 return (TREE_ISIGN(val
));
406 * Determine whether a specified permission exists.
408 * First the base attribute has to be retrieved. i.e. ul$12
409 * Once the base object has been retrieved the actual permission
410 * is lookup up in the zap object the base object points to.
412 * Return 0 if permission exists, ENOENT if there is no whokey, EPERM if
413 * there is no perm in that jumpobj.
416 dsl_check_access(objset_t
*mos
, uint64_t zapobj
,
417 char type
, char checkflag
, void *valp
, const char *perm
)
420 uint64_t jumpobj
, zero
;
421 char whokey
[ZFS_MAX_DELEG_NAME
];
423 zfs_deleg_whokey(whokey
, type
, checkflag
, valp
);
424 error
= zap_lookup(mos
, zapobj
, whokey
, 8, 1, &jumpobj
);
426 error
= zap_lookup(mos
, jumpobj
, perm
, 8, 1, &zero
);
428 error
= SET_ERROR(EPERM
);
434 * check a specified user/group for a requested permission
437 dsl_check_user_access(objset_t
*mos
, uint64_t zapobj
, const char *perm
,
438 int checkflag
, cred_t
*cr
)
447 if (dsl_check_access(mos
, zapobj
,
448 ZFS_DELEG_USER
, checkflag
, &id
, perm
) == 0)
451 /* check for users primary group */
453 if (dsl_check_access(mos
, zapobj
,
454 ZFS_DELEG_GROUP
, checkflag
, &id
, perm
) == 0)
457 /* check for everyone entry */
459 if (dsl_check_access(mos
, zapobj
,
460 ZFS_DELEG_EVERYONE
, checkflag
, &id
, perm
) == 0)
463 /* check each supplemental group user is a member of */
464 ngids
= crgetngroups(cr
);
465 gids
= crgetgroups(cr
);
466 for (i
= 0; i
!= ngids
; i
++) {
468 if (dsl_check_access(mos
, zapobj
,
469 ZFS_DELEG_GROUP
, checkflag
, &id
, perm
) == 0)
473 return (SET_ERROR(EPERM
));
477 * Iterate over the sets specified in the specified zapobj
478 * and load them into the permsets avl tree.
481 dsl_load_sets(objset_t
*mos
, uint64_t zapobj
,
482 char type
, char checkflag
, void *valp
, avl_tree_t
*avl
)
486 perm_set_t
*permnode
;
490 char whokey
[ZFS_MAX_DELEG_NAME
];
492 zfs_deleg_whokey(whokey
, type
, checkflag
, valp
);
494 error
= zap_lookup(mos
, zapobj
, whokey
, 8, 1, &jumpobj
);
498 for (zap_cursor_init(&zc
, mos
, jumpobj
);
499 zap_cursor_retrieve(&zc
, &za
) == 0;
500 zap_cursor_advance(&zc
)) {
501 permnode
= kmem_alloc(sizeof (perm_set_t
), KM_SLEEP
);
502 (void) strlcpy(permnode
->p_setname
, za
.za_name
,
503 sizeof (permnode
->p_setname
));
504 permnode
->p_matched
= B_FALSE
;
506 if (avl_find(avl
, permnode
, &idx
) == NULL
) {
507 avl_insert(avl
, permnode
, idx
);
509 kmem_free(permnode
, sizeof (perm_set_t
));
512 zap_cursor_fini(&zc
);
517 * Load all permissions user based on cred belongs to.
520 dsl_load_user_sets(objset_t
*mos
, uint64_t zapobj
, avl_tree_t
*avl
,
521 char checkflag
, cred_t
*cr
)
528 (void) dsl_load_sets(mos
, zapobj
,
529 ZFS_DELEG_USER_SETS
, checkflag
, &id
, avl
);
532 (void) dsl_load_sets(mos
, zapobj
,
533 ZFS_DELEG_GROUP_SETS
, checkflag
, &id
, avl
);
535 (void) dsl_load_sets(mos
, zapobj
,
536 ZFS_DELEG_EVERYONE_SETS
, checkflag
, NULL
, avl
);
538 ngids
= crgetngroups(cr
);
539 gids
= crgetgroups(cr
);
540 for (i
= 0; i
!= ngids
; i
++) {
542 (void) dsl_load_sets(mos
, zapobj
,
543 ZFS_DELEG_GROUP_SETS
, checkflag
, &id
, avl
);
548 * Check if user has requested permission.
551 dsl_deleg_access_impl(dsl_dataset_t
*ds
, const char *perm
, cred_t
*cr
)
562 dp
= ds
->ds_dir
->dd_pool
;
563 mos
= dp
->dp_meta_objset
;
565 if (dsl_delegation_on(mos
) == B_FALSE
)
566 return (SET_ERROR(ECANCELED
));
568 if (spa_version(dmu_objset_spa(dp
->dp_meta_objset
)) <
569 SPA_VERSION_DELEGATED_PERMS
)
570 return (SET_ERROR(EPERM
));
572 if (ds
->ds_is_snapshot
) {
574 * Snapshots are treated as descendents only,
575 * local permissions do not apply.
577 checkflag
= ZFS_DELEG_DESCENDENT
;
579 checkflag
= ZFS_DELEG_LOCAL
;
582 avl_create(&permsets
, perm_set_compare
, sizeof (perm_set_t
),
583 offsetof(perm_set_t
, p_node
));
585 ASSERT(dsl_pool_config_held(dp
));
586 for (dd
= ds
->ds_dir
; dd
!= NULL
; dd
= dd
->dd_parent
,
587 checkflag
= ZFS_DELEG_DESCENDENT
) {
592 * If not in global zone then make sure
593 * the zoned property is set
595 if (!INGLOBALZONE(curproc
)) {
598 if (dsl_prop_get_dd(dd
,
599 zfs_prop_to_name(ZFS_PROP_ZONED
),
600 8, 1, &zoned
, NULL
, B_FALSE
) != 0)
605 zapobj
= dsl_dir_phys(dd
)->dd_deleg_zapobj
;
610 dsl_load_user_sets(mos
, zapobj
, &permsets
, checkflag
, cr
);
613 for (setnode
= avl_first(&permsets
); setnode
;
614 setnode
= AVL_NEXT(&permsets
, setnode
)) {
615 if (setnode
->p_matched
== B_TRUE
)
618 /* See if this set directly grants this permission */
619 error
= dsl_check_access(mos
, zapobj
,
620 ZFS_DELEG_NAMED_SET
, 0, setnode
->p_setname
, perm
);
624 setnode
->p_matched
= B_TRUE
;
626 /* See if this set includes other sets */
627 error
= dsl_load_sets(mos
, zapobj
,
628 ZFS_DELEG_NAMED_SET_SETS
, 0,
629 setnode
->p_setname
, &permsets
);
631 setnode
->p_matched
= expanded
= B_TRUE
;
634 * If we expanded any sets, that will define more sets,
635 * which we need to check.
640 error
= dsl_check_user_access(mos
, zapobj
, perm
, checkflag
, cr
);
644 error
= SET_ERROR(EPERM
);
648 while ((setnode
= avl_destroy_nodes(&permsets
, &cookie
)) != NULL
)
649 kmem_free(setnode
, sizeof (perm_set_t
));
655 dsl_deleg_access(const char *dsname
, const char *perm
, cred_t
*cr
)
661 error
= dsl_pool_hold(dsname
, FTAG
, &dp
);
664 error
= dsl_dataset_hold(dp
, dsname
, FTAG
, &ds
);
666 error
= dsl_deleg_access_impl(ds
, perm
, cr
);
667 dsl_dataset_rele(ds
, FTAG
);
669 dsl_pool_rele(dp
, FTAG
);
679 copy_create_perms(dsl_dir_t
*dd
, uint64_t pzapobj
,
680 boolean_t dosets
, uint64_t uid
, dmu_tx_t
*tx
)
682 objset_t
*mos
= dd
->dd_pool
->dp_meta_objset
;
683 uint64_t jumpobj
, pjumpobj
;
684 uint64_t zapobj
= dsl_dir_phys(dd
)->dd_deleg_zapobj
;
687 char whokey
[ZFS_MAX_DELEG_NAME
];
689 zfs_deleg_whokey(whokey
,
690 dosets
? ZFS_DELEG_CREATE_SETS
: ZFS_DELEG_CREATE
,
691 ZFS_DELEG_LOCAL
, NULL
);
692 if (zap_lookup(mos
, pzapobj
, whokey
, 8, 1, &pjumpobj
) != 0)
696 dmu_buf_will_dirty(dd
->dd_dbuf
, tx
);
697 zapobj
= dsl_dir_phys(dd
)->dd_deleg_zapobj
= zap_create(mos
,
698 DMU_OT_DSL_PERMS
, DMU_OT_NONE
, 0, tx
);
701 zfs_deleg_whokey(whokey
,
702 dosets
? ZFS_DELEG_USER_SETS
: ZFS_DELEG_USER
,
703 ZFS_DELEG_LOCAL
, &uid
);
704 if (zap_lookup(mos
, zapobj
, whokey
, 8, 1, &jumpobj
) == ENOENT
) {
705 jumpobj
= zap_create(mos
, DMU_OT_DSL_PERMS
, DMU_OT_NONE
, 0, tx
);
706 VERIFY(zap_add(mos
, zapobj
, whokey
, 8, 1, &jumpobj
, tx
) == 0);
709 for (zap_cursor_init(&zc
, mos
, pjumpobj
);
710 zap_cursor_retrieve(&zc
, &za
) == 0;
711 zap_cursor_advance(&zc
)) {
713 ASSERT(za
.za_integer_length
== 8 && za
.za_num_integers
== 1);
715 VERIFY(zap_update(mos
, jumpobj
, za
.za_name
,
716 8, 1, &zero
, tx
) == 0);
718 zap_cursor_fini(&zc
);
722 * set all create time permission on new dataset.
725 dsl_deleg_set_create_perms(dsl_dir_t
*sdd
, dmu_tx_t
*tx
, cred_t
*cr
)
728 uint64_t uid
= crgetuid(cr
);
730 if (spa_version(dmu_objset_spa(sdd
->dd_pool
->dp_meta_objset
)) <
731 SPA_VERSION_DELEGATED_PERMS
)
734 for (dd
= sdd
->dd_parent
; dd
!= NULL
; dd
= dd
->dd_parent
) {
735 uint64_t pzapobj
= dsl_dir_phys(dd
)->dd_deleg_zapobj
;
740 copy_create_perms(sdd
, pzapobj
, B_FALSE
, uid
, tx
);
741 copy_create_perms(sdd
, pzapobj
, B_TRUE
, uid
, tx
);
746 dsl_deleg_destroy(objset_t
*mos
, uint64_t zapobj
, dmu_tx_t
*tx
)
754 for (zap_cursor_init(&zc
, mos
, zapobj
);
755 zap_cursor_retrieve(&zc
, &za
) == 0;
756 zap_cursor_advance(&zc
)) {
757 ASSERT(za
.za_integer_length
== 8 && za
.za_num_integers
== 1);
758 VERIFY(0 == zap_destroy(mos
, za
.za_first_integer
, tx
));
760 zap_cursor_fini(&zc
);
761 VERIFY(0 == zap_destroy(mos
, zapobj
, tx
));
766 dsl_delegation_on(objset_t
*os
)
768 return (!!spa_delegation(os
->os_spa
));
772 EXPORT_SYMBOL(dsl_deleg_get
);
773 EXPORT_SYMBOL(dsl_deleg_set
);