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) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
  23  * Copyright (c) 2012, 2014 by Delphix. All rights reserved.
  24  * Copyright (c) 2013 Steven Hartland. All rights reserved.
  25  */
  26 
  27 #include <sys/zfs_context.h>
  28 #include <sys/dsl_userhold.h>
  29 #include <sys/dsl_dataset.h>
  30 #include <sys/dsl_destroy.h>
  31 #include <sys/dsl_synctask.h>
  32 #include <sys/dmu_tx.h>
  33 #include <sys/zfs_onexit.h>
  34 #include <sys/dsl_pool.h>
  35 #include <sys/dsl_dir.h>
  36 #include <sys/zfs_ioctl.h>
  37 #include <sys/zap.h>
  38 
  39 typedef struct dsl_dataset_user_hold_arg {
  40         nvlist_t *dduha_holds;
  41         nvlist_t *dduha_chkholds;
  42         nvlist_t *dduha_errlist;
  43         minor_t dduha_minor;
  44 } dsl_dataset_user_hold_arg_t;
  45 
  46 /*
  47  * If you add new checks here, you may need to add additional checks to the
  48  * "temporary" case in snapshot_check() in dmu_objset.c.
  49  */
  50 int
  51 dsl_dataset_user_hold_check_one(dsl_dataset_t *ds, const char *htag,
  52     boolean_t temphold, dmu_tx_t *tx)
  53 {
  54         dsl_pool_t *dp = dmu_tx_pool(tx);
  55         objset_t *mos = dp->dp_meta_objset;
  56         int error = 0;
  57 
  58         ASSERT(dsl_pool_config_held(dp));
  59 
  60         if (strlen(htag) > MAXNAMELEN)
  61                 return (SET_ERROR(E2BIG));
  62         /* Tempholds have a more restricted length */
  63         if (temphold && strlen(htag) + MAX_TAG_PREFIX_LEN >= MAXNAMELEN)
  64                 return (SET_ERROR(E2BIG));
  65 
  66         /* tags must be unique (if ds already exists) */
  67         if (ds != NULL && dsl_dataset_phys(ds)->ds_userrefs_obj != 0) {
  68                 uint64_t value;
  69 
  70                 error = zap_lookup(mos, dsl_dataset_phys(ds)->ds_userrefs_obj,
  71                     htag, 8, 1, &value);
  72                 if (error == 0)
  73                         error = SET_ERROR(EEXIST);
  74                 else if (error == ENOENT)
  75                         error = 0;
  76         }
  77 
  78         return (error);
  79 }
  80 
  81 static int
  82 dsl_dataset_user_hold_check(void *arg, dmu_tx_t *tx)
  83 {
  84         dsl_dataset_user_hold_arg_t *dduha = arg;
  85         dsl_pool_t *dp = dmu_tx_pool(tx);
  86 
  87         if (spa_version(dp->dp_spa) < SPA_VERSION_USERREFS)
  88                 return (SET_ERROR(ENOTSUP));
  89 
  90         if (!dmu_tx_is_syncing(tx))
  91                 return (0);
  92 
  93         for (nvpair_t *pair = nvlist_next_nvpair(dduha->dduha_holds, NULL);
  94             pair != NULL; pair = nvlist_next_nvpair(dduha->dduha_holds, pair)) {
  95                 dsl_dataset_t *ds;
  96                 int error = 0;
  97                 char *htag, *name;
  98 
  99                 /* must be a snapshot */
 100                 name = nvpair_name(pair);
 101                 if (strchr(name, '@') == NULL)
 102                         error = SET_ERROR(EINVAL);
 103 
 104                 if (error == 0)
 105                         error = nvpair_value_string(pair, &htag);
 106 
 107                 if (error == 0)
 108                         error = dsl_dataset_hold(dp, name, FTAG, &ds);
 109 
 110                 if (error == 0) {
 111                         error = dsl_dataset_user_hold_check_one(ds, htag,
 112                             dduha->dduha_minor != 0, tx);
 113                         dsl_dataset_rele(ds, FTAG);
 114                 }
 115 
 116                 if (error == 0) {
 117                         fnvlist_add_string(dduha->dduha_chkholds, name, htag);
 118                 } else {
 119                         /*
 120                          * We register ENOENT errors so they can be correctly
 121                          * reported if needed, such as when all holds fail.
 122                          */
 123                         fnvlist_add_int32(dduha->dduha_errlist, name, error);
 124                         if (error != ENOENT)
 125                                 return (error);
 126                 }
 127         }
 128 
 129         return (0);
 130 }
 131 
 132 
 133 static void
 134 dsl_dataset_user_hold_sync_one_impl(nvlist_t *tmpholds, dsl_dataset_t *ds,
 135     const char *htag, minor_t minor, uint64_t now, dmu_tx_t *tx)
 136 {
 137         dsl_pool_t *dp = ds->ds_dir->dd_pool;
 138         objset_t *mos = dp->dp_meta_objset;
 139         uint64_t zapobj;
 140 
 141         ASSERT(RRW_WRITE_HELD(&dp->dp_config_rwlock));
 142 
 143         if (dsl_dataset_phys(ds)->ds_userrefs_obj == 0) {
 144                 /*
 145                  * This is the first user hold for this dataset.  Create
 146                  * the userrefs zap object.
 147                  */
 148                 dmu_buf_will_dirty(ds->ds_dbuf, tx);
 149                 zapobj = dsl_dataset_phys(ds)->ds_userrefs_obj =
 150                     zap_create(mos, DMU_OT_USERREFS, DMU_OT_NONE, 0, tx);
 151         } else {
 152                 zapobj = dsl_dataset_phys(ds)->ds_userrefs_obj;
 153         }
 154         ds->ds_userrefs++;
 155 
 156         VERIFY0(zap_add(mos, zapobj, htag, 8, 1, &now, tx));
 157 
 158         if (minor != 0) {
 159                 char name[MAXNAMELEN];
 160                 nvlist_t *tags;
 161 
 162                 VERIFY0(dsl_pool_user_hold(dp, ds->ds_object,
 163                     htag, now, tx));
 164                 (void) snprintf(name, sizeof (name), "%llx",
 165                     (u_longlong_t)ds->ds_object);
 166 
 167                 if (nvlist_lookup_nvlist(tmpholds, name, &tags) != 0) {
 168                         tags = fnvlist_alloc();
 169                         fnvlist_add_boolean(tags, htag);
 170                         fnvlist_add_nvlist(tmpholds, name, tags);
 171                         fnvlist_free(tags);
 172                 } else {
 173                         fnvlist_add_boolean(tags, htag);
 174                 }
 175         }
 176 
 177         spa_history_log_internal_ds(ds, "hold", tx,
 178             "tag=%s temp=%d refs=%llu",
 179             htag, minor != 0, ds->ds_userrefs);
 180 }
 181 
 182 typedef struct zfs_hold_cleanup_arg {
 183         char zhca_spaname[MAXNAMELEN];
 184         uint64_t zhca_spa_load_guid;
 185         nvlist_t *zhca_holds;
 186 } zfs_hold_cleanup_arg_t;
 187 
 188 static void
 189 dsl_dataset_user_release_onexit(void *arg)
 190 {
 191         zfs_hold_cleanup_arg_t *ca = arg;
 192         spa_t *spa;
 193         int error;
 194 
 195         error = spa_open(ca->zhca_spaname, &spa, FTAG);
 196         if (error != 0) {
 197                 zfs_dbgmsg("couldn't release holds on pool=%s "
 198                     "because pool is no longer loaded",
 199                     ca->zhca_spaname);
 200                 return;
 201         }
 202         if (spa_load_guid(spa) != ca->zhca_spa_load_guid) {
 203                 zfs_dbgmsg("couldn't release holds on pool=%s "
 204                     "because pool is no longer loaded (guid doesn't match)",
 205                     ca->zhca_spaname);
 206                 spa_close(spa, FTAG);
 207                 return;
 208         }
 209 
 210         (void) dsl_dataset_user_release_tmp(spa_get_dsl(spa), ca->zhca_holds);
 211         fnvlist_free(ca->zhca_holds);
 212         kmem_free(ca, sizeof (zfs_hold_cleanup_arg_t));
 213         spa_close(spa, FTAG);
 214 }
 215 
 216 static void
 217 dsl_onexit_hold_cleanup(spa_t *spa, nvlist_t *holds, minor_t minor)
 218 {
 219         zfs_hold_cleanup_arg_t *ca;
 220 
 221         if (minor == 0 || nvlist_empty(holds)) {
 222                 fnvlist_free(holds);
 223                 return;
 224         }
 225 
 226         ASSERT(spa != NULL);
 227         ca = kmem_alloc(sizeof (*ca), KM_SLEEP);
 228 
 229         (void) strlcpy(ca->zhca_spaname, spa_name(spa),
 230             sizeof (ca->zhca_spaname));
 231         ca->zhca_spa_load_guid = spa_load_guid(spa);
 232         ca->zhca_holds = holds;
 233         VERIFY0(zfs_onexit_add_cb(minor,
 234             dsl_dataset_user_release_onexit, ca, NULL));
 235 }
 236 
 237 void
 238 dsl_dataset_user_hold_sync_one(dsl_dataset_t *ds, const char *htag,
 239     minor_t minor, uint64_t now, dmu_tx_t *tx)
 240 {
 241         nvlist_t *tmpholds;
 242 
 243         if (minor != 0)
 244                 tmpholds = fnvlist_alloc();
 245         else
 246                 tmpholds = NULL;
 247         dsl_dataset_user_hold_sync_one_impl(tmpholds, ds, htag, minor, now, tx);
 248         dsl_onexit_hold_cleanup(dsl_dataset_get_spa(ds), tmpholds, minor);
 249 }
 250 
 251 static void
 252 dsl_dataset_user_hold_sync(void *arg, dmu_tx_t *tx)
 253 {
 254         dsl_dataset_user_hold_arg_t *dduha = arg;
 255         dsl_pool_t *dp = dmu_tx_pool(tx);
 256         nvlist_t *tmpholds;
 257         uint64_t now = gethrestime_sec();
 258 
 259         if (dduha->dduha_minor != 0)
 260                 tmpholds = fnvlist_alloc();
 261         else
 262                 tmpholds = NULL;
 263         for (nvpair_t *pair = nvlist_next_nvpair(dduha->dduha_chkholds, NULL);
 264             pair != NULL;
 265             pair = nvlist_next_nvpair(dduha->dduha_chkholds, pair)) {
 266                 dsl_dataset_t *ds;
 267 
 268                 VERIFY0(dsl_dataset_hold(dp, nvpair_name(pair), FTAG, &ds));
 269                 dsl_dataset_user_hold_sync_one_impl(tmpholds, ds,
 270                     fnvpair_value_string(pair), dduha->dduha_minor, now, tx);
 271                 dsl_dataset_rele(ds, FTAG);
 272         }
 273         dsl_onexit_hold_cleanup(dp->dp_spa, tmpholds, dduha->dduha_minor);
 274 }
 275 
 276 /*
 277  * The full semantics of this function are described in the comment above
 278  * lzc_hold().
 279  *
 280  * To summarize:
 281  * holds is nvl of snapname -> holdname
 282  * errlist will be filled in with snapname -> error
 283  *
 284  * The snaphosts must all be in the same pool.
 285  *
 286  * Holds for snapshots that don't exist will be skipped.
 287  *
 288  * If none of the snapshots for requested holds exist then ENOENT will be
 289  * returned.
 290  *
 291  * If cleanup_minor is not 0, the holds will be temporary, which will be cleaned
 292  * up when the process exits.
 293  *
 294  * On success all the holds, for snapshots that existed, will be created and 0
 295  * will be returned.
 296  *
 297  * On failure no holds will be created, the errlist will be filled in,
 298  * and an errno will returned.
 299  *
 300  * In all cases the errlist will contain entries for holds where the snapshot
 301  * didn't exist.
 302  */
 303 int
 304 dsl_dataset_user_hold(nvlist_t *holds, minor_t cleanup_minor, nvlist_t *errlist)
 305 {
 306         dsl_dataset_user_hold_arg_t dduha;
 307         nvpair_t *pair;
 308         int ret;
 309 
 310         pair = nvlist_next_nvpair(holds, NULL);
 311         if (pair == NULL)
 312                 return (0);
 313 
 314         dduha.dduha_holds = holds;
 315         dduha.dduha_chkholds = fnvlist_alloc();
 316         dduha.dduha_errlist = errlist;
 317         dduha.dduha_minor = cleanup_minor;
 318 
 319         ret = dsl_sync_task(nvpair_name(pair), dsl_dataset_user_hold_check,
 320             dsl_dataset_user_hold_sync, &dduha,
 321             fnvlist_num_pairs(holds), ZFS_SPACE_CHECK_RESERVED);
 322         fnvlist_free(dduha.dduha_chkholds);
 323 
 324         return (ret);
 325 }
 326 
 327 typedef int (dsl_holdfunc_t)(dsl_pool_t *dp, const char *name, void *tag,
 328     dsl_dataset_t **dsp);
 329 
 330 typedef struct dsl_dataset_user_release_arg {
 331         dsl_holdfunc_t *ddura_holdfunc;
 332         nvlist_t *ddura_holds;
 333         nvlist_t *ddura_todelete;
 334         nvlist_t *ddura_errlist;
 335         nvlist_t *ddura_chkholds;
 336 } dsl_dataset_user_release_arg_t;
 337 
 338 /* Place a dataset hold on the snapshot identified by passed dsobj string */
 339 static int
 340 dsl_dataset_hold_obj_string(dsl_pool_t *dp, const char *dsobj, void *tag,
 341     dsl_dataset_t **dsp)
 342 {
 343         return (dsl_dataset_hold_obj(dp, strtonum(dsobj, NULL), tag, dsp));
 344 }
 345 
 346 static int
 347 dsl_dataset_user_release_check_one(dsl_dataset_user_release_arg_t *ddura,
 348     dsl_dataset_t *ds, nvlist_t *holds, const char *snapname)
 349 {
 350         uint64_t zapobj;
 351         nvlist_t *holds_found;
 352         objset_t *mos;
 353         int numholds;
 354 
 355         if (!ds->ds_is_snapshot)
 356                 return (SET_ERROR(EINVAL));
 357 
 358         if (nvlist_empty(holds))
 359                 return (0);
 360 
 361         numholds = 0;
 362         mos = ds->ds_dir->dd_pool->dp_meta_objset;
 363         zapobj = dsl_dataset_phys(ds)->ds_userrefs_obj;
 364         holds_found = fnvlist_alloc();
 365 
 366         for (nvpair_t *pair = nvlist_next_nvpair(holds, NULL); pair != NULL;
 367             pair = nvlist_next_nvpair(holds, pair)) {
 368                 uint64_t tmp;
 369                 int error;
 370                 const char *holdname = nvpair_name(pair);
 371 
 372                 if (zapobj != 0)
 373                         error = zap_lookup(mos, zapobj, holdname, 8, 1, &tmp);
 374                 else
 375                         error = SET_ERROR(ENOENT);
 376 
 377                 /*
 378                  * Non-existent holds are put on the errlist, but don't
 379                  * cause an overall failure.
 380                  */
 381                 if (error == ENOENT) {
 382                         if (ddura->ddura_errlist != NULL) {
 383                                 char *errtag = kmem_asprintf("%s#%s",
 384                                     snapname, holdname);
 385                                 fnvlist_add_int32(ddura->ddura_errlist, errtag,
 386                                     ENOENT);
 387                                 strfree(errtag);
 388                         }
 389                         continue;
 390                 }
 391 
 392                 if (error != 0) {
 393                         fnvlist_free(holds_found);
 394                         return (error);
 395                 }
 396 
 397                 fnvlist_add_boolean(holds_found, holdname);
 398                 numholds++;
 399         }
 400 
 401         if (DS_IS_DEFER_DESTROY(ds) &&
 402             dsl_dataset_phys(ds)->ds_num_children == 1 &&
 403             ds->ds_userrefs == numholds) {
 404                 /* we need to destroy the snapshot as well */
 405                 if (dsl_dataset_long_held(ds)) {
 406                         fnvlist_free(holds_found);
 407                         return (SET_ERROR(EBUSY));
 408                 }
 409                 fnvlist_add_boolean(ddura->ddura_todelete, snapname);
 410         }
 411 
 412         if (numholds != 0) {
 413                 fnvlist_add_nvlist(ddura->ddura_chkholds, snapname,
 414                     holds_found);
 415         }
 416         fnvlist_free(holds_found);
 417 
 418         return (0);
 419 }
 420 
 421 static int
 422 dsl_dataset_user_release_check(void *arg, dmu_tx_t *tx)
 423 {
 424         dsl_dataset_user_release_arg_t *ddura;
 425         dsl_holdfunc_t *holdfunc;
 426         dsl_pool_t *dp;
 427 
 428         if (!dmu_tx_is_syncing(tx))
 429                 return (0);
 430 
 431         dp = dmu_tx_pool(tx);
 432 
 433         ASSERT(RRW_WRITE_HELD(&dp->dp_config_rwlock));
 434 
 435         ddura = arg;
 436         holdfunc = ddura->ddura_holdfunc;
 437 
 438         for (nvpair_t *pair = nvlist_next_nvpair(ddura->ddura_holds, NULL);
 439             pair != NULL; pair = nvlist_next_nvpair(ddura->ddura_holds, pair)) {
 440                 int error;
 441                 dsl_dataset_t *ds;
 442                 nvlist_t *holds;
 443                 const char *snapname = nvpair_name(pair);
 444 
 445                 error = nvpair_value_nvlist(pair, &holds);
 446                 if (error != 0)
 447                         error = (SET_ERROR(EINVAL));
 448                 else
 449                         error = holdfunc(dp, snapname, FTAG, &ds);
 450                 if (error == 0) {
 451                         error = dsl_dataset_user_release_check_one(ddura, ds,
 452                             holds, snapname);
 453                         dsl_dataset_rele(ds, FTAG);
 454                 }
 455                 if (error != 0) {
 456                         if (ddura->ddura_errlist != NULL) {
 457                                 fnvlist_add_int32(ddura->ddura_errlist,
 458                                     snapname, error);
 459                         }
 460                         /*
 461                          * Non-existent snapshots are put on the errlist,
 462                          * but don't cause an overall failure.
 463                          */
 464                         if (error != ENOENT)
 465                                 return (error);
 466                 }
 467         }
 468 
 469         return (0);
 470 }
 471 
 472 static void
 473 dsl_dataset_user_release_sync_one(dsl_dataset_t *ds, nvlist_t *holds,
 474     dmu_tx_t *tx)
 475 {
 476         dsl_pool_t *dp = ds->ds_dir->dd_pool;
 477         objset_t *mos = dp->dp_meta_objset;
 478 
 479         for (nvpair_t *pair = nvlist_next_nvpair(holds, NULL); pair != NULL;
 480             pair = nvlist_next_nvpair(holds, pair)) {
 481                 int error;
 482                 const char *holdname = nvpair_name(pair);
 483 
 484                 /* Remove temporary hold if one exists. */
 485                 error = dsl_pool_user_release(dp, ds->ds_object, holdname, tx);
 486                 VERIFY(error == 0 || error == ENOENT);
 487 
 488                 VERIFY0(zap_remove(mos, dsl_dataset_phys(ds)->ds_userrefs_obj,
 489                     holdname, tx));
 490                 ds->ds_userrefs--;
 491 
 492                 spa_history_log_internal_ds(ds, "release", tx,
 493                     "tag=%s refs=%lld", holdname, (longlong_t)ds->ds_userrefs);
 494         }
 495 }
 496 
 497 static void
 498 dsl_dataset_user_release_sync(void *arg, dmu_tx_t *tx)
 499 {
 500         dsl_dataset_user_release_arg_t *ddura = arg;
 501         dsl_holdfunc_t *holdfunc = ddura->ddura_holdfunc;
 502         dsl_pool_t *dp = dmu_tx_pool(tx);
 503 
 504         ASSERT(RRW_WRITE_HELD(&dp->dp_config_rwlock));
 505 
 506         for (nvpair_t *pair = nvlist_next_nvpair(ddura->ddura_chkholds, NULL);
 507             pair != NULL; pair = nvlist_next_nvpair(ddura->ddura_chkholds,
 508             pair)) {
 509                 dsl_dataset_t *ds;
 510                 const char *name = nvpair_name(pair);
 511 
 512                 VERIFY0(holdfunc(dp, name, FTAG, &ds));
 513 
 514                 dsl_dataset_user_release_sync_one(ds,
 515                     fnvpair_value_nvlist(pair), tx);
 516                 if (nvlist_exists(ddura->ddura_todelete, name)) {
 517                         ASSERT(ds->ds_userrefs == 0 &&
 518                             dsl_dataset_phys(ds)->ds_num_children == 1 &&
 519                             DS_IS_DEFER_DESTROY(ds));
 520                         dsl_destroy_snapshot_sync_impl(ds, B_FALSE, tx);
 521                 }
 522                 dsl_dataset_rele(ds, FTAG);
 523         }
 524 }
 525 
 526 /*
 527  * The full semantics of this function are described in the comment above
 528  * lzc_release().
 529  *
 530  * To summarize:
 531  * Releases holds specified in the nvl holds.
 532  *
 533  * holds is nvl of snapname -> { holdname, ... }
 534  * errlist will be filled in with snapname -> error
 535  *
 536  * If tmpdp is not NULL the names for holds should be the dsobj's of snapshots,
 537  * otherwise they should be the names of shapshots.
 538  *
 539  * As a release may cause snapshots to be destroyed this trys to ensure they
 540  * aren't mounted.
 541  *
 542  * The release of non-existent holds are skipped.
 543  *
 544  * At least one hold must have been released for the this function to succeed
 545  * and return 0.
 546  */
 547 static int
 548 dsl_dataset_user_release_impl(nvlist_t *holds, nvlist_t *errlist,
 549     dsl_pool_t *tmpdp)
 550 {
 551         dsl_dataset_user_release_arg_t ddura;
 552         nvpair_t *pair;
 553         char *pool;
 554         int error;
 555 
 556         pair = nvlist_next_nvpair(holds, NULL);
 557         if (pair == NULL)
 558                 return (0);
 559 
 560         /*
 561          * The release may cause snapshots to be destroyed; make sure they
 562          * are not mounted.
 563          */
 564         if (tmpdp != NULL) {
 565                 /* Temporary holds are specified by dsobj string. */
 566                 ddura.ddura_holdfunc = dsl_dataset_hold_obj_string;
 567                 pool = spa_name(tmpdp->dp_spa);
 568 #ifdef _KERNEL
 569                 for (pair = nvlist_next_nvpair(holds, NULL); pair != NULL;
 570                     pair = nvlist_next_nvpair(holds, pair)) {
 571                         dsl_dataset_t *ds;
 572 
 573                         dsl_pool_config_enter(tmpdp, FTAG);
 574                         error = dsl_dataset_hold_obj_string(tmpdp,
 575                             nvpair_name(pair), FTAG, &ds);
 576                         if (error == 0) {
 577                                 char name[MAXNAMELEN];
 578                                 dsl_dataset_name(ds, name);
 579                                 dsl_pool_config_exit(tmpdp, FTAG);
 580                                 dsl_dataset_rele(ds, FTAG);
 581                                 (void) zfs_unmount_snap(name);
 582                         } else {
 583                                 dsl_pool_config_exit(tmpdp, FTAG);
 584                         }
 585                 }
 586 #endif
 587         } else {
 588                 /* Non-temporary holds are specified by name. */
 589                 ddura.ddura_holdfunc = dsl_dataset_hold;
 590                 pool = nvpair_name(pair);
 591 #ifdef _KERNEL
 592                 for (pair = nvlist_next_nvpair(holds, NULL); pair != NULL;
 593                     pair = nvlist_next_nvpair(holds, pair)) {
 594                         (void) zfs_unmount_snap(nvpair_name(pair));
 595                 }
 596 #endif
 597         }
 598 
 599         ddura.ddura_holds = holds;
 600         ddura.ddura_errlist = errlist;
 601         ddura.ddura_todelete = fnvlist_alloc();
 602         ddura.ddura_chkholds = fnvlist_alloc();
 603 
 604         error = dsl_sync_task(pool, dsl_dataset_user_release_check,
 605             dsl_dataset_user_release_sync, &ddura, 0, ZFS_SPACE_CHECK_NONE);
 606         fnvlist_free(ddura.ddura_todelete);
 607         fnvlist_free(ddura.ddura_chkholds);
 608 
 609         return (error);
 610 }
 611 
 612 /*
 613  * holds is nvl of snapname -> { holdname, ... }
 614  * errlist will be filled in with snapname -> error
 615  */
 616 int
 617 dsl_dataset_user_release(nvlist_t *holds, nvlist_t *errlist)
 618 {
 619         return (dsl_dataset_user_release_impl(holds, errlist, NULL));
 620 }
 621 
 622 /*
 623  * holds is nvl of snapdsobj -> { holdname, ... }
 624  */
 625 void
 626 dsl_dataset_user_release_tmp(struct dsl_pool *dp, nvlist_t *holds)
 627 {
 628         ASSERT(dp != NULL);
 629         (void) dsl_dataset_user_release_impl(holds, NULL, dp);
 630 }
 631 
 632 int
 633 dsl_dataset_get_holds(const char *dsname, nvlist_t *nvl)
 634 {
 635         dsl_pool_t *dp;
 636         dsl_dataset_t *ds;
 637         int err;
 638 
 639         err = dsl_pool_hold(dsname, FTAG, &dp);
 640         if (err != 0)
 641                 return (err);
 642         err = dsl_dataset_hold(dp, dsname, FTAG, &ds);
 643         if (err != 0) {
 644                 dsl_pool_rele(dp, FTAG);
 645                 return (err);
 646         }
 647 
 648         if (dsl_dataset_phys(ds)->ds_userrefs_obj != 0) {
 649                 zap_attribute_t *za;
 650                 zap_cursor_t zc;
 651 
 652                 za = kmem_alloc(sizeof (zap_attribute_t), KM_SLEEP);
 653                 for (zap_cursor_init(&zc, ds->ds_dir->dd_pool->dp_meta_objset,
 654                     dsl_dataset_phys(ds)->ds_userrefs_obj);
 655                     zap_cursor_retrieve(&zc, za) == 0;
 656                     zap_cursor_advance(&zc)) {
 657                         fnvlist_add_uint64(nvl, za->za_name,
 658                             za->za_first_integer);
 659                 }
 660                 zap_cursor_fini(&zc);
 661                 kmem_free(za, sizeof (zap_attribute_t));
 662         }
 663         dsl_dataset_rele(ds, FTAG);
 664         dsl_pool_rele(dp, FTAG);
 665         return (0);
 666 }