1 /*
   2  * CDDL HEADER START
   3  *
   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.
   7  *
   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.
  12  *
  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]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
  23  * Copyright (c) 2011 by Delphix. All rights reserved.
  24  */
  25 
  26 /*
  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.
  33  *
  34  * ul$<id>    identifies permissions granted locally for this userid.
  35  * ud$<id>    identifies permissions granted on descendent datasets for
  36  *            this userid.
  37  * Ul$<id>    identifies permission sets granted locally for this userid.
  38  * Ud$<id>    identifies permission sets granted on descendent datasets for
  39  *            this userid.
  40  * gl$<id>    identifies permissions granted locally for this groupid.
  41  * gd$<id>    identifies permissions granted on descendent datasets for
  42  *            this groupid.
  43  * Gl$<id>    identifies permission sets granted locally for this groupid.
  44  * Gd$<id>    identifies permission sets granted on descendent datasets for
  45  *            this groupid.
  46  * el$        identifies permissions granted locally for everyone.
  47  * ed$        identifies permissions granted on descendent datasets
  48  *            for everyone.
  49  * El$        identifies permission sets granted locally for everyone.
  50  * Ed$        identifies permission sets granted to descendent datasets for
  51  *            everyone.
  52  * c-$        identifies permission to create at dataset creation time.
  53  * C-$        identifies permission sets to grant locally at dataset creation
  54  *            time.
  55  * s-$@<name> permissions defined in specified set @<name>
  56  * S-$@<name> Sets defined in named set @<name>
  57  *
  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.
  62  *
  63  * Basically it looks something like this:
  64  * ul$12 -> ZAP OBJ -> permissions...
  65  *
  66  * The ZAP OBJ is referred to as the jump object.
  67  */
  68 
  69 #include <sys/dmu.h>
  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>
  77 #include <sys/spa.h>
  78 #include <sys/zap.h>
  79 #include <sys/fs/zfs.h>
  80 #include <sys/cred.h>
  81 #include <sys/sunddi.h>
  82 
  83 #include "zfs_deleg.h"
  84 
  85 /*
  86  * Validate that user is allowed to delegate specified permissions.
  87  *
  88  * In order to delegate "create" you must have "create"
  89  * and "allow".
  90  */
  91 int
  92 dsl_deleg_can_allow(char *ddname, nvlist_t *nvp, cred_t *cr)
  93 {
  94         nvpair_t *whopair = NULL;
  95         int error;
  96 
  97         if ((error = dsl_deleg_access(ddname, ZFS_DELEG_PERM_ALLOW, cr)) != 0)
  98                 return (error);
  99 
 100         while (whopair = nvlist_next_nvpair(nvp, whopair)) {
 101                 nvlist_t *perms;
 102                 nvpair_t *permpair = NULL;
 103 
 104                 VERIFY(nvpair_value_nvlist(whopair, &perms) == 0);
 105 
 106                 while (permpair = nvlist_next_nvpair(perms, permpair)) {
 107                         const char *perm = nvpair_name(permpair);
 108 
 109                         if (strcmp(perm, ZFS_DELEG_PERM_ALLOW) == 0)
 110                                 return (EPERM);
 111 
 112                         if ((error = dsl_deleg_access(ddname, perm, cr)) != 0)
 113                                 return (error);
 114                 }
 115         }
 116         return (0);
 117 }
 118 
 119 /*
 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.
 123  */
 124 int
 125 dsl_deleg_can_unallow(char *ddname, nvlist_t *nvp, cred_t *cr)
 126 {
 127         nvpair_t *whopair = NULL;
 128         int error;
 129         char idstr[32];
 130 
 131         if ((error = dsl_deleg_access(ddname, ZFS_DELEG_PERM_ALLOW, cr)) != 0)
 132                 return (error);
 133 
 134         (void) snprintf(idstr, sizeof (idstr), "%lld",
 135             (longlong_t)crgetuid(cr));
 136 
 137         while (whopair = nvlist_next_nvpair(nvp, whopair)) {
 138                 zfs_deleg_who_type_t type = nvpair_name(whopair)[0];
 139 
 140                 if (type != ZFS_DELEG_USER &&
 141                     type != ZFS_DELEG_USER_SETS)
 142                         return (EPERM);
 143 
 144                 if (strcmp(idstr, &nvpair_name(whopair)[3]) != 0)
 145                         return (EPERM);
 146         }
 147         return (0);
 148 }
 149 
 150 static void
 151 dsl_deleg_set_sync(void *arg1, void *arg2, dmu_tx_t *tx)
 152 {
 153         dsl_dir_t *dd = arg1;
 154         nvlist_t *nvp = arg2;
 155         objset_t *mos = dd->dd_pool->dp_meta_objset;
 156         nvpair_t *whopair = NULL;
 157         uint64_t zapobj = dd->dd_phys->dd_deleg_zapobj;
 158 
 159         if (zapobj == 0) {
 160                 dmu_buf_will_dirty(dd->dd_dbuf, tx);
 161                 zapobj = dd->dd_phys->dd_deleg_zapobj = zap_create(mos,
 162                     DMU_OT_DSL_PERMS, DMU_OT_NONE, 0, tx);
 163         }
 164 
 165         while (whopair = nvlist_next_nvpair(nvp, whopair)) {
 166                 const char *whokey = nvpair_name(whopair);
 167                 nvlist_t *perms;
 168                 nvpair_t *permpair = NULL;
 169                 uint64_t jumpobj;
 170 
 171                 VERIFY(nvpair_value_nvlist(whopair, &perms) == 0);
 172 
 173                 if (zap_lookup(mos, zapobj, whokey, 8, 1, &jumpobj) != 0) {
 174                         jumpobj = zap_create(mos, DMU_OT_DSL_PERMS,
 175                             DMU_OT_NONE, 0, tx);
 176                         VERIFY(zap_update(mos, zapobj,
 177                             whokey, 8, 1, &jumpobj, tx) == 0);
 178                 }
 179 
 180                 while (permpair = nvlist_next_nvpair(perms, permpair)) {
 181                         const char *perm = nvpair_name(permpair);
 182                         uint64_t n = 0;
 183 
 184                         VERIFY(zap_update(mos, jumpobj,
 185                             perm, 8, 1, &n, tx) == 0);
 186                         spa_history_log_internal(LOG_DS_PERM_UPDATE,
 187                             dd->dd_pool->dp_spa, tx,
 188                             "%s %s dataset = %llu", whokey, perm,
 189                             dd->dd_phys->dd_head_dataset_obj);
 190                 }
 191         }
 192 }
 193 
 194 static void
 195 dsl_deleg_unset_sync(void *arg1, void *arg2, dmu_tx_t *tx)
 196 {
 197         dsl_dir_t *dd = arg1;
 198         nvlist_t *nvp = arg2;
 199         objset_t *mos = dd->dd_pool->dp_meta_objset;
 200         nvpair_t *whopair = NULL;
 201         uint64_t zapobj = dd->dd_phys->dd_deleg_zapobj;
 202 
 203         if (zapobj == 0)
 204                 return;
 205 
 206         while (whopair = nvlist_next_nvpair(nvp, whopair)) {
 207                 const char *whokey = nvpair_name(whopair);
 208                 nvlist_t *perms;
 209                 nvpair_t *permpair = NULL;
 210                 uint64_t jumpobj;
 211 
 212                 if (nvpair_value_nvlist(whopair, &perms) != 0) {
 213                         if (zap_lookup(mos, zapobj, whokey, 8,
 214                             1, &jumpobj) == 0) {
 215                                 (void) zap_remove(mos, zapobj, whokey, tx);
 216                                 VERIFY(0 == zap_destroy(mos, jumpobj, tx));
 217                         }
 218                         spa_history_log_internal(LOG_DS_PERM_WHO_REMOVE,
 219                             dd->dd_pool->dp_spa, tx,
 220                             "%s dataset = %llu", whokey,
 221                             dd->dd_phys->dd_head_dataset_obj);
 222                         continue;
 223                 }
 224 
 225                 if (zap_lookup(mos, zapobj, whokey, 8, 1, &jumpobj) != 0)
 226                         continue;
 227 
 228                 while (permpair = nvlist_next_nvpair(perms, permpair)) {
 229                         const char *perm = nvpair_name(permpair);
 230                         uint64_t n = 0;
 231 
 232                         (void) zap_remove(mos, jumpobj, perm, tx);
 233                         if (zap_count(mos, jumpobj, &n) == 0 && n == 0) {
 234                                 (void) zap_remove(mos, zapobj,
 235                                     whokey, tx);
 236                                 VERIFY(0 == zap_destroy(mos,
 237                                     jumpobj, tx));
 238                         }
 239                         spa_history_log_internal(LOG_DS_PERM_REMOVE,
 240                             dd->dd_pool->dp_spa, tx,
 241                             "%s %s dataset = %llu", whokey, perm,
 242                             dd->dd_phys->dd_head_dataset_obj);
 243                 }
 244         }
 245 }
 246 
 247 int
 248 dsl_deleg_set(const char *ddname, nvlist_t *nvp, boolean_t unset)
 249 {
 250         dsl_dir_t *dd;
 251         int error;
 252         nvpair_t *whopair = NULL;
 253         int blocks_modified = 0;
 254 
 255         error = dsl_dir_open(ddname, FTAG, &dd, NULL);
 256         if (error)
 257                 return (error);
 258 
 259         if (spa_version(dmu_objset_spa(dd->dd_pool->dp_meta_objset)) <
 260             SPA_VERSION_DELEGATED_PERMS) {
 261                 dsl_dir_close(dd, FTAG);
 262                 return (ENOTSUP);
 263         }
 264 
 265         while (whopair = nvlist_next_nvpair(nvp, whopair))
 266                 blocks_modified++;
 267 
 268         error = dsl_sync_task_do(dd->dd_pool, NULL,
 269             unset ? dsl_deleg_unset_sync : dsl_deleg_set_sync,
 270             dd, nvp, blocks_modified);
 271         dsl_dir_close(dd, FTAG);
 272 
 273         return (error);
 274 }
 275 
 276 /*
 277  * Find all 'allow' permissions from a given point and then continue
 278  * traversing up to the root.
 279  *
 280  * This function constructs an nvlist of nvlists.
 281  * each setpoint is an nvlist composed of an nvlist of an nvlist
 282  * of the individual * users/groups/everyone/create
 283  * permissions.
 284  *
 285  * The nvlist will look like this.
 286  *
 287  * { source fsname -> { whokeys { permissions,...}, ...}}
 288  *
 289  * The fsname nvpairs will be arranged in a bottom up order.  For example,
 290  * if we have the following structure a/b/c then the nvpairs for the fsnames
 291  * will be ordered a/b/c, a/b, a.
 292  */
 293 int
 294 dsl_deleg_get(const char *ddname, nvlist_t **nvp)
 295 {
 296         dsl_dir_t *dd, *startdd;
 297         dsl_pool_t *dp;
 298         int error;
 299         objset_t *mos;
 300 
 301         error = dsl_dir_open(ddname, FTAG, &startdd, NULL);
 302         if (error)
 303                 return (error);
 304 
 305         dp = startdd->dd_pool;
 306         mos = dp->dp_meta_objset;
 307 
 308         VERIFY(nvlist_alloc(nvp, NV_UNIQUE_NAME, KM_SLEEP) == 0);
 309 
 310         rw_enter(&dp->dp_config_rwlock, RW_READER);
 311         for (dd = startdd; dd != NULL; dd = dd->dd_parent) {
 312                 zap_cursor_t basezc;
 313                 zap_attribute_t baseza;
 314                 nvlist_t *sp_nvp;
 315                 uint64_t n;
 316                 char source[MAXNAMELEN];
 317 
 318                 if (dd->dd_phys->dd_deleg_zapobj &&
 319                     (zap_count(mos, dd->dd_phys->dd_deleg_zapobj,
 320                     &n) == 0) && n) {
 321                         VERIFY(nvlist_alloc(&sp_nvp,
 322                             NV_UNIQUE_NAME, KM_SLEEP) == 0);
 323                 } else {
 324                         continue;
 325                 }
 326 
 327                 for (zap_cursor_init(&basezc, mos,
 328                     dd->dd_phys->dd_deleg_zapobj);
 329                     zap_cursor_retrieve(&basezc, &baseza) == 0;
 330                     zap_cursor_advance(&basezc)) {
 331                         zap_cursor_t zc;
 332                         zap_attribute_t za;
 333                         nvlist_t *perms_nvp;
 334 
 335                         ASSERT(baseza.za_integer_length == 8);
 336                         ASSERT(baseza.za_num_integers == 1);
 337 
 338                         VERIFY(nvlist_alloc(&perms_nvp,
 339                             NV_UNIQUE_NAME, KM_SLEEP) == 0);
 340                         for (zap_cursor_init(&zc, mos, baseza.za_first_integer);
 341                             zap_cursor_retrieve(&zc, &za) == 0;
 342                             zap_cursor_advance(&zc)) {
 343                                 VERIFY(nvlist_add_boolean(perms_nvp,
 344                                     za.za_name) == 0);
 345                         }
 346                         zap_cursor_fini(&zc);
 347                         VERIFY(nvlist_add_nvlist(sp_nvp, baseza.za_name,
 348                             perms_nvp) == 0);
 349                         nvlist_free(perms_nvp);
 350                 }
 351 
 352                 zap_cursor_fini(&basezc);
 353 
 354                 dsl_dir_name(dd, source);
 355                 VERIFY(nvlist_add_nvlist(*nvp, source, sp_nvp) == 0);
 356                 nvlist_free(sp_nvp);
 357         }
 358         rw_exit(&dp->dp_config_rwlock);
 359 
 360         dsl_dir_close(startdd, FTAG);
 361         return (0);
 362 }
 363 
 364 /*
 365  * Routines for dsl_deleg_access() -- access checking.
 366  */
 367 typedef struct perm_set {
 368         avl_node_t      p_node;
 369         boolean_t       p_matched;
 370         char            p_setname[ZFS_MAX_DELEG_NAME];
 371 } perm_set_t;
 372 
 373 static int
 374 perm_set_compare(const void *arg1, const void *arg2)
 375 {
 376         const perm_set_t *node1 = arg1;
 377         const perm_set_t *node2 = arg2;
 378         int val;
 379 
 380         val = strcmp(node1->p_setname, node2->p_setname);
 381         if (val == 0)
 382                 return (0);
 383         return (val > 0 ? 1 : -1);
 384 }
 385 
 386 /*
 387  * Determine whether a specified permission exists.
 388  *
 389  * First the base attribute has to be retrieved.  i.e. ul$12
 390  * Once the base object has been retrieved the actual permission
 391  * is lookup up in the zap object the base object points to.
 392  *
 393  * Return 0 if permission exists, ENOENT if there is no whokey, EPERM if
 394  * there is no perm in that jumpobj.
 395  */
 396 static int
 397 dsl_check_access(objset_t *mos, uint64_t zapobj,
 398     char type, char checkflag, void *valp, const char *perm)
 399 {
 400         int error;
 401         uint64_t jumpobj, zero;
 402         char whokey[ZFS_MAX_DELEG_NAME];
 403 
 404         zfs_deleg_whokey(whokey, type, checkflag, valp);
 405         error = zap_lookup(mos, zapobj, whokey, 8, 1, &jumpobj);
 406         if (error == 0) {
 407                 error = zap_lookup(mos, jumpobj, perm, 8, 1, &zero);
 408                 if (error == ENOENT)
 409                         error = EPERM;
 410         }
 411         return (error);
 412 }
 413 
 414 /*
 415  * check a specified user/group for a requested permission
 416  */
 417 static int
 418 dsl_check_user_access(objset_t *mos, uint64_t zapobj, const char *perm,
 419     int checkflag, cred_t *cr)
 420 {
 421         const   gid_t *gids;
 422         int     ngids;
 423         int     i;
 424         uint64_t id;
 425 
 426         /* check for user */
 427         id = crgetuid(cr);
 428         if (dsl_check_access(mos, zapobj,
 429             ZFS_DELEG_USER, checkflag, &id, perm) == 0)
 430                 return (0);
 431 
 432         /* check for users primary group */
 433         id = crgetgid(cr);
 434         if (dsl_check_access(mos, zapobj,
 435             ZFS_DELEG_GROUP, checkflag, &id, perm) == 0)
 436                 return (0);
 437 
 438         /* check for everyone entry */
 439         id = -1;
 440         if (dsl_check_access(mos, zapobj,
 441             ZFS_DELEG_EVERYONE, checkflag, &id, perm) == 0)
 442                 return (0);
 443 
 444         /* check each supplemental group user is a member of */
 445         ngids = crgetngroups(cr);
 446         gids = crgetgroups(cr);
 447         for (i = 0; i != ngids; i++) {
 448                 id = gids[i];
 449                 if (dsl_check_access(mos, zapobj,
 450                     ZFS_DELEG_GROUP, checkflag, &id, perm) == 0)
 451                         return (0);
 452         }
 453 
 454         return (EPERM);
 455 }
 456 
 457 /*
 458  * Iterate over the sets specified in the specified zapobj
 459  * and load them into the permsets avl tree.
 460  */
 461 static int
 462 dsl_load_sets(objset_t *mos, uint64_t zapobj,
 463     char type, char checkflag, void *valp, avl_tree_t *avl)
 464 {
 465         zap_cursor_t zc;
 466         zap_attribute_t za;
 467         perm_set_t *permnode;
 468         avl_index_t idx;
 469         uint64_t jumpobj;
 470         int error;
 471         char whokey[ZFS_MAX_DELEG_NAME];
 472 
 473         zfs_deleg_whokey(whokey, type, checkflag, valp);
 474 
 475         error = zap_lookup(mos, zapobj, whokey, 8, 1, &jumpobj);
 476         if (error != 0)
 477                 return (error);
 478 
 479         for (zap_cursor_init(&zc, mos, jumpobj);
 480             zap_cursor_retrieve(&zc, &za) == 0;
 481             zap_cursor_advance(&zc)) {
 482                 permnode = kmem_alloc(sizeof (perm_set_t), KM_SLEEP);
 483                 (void) strlcpy(permnode->p_setname, za.za_name,
 484                     sizeof (permnode->p_setname));
 485                 permnode->p_matched = B_FALSE;
 486 
 487                 if (avl_find(avl, permnode, &idx) == NULL) {
 488                         avl_insert(avl, permnode, idx);
 489                 } else {
 490                         kmem_free(permnode, sizeof (perm_set_t));
 491                 }
 492         }
 493         zap_cursor_fini(&zc);
 494         return (0);
 495 }
 496 
 497 /*
 498  * Load all permissions user based on cred belongs to.
 499  */
 500 static void
 501 dsl_load_user_sets(objset_t *mos, uint64_t zapobj, avl_tree_t *avl,
 502     char checkflag, cred_t *cr)
 503 {
 504         const   gid_t *gids;
 505         int     ngids, i;
 506         uint64_t id;
 507 
 508         id = crgetuid(cr);
 509         (void) dsl_load_sets(mos, zapobj,
 510             ZFS_DELEG_USER_SETS, checkflag, &id, avl);
 511 
 512         id = crgetgid(cr);
 513         (void) dsl_load_sets(mos, zapobj,
 514             ZFS_DELEG_GROUP_SETS, checkflag, &id, avl);
 515 
 516         (void) dsl_load_sets(mos, zapobj,
 517             ZFS_DELEG_EVERYONE_SETS, checkflag, NULL, avl);
 518 
 519         ngids = crgetngroups(cr);
 520         gids = crgetgroups(cr);
 521         for (i = 0; i != ngids; i++) {
 522                 id = gids[i];
 523                 (void) dsl_load_sets(mos, zapobj,
 524                     ZFS_DELEG_GROUP_SETS, checkflag, &id, avl);
 525         }
 526 }
 527 
 528 /*
 529  * Check if user has requested permission.  If descendent is set, must have
 530  * descendent perms.
 531  */
 532 int
 533 dsl_deleg_access_impl(dsl_dataset_t *ds, boolean_t descendent, const char *perm,
 534     cred_t *cr)
 535 {
 536         dsl_dir_t *dd;
 537         dsl_pool_t *dp;
 538         void *cookie;
 539         int     error;
 540         char    checkflag;
 541         objset_t *mos;
 542         avl_tree_t permsets;
 543         perm_set_t *setnode;
 544 
 545         dp = ds->ds_dir->dd_pool;
 546         mos = dp->dp_meta_objset;
 547 
 548         if (dsl_delegation_on(mos) == B_FALSE)
 549                 return (ECANCELED);
 550 
 551         if (spa_version(dmu_objset_spa(dp->dp_meta_objset)) <
 552             SPA_VERSION_DELEGATED_PERMS)
 553                 return (EPERM);
 554 
 555         if (dsl_dataset_is_snapshot(ds) || descendent) {
 556                 /*
 557                  * Snapshots are treated as descendents only,
 558                  * local permissions do not apply.
 559                  */
 560                 checkflag = ZFS_DELEG_DESCENDENT;
 561         } else {
 562                 checkflag = ZFS_DELEG_LOCAL;
 563         }
 564 
 565         avl_create(&permsets, perm_set_compare, sizeof (perm_set_t),
 566             offsetof(perm_set_t, p_node));
 567 
 568         rw_enter(&dp->dp_config_rwlock, RW_READER);
 569         for (dd = ds->ds_dir; dd != NULL; dd = dd->dd_parent,
 570             checkflag = ZFS_DELEG_DESCENDENT) {
 571                 uint64_t zapobj;
 572                 boolean_t expanded;
 573 
 574                 /*
 575                  * If not in global zone then make sure
 576                  * the zoned property is set
 577                  */
 578                 if (!INGLOBALZONE(curproc)) {
 579                         uint64_t zoned;
 580 
 581                         if (dsl_prop_get_dd(dd,
 582                             zfs_prop_to_name(ZFS_PROP_ZONED),
 583                             8, 1, &zoned, NULL, B_FALSE) != 0)
 584                                 break;
 585                         if (!zoned)
 586                                 break;
 587                 }
 588                 zapobj = dd->dd_phys->dd_deleg_zapobj;
 589 
 590                 if (zapobj == 0)
 591                         continue;
 592 
 593                 dsl_load_user_sets(mos, zapobj, &permsets, checkflag, cr);
 594 again:
 595                 expanded = B_FALSE;
 596                 for (setnode = avl_first(&permsets); setnode;
 597                     setnode = AVL_NEXT(&permsets, setnode)) {
 598                         if (setnode->p_matched == B_TRUE)
 599                                 continue;
 600 
 601                         /* See if this set directly grants this permission */
 602                         error = dsl_check_access(mos, zapobj,
 603                             ZFS_DELEG_NAMED_SET, 0, setnode->p_setname, perm);
 604                         if (error == 0)
 605                                 goto success;
 606                         if (error == EPERM)
 607                                 setnode->p_matched = B_TRUE;
 608 
 609                         /* See if this set includes other sets */
 610                         error = dsl_load_sets(mos, zapobj,
 611                             ZFS_DELEG_NAMED_SET_SETS, 0,
 612                             setnode->p_setname, &permsets);
 613                         if (error == 0)
 614                                 setnode->p_matched = expanded = B_TRUE;
 615                 }
 616                 /*
 617                  * If we expanded any sets, that will define more sets,
 618                  * which we need to check.
 619                  */
 620                 if (expanded)
 621                         goto again;
 622 
 623                 error = dsl_check_user_access(mos, zapobj, perm, checkflag, cr);
 624                 if (error == 0)
 625                         goto success;
 626         }
 627         error = EPERM;
 628 success:
 629         rw_exit(&dp->dp_config_rwlock);
 630 
 631         cookie = NULL;
 632         while ((setnode = avl_destroy_nodes(&permsets, &cookie)) != NULL)
 633                 kmem_free(setnode, sizeof (perm_set_t));
 634 
 635         return (error);
 636 }
 637 
 638 int
 639 dsl_deleg_access(const char *dsname, const char *perm, cred_t *cr)
 640 {
 641         dsl_dataset_t *ds;
 642         int error;
 643 
 644         error = dsl_dataset_hold(dsname, FTAG, &ds);
 645         if (error)
 646                 return (error);
 647 
 648         error = dsl_deleg_access_impl(ds, B_FALSE, perm, cr);
 649         dsl_dataset_rele(ds, FTAG);
 650 
 651         return (error);
 652 }
 653 
 654 /*
 655  * Other routines.
 656  */
 657 
 658 static void
 659 copy_create_perms(dsl_dir_t *dd, uint64_t pzapobj,
 660     boolean_t dosets, uint64_t uid, dmu_tx_t *tx)
 661 {
 662         objset_t *mos = dd->dd_pool->dp_meta_objset;
 663         uint64_t jumpobj, pjumpobj;
 664         uint64_t zapobj = dd->dd_phys->dd_deleg_zapobj;
 665         zap_cursor_t zc;
 666         zap_attribute_t za;
 667         char whokey[ZFS_MAX_DELEG_NAME];
 668 
 669         zfs_deleg_whokey(whokey,
 670             dosets ? ZFS_DELEG_CREATE_SETS : ZFS_DELEG_CREATE,
 671             ZFS_DELEG_LOCAL, NULL);
 672         if (zap_lookup(mos, pzapobj, whokey, 8, 1, &pjumpobj) != 0)
 673                 return;
 674 
 675         if (zapobj == 0) {
 676                 dmu_buf_will_dirty(dd->dd_dbuf, tx);
 677                 zapobj = dd->dd_phys->dd_deleg_zapobj = zap_create(mos,
 678                     DMU_OT_DSL_PERMS, DMU_OT_NONE, 0, tx);
 679         }
 680 
 681         zfs_deleg_whokey(whokey,
 682             dosets ? ZFS_DELEG_USER_SETS : ZFS_DELEG_USER,
 683             ZFS_DELEG_LOCAL, &uid);
 684         if (zap_lookup(mos, zapobj, whokey, 8, 1, &jumpobj) == ENOENT) {
 685                 jumpobj = zap_create(mos, DMU_OT_DSL_PERMS, DMU_OT_NONE, 0, tx);
 686                 VERIFY(zap_add(mos, zapobj, whokey, 8, 1, &jumpobj, tx) == 0);
 687         }
 688 
 689         for (zap_cursor_init(&zc, mos, pjumpobj);
 690             zap_cursor_retrieve(&zc, &za) == 0;
 691             zap_cursor_advance(&zc)) {
 692                 uint64_t zero = 0;
 693                 ASSERT(za.za_integer_length == 8 && za.za_num_integers == 1);
 694 
 695                 VERIFY(zap_update(mos, jumpobj, za.za_name,
 696                     8, 1, &zero, tx) == 0);
 697         }
 698         zap_cursor_fini(&zc);
 699 }
 700 
 701 /*
 702  * set all create time permission on new dataset.
 703  */
 704 void
 705 dsl_deleg_set_create_perms(dsl_dir_t *sdd, dmu_tx_t *tx, cred_t *cr)
 706 {
 707         dsl_dir_t *dd;
 708         uint64_t uid = crgetuid(cr);
 709 
 710         if (spa_version(dmu_objset_spa(sdd->dd_pool->dp_meta_objset)) <
 711             SPA_VERSION_DELEGATED_PERMS)
 712                 return;
 713 
 714         for (dd = sdd->dd_parent; dd != NULL; dd = dd->dd_parent) {
 715                 uint64_t pzapobj = dd->dd_phys->dd_deleg_zapobj;
 716 
 717                 if (pzapobj == 0)
 718                         continue;
 719 
 720                 copy_create_perms(sdd, pzapobj, B_FALSE, uid, tx);
 721                 copy_create_perms(sdd, pzapobj, B_TRUE, uid, tx);
 722         }
 723 }
 724 
 725 int
 726 dsl_deleg_destroy(objset_t *mos, uint64_t zapobj, dmu_tx_t *tx)
 727 {
 728         zap_cursor_t zc;
 729         zap_attribute_t za;
 730 
 731         if (zapobj == 0)
 732                 return (0);
 733 
 734         for (zap_cursor_init(&zc, mos, zapobj);
 735             zap_cursor_retrieve(&zc, &za) == 0;
 736             zap_cursor_advance(&zc)) {
 737                 ASSERT(za.za_integer_length == 8 && za.za_num_integers == 1);
 738                 VERIFY(0 == zap_destroy(mos, za.za_first_integer, tx));
 739         }
 740         zap_cursor_fini(&zc);
 741         VERIFY(0 == zap_destroy(mos, zapobj, tx));
 742         return (0);
 743 }
 744 
 745 boolean_t
 746 dsl_delegation_on(objset_t *os)
 747 {
 748         return (!!spa_delegation(os->os_spa));
 749 }