Home | History | Annotate | Line # | Download | only in zfs
      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, 2015 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[ZFS_MAX_DATASET_NAME_LEN];
    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[ZFS_MAX_DATASET_NAME_LEN];
    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 }
    667