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) 2012 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_link(mos, DMU_OT_DSL_PERMS,
 175                             zapobj, whokey, tx);
 176                 }
 177 
 178                 while (permpair = nvlist_next_nvpair(perms, permpair)) {
 179                         const char *perm = nvpair_name(permpair);
 180                         uint64_t n = 0;
 181 
 182                         VERIFY(zap_update(mos, jumpobj,
 183                             perm, 8, 1, &n, tx) == 0);
 184                         spa_history_log_internal_dd(dd, "permission update", tx,
 185                             "%s %s", whokey, perm);
 186                 }
 187         }
 188 }
 189 
 190 static void
 191 dsl_deleg_unset_sync(void *arg1, void *arg2, dmu_tx_t *tx)
 192 {
 193         dsl_dir_t *dd = arg1;
 194         nvlist_t *nvp = arg2;
 195         objset_t *mos = dd->dd_pool->dp_meta_objset;
 196         nvpair_t *whopair = NULL;
 197         uint64_t zapobj = dd->dd_phys->dd_deleg_zapobj;
 198 
 199         if (zapobj == 0)
 200                 return;
 201 
 202         while (whopair = nvlist_next_nvpair(nvp, whopair)) {
 203                 const char *whokey = nvpair_name(whopair);
 204                 nvlist_t *perms;
 205                 nvpair_t *permpair = NULL;
 206                 uint64_t jumpobj;
 207 
 208                 if (nvpair_value_nvlist(whopair, &perms) != 0) {
 209                         if (zap_lookup(mos, zapobj, whokey, 8,
 210                             1, &jumpobj) == 0) {
 211                                 (void) zap_remove(mos, zapobj, whokey, tx);
 212                                 VERIFY(0 == zap_destroy(mos, jumpobj, tx));
 213                         }
 214                         spa_history_log_internal_dd(dd, "permission who remove",
 215                             tx, "%s", whokey);
 216                         continue;
 217                 }
 218 
 219                 if (zap_lookup(mos, zapobj, whokey, 8, 1, &jumpobj) != 0)
 220                         continue;
 221 
 222                 while (permpair = nvlist_next_nvpair(perms, permpair)) {
 223                         const char *perm = nvpair_name(permpair);
 224                         uint64_t n = 0;
 225 
 226                         (void) zap_remove(mos, jumpobj, perm, tx);
 227                         if (zap_count(mos, jumpobj, &n) == 0 && n == 0) {
 228                                 (void) zap_remove(mos, zapobj,
 229                                     whokey, tx);
 230                                 VERIFY(0 == zap_destroy(mos,
 231                                     jumpobj, tx));
 232                         }
 233                         spa_history_log_internal_dd(dd, "permission remove", tx,
 234                             "%s %s", whokey, perm);
 235                 }
 236         }
 237 }
 238 
 239 int
 240 dsl_deleg_set(const char *ddname, nvlist_t *nvp, boolean_t unset)
 241 {
 242         dsl_dir_t *dd;
 243         int error;
 244         nvpair_t *whopair = NULL;
 245         int blocks_modified = 0;
 246 
 247         error = dsl_dir_open(ddname, FTAG, &dd, NULL);
 248         if (error)
 249                 return (error);
 250 
 251         if (spa_version(dmu_objset_spa(dd->dd_pool->dp_meta_objset)) <
 252             SPA_VERSION_DELEGATED_PERMS) {
 253                 dsl_dir_close(dd, FTAG);
 254                 return (ENOTSUP);
 255         }
 256 
 257         while (whopair = nvlist_next_nvpair(nvp, whopair))
 258                 blocks_modified++;
 259 
 260         error = dsl_sync_task_do(dd->dd_pool, NULL,
 261             unset ? dsl_deleg_unset_sync : dsl_deleg_set_sync,
 262             dd, nvp, blocks_modified);
 263         dsl_dir_close(dd, FTAG);
 264 
 265         return (error);
 266 }
 267 
 268 /*
 269  * Find all 'allow' permissions from a given point and then continue
 270  * traversing up to the root.
 271  *
 272  * This function constructs an nvlist of nvlists.
 273  * each setpoint is an nvlist composed of an nvlist of an nvlist
 274  * of the individual * users/groups/everyone/create
 275  * permissions.
 276  *
 277  * The nvlist will look like this.
 278  *
 279  * { source fsname -> { whokeys { permissions,...}, ...}}
 280  *
 281  * The fsname nvpairs will be arranged in a bottom up order.  For example,
 282  * if we have the following structure a/b/c then the nvpairs for the fsnames
 283  * will be ordered a/b/c, a/b, a.
 284  */
 285 int
 286 dsl_deleg_get(const char *ddname, nvlist_t **nvp)
 287 {
 288         dsl_dir_t *dd, *startdd;
 289         dsl_pool_t *dp;
 290         int error;
 291         objset_t *mos;
 292 
 293         error = dsl_dir_open(ddname, FTAG, &startdd, NULL);
 294         if (error)
 295                 return (error);
 296 
 297         dp = startdd->dd_pool;
 298         mos = dp->dp_meta_objset;
 299 
 300         VERIFY(nvlist_alloc(nvp, NV_UNIQUE_NAME, KM_SLEEP) == 0);
 301 
 302         rw_enter(&dp->dp_config_rwlock, RW_READER);
 303         for (dd = startdd; dd != NULL; dd = dd->dd_parent) {
 304                 zap_cursor_t basezc;
 305                 zap_attribute_t baseza;
 306                 nvlist_t *sp_nvp;
 307                 uint64_t n;
 308                 char source[MAXNAMELEN];
 309 
 310                 if (dd->dd_phys->dd_deleg_zapobj &&
 311                     (zap_count(mos, dd->dd_phys->dd_deleg_zapobj,
 312                     &n) == 0) && n) {
 313                         VERIFY(nvlist_alloc(&sp_nvp,
 314                             NV_UNIQUE_NAME, KM_SLEEP) == 0);
 315                 } else {
 316                         continue;
 317                 }
 318 
 319                 for (zap_cursor_init(&basezc, mos,
 320                     dd->dd_phys->dd_deleg_zapobj);
 321                     zap_cursor_retrieve(&basezc, &baseza) == 0;
 322                     zap_cursor_advance(&basezc)) {
 323                         zap_cursor_t zc;
 324                         zap_attribute_t za;
 325                         nvlist_t *perms_nvp;
 326 
 327                         ASSERT(baseza.za_integer_length == 8);
 328                         ASSERT(baseza.za_num_integers == 1);
 329 
 330                         VERIFY(nvlist_alloc(&perms_nvp,
 331                             NV_UNIQUE_NAME, KM_SLEEP) == 0);
 332                         for (zap_cursor_init(&zc, mos, baseza.za_first_integer);
 333                             zap_cursor_retrieve(&zc, &za) == 0;
 334                             zap_cursor_advance(&zc)) {
 335                                 VERIFY(nvlist_add_boolean(perms_nvp,
 336                                     za.za_name) == 0);
 337                         }
 338                         zap_cursor_fini(&zc);
 339                         VERIFY(nvlist_add_nvlist(sp_nvp, baseza.za_name,
 340                             perms_nvp) == 0);
 341                         nvlist_free(perms_nvp);
 342                 }
 343 
 344                 zap_cursor_fini(&basezc);
 345 
 346                 dsl_dir_name(dd, source);
 347                 VERIFY(nvlist_add_nvlist(*nvp, source, sp_nvp) == 0);
 348                 nvlist_free(sp_nvp);
 349         }
 350         rw_exit(&dp->dp_config_rwlock);
 351 
 352         dsl_dir_close(startdd, FTAG);
 353         return (0);
 354 }
 355 
 356 /*
 357  * Routines for dsl_deleg_access() -- access checking.
 358  */
 359 typedef struct perm_set {
 360         avl_node_t      p_node;
 361         boolean_t       p_matched;
 362         char            p_setname[ZFS_MAX_DELEG_NAME];
 363 } perm_set_t;
 364 
 365 static int
 366 perm_set_compare(const void *arg1, const void *arg2)
 367 {
 368         const perm_set_t *node1 = arg1;
 369         const perm_set_t *node2 = arg2;
 370         int val;
 371 
 372         val = strcmp(node1->p_setname, node2->p_setname);
 373         if (val == 0)
 374                 return (0);
 375         return (val > 0 ? 1 : -1);
 376 }
 377 
 378 /*
 379  * Determine whether a specified permission exists.
 380  *
 381  * First the base attribute has to be retrieved.  i.e. ul$12
 382  * Once the base object has been retrieved the actual permission
 383  * is lookup up in the zap object the base object points to.
 384  *
 385  * Return 0 if permission exists, ENOENT if there is no whokey, EPERM if
 386  * there is no perm in that jumpobj.
 387  */
 388 static int
 389 dsl_check_access(objset_t *mos, uint64_t zapobj,
 390     char type, char checkflag, void *valp, const char *perm)
 391 {
 392         int error;
 393         uint64_t jumpobj, zero;
 394         char whokey[ZFS_MAX_DELEG_NAME];
 395 
 396         zfs_deleg_whokey(whokey, type, checkflag, valp);
 397         error = zap_lookup(mos, zapobj, whokey, 8, 1, &jumpobj);
 398         if (error == 0) {
 399                 error = zap_lookup(mos, jumpobj, perm, 8, 1, &zero);
 400                 if (error == ENOENT)
 401                         error = EPERM;
 402         }
 403         return (error);
 404 }
 405 
 406 /*
 407  * check a specified user/group for a requested permission
 408  */
 409 static int
 410 dsl_check_user_access(objset_t *mos, uint64_t zapobj, const char *perm,
 411     int checkflag, cred_t *cr)
 412 {
 413         const   gid_t *gids;
 414         int     ngids;
 415         int     i;
 416         uint64_t id;
 417 
 418         /* check for user */
 419         id = crgetuid(cr);
 420         if (dsl_check_access(mos, zapobj,
 421             ZFS_DELEG_USER, checkflag, &id, perm) == 0)
 422                 return (0);
 423 
 424         /* check for users primary group */
 425         id = crgetgid(cr);
 426         if (dsl_check_access(mos, zapobj,
 427             ZFS_DELEG_GROUP, checkflag, &id, perm) == 0)
 428                 return (0);
 429 
 430         /* check for everyone entry */
 431         id = -1;
 432         if (dsl_check_access(mos, zapobj,
 433             ZFS_DELEG_EVERYONE, checkflag, &id, perm) == 0)
 434                 return (0);
 435 
 436         /* check each supplemental group user is a member of */
 437         ngids = crgetngroups(cr);
 438         gids = crgetgroups(cr);
 439         for (i = 0; i != ngids; i++) {
 440                 id = gids[i];
 441                 if (dsl_check_access(mos, zapobj,
 442                     ZFS_DELEG_GROUP, checkflag, &id, perm) == 0)
 443                         return (0);
 444         }
 445 
 446         return (EPERM);
 447 }
 448 
 449 /*
 450  * Iterate over the sets specified in the specified zapobj
 451  * and load them into the permsets avl tree.
 452  */
 453 static int
 454 dsl_load_sets(objset_t *mos, uint64_t zapobj,
 455     char type, char checkflag, void *valp, avl_tree_t *avl)
 456 {
 457         zap_cursor_t zc;
 458         zap_attribute_t za;
 459         perm_set_t *permnode;
 460         avl_index_t idx;
 461         uint64_t jumpobj;
 462         int error;
 463         char whokey[ZFS_MAX_DELEG_NAME];
 464 
 465         zfs_deleg_whokey(whokey, type, checkflag, valp);
 466 
 467         error = zap_lookup(mos, zapobj, whokey, 8, 1, &jumpobj);
 468         if (error != 0)
 469                 return (error);
 470 
 471         for (zap_cursor_init(&zc, mos, jumpobj);
 472             zap_cursor_retrieve(&zc, &za) == 0;
 473             zap_cursor_advance(&zc)) {
 474                 permnode = kmem_alloc(sizeof (perm_set_t), KM_SLEEP);
 475                 (void) strlcpy(permnode->p_setname, za.za_name,
 476                     sizeof (permnode->p_setname));
 477                 permnode->p_matched = B_FALSE;
 478 
 479                 if (avl_find(avl, permnode, &idx) == NULL) {
 480                         avl_insert(avl, permnode, idx);
 481                 } else {
 482                         kmem_free(permnode, sizeof (perm_set_t));
 483                 }
 484         }
 485         zap_cursor_fini(&zc);
 486         return (0);
 487 }
 488 
 489 /*
 490  * Load all permissions user based on cred belongs to.
 491  */
 492 static void
 493 dsl_load_user_sets(objset_t *mos, uint64_t zapobj, avl_tree_t *avl,
 494     char checkflag, cred_t *cr)
 495 {
 496         const   gid_t *gids;
 497         int     ngids, i;
 498         uint64_t id;
 499 
 500         id = crgetuid(cr);
 501         (void) dsl_load_sets(mos, zapobj,
 502             ZFS_DELEG_USER_SETS, checkflag, &id, avl);
 503 
 504         id = crgetgid(cr);
 505         (void) dsl_load_sets(mos, zapobj,
 506             ZFS_DELEG_GROUP_SETS, checkflag, &id, avl);
 507 
 508         (void) dsl_load_sets(mos, zapobj,
 509             ZFS_DELEG_EVERYONE_SETS, checkflag, NULL, avl);
 510 
 511         ngids = crgetngroups(cr);
 512         gids = crgetgroups(cr);
 513         for (i = 0; i != ngids; i++) {
 514                 id = gids[i];
 515                 (void) dsl_load_sets(mos, zapobj,
 516                     ZFS_DELEG_GROUP_SETS, checkflag, &id, avl);
 517         }
 518 }
 519 
 520 /*
 521  * Check if user has requested permission.
 522  */
 523 int
 524 dsl_deleg_access_impl(dsl_dataset_t *ds, const char *perm, cred_t *cr)
 525 {
 526         dsl_dir_t *dd;
 527         dsl_pool_t *dp;
 528         void *cookie;
 529         int     error;
 530         char    checkflag;
 531         objset_t *mos;
 532         avl_tree_t permsets;
 533         perm_set_t *setnode;
 534 
 535         dp = ds->ds_dir->dd_pool;
 536         mos = dp->dp_meta_objset;
 537 
 538         if (dsl_delegation_on(mos) == B_FALSE)
 539                 return (ECANCELED);
 540 
 541         if (spa_version(dmu_objset_spa(dp->dp_meta_objset)) <
 542             SPA_VERSION_DELEGATED_PERMS)
 543                 return (EPERM);
 544 
 545         if (dsl_dataset_is_snapshot(ds)) {
 546                 /*
 547                  * Snapshots are treated as descendents only,
 548                  * local permissions do not apply.
 549                  */
 550                 checkflag = ZFS_DELEG_DESCENDENT;
 551         } else {
 552                 checkflag = ZFS_DELEG_LOCAL;
 553         }
 554 
 555         avl_create(&permsets, perm_set_compare, sizeof (perm_set_t),
 556             offsetof(perm_set_t, p_node));
 557 
 558         rw_enter(&dp->dp_config_rwlock, RW_READER);
 559         for (dd = ds->ds_dir; dd != NULL; dd = dd->dd_parent,
 560             checkflag = ZFS_DELEG_DESCENDENT) {
 561                 uint64_t zapobj;
 562                 boolean_t expanded;
 563 
 564                 /*
 565                  * If not in global zone then make sure
 566                  * the zoned property is set
 567                  */
 568                 if (!INGLOBALZONE(curproc)) {
 569                         uint64_t zoned;
 570 
 571                         if (dsl_prop_get_dd(dd,
 572                             zfs_prop_to_name(ZFS_PROP_ZONED),
 573                             8, 1, &zoned, NULL, B_FALSE) != 0)
 574                                 break;
 575                         if (!zoned)
 576                                 break;
 577                 }
 578                 zapobj = dd->dd_phys->dd_deleg_zapobj;
 579 
 580                 if (zapobj == 0)
 581                         continue;
 582 
 583                 dsl_load_user_sets(mos, zapobj, &permsets, checkflag, cr);
 584 again:
 585                 expanded = B_FALSE;
 586                 for (setnode = avl_first(&permsets); setnode;
 587                     setnode = AVL_NEXT(&permsets, setnode)) {
 588                         if (setnode->p_matched == B_TRUE)
 589                                 continue;
 590 
 591                         /* See if this set directly grants this permission */
 592                         error = dsl_check_access(mos, zapobj,
 593                             ZFS_DELEG_NAMED_SET, 0, setnode->p_setname, perm);
 594                         if (error == 0)
 595                                 goto success;
 596                         if (error == EPERM)
 597                                 setnode->p_matched = B_TRUE;
 598 
 599                         /* See if this set includes other sets */
 600                         error = dsl_load_sets(mos, zapobj,
 601                             ZFS_DELEG_NAMED_SET_SETS, 0,
 602                             setnode->p_setname, &permsets);
 603                         if (error == 0)
 604                                 setnode->p_matched = expanded = B_TRUE;
 605                 }
 606                 /*
 607                  * If we expanded any sets, that will define more sets,
 608                  * which we need to check.
 609                  */
 610                 if (expanded)
 611                         goto again;
 612 
 613                 error = dsl_check_user_access(mos, zapobj, perm, checkflag, cr);
 614                 if (error == 0)
 615                         goto success;
 616         }
 617         error = EPERM;
 618 success:
 619         rw_exit(&dp->dp_config_rwlock);
 620 
 621         cookie = NULL;
 622         while ((setnode = avl_destroy_nodes(&permsets, &cookie)) != NULL)
 623                 kmem_free(setnode, sizeof (perm_set_t));
 624 
 625         return (error);
 626 }
 627 
 628 int
 629 dsl_deleg_access(const char *dsname, const char *perm, cred_t *cr)
 630 {
 631         dsl_dataset_t *ds;
 632         int error;
 633 
 634         error = dsl_dataset_hold(dsname, FTAG, &ds);
 635         if (error)
 636                 return (error);
 637 
 638         error = dsl_deleg_access_impl(ds, perm, cr);
 639         dsl_dataset_rele(ds, FTAG);
 640 
 641         return (error);
 642 }
 643 
 644 /*
 645  * Other routines.
 646  */
 647 
 648 static void
 649 copy_create_perms(dsl_dir_t *dd, uint64_t pzapobj,
 650     boolean_t dosets, uint64_t uid, dmu_tx_t *tx)
 651 {
 652         objset_t *mos = dd->dd_pool->dp_meta_objset;
 653         uint64_t jumpobj, pjumpobj;
 654         uint64_t zapobj = dd->dd_phys->dd_deleg_zapobj;
 655         zap_cursor_t zc;
 656         zap_attribute_t za;
 657         char whokey[ZFS_MAX_DELEG_NAME];
 658 
 659         zfs_deleg_whokey(whokey,
 660             dosets ? ZFS_DELEG_CREATE_SETS : ZFS_DELEG_CREATE,
 661             ZFS_DELEG_LOCAL, NULL);
 662         if (zap_lookup(mos, pzapobj, whokey, 8, 1, &pjumpobj) != 0)
 663                 return;
 664 
 665         if (zapobj == 0) {
 666                 dmu_buf_will_dirty(dd->dd_dbuf, tx);
 667                 zapobj = dd->dd_phys->dd_deleg_zapobj = zap_create(mos,
 668                     DMU_OT_DSL_PERMS, DMU_OT_NONE, 0, tx);
 669         }
 670 
 671         zfs_deleg_whokey(whokey,
 672             dosets ? ZFS_DELEG_USER_SETS : ZFS_DELEG_USER,
 673             ZFS_DELEG_LOCAL, &uid);
 674         if (zap_lookup(mos, zapobj, whokey, 8, 1, &jumpobj) == ENOENT) {
 675                 jumpobj = zap_create(mos, DMU_OT_DSL_PERMS, DMU_OT_NONE, 0, tx);
 676                 VERIFY(zap_add(mos, zapobj, whokey, 8, 1, &jumpobj, tx) == 0);
 677         }
 678 
 679         for (zap_cursor_init(&zc, mos, pjumpobj);
 680             zap_cursor_retrieve(&zc, &za) == 0;
 681             zap_cursor_advance(&zc)) {
 682                 uint64_t zero = 0;
 683                 ASSERT(za.za_integer_length == 8 && za.za_num_integers == 1);
 684 
 685                 VERIFY(zap_update(mos, jumpobj, za.za_name,
 686                     8, 1, &zero, tx) == 0);
 687         }
 688         zap_cursor_fini(&zc);
 689 }
 690 
 691 /*
 692  * set all create time permission on new dataset.
 693  */
 694 void
 695 dsl_deleg_set_create_perms(dsl_dir_t *sdd, dmu_tx_t *tx, cred_t *cr)
 696 {
 697         dsl_dir_t *dd;
 698         uint64_t uid = crgetuid(cr);
 699 
 700         if (spa_version(dmu_objset_spa(sdd->dd_pool->dp_meta_objset)) <
 701             SPA_VERSION_DELEGATED_PERMS)
 702                 return;
 703 
 704         for (dd = sdd->dd_parent; dd != NULL; dd = dd->dd_parent) {
 705                 uint64_t pzapobj = dd->dd_phys->dd_deleg_zapobj;
 706 
 707                 if (pzapobj == 0)
 708                         continue;
 709 
 710                 copy_create_perms(sdd, pzapobj, B_FALSE, uid, tx);
 711                 copy_create_perms(sdd, pzapobj, B_TRUE, uid, tx);
 712         }
 713 }
 714 
 715 int
 716 dsl_deleg_destroy(objset_t *mos, uint64_t zapobj, dmu_tx_t *tx)
 717 {
 718         zap_cursor_t zc;
 719         zap_attribute_t za;
 720 
 721         if (zapobj == 0)
 722                 return (0);
 723 
 724         for (zap_cursor_init(&zc, mos, zapobj);
 725             zap_cursor_retrieve(&zc, &za) == 0;
 726             zap_cursor_advance(&zc)) {
 727                 ASSERT(za.za_integer_length == 8 && za.za_num_integers == 1);
 728                 VERIFY(0 == zap_destroy(mos, za.za_first_integer, tx));
 729         }
 730         zap_cursor_fini(&zc);
 731         VERIFY(0 == zap_destroy(mos, zapobj, tx));
 732         return (0);
 733 }
 734 
 735 boolean_t
 736 dsl_delegation_on(objset_t *os)
 737 {
 738         return (!!spa_delegation(os->os_spa));
 739 }