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