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
= zap_attribute_alloc();
331 basezc
= kmem_alloc(sizeof (zap_cursor_t
), KM_SLEEP
);
332 baseza
= zap_attribute_alloc();
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 zap_attribute_free(baseza
);
375 kmem_free(basezc
, sizeof (zap_cursor_t
));
376 zap_attribute_free(za
);
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 za
= zap_attribute_alloc();
499 for (zap_cursor_init(&zc
, mos
, jumpobj
);
500 zap_cursor_retrieve(&zc
, za
) == 0;
501 zap_cursor_advance(&zc
)) {
502 permnode
= kmem_alloc(sizeof (perm_set_t
), KM_SLEEP
);
503 (void) strlcpy(permnode
->p_setname
, za
->za_name
,
504 sizeof (permnode
->p_setname
));
505 permnode
->p_matched
= B_FALSE
;
507 if (avl_find(avl
, permnode
, &idx
) == NULL
) {
508 avl_insert(avl
, permnode
, idx
);
510 kmem_free(permnode
, sizeof (perm_set_t
));
513 zap_cursor_fini(&zc
);
514 zap_attribute_free(za
);
519 * Load all permissions user based on cred belongs to.
522 dsl_load_user_sets(objset_t
*mos
, uint64_t zapobj
, avl_tree_t
*avl
,
523 char checkflag
, cred_t
*cr
)
530 (void) dsl_load_sets(mos
, zapobj
,
531 ZFS_DELEG_USER_SETS
, checkflag
, &id
, avl
);
534 (void) dsl_load_sets(mos
, zapobj
,
535 ZFS_DELEG_GROUP_SETS
, checkflag
, &id
, avl
);
537 (void) dsl_load_sets(mos
, zapobj
,
538 ZFS_DELEG_EVERYONE_SETS
, checkflag
, NULL
, avl
);
540 ngids
= crgetngroups(cr
);
541 gids
= crgetgroups(cr
);
542 for (i
= 0; i
!= ngids
; i
++) {
544 (void) dsl_load_sets(mos
, zapobj
,
545 ZFS_DELEG_GROUP_SETS
, checkflag
, &id
, avl
);
550 * Check if user has requested permission.
553 dsl_deleg_access_impl(dsl_dataset_t
*ds
, const char *perm
, cred_t
*cr
)
564 dp
= ds
->ds_dir
->dd_pool
;
565 mos
= dp
->dp_meta_objset
;
567 if (dsl_delegation_on(mos
) == B_FALSE
)
568 return (SET_ERROR(ECANCELED
));
570 if (spa_version(dmu_objset_spa(dp
->dp_meta_objset
)) <
571 SPA_VERSION_DELEGATED_PERMS
)
572 return (SET_ERROR(EPERM
));
574 if (ds
->ds_is_snapshot
) {
576 * Snapshots are treated as descendents only,
577 * local permissions do not apply.
579 checkflag
= ZFS_DELEG_DESCENDENT
;
581 checkflag
= ZFS_DELEG_LOCAL
;
584 avl_create(&permsets
, perm_set_compare
, sizeof (perm_set_t
),
585 offsetof(perm_set_t
, p_node
));
587 ASSERT(dsl_pool_config_held(dp
));
588 for (dd
= ds
->ds_dir
; dd
!= NULL
; dd
= dd
->dd_parent
,
589 checkflag
= ZFS_DELEG_DESCENDENT
) {
594 * If not in global zone then make sure
595 * the zoned property is set
597 if (!INGLOBALZONE(curproc
)) {
600 if (dsl_prop_get_dd(dd
,
601 zfs_prop_to_name(ZFS_PROP_ZONED
),
602 8, 1, &zoned
, NULL
, B_FALSE
) != 0)
607 zapobj
= dsl_dir_phys(dd
)->dd_deleg_zapobj
;
612 dsl_load_user_sets(mos
, zapobj
, &permsets
, checkflag
, cr
);
615 for (setnode
= avl_first(&permsets
); setnode
;
616 setnode
= AVL_NEXT(&permsets
, setnode
)) {
617 if (setnode
->p_matched
== B_TRUE
)
620 /* See if this set directly grants this permission */
621 error
= dsl_check_access(mos
, zapobj
,
622 ZFS_DELEG_NAMED_SET
, 0, setnode
->p_setname
, perm
);
626 setnode
->p_matched
= B_TRUE
;
628 /* See if this set includes other sets */
629 error
= dsl_load_sets(mos
, zapobj
,
630 ZFS_DELEG_NAMED_SET_SETS
, 0,
631 setnode
->p_setname
, &permsets
);
633 setnode
->p_matched
= expanded
= B_TRUE
;
636 * If we expanded any sets, that will define more sets,
637 * which we need to check.
642 error
= dsl_check_user_access(mos
, zapobj
, perm
, checkflag
, cr
);
646 error
= SET_ERROR(EPERM
);
650 while ((setnode
= avl_destroy_nodes(&permsets
, &cookie
)) != NULL
)
651 kmem_free(setnode
, sizeof (perm_set_t
));
657 dsl_deleg_access(const char *dsname
, const char *perm
, cred_t
*cr
)
663 error
= dsl_pool_hold(dsname
, FTAG
, &dp
);
666 error
= dsl_dataset_hold(dp
, dsname
, FTAG
, &ds
);
668 error
= dsl_deleg_access_impl(ds
, perm
, cr
);
669 dsl_dataset_rele(ds
, FTAG
);
671 dsl_pool_rele(dp
, FTAG
);
681 copy_create_perms(dsl_dir_t
*dd
, uint64_t pzapobj
,
682 boolean_t dosets
, uint64_t uid
, dmu_tx_t
*tx
)
684 objset_t
*mos
= dd
->dd_pool
->dp_meta_objset
;
685 uint64_t jumpobj
, pjumpobj
;
686 uint64_t zapobj
= dsl_dir_phys(dd
)->dd_deleg_zapobj
;
689 char whokey
[ZFS_MAX_DELEG_NAME
];
691 zfs_deleg_whokey(whokey
,
692 dosets
? ZFS_DELEG_CREATE_SETS
: ZFS_DELEG_CREATE
,
693 ZFS_DELEG_LOCAL
, NULL
);
694 if (zap_lookup(mos
, pzapobj
, whokey
, 8, 1, &pjumpobj
) != 0)
698 dmu_buf_will_dirty(dd
->dd_dbuf
, tx
);
699 zapobj
= dsl_dir_phys(dd
)->dd_deleg_zapobj
= zap_create(mos
,
700 DMU_OT_DSL_PERMS
, DMU_OT_NONE
, 0, tx
);
703 zfs_deleg_whokey(whokey
,
704 dosets
? ZFS_DELEG_USER_SETS
: ZFS_DELEG_USER
,
705 ZFS_DELEG_LOCAL
, &uid
);
706 if (zap_lookup(mos
, zapobj
, whokey
, 8, 1, &jumpobj
) == ENOENT
) {
707 jumpobj
= zap_create(mos
, DMU_OT_DSL_PERMS
, DMU_OT_NONE
, 0, tx
);
708 VERIFY(zap_add(mos
, zapobj
, whokey
, 8, 1, &jumpobj
, tx
) == 0);
711 za
= zap_attribute_alloc();
712 for (zap_cursor_init(&zc
, mos
, pjumpobj
);
713 zap_cursor_retrieve(&zc
, za
) == 0;
714 zap_cursor_advance(&zc
)) {
716 ASSERT(za
->za_integer_length
== 8 && za
->za_num_integers
== 1);
718 VERIFY(zap_update(mos
, jumpobj
, za
->za_name
,
719 8, 1, &zero
, tx
) == 0);
721 zap_cursor_fini(&zc
);
722 zap_attribute_free(za
);
726 * set all create time permission on new dataset.
729 dsl_deleg_set_create_perms(dsl_dir_t
*sdd
, dmu_tx_t
*tx
, cred_t
*cr
)
732 uint64_t uid
= crgetuid(cr
);
734 if (spa_version(dmu_objset_spa(sdd
->dd_pool
->dp_meta_objset
)) <
735 SPA_VERSION_DELEGATED_PERMS
)
738 for (dd
= sdd
->dd_parent
; dd
!= NULL
; dd
= dd
->dd_parent
) {
739 uint64_t pzapobj
= dsl_dir_phys(dd
)->dd_deleg_zapobj
;
744 copy_create_perms(sdd
, pzapobj
, B_FALSE
, uid
, tx
);
745 copy_create_perms(sdd
, pzapobj
, B_TRUE
, uid
, tx
);
750 dsl_deleg_destroy(objset_t
*mos
, uint64_t zapobj
, dmu_tx_t
*tx
)
758 za
= zap_attribute_alloc();
759 for (zap_cursor_init(&zc
, mos
, zapobj
);
760 zap_cursor_retrieve(&zc
, za
) == 0;
761 zap_cursor_advance(&zc
)) {
762 ASSERT(za
->za_integer_length
== 8 && za
->za_num_integers
== 1);
763 VERIFY(0 == zap_destroy(mos
, za
->za_first_integer
, tx
));
765 zap_cursor_fini(&zc
);
766 VERIFY(0 == zap_destroy(mos
, zapobj
, tx
));
767 zap_attribute_free(za
);
772 dsl_delegation_on(objset_t
*os
)
774 return (!!spa_delegation(os
->os_spa
));
778 EXPORT_SYMBOL(dsl_deleg_get
);
779 EXPORT_SYMBOL(dsl_deleg_set
);