13b2aab18SMatthew Ahrens /*
23b2aab18SMatthew Ahrens  * CDDL HEADER START
33b2aab18SMatthew Ahrens  *
43b2aab18SMatthew Ahrens  * The contents of this file are subject to the terms of the
53b2aab18SMatthew Ahrens  * Common Development and Distribution License (the "License").
63b2aab18SMatthew Ahrens  * You may not use this file except in compliance with the License.
73b2aab18SMatthew Ahrens  *
83b2aab18SMatthew Ahrens  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
93b2aab18SMatthew Ahrens  * or http://www.opensolaris.org/os/licensing.
103b2aab18SMatthew Ahrens  * See the License for the specific language governing permissions
113b2aab18SMatthew Ahrens  * and limitations under the License.
123b2aab18SMatthew Ahrens  *
133b2aab18SMatthew Ahrens  * When distributing Covered Code, include this CDDL HEADER in each
143b2aab18SMatthew Ahrens  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
153b2aab18SMatthew Ahrens  * If applicable, add the following below this CDDL HEADER, with the
163b2aab18SMatthew Ahrens  * fields enclosed by brackets "[]" replaced with your own identifying
173b2aab18SMatthew Ahrens  * information: Portions Copyright [yyyy] [name of copyright owner]
183b2aab18SMatthew Ahrens  *
193b2aab18SMatthew Ahrens  * CDDL HEADER END
203b2aab18SMatthew Ahrens  */
213b2aab18SMatthew Ahrens /*
223b2aab18SMatthew Ahrens  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
2386714001SSerapheim Dimitropoulos  * Copyright (c) 2012, 2017 by Delphix. All rights reserved.
24a7a845e4SSteven Hartland  * Copyright (c) 2013 Steven Hartland. All rights reserved.
253b2aab18SMatthew Ahrens  */
263b2aab18SMatthew Ahrens 
273b2aab18SMatthew Ahrens #include <sys/zfs_context.h>
283b2aab18SMatthew Ahrens #include <sys/dsl_userhold.h>
293b2aab18SMatthew Ahrens #include <sys/dsl_dataset.h>
303b2aab18SMatthew Ahrens #include <sys/dsl_destroy.h>
313b2aab18SMatthew Ahrens #include <sys/dsl_synctask.h>
323b2aab18SMatthew Ahrens #include <sys/dmu_tx.h>
333b2aab18SMatthew Ahrens #include <sys/zfs_onexit.h>
343b2aab18SMatthew Ahrens #include <sys/dsl_pool.h>
353b2aab18SMatthew Ahrens #include <sys/dsl_dir.h>
363b2aab18SMatthew Ahrens #include <sys/zfs_ioctl.h>
373b2aab18SMatthew Ahrens #include <sys/zap.h>
383b2aab18SMatthew Ahrens 
393b2aab18SMatthew Ahrens typedef struct dsl_dataset_user_hold_arg {
403b2aab18SMatthew Ahrens 	nvlist_t *dduha_holds;
41a7a845e4SSteven Hartland 	nvlist_t *dduha_chkholds;
423b2aab18SMatthew Ahrens 	nvlist_t *dduha_errlist;
433b2aab18SMatthew Ahrens 	minor_t dduha_minor;
443b2aab18SMatthew Ahrens } dsl_dataset_user_hold_arg_t;
453b2aab18SMatthew Ahrens 
463b2aab18SMatthew Ahrens /*
473b2aab18SMatthew Ahrens  * If you add new checks here, you may need to add additional checks to the
483b2aab18SMatthew Ahrens  * "temporary" case in snapshot_check() in dmu_objset.c.
493b2aab18SMatthew Ahrens  */
503b2aab18SMatthew Ahrens int
dsl_dataset_user_hold_check_one(dsl_dataset_t * ds,const char * htag,boolean_t temphold,dmu_tx_t * tx)513b2aab18SMatthew Ahrens dsl_dataset_user_hold_check_one(dsl_dataset_t *ds, const char *htag,
523b2aab18SMatthew Ahrens     boolean_t temphold, dmu_tx_t *tx)
533b2aab18SMatthew Ahrens {
543b2aab18SMatthew Ahrens 	dsl_pool_t *dp = dmu_tx_pool(tx);
553b2aab18SMatthew Ahrens 	objset_t *mos = dp->dp_meta_objset;
563b2aab18SMatthew Ahrens 	int error = 0;
573b2aab18SMatthew Ahrens 
58a7a845e4SSteven Hartland 	ASSERT(dsl_pool_config_held(dp));
59a7a845e4SSteven Hartland 
603b2aab18SMatthew Ahrens 	if (strlen(htag) > MAXNAMELEN)
61a7a845e4SSteven Hartland 		return (SET_ERROR(E2BIG));
623b2aab18SMatthew Ahrens 	/* Tempholds have a more restricted length */
633b2aab18SMatthew Ahrens 	if (temphold && strlen(htag) + MAX_TAG_PREFIX_LEN >= MAXNAMELEN)
64a7a845e4SSteven Hartland 		return (SET_ERROR(E2BIG));
653b2aab18SMatthew Ahrens 
663b2aab18SMatthew Ahrens 	/* tags must be unique (if ds already exists) */
67c1379625SJustin T. Gibbs 	if (ds != NULL && dsl_dataset_phys(ds)->ds_userrefs_obj != 0) {
68a7a845e4SSteven Hartland 		uint64_t value;
69a7a845e4SSteven Hartland 
70c1379625SJustin T. Gibbs 		error = zap_lookup(mos, dsl_dataset_phys(ds)->ds_userrefs_obj,
71a7a845e4SSteven Hartland 		    htag, 8, 1, &value);
72a7a845e4SSteven Hartland 		if (error == 0)
73a7a845e4SSteven Hartland 			error = SET_ERROR(EEXIST);
74a7a845e4SSteven Hartland 		else if (error == ENOENT)
75a7a845e4SSteven Hartland 			error = 0;
763b2aab18SMatthew Ahrens 	}
773b2aab18SMatthew Ahrens 
783b2aab18SMatthew Ahrens 	return (error);
793b2aab18SMatthew Ahrens }
803b2aab18SMatthew Ahrens 
813b2aab18SMatthew Ahrens static int
dsl_dataset_user_hold_check(void * arg,dmu_tx_t * tx)823b2aab18SMatthew Ahrens dsl_dataset_user_hold_check(void *arg, dmu_tx_t *tx)
833b2aab18SMatthew Ahrens {
843b2aab18SMatthew Ahrens 	dsl_dataset_user_hold_arg_t *dduha = arg;
853b2aab18SMatthew Ahrens 	dsl_pool_t *dp = dmu_tx_pool(tx);
86*6ccda740Sloli 	nvlist_t *tmp_holds;
873b2aab18SMatthew Ahrens 
883b2aab18SMatthew Ahrens 	if (spa_version(dp->dp_spa) < SPA_VERSION_USERREFS)
89be6fd75aSMatthew Ahrens 		return (SET_ERROR(ENOTSUP));
903b2aab18SMatthew Ahrens 
91a7a845e4SSteven Hartland 	if (!dmu_tx_is_syncing(tx))
92a7a845e4SSteven Hartland 		return (0);
93a7a845e4SSteven Hartland 
94*6ccda740Sloli 	/*
95*6ccda740Sloli 	 * Ensure the list has no duplicates by copying name/values from
96*6ccda740Sloli 	 * non-unique dduha_holds to unique tmp_holds, and comparing counts.
97*6ccda740Sloli 	 */
98*6ccda740Sloli 	tmp_holds = fnvlist_alloc();
99*6ccda740Sloli 	for (nvpair_t *pair = nvlist_next_nvpair(dduha->dduha_holds, NULL);
100*6ccda740Sloli 	    pair != NULL; pair = nvlist_next_nvpair(dduha->dduha_holds, pair)) {
101*6ccda740Sloli 		size_t len = strlen(nvpair_name(pair)) +
102*6ccda740Sloli 		    strlen(fnvpair_value_string(pair));
103*6ccda740Sloli 		char *nameval = kmem_zalloc(len + 2, KM_SLEEP);
104*6ccda740Sloli 		(void) strcpy(nameval, nvpair_name(pair));
105*6ccda740Sloli 		(void) strcat(nameval, "@");
106*6ccda740Sloli 		(void) strcat(nameval, fnvpair_value_string(pair));
107*6ccda740Sloli 		fnvlist_add_string(tmp_holds, nameval, "");
108*6ccda740Sloli 		kmem_free(nameval, len + 2);
109*6ccda740Sloli 	}
110*6ccda740Sloli 	size_t tmp_count = fnvlist_num_pairs(tmp_holds);
111*6ccda740Sloli 	fnvlist_free(tmp_holds);
112*6ccda740Sloli 	if (tmp_count != fnvlist_num_pairs(dduha->dduha_holds))
113*6ccda740Sloli 		return (SET_ERROR(EEXIST));
114a7a845e4SSteven Hartland 	for (nvpair_t *pair = nvlist_next_nvpair(dduha->dduha_holds, NULL);
115a7a845e4SSteven Hartland 	    pair != NULL; pair = nvlist_next_nvpair(dduha->dduha_holds, pair)) {
1163b2aab18SMatthew Ahrens 		dsl_dataset_t *ds;
117a7a845e4SSteven Hartland 		int error = 0;
118a7a845e4SSteven Hartland 		char *htag, *name;
1193b2aab18SMatthew Ahrens 
1203b2aab18SMatthew Ahrens 		/* must be a snapshot */
121a7a845e4SSteven Hartland 		name = nvpair_name(pair);
122a7a845e4SSteven Hartland 		if (strchr(name, '@') == NULL)
123be6fd75aSMatthew Ahrens 			error = SET_ERROR(EINVAL);
1243b2aab18SMatthew Ahrens 
1253b2aab18SMatthew Ahrens 		if (error == 0)
1263b2aab18SMatthew Ahrens 			error = nvpair_value_string(pair, &htag);
127a7a845e4SSteven Hartland 
128a7a845e4SSteven Hartland 		if (error == 0)
129a7a845e4SSteven Hartland 			error = dsl_dataset_hold(dp, name, FTAG, &ds);
130a7a845e4SSteven Hartland 
1313b2aab18SMatthew Ahrens 		if (error == 0) {
1323b2aab18SMatthew Ahrens 			error = dsl_dataset_user_hold_check_one(ds, htag,
1333b2aab18SMatthew Ahrens 			    dduha->dduha_minor != 0, tx);
1343b2aab18SMatthew Ahrens 			dsl_dataset_rele(ds, FTAG);
1353b2aab18SMatthew Ahrens 		}
1363b2aab18SMatthew Ahrens 
137a7a845e4SSteven Hartland 		if (error == 0) {
138a7a845e4SSteven Hartland 			fnvlist_add_string(dduha->dduha_chkholds, name, htag);
139a7a845e4SSteven Hartland 		} else {
140a7a845e4SSteven Hartland 			/*
141a7a845e4SSteven Hartland 			 * We register ENOENT errors so they can be correctly
142a7a845e4SSteven Hartland 			 * reported if needed, such as when all holds fail.
143a7a845e4SSteven Hartland 			 */
144a7a845e4SSteven Hartland 			fnvlist_add_int32(dduha->dduha_errlist, name, error);
145a7a845e4SSteven Hartland 			if (error != ENOENT)
146a7a845e4SSteven Hartland 				return (error);
1473b2aab18SMatthew Ahrens 		}
1483b2aab18SMatthew Ahrens 	}
149a7a845e4SSteven Hartland 
150a7a845e4SSteven Hartland 	return (0);
1513b2aab18SMatthew Ahrens }
1523b2aab18SMatthew Ahrens 
153a7a845e4SSteven Hartland 
154a7a845e4SSteven Hartland static void
dsl_dataset_user_hold_sync_one_impl(nvlist_t * tmpholds,dsl_dataset_t * ds,const char * htag,minor_t minor,uint64_t now,dmu_tx_t * tx)155a7a845e4SSteven Hartland dsl_dataset_user_hold_sync_one_impl(nvlist_t *tmpholds, dsl_dataset_t *ds,
156a7a845e4SSteven Hartland     const char *htag, minor_t minor, uint64_t now, dmu_tx_t *tx)
1573b2aab18SMatthew Ahrens {
1583b2aab18SMatthew Ahrens 	dsl_pool_t *dp = ds->ds_dir->dd_pool;
1593b2aab18SMatthew Ahrens 	objset_t *mos = dp->dp_meta_objset;
1603b2aab18SMatthew Ahrens 	uint64_t zapobj;
1613b2aab18SMatthew Ahrens 
162a7a845e4SSteven Hartland 	ASSERT(RRW_WRITE_HELD(&dp->dp_config_rwlock));
163a7a845e4SSteven Hartland 
164c1379625SJustin T. Gibbs 	if (dsl_dataset_phys(ds)->ds_userrefs_obj == 0) {
1653b2aab18SMatthew Ahrens 		/*
1663b2aab18SMatthew Ahrens 		 * This is the first user hold for this dataset.  Create
1673b2aab18SMatthew Ahrens 		 * the userrefs zap object.
1683b2aab18SMatthew Ahrens 		 */
1693b2aab18SMatthew Ahrens 		dmu_buf_will_dirty(ds->ds_dbuf, tx);
170c1379625SJustin T. Gibbs 		zapobj = dsl_dataset_phys(ds)->ds_userrefs_obj =
1713b2aab18SMatthew Ahrens 		    zap_create(mos, DMU_OT_USERREFS, DMU_OT_NONE, 0, tx);
1723b2aab18SMatthew Ahrens 	} else {
173c1379625SJustin T. Gibbs 		zapobj = dsl_dataset_phys(ds)->ds_userrefs_obj;
1743b2aab18SMatthew Ahrens 	}
1753b2aab18SMatthew Ahrens 	ds->ds_userrefs++;
1763b2aab18SMatthew Ahrens 
1773b2aab18SMatthew Ahrens 	VERIFY0(zap_add(mos, zapobj, htag, 8, 1, &now, tx));
1783b2aab18SMatthew Ahrens 
1793b2aab18SMatthew Ahrens 	if (minor != 0) {
180a7a845e4SSteven Hartland 		char name[MAXNAMELEN];
181a7a845e4SSteven Hartland 		nvlist_t *tags;
182a7a845e4SSteven Hartland 
1833b2aab18SMatthew Ahrens 		VERIFY0(dsl_pool_user_hold(dp, ds->ds_object,
1843b2aab18SMatthew Ahrens 		    htag, now, tx));
185a7a845e4SSteven Hartland 		(void) snprintf(name, sizeof (name), "%llx",
186a7a845e4SSteven Hartland 		    (u_longlong_t)ds->ds_object);
187a7a845e4SSteven Hartland 
188a7a845e4SSteven Hartland 		if (nvlist_lookup_nvlist(tmpholds, name, &tags) != 0) {
189a7a845e4SSteven Hartland 			tags = fnvlist_alloc();
190a7a845e4SSteven Hartland 			fnvlist_add_boolean(tags, htag);
191a7a845e4SSteven Hartland 			fnvlist_add_nvlist(tmpholds, name, tags);
192a7a845e4SSteven Hartland 			fnvlist_free(tags);
193a7a845e4SSteven Hartland 		} else {
194a7a845e4SSteven Hartland 			fnvlist_add_boolean(tags, htag);
195a7a845e4SSteven Hartland 		}
1963b2aab18SMatthew Ahrens 	}
1973b2aab18SMatthew Ahrens 
1983b2aab18SMatthew Ahrens 	spa_history_log_internal_ds(ds, "hold", tx,
1993b2aab18SMatthew Ahrens 	    "tag=%s temp=%d refs=%llu",
2003b2aab18SMatthew Ahrens 	    htag, minor != 0, ds->ds_userrefs);
2013b2aab18SMatthew Ahrens }
2023b2aab18SMatthew Ahrens 
203a7a845e4SSteven Hartland typedef struct zfs_hold_cleanup_arg {
2049adfa60dSMatthew Ahrens 	char zhca_spaname[ZFS_MAX_DATASET_NAME_LEN];
205a7a845e4SSteven Hartland 	uint64_t zhca_spa_load_guid;
206a7a845e4SSteven Hartland 	nvlist_t *zhca_holds;
207a7a845e4SSteven Hartland } zfs_hold_cleanup_arg_t;
208a7a845e4SSteven Hartland 
209a7a845e4SSteven Hartland static void
dsl_dataset_user_release_onexit(void * arg)210a7a845e4SSteven Hartland dsl_dataset_user_release_onexit(void *arg)
211a7a845e4SSteven Hartland {
212a7a845e4SSteven Hartland 	zfs_hold_cleanup_arg_t *ca = arg;
213a7a845e4SSteven Hartland 	spa_t *spa;
214a7a845e4SSteven Hartland 	int error;
215a7a845e4SSteven Hartland 
216a7a845e4SSteven Hartland 	error = spa_open(ca->zhca_spaname, &spa, FTAG);
217a7a845e4SSteven Hartland 	if (error != 0) {
218a7a845e4SSteven Hartland 		zfs_dbgmsg("couldn't release holds on pool=%s "
219a7a845e4SSteven Hartland 		    "because pool is no longer loaded",
220a7a845e4SSteven Hartland 		    ca->zhca_spaname);
221a7a845e4SSteven Hartland 		return;
222a7a845e4SSteven Hartland 	}
223a7a845e4SSteven Hartland 	if (spa_load_guid(spa) != ca->zhca_spa_load_guid) {
224a7a845e4SSteven Hartland 		zfs_dbgmsg("couldn't release holds on pool=%s "
225a7a845e4SSteven Hartland 		    "because pool is no longer loaded (guid doesn't match)",
226a7a845e4SSteven Hartland 		    ca->zhca_spaname);
227a7a845e4SSteven Hartland 		spa_close(spa, FTAG);
228a7a845e4SSteven Hartland 		return;
229a7a845e4SSteven Hartland 	}
230a7a845e4SSteven Hartland 
231a7a845e4SSteven Hartland 	(void) dsl_dataset_user_release_tmp(spa_get_dsl(spa), ca->zhca_holds);
232a7a845e4SSteven Hartland 	fnvlist_free(ca->zhca_holds);
233a7a845e4SSteven Hartland 	kmem_free(ca, sizeof (zfs_hold_cleanup_arg_t));
234a7a845e4SSteven Hartland 	spa_close(spa, FTAG);
235a7a845e4SSteven Hartland }
236a7a845e4SSteven Hartland 
237a7a845e4SSteven Hartland static void
dsl_onexit_hold_cleanup(spa_t * spa,nvlist_t * holds,minor_t minor)238a7a845e4SSteven Hartland dsl_onexit_hold_cleanup(spa_t *spa, nvlist_t *holds, minor_t minor)
239a7a845e4SSteven Hartland {
240a7a845e4SSteven Hartland 	zfs_hold_cleanup_arg_t *ca;
241a7a845e4SSteven Hartland 
242a7a845e4SSteven Hartland 	if (minor == 0 || nvlist_empty(holds)) {
243a7a845e4SSteven Hartland 		fnvlist_free(holds);
244a7a845e4SSteven Hartland 		return;
245a7a845e4SSteven Hartland 	}
246a7a845e4SSteven Hartland 
247a7a845e4SSteven Hartland 	ASSERT(spa != NULL);
248a7a845e4SSteven Hartland 	ca = kmem_alloc(sizeof (*ca), KM_SLEEP);
249a7a845e4SSteven Hartland 
250a7a845e4SSteven Hartland 	(void) strlcpy(ca->zhca_spaname, spa_name(spa),
251a7a845e4SSteven Hartland 	    sizeof (ca->zhca_spaname));
252a7a845e4SSteven Hartland 	ca->zhca_spa_load_guid = spa_load_guid(spa);
253a7a845e4SSteven Hartland 	ca->zhca_holds = holds;
254a7a845e4SSteven Hartland 	VERIFY0(zfs_onexit_add_cb(minor,
255a7a845e4SSteven Hartland 	    dsl_dataset_user_release_onexit, ca, NULL));
256a7a845e4SSteven Hartland }
257a7a845e4SSteven Hartland 
258a7a845e4SSteven Hartland void
dsl_dataset_user_hold_sync_one(dsl_dataset_t * ds,const char * htag,minor_t minor,uint64_t now,dmu_tx_t * tx)259a7a845e4SSteven Hartland dsl_dataset_user_hold_sync_one(dsl_dataset_t *ds, const char *htag,
260a7a845e4SSteven Hartland     minor_t minor, uint64_t now, dmu_tx_t *tx)
261a7a845e4SSteven Hartland {
262a7a845e4SSteven Hartland 	nvlist_t *tmpholds;
263a7a845e4SSteven Hartland 
264a7a845e4SSteven Hartland 	if (minor != 0)
265a7a845e4SSteven Hartland 		tmpholds = fnvlist_alloc();
266a7a845e4SSteven Hartland 	else
267a7a845e4SSteven Hartland 		tmpholds = NULL;
268a7a845e4SSteven Hartland 	dsl_dataset_user_hold_sync_one_impl(tmpholds, ds, htag, minor, now, tx);
269a7a845e4SSteven Hartland 	dsl_onexit_hold_cleanup(dsl_dataset_get_spa(ds), tmpholds, minor);
270a7a845e4SSteven Hartland }
271a7a845e4SSteven Hartland 
2723b2aab18SMatthew Ahrens static void
dsl_dataset_user_hold_sync(void * arg,dmu_tx_t * tx)2733b2aab18SMatthew Ahrens dsl_dataset_user_hold_sync(void *arg, dmu_tx_t *tx)
2743b2aab18SMatthew Ahrens {
2753b2aab18SMatthew Ahrens 	dsl_dataset_user_hold_arg_t *dduha = arg;
2763b2aab18SMatthew Ahrens 	dsl_pool_t *dp = dmu_tx_pool(tx);
277a7a845e4SSteven Hartland 	nvlist_t *tmpholds;
2783b2aab18SMatthew Ahrens 	uint64_t now = gethrestime_sec();
2793b2aab18SMatthew Ahrens 
280a7a845e4SSteven Hartland 	if (dduha->dduha_minor != 0)
281a7a845e4SSteven Hartland 		tmpholds = fnvlist_alloc();
282a7a845e4SSteven Hartland 	else
283a7a845e4SSteven Hartland 		tmpholds = NULL;
284a7a845e4SSteven Hartland 	for (nvpair_t *pair = nvlist_next_nvpair(dduha->dduha_chkholds, NULL);
285a7a845e4SSteven Hartland 	    pair != NULL;
286a7a845e4SSteven Hartland 	    pair = nvlist_next_nvpair(dduha->dduha_chkholds, pair)) {
2873b2aab18SMatthew Ahrens 		dsl_dataset_t *ds;
288a7a845e4SSteven Hartland 
2893b2aab18SMatthew Ahrens 		VERIFY0(dsl_dataset_hold(dp, nvpair_name(pair), FTAG, &ds));
290a7a845e4SSteven Hartland 		dsl_dataset_user_hold_sync_one_impl(tmpholds, ds,
291a7a845e4SSteven Hartland 		    fnvpair_value_string(pair), dduha->dduha_minor, now, tx);
2923b2aab18SMatthew Ahrens 		dsl_dataset_rele(ds, FTAG);
2933b2aab18SMatthew Ahrens 	}
294a7a845e4SSteven Hartland 	dsl_onexit_hold_cleanup(dp->dp_spa, tmpholds, dduha->dduha_minor);
2953b2aab18SMatthew Ahrens }
2963b2aab18SMatthew Ahrens 
2973b2aab18SMatthew Ahrens /*
298a7a845e4SSteven Hartland  * The full semantics of this function are described in the comment above
299a7a845e4SSteven Hartland  * lzc_hold().
300a7a845e4SSteven Hartland  *
301a7a845e4SSteven Hartland  * To summarize:
3023b2aab18SMatthew Ahrens  * holds is nvl of snapname -> holdname
3033b2aab18SMatthew Ahrens  * errlist will be filled in with snapname -> error
3043b2aab18SMatthew Ahrens  *
305a7a845e4SSteven Hartland  * The snaphosts must all be in the same pool.
306a7a845e4SSteven Hartland  *
307a7a845e4SSteven Hartland  * Holds for snapshots that don't exist will be skipped.
308a7a845e4SSteven Hartland  *
309a7a845e4SSteven Hartland  * If none of the snapshots for requested holds exist then ENOENT will be
310a7a845e4SSteven Hartland  * returned.
311a7a845e4SSteven Hartland  *
312a7a845e4SSteven Hartland  * If cleanup_minor is not 0, the holds will be temporary, which will be cleaned
313a7a845e4SSteven Hartland  * up when the process exits.
314a7a845e4SSteven Hartland  *
315a7a845e4SSteven Hartland  * On success all the holds, for snapshots that existed, will be created and 0
316a7a845e4SSteven Hartland  * will be returned.
317a7a845e4SSteven Hartland  *
318a7a845e4SSteven Hartland  * On failure no holds will be created, the errlist will be filled in,
319a7a845e4SSteven Hartland  * and an errno will returned.
320a7a845e4SSteven Hartland  *
321a7a845e4SSteven Hartland  * In all cases the errlist will contain entries for holds where the snapshot
322a7a845e4SSteven Hartland  * didn't exist.
3233b2aab18SMatthew Ahrens  */
3243b2aab18SMatthew Ahrens int
dsl_dataset_user_hold(nvlist_t * holds,minor_t cleanup_minor,nvlist_t * errlist)3253b2aab18SMatthew Ahrens dsl_dataset_user_hold(nvlist_t *holds, minor_t cleanup_minor, nvlist_t *errlist)
3263b2aab18SMatthew Ahrens {
3273b2aab18SMatthew Ahrens 	dsl_dataset_user_hold_arg_t dduha;
3283b2aab18SMatthew Ahrens 	nvpair_t *pair;
329a7a845e4SSteven Hartland 	int ret;
3303b2aab18SMatthew Ahrens 
3313b2aab18SMatthew Ahrens 	pair = nvlist_next_nvpair(holds, NULL);
3323b2aab18SMatthew Ahrens 	if (pair == NULL)
3333b2aab18SMatthew Ahrens 		return (0);
3343b2aab18SMatthew Ahrens 
3353b2aab18SMatthew Ahrens 	dduha.dduha_holds = holds;
336*6ccda740Sloli 	/* chkholds can have non-unique name */
337*6ccda740Sloli 	VERIFY(0 == nvlist_alloc(&dduha.dduha_chkholds, 0, KM_SLEEP));
3383b2aab18SMatthew Ahrens 	dduha.dduha_errlist = errlist;
3393b2aab18SMatthew Ahrens 	dduha.dduha_minor = cleanup_minor;
3403b2aab18SMatthew Ahrens 
341a7a845e4SSteven Hartland 	ret = dsl_sync_task(nvpair_name(pair), dsl_dataset_user_hold_check,
3427d46dc6cSMatthew Ahrens 	    dsl_dataset_user_hold_sync, &dduha,
3437d46dc6cSMatthew Ahrens 	    fnvlist_num_pairs(holds), ZFS_SPACE_CHECK_RESERVED);
344a7a845e4SSteven Hartland 	fnvlist_free(dduha.dduha_chkholds);
345a7a845e4SSteven Hartland 
346a7a845e4SSteven Hartland 	return (ret);
3473b2aab18SMatthew Ahrens }
3483b2aab18SMatthew Ahrens 
349a7a845e4SSteven Hartland typedef int (dsl_holdfunc_t)(dsl_pool_t *dp, const char *name, void *tag,
350a7a845e4SSteven Hartland     dsl_dataset_t **dsp);
351a7a845e4SSteven Hartland 
3523b2aab18SMatthew Ahrens typedef struct dsl_dataset_user_release_arg {
353a7a845e4SSteven Hartland 	dsl_holdfunc_t *ddura_holdfunc;
3543b2aab18SMatthew Ahrens 	nvlist_t *ddura_holds;
3553b2aab18SMatthew Ahrens 	nvlist_t *ddura_todelete;
3563b2aab18SMatthew Ahrens 	nvlist_t *ddura_errlist;
357a7a845e4SSteven Hartland 	nvlist_t *ddura_chkholds;
3583b2aab18SMatthew Ahrens } dsl_dataset_user_release_arg_t;
3593b2aab18SMatthew Ahrens 
360a7a845e4SSteven Hartland /* Place a dataset hold on the snapshot identified by passed dsobj string */
3613b2aab18SMatthew Ahrens static int
dsl_dataset_hold_obj_string(dsl_pool_t * dp,const char * dsobj,void * tag,dsl_dataset_t ** dsp)362a7a845e4SSteven Hartland dsl_dataset_hold_obj_string(dsl_pool_t *dp, const char *dsobj, void *tag,
363a7a845e4SSteven Hartland     dsl_dataset_t **dsp)
3643b2aab18SMatthew Ahrens {
3654585130bSYuri Pankov 	return (dsl_dataset_hold_obj(dp, zfs_strtonum(dsobj, NULL), tag, dsp));
366a7a845e4SSteven Hartland }
3673b2aab18SMatthew Ahrens 
368a7a845e4SSteven Hartland static int
dsl_dataset_user_release_check_one(dsl_dataset_user_release_arg_t * ddura,dsl_dataset_t * ds,nvlist_t * holds,const char * snapname)369a7a845e4SSteven Hartland dsl_dataset_user_release_check_one(dsl_dataset_user_release_arg_t *ddura,
370a7a845e4SSteven Hartland     dsl_dataset_t *ds, nvlist_t *holds, const char *snapname)
371a7a845e4SSteven Hartland {
372a7a845e4SSteven Hartland 	uint64_t zapobj;
373a7a845e4SSteven Hartland 	nvlist_t *holds_found;
374a7a845e4SSteven Hartland 	objset_t *mos;
375a7a845e4SSteven Hartland 	int numholds;
3763b2aab18SMatthew Ahrens 
377bc9014e6SJustin Gibbs 	if (!ds->ds_is_snapshot)
378be6fd75aSMatthew Ahrens 		return (SET_ERROR(EINVAL));
3793b2aab18SMatthew Ahrens 
380a7a845e4SSteven Hartland 	if (nvlist_empty(holds))
381a7a845e4SSteven Hartland 		return (0);
382a7a845e4SSteven Hartland 
383a7a845e4SSteven Hartland 	numholds = 0;
384a7a845e4SSteven Hartland 	mos = ds->ds_dir->dd_pool->dp_meta_objset;
385c1379625SJustin T. Gibbs 	zapobj = dsl_dataset_phys(ds)->ds_userrefs_obj;
386a7a845e4SSteven Hartland 	holds_found = fnvlist_alloc();
3873b2aab18SMatthew Ahrens 
388a7a845e4SSteven Hartland 	for (nvpair_t *pair = nvlist_next_nvpair(holds, NULL); pair != NULL;
3893b2aab18SMatthew Ahrens 	    pair = nvlist_next_nvpair(holds, pair)) {
3903b2aab18SMatthew Ahrens 		uint64_t tmp;
391a7a845e4SSteven Hartland 		int error;
392a7a845e4SSteven Hartland 		const char *holdname = nvpair_name(pair);
393a7a845e4SSteven Hartland 
394a7a845e4SSteven Hartland 		if (zapobj != 0)
395a7a845e4SSteven Hartland 			error = zap_lookup(mos, zapobj, holdname, 8, 1, &tmp);
396a7a845e4SSteven Hartland 		else
397a7a845e4SSteven Hartland 			error = SET_ERROR(ENOENT);
398a7a845e4SSteven Hartland 
399a7a845e4SSteven Hartland 		/*
400a7a845e4SSteven Hartland 		 * Non-existent holds are put on the errlist, but don't
401a7a845e4SSteven Hartland 		 * cause an overall failure.
402a7a845e4SSteven Hartland 		 */
403a7a845e4SSteven Hartland 		if (error == ENOENT) {
404a7a845e4SSteven Hartland 			if (ddura->ddura_errlist != NULL) {
405a7a845e4SSteven Hartland 				char *errtag = kmem_asprintf("%s#%s",
406a7a845e4SSteven Hartland 				    snapname, holdname);
407a7a845e4SSteven Hartland 				fnvlist_add_int32(ddura->ddura_errlist, errtag,
408a7a845e4SSteven Hartland 				    ENOENT);
409a7a845e4SSteven Hartland 				strfree(errtag);
410a7a845e4SSteven Hartland 			}
411a7a845e4SSteven Hartland 			continue;
412a7a845e4SSteven Hartland 		}
413a7a845e4SSteven Hartland 
414a7a845e4SSteven Hartland 		if (error != 0) {
415a7a845e4SSteven Hartland 			fnvlist_free(holds_found);
4163b2aab18SMatthew Ahrens 			return (error);
417a7a845e4SSteven Hartland 		}
418a7a845e4SSteven Hartland 
419a7a845e4SSteven Hartland 		fnvlist_add_boolean(holds_found, holdname);
4203b2aab18SMatthew Ahrens 		numholds++;
4213b2aab18SMatthew Ahrens 	}
4223b2aab18SMatthew Ahrens 
423c1379625SJustin T. Gibbs 	if (DS_IS_DEFER_DESTROY(ds) &&
424c1379625SJustin T. Gibbs 	    dsl_dataset_phys(ds)->ds_num_children == 1 &&
4253b2aab18SMatthew Ahrens 	    ds->ds_userrefs == numholds) {
4263b2aab18SMatthew Ahrens 		/* we need to destroy the snapshot as well */
427a7a845e4SSteven Hartland 		if (dsl_dataset_long_held(ds)) {
428a7a845e4SSteven Hartland 			fnvlist_free(holds_found);
429be6fd75aSMatthew Ahrens 			return (SET_ERROR(EBUSY));
430a7a845e4SSteven Hartland 		}
431a7a845e4SSteven Hartland 		fnvlist_add_boolean(ddura->ddura_todelete, snapname);
432a7a845e4SSteven Hartland 	}
433a7a845e4SSteven Hartland 
434a7a845e4SSteven Hartland 	if (numholds != 0) {
435a7a845e4SSteven Hartland 		fnvlist_add_nvlist(ddura->ddura_chkholds, snapname,
436a7a845e4SSteven Hartland 		    holds_found);
4373b2aab18SMatthew Ahrens 	}
438a7a845e4SSteven Hartland 	fnvlist_free(holds_found);
439a7a845e4SSteven Hartland 
4403b2aab18SMatthew Ahrens 	return (0);
4413b2aab18SMatthew Ahrens }
4423b2aab18SMatthew Ahrens 
4433b2aab18SMatthew Ahrens static int
dsl_dataset_user_release_check(void * arg,dmu_tx_t * tx)4443b2aab18SMatthew Ahrens dsl_dataset_user_release_check(void *arg, dmu_tx_t *tx)
4453b2aab18SMatthew Ahrens {
446a7a845e4SSteven Hartland 	dsl_dataset_user_release_arg_t *ddura;
447a7a845e4SSteven Hartland 	dsl_holdfunc_t *holdfunc;
448a7a845e4SSteven Hartland 	dsl_pool_t *dp;
4493b2aab18SMatthew Ahrens 
4503b2aab18SMatthew Ahrens 	if (!dmu_tx_is_syncing(tx))
4513b2aab18SMatthew Ahrens 		return (0);
4523b2aab18SMatthew Ahrens 
453a7a845e4SSteven Hartland 	dp = dmu_tx_pool(tx);
454a7a845e4SSteven Hartland 
455a7a845e4SSteven Hartland 	ASSERT(RRW_WRITE_HELD(&dp->dp_config_rwlock));
456a7a845e4SSteven Hartland 
457a7a845e4SSteven Hartland 	ddura = arg;
458a7a845e4SSteven Hartland 	holdfunc = ddura->ddura_holdfunc;
459a7a845e4SSteven Hartland 
460a7a845e4SSteven Hartland 	for (nvpair_t *pair = nvlist_next_nvpair(ddura->ddura_holds, NULL);
461a7a845e4SSteven Hartland 	    pair != NULL; pair = nvlist_next_nvpair(ddura->ddura_holds, pair)) {
4623b2aab18SMatthew Ahrens 		int error;
4633b2aab18SMatthew Ahrens 		dsl_dataset_t *ds;
4643b2aab18SMatthew Ahrens 		nvlist_t *holds;
465a7a845e4SSteven Hartland 		const char *snapname = nvpair_name(pair);
4663b2aab18SMatthew Ahrens 
4673b2aab18SMatthew Ahrens 		error = nvpair_value_nvlist(pair, &holds);
4683b2aab18SMatthew Ahrens 		if (error != 0)
469a7a845e4SSteven Hartland 			error = (SET_ERROR(EINVAL));
470a7a845e4SSteven Hartland 		else
471a7a845e4SSteven Hartland 			error = holdfunc(dp, snapname, FTAG, &ds);
4723b2aab18SMatthew Ahrens 		if (error == 0) {
473a7a845e4SSteven Hartland 			error = dsl_dataset_user_release_check_one(ddura, ds,
474a7a845e4SSteven Hartland 			    holds, snapname);
4753b2aab18SMatthew Ahrens 			dsl_dataset_rele(ds, FTAG);
4763b2aab18SMatthew Ahrens 		}
4773b2aab18SMatthew Ahrens 		if (error != 0) {
4783b2aab18SMatthew Ahrens 			if (ddura->ddura_errlist != NULL) {
4793b2aab18SMatthew Ahrens 				fnvlist_add_int32(ddura->ddura_errlist,
480a7a845e4SSteven Hartland 				    snapname, error);
4813b2aab18SMatthew Ahrens 			}
482a7a845e4SSteven Hartland 			/*
483a7a845e4SSteven Hartland 			 * Non-existent snapshots are put on the errlist,
484a7a845e4SSteven Hartland 			 * but don't cause an overall failure.
485a7a845e4SSteven Hartland 			 */
486a7a845e4SSteven Hartland 			if (error != ENOENT)
487a7a845e4SSteven Hartland 				return (error);
4883b2aab18SMatthew Ahrens 		}
4893b2aab18SMatthew Ahrens 	}
490a7a845e4SSteven Hartland 
491a7a845e4SSteven Hartland 	return (0);
4923b2aab18SMatthew Ahrens }
4933b2aab18SMatthew Ahrens 
4943b2aab18SMatthew Ahrens static void
dsl_dataset_user_release_sync_one(dsl_dataset_t * ds,nvlist_t * holds,dmu_tx_t * tx)4953b2aab18SMatthew Ahrens dsl_dataset_user_release_sync_one(dsl_dataset_t *ds, nvlist_t *holds,
4963b2aab18SMatthew Ahrens     dmu_tx_t *tx)
4973b2aab18SMatthew Ahrens {
4983b2aab18SMatthew Ahrens 	dsl_pool_t *dp = ds->ds_dir->dd_pool;
4993b2aab18SMatthew Ahrens 	objset_t *mos = dp->dp_meta_objset;
5003b2aab18SMatthew Ahrens 
501a7a845e4SSteven Hartland 	for (nvpair_t *pair = nvlist_next_nvpair(holds, NULL); pair != NULL;
5023b2aab18SMatthew Ahrens 	    pair = nvlist_next_nvpair(holds, pair)) {
503a7a845e4SSteven Hartland 		int error;
504a7a845e4SSteven Hartland 		const char *holdname = nvpair_name(pair);
505a7a845e4SSteven Hartland 
506a7a845e4SSteven Hartland 		/* Remove temporary hold if one exists. */
507a7a845e4SSteven Hartland 		error = dsl_pool_user_release(dp, ds->ds_object, holdname, tx);
5083b2aab18SMatthew Ahrens 		VERIFY(error == 0 || error == ENOENT);
509a7a845e4SSteven Hartland 
510c1379625SJustin T. Gibbs 		VERIFY0(zap_remove(mos, dsl_dataset_phys(ds)->ds_userrefs_obj,
511c1379625SJustin T. Gibbs 		    holdname, tx));
512a7a845e4SSteven Hartland 		ds->ds_userrefs--;
5133b2aab18SMatthew Ahrens 
5143b2aab18SMatthew Ahrens 		spa_history_log_internal_ds(ds, "release", tx,
515a7a845e4SSteven Hartland 		    "tag=%s refs=%lld", holdname, (longlong_t)ds->ds_userrefs);
5163b2aab18SMatthew Ahrens 	}
5173b2aab18SMatthew Ahrens }
5183b2aab18SMatthew Ahrens 
5193b2aab18SMatthew Ahrens static void
dsl_dataset_user_release_sync(void * arg,dmu_tx_t * tx)5203b2aab18SMatthew Ahrens dsl_dataset_user_release_sync(void *arg, dmu_tx_t *tx)
5213b2aab18SMatthew Ahrens {
5223b2aab18SMatthew Ahrens 	dsl_dataset_user_release_arg_t *ddura = arg;
523a7a845e4SSteven Hartland 	dsl_holdfunc_t *holdfunc = ddura->ddura_holdfunc;
5243b2aab18SMatthew Ahrens 	dsl_pool_t *dp = dmu_tx_pool(tx);
5253b2aab18SMatthew Ahrens 
526a7a845e4SSteven Hartland 	ASSERT(RRW_WRITE_HELD(&dp->dp_config_rwlock));
527a7a845e4SSteven Hartland 
528a7a845e4SSteven Hartland 	for (nvpair_t *pair = nvlist_next_nvpair(ddura->ddura_chkholds, NULL);
529a7a845e4SSteven Hartland 	    pair != NULL; pair = nvlist_next_nvpair(ddura->ddura_chkholds,
530a7a845e4SSteven Hartland 	    pair)) {
5313b2aab18SMatthew Ahrens 		dsl_dataset_t *ds;
532a7a845e4SSteven Hartland 		const char *name = nvpair_name(pair);
533a7a845e4SSteven Hartland 
534a7a845e4SSteven Hartland 		VERIFY0(holdfunc(dp, name, FTAG, &ds));
5353b2aab18SMatthew Ahrens 
5363b2aab18SMatthew Ahrens 		dsl_dataset_user_release_sync_one(ds,
5373b2aab18SMatthew Ahrens 		    fnvpair_value_nvlist(pair), tx);
538a7a845e4SSteven Hartland 		if (nvlist_exists(ddura->ddura_todelete, name)) {
5393b2aab18SMatthew Ahrens 			ASSERT(ds->ds_userrefs == 0 &&
540c1379625SJustin T. Gibbs 			    dsl_dataset_phys(ds)->ds_num_children == 1 &&
5413b2aab18SMatthew Ahrens 			    DS_IS_DEFER_DESTROY(ds));
5423b2aab18SMatthew Ahrens 			dsl_destroy_snapshot_sync_impl(ds, B_FALSE, tx);
5433b2aab18SMatthew Ahrens 		}
5443b2aab18SMatthew Ahrens 		dsl_dataset_rele(ds, FTAG);
5453b2aab18SMatthew Ahrens 	}
5463b2aab18SMatthew Ahrens }
5473b2aab18SMatthew Ahrens 
5483b2aab18SMatthew Ahrens /*
549a7a845e4SSteven Hartland  * The full semantics of this function are described in the comment above
550a7a845e4SSteven Hartland  * lzc_release().
551a7a845e4SSteven Hartland  *
552a7a845e4SSteven Hartland  * To summarize:
553a7a845e4SSteven Hartland  * Releases holds specified in the nvl holds.
554a7a845e4SSteven Hartland  *
5553b2aab18SMatthew Ahrens  * holds is nvl of snapname -> { holdname, ... }
5563b2aab18SMatthew Ahrens  * errlist will be filled in with snapname -> error
5573b2aab18SMatthew Ahrens  *
558a7a845e4SSteven Hartland  * If tmpdp is not NULL the names for holds should be the dsobj's of snapshots,
559a7a845e4SSteven Hartland  * otherwise they should be the names of shapshots.
560a7a845e4SSteven Hartland  *
561a7a845e4SSteven Hartland  * As a release may cause snapshots to be destroyed this trys to ensure they
562a7a845e4SSteven Hartland  * aren't mounted.
563a7a845e4SSteven Hartland  *
564a7a845e4SSteven Hartland  * The release of non-existent holds are skipped.
565a7a845e4SSteven Hartland  *
566a7a845e4SSteven Hartland  * At least one hold must have been released for the this function to succeed
567a7a845e4SSteven Hartland  * and return 0.
5683b2aab18SMatthew Ahrens  */
569a7a845e4SSteven Hartland static int
dsl_dataset_user_release_impl(nvlist_t * holds,nvlist_t * errlist,dsl_pool_t * tmpdp)570a7a845e4SSteven Hartland dsl_dataset_user_release_impl(nvlist_t *holds, nvlist_t *errlist,
571a7a845e4SSteven Hartland     dsl_pool_t *tmpdp)
5723b2aab18SMatthew Ahrens {
5733b2aab18SMatthew Ahrens 	dsl_dataset_user_release_arg_t ddura;
5743b2aab18SMatthew Ahrens 	nvpair_t *pair;
575a7a845e4SSteven Hartland 	char *pool;
5763b2aab18SMatthew Ahrens 	int error;
5773b2aab18SMatthew Ahrens 
5783b2aab18SMatthew Ahrens 	pair = nvlist_next_nvpair(holds, NULL);
5793b2aab18SMatthew Ahrens 	if (pair == NULL)
5803b2aab18SMatthew Ahrens 		return (0);
5813b2aab18SMatthew Ahrens 
582a7a845e4SSteven Hartland 	/*
583a7a845e4SSteven Hartland 	 * The release may cause snapshots to be destroyed; make sure they
584a7a845e4SSteven Hartland 	 * are not mounted.
585a7a845e4SSteven Hartland 	 */
586a7a845e4SSteven Hartland 	if (tmpdp != NULL) {
587a7a845e4SSteven Hartland 		/* Temporary holds are specified by dsobj string. */
588a7a845e4SSteven Hartland 		ddura.ddura_holdfunc = dsl_dataset_hold_obj_string;
589a7a845e4SSteven Hartland 		pool = spa_name(tmpdp->dp_spa);
590a7a845e4SSteven Hartland #ifdef _KERNEL
591a7a845e4SSteven Hartland 		for (pair = nvlist_next_nvpair(holds, NULL); pair != NULL;
592a7a845e4SSteven Hartland 		    pair = nvlist_next_nvpair(holds, pair)) {
593a7a845e4SSteven Hartland 			dsl_dataset_t *ds;
594a7a845e4SSteven Hartland 
595c50d56f6SSteven Hartland 			dsl_pool_config_enter(tmpdp, FTAG);
596a7a845e4SSteven Hartland 			error = dsl_dataset_hold_obj_string(tmpdp,
597a7a845e4SSteven Hartland 			    nvpair_name(pair), FTAG, &ds);
598a7a845e4SSteven Hartland 			if (error == 0) {
5999adfa60dSMatthew Ahrens 				char name[ZFS_MAX_DATASET_NAME_LEN];
600a7a845e4SSteven Hartland 				dsl_dataset_name(ds, name);
601c50d56f6SSteven Hartland 				dsl_pool_config_exit(tmpdp, FTAG);
602a7a845e4SSteven Hartland 				dsl_dataset_rele(ds, FTAG);
603a7a845e4SSteven Hartland 				(void) zfs_unmount_snap(name);
604c50d56f6SSteven Hartland 			} else {
605c50d56f6SSteven Hartland 				dsl_pool_config_exit(tmpdp, FTAG);
606a7a845e4SSteven Hartland 			}
607a7a845e4SSteven Hartland 		}
608a7a845e4SSteven Hartland #endif
609a7a845e4SSteven Hartland 	} else {
610a7a845e4SSteven Hartland 		/* Non-temporary holds are specified by name. */
611a7a845e4SSteven Hartland 		ddura.ddura_holdfunc = dsl_dataset_hold;
612a7a845e4SSteven Hartland 		pool = nvpair_name(pair);
613a7a845e4SSteven Hartland #ifdef _KERNEL
614a7a845e4SSteven Hartland 		for (pair = nvlist_next_nvpair(holds, NULL); pair != NULL;
615a7a845e4SSteven Hartland 		    pair = nvlist_next_nvpair(holds, pair)) {
616a7a845e4SSteven Hartland 			(void) zfs_unmount_snap(nvpair_name(pair));
617a7a845e4SSteven Hartland 		}
618a7a845e4SSteven Hartland #endif
619a7a845e4SSteven Hartland 	}
620a7a845e4SSteven Hartland 
6213b2aab18SMatthew Ahrens 	ddura.ddura_holds = holds;
6223b2aab18SMatthew Ahrens 	ddura.ddura_errlist = errlist;
6233b2aab18SMatthew Ahrens 	ddura.ddura_todelete = fnvlist_alloc();
624a7a845e4SSteven Hartland 	ddura.ddura_chkholds = fnvlist_alloc();
6253b2aab18SMatthew Ahrens 
626a7a845e4SSteven Hartland 	error = dsl_sync_task(pool, dsl_dataset_user_release_check,
62786714001SSerapheim Dimitropoulos 	    dsl_dataset_user_release_sync, &ddura, 0,
62886714001SSerapheim Dimitropoulos 	    ZFS_SPACE_CHECK_EXTRA_RESERVED);
6293b2aab18SMatthew Ahrens 	fnvlist_free(ddura.ddura_todelete);
630a7a845e4SSteven Hartland 	fnvlist_free(ddura.ddura_chkholds);
6313b2aab18SMatthew Ahrens 
6323b2aab18SMatthew Ahrens 	return (error);
6333b2aab18SMatthew Ahrens }
6343b2aab18SMatthew Ahrens 
6353b2aab18SMatthew Ahrens /*
636a7a845e4SSteven Hartland  * holds is nvl of snapname -> { holdname, ... }
637a7a845e4SSteven Hartland  * errlist will be filled in with snapname -> error
6383b2aab18SMatthew Ahrens  */
639a7a845e4SSteven Hartland int
dsl_dataset_user_release(nvlist_t * holds,nvlist_t * errlist)640a7a845e4SSteven Hartland dsl_dataset_user_release(nvlist_t *holds, nvlist_t *errlist)
6413b2aab18SMatthew Ahrens {
642a7a845e4SSteven Hartland 	return (dsl_dataset_user_release_impl(holds, errlist, NULL));
6433b2aab18SMatthew Ahrens }
6443b2aab18SMatthew Ahrens 
645a7a845e4SSteven Hartland /*
646a7a845e4SSteven Hartland  * holds is nvl of snapdsobj -> { holdname, ... }
647a7a845e4SSteven Hartland  */
6483b2aab18SMatthew Ahrens void
dsl_dataset_user_release_tmp(struct dsl_pool * dp,nvlist_t * holds)649a7a845e4SSteven Hartland dsl_dataset_user_release_tmp(struct dsl_pool *dp, nvlist_t *holds)
6503b2aab18SMatthew Ahrens {
651a7a845e4SSteven Hartland 	ASSERT(dp != NULL);
652a7a845e4SSteven Hartland 	(void) dsl_dataset_user_release_impl(holds, NULL, dp);
6533b2aab18SMatthew Ahrens }
6543b2aab18SMatthew Ahrens 
6553b2aab18SMatthew Ahrens int
dsl_dataset_get_holds(const char * dsname,nvlist_t * nvl)6563b2aab18SMatthew Ahrens dsl_dataset_get_holds(const char *dsname, nvlist_t *nvl)
6573b2aab18SMatthew Ahrens {
6583b2aab18SMatthew Ahrens 	dsl_pool_t *dp;
6593b2aab18SMatthew Ahrens 	dsl_dataset_t *ds;
6603b2aab18SMatthew Ahrens 	int err;
6613b2aab18SMatthew Ahrens 
6623b2aab18SMatthew Ahrens 	err = dsl_pool_hold(dsname, FTAG, &dp);
6633b2aab18SMatthew Ahrens 	if (err != 0)
6643b2aab18SMatthew Ahrens 		return (err);
6653b2aab18SMatthew Ahrens 	err = dsl_dataset_hold(dp, dsname, FTAG, &ds);
6663b2aab18SMatthew Ahrens 	if (err != 0) {
6673b2aab18SMatthew Ahrens 		dsl_pool_rele(dp, FTAG);
6683b2aab18SMatthew Ahrens 		return (err);
6693b2aab18SMatthew Ahrens 	}
6703b2aab18SMatthew Ahrens 
671c1379625SJustin T. Gibbs 	if (dsl_dataset_phys(ds)->ds_userrefs_obj != 0) {
6723b2aab18SMatthew Ahrens 		zap_attribute_t *za;
6733b2aab18SMatthew Ahrens 		zap_cursor_t zc;
6743b2aab18SMatthew Ahrens 
6753b2aab18SMatthew Ahrens 		za = kmem_alloc(sizeof (zap_attribute_t), KM_SLEEP);
6763b2aab18SMatthew Ahrens 		for (zap_cursor_init(&zc, ds->ds_dir->dd_pool->dp_meta_objset,
677c1379625SJustin T. Gibbs 		    dsl_dataset_phys(ds)->ds_userrefs_obj);
6783b2aab18SMatthew Ahrens 		    zap_cursor_retrieve(&zc, za) == 0;
6793b2aab18SMatthew Ahrens 		    zap_cursor_advance(&zc)) {
6803b2aab18SMatthew Ahrens 			fnvlist_add_uint64(nvl, za->za_name,
6813b2aab18SMatthew Ahrens 			    za->za_first_integer);
6823b2aab18SMatthew Ahrens 		}
6833b2aab18SMatthew Ahrens 		zap_cursor_fini(&zc);
6843b2aab18SMatthew Ahrens 		kmem_free(za, sizeof (zap_attribute_t));
6853b2aab18SMatthew Ahrens 	}
6863b2aab18SMatthew Ahrens 	dsl_dataset_rele(ds, FTAG);
6873b2aab18SMatthew Ahrens 	dsl_pool_rele(dp, FTAG);
6883b2aab18SMatthew Ahrens 	return (0);
6893b2aab18SMatthew Ahrens }
690