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 http://www.opensolaris.org/os/licensing.
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) 2013 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
)));
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(MAXNAMELEN
+ strlen(MOS_DIR_NAME
) + 1, 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
, MAXNAMELEN
+ strlen(MOS_DIR_NAME
) + 1);
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
= arg1
;
397 const perm_set_t
*node2
= arg2
;
400 val
= strcmp(node1
->p_setname
, node2
->p_setname
);
403 return (val
> 0 ? 1 : -1);
407 * Determine whether a specified permission exists.
409 * First the base attribute has to be retrieved. i.e. ul$12
410 * Once the base object has been retrieved the actual permission
411 * is lookup up in the zap object the base object points to.
413 * Return 0 if permission exists, ENOENT if there is no whokey, EPERM if
414 * there is no perm in that jumpobj.
417 dsl_check_access(objset_t
*mos
, uint64_t zapobj
,
418 char type
, char checkflag
, void *valp
, const char *perm
)
421 uint64_t jumpobj
, zero
;
422 char whokey
[ZFS_MAX_DELEG_NAME
];
424 zfs_deleg_whokey(whokey
, type
, checkflag
, valp
);
425 error
= zap_lookup(mos
, zapobj
, whokey
, 8, 1, &jumpobj
);
427 error
= zap_lookup(mos
, jumpobj
, perm
, 8, 1, &zero
);
429 error
= SET_ERROR(EPERM
);
435 * check a specified user/group for a requested permission
438 dsl_check_user_access(objset_t
*mos
, uint64_t zapobj
, const char *perm
,
439 int checkflag
, cred_t
*cr
)
448 if (dsl_check_access(mos
, zapobj
,
449 ZFS_DELEG_USER
, checkflag
, &id
, perm
) == 0)
452 /* check for users primary group */
454 if (dsl_check_access(mos
, zapobj
,
455 ZFS_DELEG_GROUP
, checkflag
, &id
, perm
) == 0)
458 /* check for everyone entry */
460 if (dsl_check_access(mos
, zapobj
,
461 ZFS_DELEG_EVERYONE
, checkflag
, &id
, perm
) == 0)
464 /* check each supplemental group user is a member of */
465 ngids
= crgetngroups(cr
);
466 gids
= crgetgroups(cr
);
467 for (i
= 0; i
!= ngids
; i
++) {
469 if (dsl_check_access(mos
, zapobj
,
470 ZFS_DELEG_GROUP
, checkflag
, &id
, perm
) == 0)
474 return (SET_ERROR(EPERM
));
478 * Iterate over the sets specified in the specified zapobj
479 * and load them into the permsets avl tree.
482 dsl_load_sets(objset_t
*mos
, uint64_t zapobj
,
483 char type
, char checkflag
, void *valp
, avl_tree_t
*avl
)
487 perm_set_t
*permnode
;
491 char whokey
[ZFS_MAX_DELEG_NAME
];
493 zfs_deleg_whokey(whokey
, type
, checkflag
, valp
);
495 error
= zap_lookup(mos
, zapobj
, whokey
, 8, 1, &jumpobj
);
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
);
518 * Load all permissions user based on cred belongs to.
521 dsl_load_user_sets(objset_t
*mos
, uint64_t zapobj
, avl_tree_t
*avl
,
522 char checkflag
, cred_t
*cr
)
529 (void) dsl_load_sets(mos
, zapobj
,
530 ZFS_DELEG_USER_SETS
, checkflag
, &id
, avl
);
533 (void) dsl_load_sets(mos
, zapobj
,
534 ZFS_DELEG_GROUP_SETS
, checkflag
, &id
, avl
);
536 (void) dsl_load_sets(mos
, zapobj
,
537 ZFS_DELEG_EVERYONE_SETS
, checkflag
, NULL
, avl
);
539 ngids
= crgetngroups(cr
);
540 gids
= crgetgroups(cr
);
541 for (i
= 0; i
!= ngids
; i
++) {
543 (void) dsl_load_sets(mos
, zapobj
,
544 ZFS_DELEG_GROUP_SETS
, checkflag
, &id
, avl
);
549 * Check if user has requested permission.
552 dsl_deleg_access_impl(dsl_dataset_t
*ds
, const char *perm
, cred_t
*cr
)
563 dp
= ds
->ds_dir
->dd_pool
;
564 mos
= dp
->dp_meta_objset
;
566 if (dsl_delegation_on(mos
) == B_FALSE
)
567 return (SET_ERROR(ECANCELED
));
569 if (spa_version(dmu_objset_spa(dp
->dp_meta_objset
)) <
570 SPA_VERSION_DELEGATED_PERMS
)
571 return (SET_ERROR(EPERM
));
573 if (dsl_dataset_is_snapshot(ds
)) {
575 * Snapshots are treated as descendents only,
576 * local permissions do not apply.
578 checkflag
= ZFS_DELEG_DESCENDENT
;
580 checkflag
= ZFS_DELEG_LOCAL
;
583 avl_create(&permsets
, perm_set_compare
, sizeof (perm_set_t
),
584 offsetof(perm_set_t
, p_node
));
586 ASSERT(dsl_pool_config_held(dp
));
587 for (dd
= ds
->ds_dir
; dd
!= NULL
; dd
= dd
->dd_parent
,
588 checkflag
= ZFS_DELEG_DESCENDENT
) {
593 * If not in global zone then make sure
594 * the zoned property is set
596 if (!INGLOBALZONE(curproc
)) {
599 if (dsl_prop_get_dd(dd
,
600 zfs_prop_to_name(ZFS_PROP_ZONED
),
601 8, 1, &zoned
, NULL
, B_FALSE
) != 0)
606 zapobj
= dsl_dir_phys(dd
)->dd_deleg_zapobj
;
611 dsl_load_user_sets(mos
, zapobj
, &permsets
, checkflag
, cr
);
614 for (setnode
= avl_first(&permsets
); setnode
;
615 setnode
= AVL_NEXT(&permsets
, setnode
)) {
616 if (setnode
->p_matched
== B_TRUE
)
619 /* See if this set directly grants this permission */
620 error
= dsl_check_access(mos
, zapobj
,
621 ZFS_DELEG_NAMED_SET
, 0, setnode
->p_setname
, perm
);
625 setnode
->p_matched
= B_TRUE
;
627 /* See if this set includes other sets */
628 error
= dsl_load_sets(mos
, zapobj
,
629 ZFS_DELEG_NAMED_SET_SETS
, 0,
630 setnode
->p_setname
, &permsets
);
632 setnode
->p_matched
= expanded
= B_TRUE
;
635 * If we expanded any sets, that will define more sets,
636 * which we need to check.
641 error
= dsl_check_user_access(mos
, zapobj
, perm
, checkflag
, cr
);
645 error
= SET_ERROR(EPERM
);
649 while ((setnode
= avl_destroy_nodes(&permsets
, &cookie
)) != NULL
)
650 kmem_free(setnode
, sizeof (perm_set_t
));
656 dsl_deleg_access(const char *dsname
, const char *perm
, cred_t
*cr
)
662 error
= dsl_pool_hold(dsname
, FTAG
, &dp
);
665 error
= dsl_dataset_hold(dp
, dsname
, FTAG
, &ds
);
667 error
= dsl_deleg_access_impl(ds
, perm
, cr
);
668 dsl_dataset_rele(ds
, FTAG
);
670 dsl_pool_rele(dp
, FTAG
);
680 copy_create_perms(dsl_dir_t
*dd
, uint64_t pzapobj
,
681 boolean_t dosets
, uint64_t uid
, dmu_tx_t
*tx
)
683 objset_t
*mos
= dd
->dd_pool
->dp_meta_objset
;
684 uint64_t jumpobj
, pjumpobj
;
685 uint64_t zapobj
= dsl_dir_phys(dd
)->dd_deleg_zapobj
;
688 char whokey
[ZFS_MAX_DELEG_NAME
];
690 zfs_deleg_whokey(whokey
,
691 dosets
? ZFS_DELEG_CREATE_SETS
: ZFS_DELEG_CREATE
,
692 ZFS_DELEG_LOCAL
, NULL
);
693 if (zap_lookup(mos
, pzapobj
, whokey
, 8, 1, &pjumpobj
) != 0)
697 dmu_buf_will_dirty(dd
->dd_dbuf
, tx
);
698 zapobj
= dsl_dir_phys(dd
)->dd_deleg_zapobj
= zap_create(mos
,
699 DMU_OT_DSL_PERMS
, DMU_OT_NONE
, 0, tx
);
702 zfs_deleg_whokey(whokey
,
703 dosets
? ZFS_DELEG_USER_SETS
: ZFS_DELEG_USER
,
704 ZFS_DELEG_LOCAL
, &uid
);
705 if (zap_lookup(mos
, zapobj
, whokey
, 8, 1, &jumpobj
) == ENOENT
) {
706 jumpobj
= zap_create(mos
, DMU_OT_DSL_PERMS
, DMU_OT_NONE
, 0, tx
);
707 VERIFY(zap_add(mos
, zapobj
, whokey
, 8, 1, &jumpobj
, tx
) == 0);
710 for (zap_cursor_init(&zc
, mos
, pjumpobj
);
711 zap_cursor_retrieve(&zc
, &za
) == 0;
712 zap_cursor_advance(&zc
)) {
714 ASSERT(za
.za_integer_length
== 8 && za
.za_num_integers
== 1);
716 VERIFY(zap_update(mos
, jumpobj
, za
.za_name
,
717 8, 1, &zero
, tx
) == 0);
719 zap_cursor_fini(&zc
);
723 * set all create time permission on new dataset.
726 dsl_deleg_set_create_perms(dsl_dir_t
*sdd
, dmu_tx_t
*tx
, cred_t
*cr
)
729 uint64_t uid
= crgetuid(cr
);
731 if (spa_version(dmu_objset_spa(sdd
->dd_pool
->dp_meta_objset
)) <
732 SPA_VERSION_DELEGATED_PERMS
)
735 for (dd
= sdd
->dd_parent
; dd
!= NULL
; dd
= dd
->dd_parent
) {
736 uint64_t pzapobj
= dsl_dir_phys(dd
)->dd_deleg_zapobj
;
741 copy_create_perms(sdd
, pzapobj
, B_FALSE
, uid
, tx
);
742 copy_create_perms(sdd
, pzapobj
, B_TRUE
, uid
, tx
);
747 dsl_deleg_destroy(objset_t
*mos
, uint64_t zapobj
, dmu_tx_t
*tx
)
755 for (zap_cursor_init(&zc
, mos
, zapobj
);
756 zap_cursor_retrieve(&zc
, &za
) == 0;
757 zap_cursor_advance(&zc
)) {
758 ASSERT(za
.za_integer_length
== 8 && za
.za_num_integers
== 1);
759 VERIFY(0 == zap_destroy(mos
, za
.za_first_integer
, tx
));
761 zap_cursor_fini(&zc
);
762 VERIFY(0 == zap_destroy(mos
, zapobj
, tx
));
767 dsl_delegation_on(objset_t
*os
)
769 return (!!spa_delegation(os
->os_spa
));
772 #if defined(_KERNEL) && defined(HAVE_SPL)
773 EXPORT_SYMBOL(dsl_deleg_get
);
774 EXPORT_SYMBOL(dsl_deleg_set
);