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