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