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.
23*a1988827SMatthew Ahrens * Copyright (c) 2012, 2015 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) {
683b2aab18SMatthew Ahrens uint64_t value;
69a7a845e4SSteven Hartland
70c1379625SJustin T. Gibbs error = zap_lookup(mos, dsl_dataset_phys(ds)->ds_userrefs_obj,
713b2aab18SMatthew Ahrens htag, 8, 1, &value);
723b2aab18SMatthew Ahrens if (error == 0)
73be6fd75aSMatthew Ahrens error = SET_ERROR(EEXIST);
743b2aab18SMatthew Ahrens else if (error == ENOENT)
753b2aab18SMatthew Ahrens 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);
863b2aab18SMatthew Ahrens
873b2aab18SMatthew Ahrens if (spa_version(dp->dp_spa) < SPA_VERSION_USERREFS)
88be6fd75aSMatthew Ahrens return (SET_ERROR(ENOTSUP));
893b2aab18SMatthew Ahrens
90a7a845e4SSteven Hartland if (!dmu_tx_is_syncing(tx))
91a7a845e4SSteven Hartland return (0);
92a7a845e4SSteven Hartland
93a7a845e4SSteven Hartland for (nvpair_t *pair = nvlist_next_nvpair(dduha->dduha_holds, NULL);
94a7a845e4SSteven Hartland pair != NULL; pair = nvlist_next_nvpair(dduha->dduha_holds, pair)) {
953b2aab18SMatthew Ahrens dsl_dataset_t *ds;
96a7a845e4SSteven Hartland int error = 0;
97a7a845e4SSteven Hartland char *htag, *name;
983b2aab18SMatthew Ahrens
993b2aab18SMatthew Ahrens /* must be a snapshot */
100a7a845e4SSteven Hartland name = nvpair_name(pair);
101a7a845e4SSteven Hartland if (strchr(name, '@') == NULL)
102be6fd75aSMatthew Ahrens error = SET_ERROR(EINVAL);
1033b2aab18SMatthew Ahrens
1043b2aab18SMatthew Ahrens if (error == 0)
1053b2aab18SMatthew Ahrens error = nvpair_value_string(pair, &htag);
106a7a845e4SSteven Hartland
107a7a845e4SSteven Hartland if (error == 0)
108a7a845e4SSteven Hartland error = dsl_dataset_hold(dp, name, FTAG, &ds);
109a7a845e4SSteven Hartland
1103b2aab18SMatthew Ahrens if (error == 0) {
1113b2aab18SMatthew Ahrens error = dsl_dataset_user_hold_check_one(ds, htag,
1123b2aab18SMatthew Ahrens dduha->dduha_minor != 0, tx);
1133b2aab18SMatthew Ahrens dsl_dataset_rele(ds, FTAG);
1143b2aab18SMatthew Ahrens }
1153b2aab18SMatthew Ahrens
116a7a845e4SSteven Hartland if (error == 0) {
117a7a845e4SSteven Hartland fnvlist_add_string(dduha->dduha_chkholds, name, htag);
118a7a845e4SSteven Hartland } else {
119a7a845e4SSteven Hartland /*
120a7a845e4SSteven Hartland * We register ENOENT errors so they can be correctly
121a7a845e4SSteven Hartland * reported if needed, such as when all holds fail.
122a7a845e4SSteven Hartland */
123a7a845e4SSteven Hartland fnvlist_add_int32(dduha->dduha_errlist, name, error);
124a7a845e4SSteven Hartland if (error != ENOENT)
125a7a845e4SSteven Hartland return (error);
1263b2aab18SMatthew Ahrens }
1273b2aab18SMatthew Ahrens }
1283b2aab18SMatthew Ahrens
129a7a845e4SSteven Hartland return (0);
130a7a845e4SSteven Hartland }
131a7a845e4SSteven Hartland
132a7a845e4SSteven Hartland
133a7a845e4SSteven 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)134a7a845e4SSteven Hartland dsl_dataset_user_hold_sync_one_impl(nvlist_t *tmpholds, dsl_dataset_t *ds,
135a7a845e4SSteven Hartland const char *htag, minor_t minor, uint64_t now, dmu_tx_t *tx)
1363b2aab18SMatthew Ahrens {
1373b2aab18SMatthew Ahrens dsl_pool_t *dp = ds->ds_dir->dd_pool;
1383b2aab18SMatthew Ahrens objset_t *mos = dp->dp_meta_objset;
1393b2aab18SMatthew Ahrens uint64_t zapobj;
1403b2aab18SMatthew Ahrens
141a7a845e4SSteven Hartland ASSERT(RRW_WRITE_HELD(&dp->dp_config_rwlock));
142a7a845e4SSteven Hartland
143c1379625SJustin T. Gibbs if (dsl_dataset_phys(ds)->ds_userrefs_obj == 0) {
1443b2aab18SMatthew Ahrens /*
1453b2aab18SMatthew Ahrens * This is the first user hold for this dataset. Create
1463b2aab18SMatthew Ahrens * the userrefs zap object.
1473b2aab18SMatthew Ahrens */
1483b2aab18SMatthew Ahrens dmu_buf_will_dirty(ds->ds_dbuf, tx);
149c1379625SJustin T. Gibbs zapobj = dsl_dataset_phys(ds)->ds_userrefs_obj =
1503b2aab18SMatthew Ahrens zap_create(mos, DMU_OT_USERREFS, DMU_OT_NONE, 0, tx);
1513b2aab18SMatthew Ahrens } else {
152c1379625SJustin T. Gibbs zapobj = dsl_dataset_phys(ds)->ds_userrefs_obj;
1533b2aab18SMatthew Ahrens }
1543b2aab18SMatthew Ahrens ds->ds_userrefs++;
1553b2aab18SMatthew Ahrens
1563b2aab18SMatthew Ahrens VERIFY0(zap_add(mos, zapobj, htag, 8, 1, &now, tx));
1573b2aab18SMatthew Ahrens
1583b2aab18SMatthew Ahrens if (minor != 0) {
159a7a845e4SSteven Hartland char name[MAXNAMELEN];
160a7a845e4SSteven Hartland nvlist_t *tags;
161a7a845e4SSteven Hartland
1623b2aab18SMatthew Ahrens VERIFY0(dsl_pool_user_hold(dp, ds->ds_object,
1633b2aab18SMatthew Ahrens htag, now, tx));
164a7a845e4SSteven Hartland (void) snprintf(name, sizeof (name), "%llx",
165a7a845e4SSteven Hartland (u_longlong_t)ds->ds_object);
166a7a845e4SSteven Hartland
167a7a845e4SSteven Hartland if (nvlist_lookup_nvlist(tmpholds, name, &tags) != 0) {
168a7a845e4SSteven Hartland tags = fnvlist_alloc();
169a7a845e4SSteven Hartland fnvlist_add_boolean(tags, htag);
170a7a845e4SSteven Hartland fnvlist_add_nvlist(tmpholds, name, tags);
171a7a845e4SSteven Hartland fnvlist_free(tags);
172a7a845e4SSteven Hartland } else {
173a7a845e4SSteven Hartland fnvlist_add_boolean(tags, htag);
174a7a845e4SSteven Hartland }
1753b2aab18SMatthew Ahrens }
1763b2aab18SMatthew Ahrens
1773b2aab18SMatthew Ahrens spa_history_log_internal_ds(ds, "hold", tx,
1783b2aab18SMatthew Ahrens "tag=%s temp=%d refs=%llu",
1793b2aab18SMatthew Ahrens htag, minor != 0, ds->ds_userrefs);
1803b2aab18SMatthew Ahrens }
1813b2aab18SMatthew Ahrens
1823b2aab18SMatthew Ahrens typedef struct zfs_hold_cleanup_arg {
183*a1988827SMatthew Ahrens char zhca_spaname[ZFS_MAX_DATASET_NAME_LEN];
1843b2aab18SMatthew Ahrens uint64_t zhca_spa_load_guid;
185a7a845e4SSteven Hartland nvlist_t *zhca_holds;
1863b2aab18SMatthew Ahrens } zfs_hold_cleanup_arg_t;
1873b2aab18SMatthew Ahrens
1883b2aab18SMatthew Ahrens static void
dsl_dataset_user_release_onexit(void * arg)1893b2aab18SMatthew Ahrens dsl_dataset_user_release_onexit(void *arg)
1903b2aab18SMatthew Ahrens {
1913b2aab18SMatthew Ahrens zfs_hold_cleanup_arg_t *ca = arg;
1923b2aab18SMatthew Ahrens spa_t *spa;
1933b2aab18SMatthew Ahrens int error;
1943b2aab18SMatthew Ahrens
1953b2aab18SMatthew Ahrens error = spa_open(ca->zhca_spaname, &spa, FTAG);
1963b2aab18SMatthew Ahrens if (error != 0) {
197a7a845e4SSteven Hartland zfs_dbgmsg("couldn't release holds on pool=%s "
1983b2aab18SMatthew Ahrens "because pool is no longer loaded",
199a7a845e4SSteven Hartland ca->zhca_spaname);
2003b2aab18SMatthew Ahrens return;
2013b2aab18SMatthew Ahrens }
2023b2aab18SMatthew Ahrens if (spa_load_guid(spa) != ca->zhca_spa_load_guid) {
203a7a845e4SSteven Hartland zfs_dbgmsg("couldn't release holds on pool=%s "
2043b2aab18SMatthew Ahrens "because pool is no longer loaded (guid doesn't match)",
205a7a845e4SSteven Hartland ca->zhca_spaname);
2063b2aab18SMatthew Ahrens spa_close(spa, FTAG);
2073b2aab18SMatthew Ahrens return;
2083b2aab18SMatthew Ahrens }
2093b2aab18SMatthew Ahrens
210a7a845e4SSteven Hartland (void) dsl_dataset_user_release_tmp(spa_get_dsl(spa), ca->zhca_holds);
211a7a845e4SSteven Hartland fnvlist_free(ca->zhca_holds);
2123b2aab18SMatthew Ahrens kmem_free(ca, sizeof (zfs_hold_cleanup_arg_t));
2133b2aab18SMatthew Ahrens spa_close(spa, FTAG);
2143b2aab18SMatthew Ahrens }
2153b2aab18SMatthew Ahrens
216a7a845e4SSteven Hartland static void
dsl_onexit_hold_cleanup(spa_t * spa,nvlist_t * holds,minor_t minor)217a7a845e4SSteven Hartland dsl_onexit_hold_cleanup(spa_t *spa, nvlist_t *holds, minor_t minor)
2183b2aab18SMatthew Ahrens {
219a7a845e4SSteven Hartland zfs_hold_cleanup_arg_t *ca;
220a7a845e4SSteven Hartland
221a7a845e4SSteven Hartland if (minor == 0 || nvlist_empty(holds)) {
222a7a845e4SSteven Hartland fnvlist_free(holds);
223a7a845e4SSteven Hartland return;
224a7a845e4SSteven Hartland }
225a7a845e4SSteven Hartland
226a7a845e4SSteven Hartland ASSERT(spa != NULL);
227a7a845e4SSteven Hartland ca = kmem_alloc(sizeof (*ca), KM_SLEEP);
228a7a845e4SSteven Hartland
2293b2aab18SMatthew Ahrens (void) strlcpy(ca->zhca_spaname, spa_name(spa),
2303b2aab18SMatthew Ahrens sizeof (ca->zhca_spaname));
2313b2aab18SMatthew Ahrens ca->zhca_spa_load_guid = spa_load_guid(spa);
232a7a845e4SSteven Hartland ca->zhca_holds = holds;
2333b2aab18SMatthew Ahrens VERIFY0(zfs_onexit_add_cb(minor,
2343b2aab18SMatthew Ahrens dsl_dataset_user_release_onexit, ca, NULL));
2353b2aab18SMatthew Ahrens }
2363b2aab18SMatthew Ahrens
237a7a845e4SSteven 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)238a7a845e4SSteven Hartland dsl_dataset_user_hold_sync_one(dsl_dataset_t *ds, const char *htag,
239a7a845e4SSteven Hartland minor_t minor, uint64_t now, dmu_tx_t *tx)
240a7a845e4SSteven Hartland {
241a7a845e4SSteven Hartland nvlist_t *tmpholds;
242a7a845e4SSteven Hartland
243a7a845e4SSteven Hartland if (minor != 0)
244a7a845e4SSteven Hartland tmpholds = fnvlist_alloc();
245a7a845e4SSteven Hartland else
246a7a845e4SSteven Hartland tmpholds = NULL;
247a7a845e4SSteven Hartland dsl_dataset_user_hold_sync_one_impl(tmpholds, ds, htag, minor, now, tx);
248a7a845e4SSteven Hartland dsl_onexit_hold_cleanup(dsl_dataset_get_spa(ds), tmpholds, minor);
249a7a845e4SSteven Hartland }
250a7a845e4SSteven Hartland
251a7a845e4SSteven Hartland static void
dsl_dataset_user_hold_sync(void * arg,dmu_tx_t * tx)252a7a845e4SSteven Hartland dsl_dataset_user_hold_sync(void *arg, dmu_tx_t *tx)
253a7a845e4SSteven Hartland {
254a7a845e4SSteven Hartland dsl_dataset_user_hold_arg_t *dduha = arg;
255a7a845e4SSteven Hartland dsl_pool_t *dp = dmu_tx_pool(tx);
256a7a845e4SSteven Hartland nvlist_t *tmpholds;
257a7a845e4SSteven Hartland uint64_t now = gethrestime_sec();
258a7a845e4SSteven Hartland
259a7a845e4SSteven Hartland if (dduha->dduha_minor != 0)
260a7a845e4SSteven Hartland tmpholds = fnvlist_alloc();
261a7a845e4SSteven Hartland else
262a7a845e4SSteven Hartland tmpholds = NULL;
263a7a845e4SSteven Hartland for (nvpair_t *pair = nvlist_next_nvpair(dduha->dduha_chkholds, NULL);
264a7a845e4SSteven Hartland pair != NULL;
265a7a845e4SSteven Hartland pair = nvlist_next_nvpair(dduha->dduha_chkholds, pair)) {
266a7a845e4SSteven Hartland dsl_dataset_t *ds;
267a7a845e4SSteven Hartland
268a7a845e4SSteven Hartland VERIFY0(dsl_dataset_hold(dp, nvpair_name(pair), FTAG, &ds));
269a7a845e4SSteven Hartland dsl_dataset_user_hold_sync_one_impl(tmpholds, ds,
270a7a845e4SSteven Hartland fnvpair_value_string(pair), dduha->dduha_minor, now, tx);
271a7a845e4SSteven Hartland dsl_dataset_rele(ds, FTAG);
272a7a845e4SSteven Hartland }
273a7a845e4SSteven Hartland dsl_onexit_hold_cleanup(dp->dp_spa, tmpholds, dduha->dduha_minor);
274a7a845e4SSteven Hartland }
275a7a845e4SSteven Hartland
276a7a845e4SSteven Hartland /*
277a7a845e4SSteven Hartland * The full semantics of this function are described in the comment above
278a7a845e4SSteven Hartland * lzc_hold().
279a7a845e4SSteven Hartland *
280a7a845e4SSteven Hartland * To summarize:
281a7a845e4SSteven Hartland * holds is nvl of snapname -> holdname
282a7a845e4SSteven Hartland * errlist will be filled in with snapname -> error
283a7a845e4SSteven Hartland *
284a7a845e4SSteven Hartland * The snaphosts must all be in the same pool.
285a7a845e4SSteven Hartland *
286a7a845e4SSteven Hartland * Holds for snapshots that don't exist will be skipped.
287a7a845e4SSteven Hartland *
288a7a845e4SSteven Hartland * If none of the snapshots for requested holds exist then ENOENT will be
289a7a845e4SSteven Hartland * returned.
290a7a845e4SSteven Hartland *
291a7a845e4SSteven Hartland * If cleanup_minor is not 0, the holds will be temporary, which will be cleaned
292a7a845e4SSteven Hartland * up when the process exits.
293a7a845e4SSteven Hartland *
294a7a845e4SSteven Hartland * On success all the holds, for snapshots that existed, will be created and 0
295a7a845e4SSteven Hartland * will be returned.
296a7a845e4SSteven Hartland *
297a7a845e4SSteven Hartland * On failure no holds will be created, the errlist will be filled in,
298a7a845e4SSteven Hartland * and an errno will returned.
299a7a845e4SSteven Hartland *
300a7a845e4SSteven Hartland * In all cases the errlist will contain entries for holds where the snapshot
301a7a845e4SSteven Hartland * didn't exist.
302a7a845e4SSteven Hartland */
303a7a845e4SSteven Hartland int
dsl_dataset_user_hold(nvlist_t * holds,minor_t cleanup_minor,nvlist_t * errlist)304a7a845e4SSteven Hartland dsl_dataset_user_hold(nvlist_t *holds, minor_t cleanup_minor, nvlist_t *errlist)
305a7a845e4SSteven Hartland {
306a7a845e4SSteven Hartland dsl_dataset_user_hold_arg_t dduha;
307a7a845e4SSteven Hartland nvpair_t *pair;
308a7a845e4SSteven Hartland int ret;
309a7a845e4SSteven Hartland
310a7a845e4SSteven Hartland pair = nvlist_next_nvpair(holds, NULL);
311a7a845e4SSteven Hartland if (pair == NULL)
312a7a845e4SSteven Hartland return (0);
313a7a845e4SSteven Hartland
314a7a845e4SSteven Hartland dduha.dduha_holds = holds;
315a7a845e4SSteven Hartland dduha.dduha_chkholds = fnvlist_alloc();
316a7a845e4SSteven Hartland dduha.dduha_errlist = errlist;
317a7a845e4SSteven Hartland dduha.dduha_minor = cleanup_minor;
318a7a845e4SSteven Hartland
319a7a845e4SSteven Hartland ret = dsl_sync_task(nvpair_name(pair), dsl_dataset_user_hold_check,
3207d46dc6cSMatthew Ahrens dsl_dataset_user_hold_sync, &dduha,
3217d46dc6cSMatthew Ahrens fnvlist_num_pairs(holds), ZFS_SPACE_CHECK_RESERVED);
322a7a845e4SSteven Hartland fnvlist_free(dduha.dduha_chkholds);
323a7a845e4SSteven Hartland
324a7a845e4SSteven Hartland return (ret);
325a7a845e4SSteven Hartland }
326a7a845e4SSteven Hartland
327a7a845e4SSteven Hartland typedef int (dsl_holdfunc_t)(dsl_pool_t *dp, const char *name, void *tag,
328a7a845e4SSteven Hartland dsl_dataset_t **dsp);
329a7a845e4SSteven Hartland
330a7a845e4SSteven Hartland typedef struct dsl_dataset_user_release_arg {
331a7a845e4SSteven Hartland dsl_holdfunc_t *ddura_holdfunc;
332a7a845e4SSteven Hartland nvlist_t *ddura_holds;
333a7a845e4SSteven Hartland nvlist_t *ddura_todelete;
334a7a845e4SSteven Hartland nvlist_t *ddura_errlist;
335a7a845e4SSteven Hartland nvlist_t *ddura_chkholds;
336a7a845e4SSteven Hartland } dsl_dataset_user_release_arg_t;
337a7a845e4SSteven Hartland
338a7a845e4SSteven Hartland /* Place a dataset hold on the snapshot identified by passed dsobj string */
339a7a845e4SSteven Hartland static int
dsl_dataset_hold_obj_string(dsl_pool_t * dp,const char * dsobj,void * tag,dsl_dataset_t ** dsp)340a7a845e4SSteven Hartland dsl_dataset_hold_obj_string(dsl_pool_t *dp, const char *dsobj, void *tag,
341a7a845e4SSteven Hartland dsl_dataset_t **dsp)
342a7a845e4SSteven Hartland {
343a7a845e4SSteven Hartland return (dsl_dataset_hold_obj(dp, strtonum(dsobj, NULL), tag, dsp));
344a7a845e4SSteven Hartland }
345a7a845e4SSteven Hartland
346a7a845e4SSteven 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)347a7a845e4SSteven Hartland dsl_dataset_user_release_check_one(dsl_dataset_user_release_arg_t *ddura,
348a7a845e4SSteven Hartland dsl_dataset_t *ds, nvlist_t *holds, const char *snapname)
349a7a845e4SSteven Hartland {
350a7a845e4SSteven Hartland uint64_t zapobj;
351a7a845e4SSteven Hartland nvlist_t *holds_found;
352a7a845e4SSteven Hartland objset_t *mos;
353a7a845e4SSteven Hartland int numholds;
354a7a845e4SSteven Hartland
355bc9014e6SJustin Gibbs if (!ds->ds_is_snapshot)
356a7a845e4SSteven Hartland return (SET_ERROR(EINVAL));
357a7a845e4SSteven Hartland
358a7a845e4SSteven Hartland if (nvlist_empty(holds))
359a7a845e4SSteven Hartland return (0);
360a7a845e4SSteven Hartland
361a7a845e4SSteven Hartland numholds = 0;
362a7a845e4SSteven Hartland mos = ds->ds_dir->dd_pool->dp_meta_objset;
363c1379625SJustin T. Gibbs zapobj = dsl_dataset_phys(ds)->ds_userrefs_obj;
364a7a845e4SSteven Hartland holds_found = fnvlist_alloc();
365a7a845e4SSteven Hartland
366a7a845e4SSteven Hartland for (nvpair_t *pair = nvlist_next_nvpair(holds, NULL); pair != NULL;
367a7a845e4SSteven Hartland pair = nvlist_next_nvpair(holds, pair)) {
368a7a845e4SSteven Hartland uint64_t tmp;
369a7a845e4SSteven Hartland int error;
370a7a845e4SSteven Hartland const char *holdname = nvpair_name(pair);
371a7a845e4SSteven Hartland
372a7a845e4SSteven Hartland if (zapobj != 0)
373a7a845e4SSteven Hartland error = zap_lookup(mos, zapobj, holdname, 8, 1, &tmp);
374a7a845e4SSteven Hartland else
375a7a845e4SSteven Hartland error = SET_ERROR(ENOENT);
376a7a845e4SSteven Hartland
377a7a845e4SSteven Hartland /*
378a7a845e4SSteven Hartland * Non-existent holds are put on the errlist, but don't
379a7a845e4SSteven Hartland * cause an overall failure.
380a7a845e4SSteven Hartland */
381a7a845e4SSteven Hartland if (error == ENOENT) {
382a7a845e4SSteven Hartland if (ddura->ddura_errlist != NULL) {
383a7a845e4SSteven Hartland char *errtag = kmem_asprintf("%s#%s",
384a7a845e4SSteven Hartland snapname, holdname);
385a7a845e4SSteven Hartland fnvlist_add_int32(ddura->ddura_errlist, errtag,
386a7a845e4SSteven Hartland ENOENT);
387a7a845e4SSteven Hartland strfree(errtag);
388a7a845e4SSteven Hartland }
389a7a845e4SSteven Hartland continue;
390a7a845e4SSteven Hartland }
391a7a845e4SSteven Hartland
392a7a845e4SSteven Hartland if (error != 0) {
393a7a845e4SSteven Hartland fnvlist_free(holds_found);
394a7a845e4SSteven Hartland return (error);
395a7a845e4SSteven Hartland }
396a7a845e4SSteven Hartland
397a7a845e4SSteven Hartland fnvlist_add_boolean(holds_found, holdname);
398a7a845e4SSteven Hartland numholds++;
399a7a845e4SSteven Hartland }
400a7a845e4SSteven Hartland
401c1379625SJustin T. Gibbs if (DS_IS_DEFER_DESTROY(ds) &&
402c1379625SJustin T. Gibbs dsl_dataset_phys(ds)->ds_num_children == 1 &&
403a7a845e4SSteven Hartland ds->ds_userrefs == numholds) {
404a7a845e4SSteven Hartland /* we need to destroy the snapshot as well */
405a7a845e4SSteven Hartland if (dsl_dataset_long_held(ds)) {
406a7a845e4SSteven Hartland fnvlist_free(holds_found);
407a7a845e4SSteven Hartland return (SET_ERROR(EBUSY));
408a7a845e4SSteven Hartland }
409a7a845e4SSteven Hartland fnvlist_add_boolean(ddura->ddura_todelete, snapname);
410a7a845e4SSteven Hartland }
411a7a845e4SSteven Hartland
412a7a845e4SSteven Hartland if (numholds != 0) {
413a7a845e4SSteven Hartland fnvlist_add_nvlist(ddura->ddura_chkholds, snapname,
414a7a845e4SSteven Hartland holds_found);
415a7a845e4SSteven Hartland }
416a7a845e4SSteven Hartland fnvlist_free(holds_found);
417a7a845e4SSteven Hartland
418a7a845e4SSteven Hartland return (0);
419a7a845e4SSteven Hartland }
420a7a845e4SSteven Hartland
421a7a845e4SSteven Hartland static int
dsl_dataset_user_release_check(void * arg,dmu_tx_t * tx)422a7a845e4SSteven Hartland dsl_dataset_user_release_check(void *arg, dmu_tx_t *tx)
423a7a845e4SSteven Hartland {
424a7a845e4SSteven Hartland dsl_dataset_user_release_arg_t *ddura;
425a7a845e4SSteven Hartland dsl_holdfunc_t *holdfunc;
426a7a845e4SSteven Hartland dsl_pool_t *dp;
427a7a845e4SSteven Hartland
428a7a845e4SSteven Hartland if (!dmu_tx_is_syncing(tx))
429a7a845e4SSteven Hartland return (0);
430a7a845e4SSteven Hartland
431a7a845e4SSteven Hartland dp = dmu_tx_pool(tx);
432a7a845e4SSteven Hartland
433a7a845e4SSteven Hartland ASSERT(RRW_WRITE_HELD(&dp->dp_config_rwlock));
434a7a845e4SSteven Hartland
435a7a845e4SSteven Hartland ddura = arg;
436a7a845e4SSteven Hartland holdfunc = ddura->ddura_holdfunc;
437a7a845e4SSteven Hartland
438a7a845e4SSteven Hartland for (nvpair_t *pair = nvlist_next_nvpair(ddura->ddura_holds, NULL);
439a7a845e4SSteven Hartland pair != NULL; pair = nvlist_next_nvpair(ddura->ddura_holds, pair)) {
440a7a845e4SSteven Hartland int error;
441a7a845e4SSteven Hartland dsl_dataset_t *ds;
442a7a845e4SSteven Hartland nvlist_t *holds;
443a7a845e4SSteven Hartland const char *snapname = nvpair_name(pair);
444a7a845e4SSteven Hartland
445a7a845e4SSteven Hartland error = nvpair_value_nvlist(pair, &holds);
446a7a845e4SSteven Hartland if (error != 0)
447a7a845e4SSteven Hartland error = (SET_ERROR(EINVAL));
448a7a845e4SSteven Hartland else
449a7a845e4SSteven Hartland error = holdfunc(dp, snapname, FTAG, &ds);
450a7a845e4SSteven Hartland if (error == 0) {
451a7a845e4SSteven Hartland error = dsl_dataset_user_release_check_one(ddura, ds,
452a7a845e4SSteven Hartland holds, snapname);
453a7a845e4SSteven Hartland dsl_dataset_rele(ds, FTAG);
454a7a845e4SSteven Hartland }
455a7a845e4SSteven Hartland if (error != 0) {
456a7a845e4SSteven Hartland if (ddura->ddura_errlist != NULL) {
457a7a845e4SSteven Hartland fnvlist_add_int32(ddura->ddura_errlist,
458a7a845e4SSteven Hartland snapname, error);
459a7a845e4SSteven Hartland }
460a7a845e4SSteven Hartland /*
461a7a845e4SSteven Hartland * Non-existent snapshots are put on the errlist,
462a7a845e4SSteven Hartland * but don't cause an overall failure.
463a7a845e4SSteven Hartland */
464a7a845e4SSteven Hartland if (error != ENOENT)
465a7a845e4SSteven Hartland return (error);
466a7a845e4SSteven Hartland }
467a7a845e4SSteven Hartland }
468a7a845e4SSteven Hartland
469a7a845e4SSteven Hartland return (0);
470a7a845e4SSteven Hartland }
471a7a845e4SSteven Hartland
472a7a845e4SSteven Hartland static void
dsl_dataset_user_release_sync_one(dsl_dataset_t * ds,nvlist_t * holds,dmu_tx_t * tx)473a7a845e4SSteven Hartland dsl_dataset_user_release_sync_one(dsl_dataset_t *ds, nvlist_t *holds,
474a7a845e4SSteven Hartland dmu_tx_t *tx)
475a7a845e4SSteven Hartland {
476a7a845e4SSteven Hartland dsl_pool_t *dp = ds->ds_dir->dd_pool;
477a7a845e4SSteven Hartland objset_t *mos = dp->dp_meta_objset;
478a7a845e4SSteven Hartland
479a7a845e4SSteven Hartland for (nvpair_t *pair = nvlist_next_nvpair(holds, NULL); pair != NULL;
480a7a845e4SSteven Hartland pair = nvlist_next_nvpair(holds, pair)) {
481a7a845e4SSteven Hartland int error;
482a7a845e4SSteven Hartland const char *holdname = nvpair_name(pair);
483a7a845e4SSteven Hartland
484a7a845e4SSteven Hartland /* Remove temporary hold if one exists. */
485a7a845e4SSteven Hartland error = dsl_pool_user_release(dp, ds->ds_object, holdname, tx);
486a7a845e4SSteven Hartland VERIFY(error == 0 || error == ENOENT);
487a7a845e4SSteven Hartland
488c1379625SJustin T. Gibbs VERIFY0(zap_remove(mos, dsl_dataset_phys(ds)->ds_userrefs_obj,
489c1379625SJustin T. Gibbs holdname, tx));
490a7a845e4SSteven Hartland ds->ds_userrefs--;
491a7a845e4SSteven Hartland
492a7a845e4SSteven Hartland spa_history_log_internal_ds(ds, "release", tx,
493a7a845e4SSteven Hartland "tag=%s refs=%lld", holdname, (longlong_t)ds->ds_userrefs);
494a7a845e4SSteven Hartland }
495a7a845e4SSteven Hartland }
496a7a845e4SSteven Hartland
497a7a845e4SSteven Hartland static void
dsl_dataset_user_release_sync(void * arg,dmu_tx_t * tx)498a7a845e4SSteven Hartland dsl_dataset_user_release_sync(void *arg, dmu_tx_t *tx)
499a7a845e4SSteven Hartland {
500a7a845e4SSteven Hartland dsl_dataset_user_release_arg_t *ddura = arg;
501a7a845e4SSteven Hartland dsl_holdfunc_t *holdfunc = ddura->ddura_holdfunc;
502a7a845e4SSteven Hartland dsl_pool_t *dp = dmu_tx_pool(tx);
503a7a845e4SSteven Hartland
504a7a845e4SSteven Hartland ASSERT(RRW_WRITE_HELD(&dp->dp_config_rwlock));
505a7a845e4SSteven Hartland
506a7a845e4SSteven Hartland for (nvpair_t *pair = nvlist_next_nvpair(ddura->ddura_chkholds, NULL);
507a7a845e4SSteven Hartland pair != NULL; pair = nvlist_next_nvpair(ddura->ddura_chkholds,
508a7a845e4SSteven Hartland pair)) {
509a7a845e4SSteven Hartland dsl_dataset_t *ds;
510a7a845e4SSteven Hartland const char *name = nvpair_name(pair);
511a7a845e4SSteven Hartland
512a7a845e4SSteven Hartland VERIFY0(holdfunc(dp, name, FTAG, &ds));
513a7a845e4SSteven Hartland
514a7a845e4SSteven Hartland dsl_dataset_user_release_sync_one(ds,
515a7a845e4SSteven Hartland fnvpair_value_nvlist(pair), tx);
516a7a845e4SSteven Hartland if (nvlist_exists(ddura->ddura_todelete, name)) {
517a7a845e4SSteven Hartland ASSERT(ds->ds_userrefs == 0 &&
518c1379625SJustin T. Gibbs dsl_dataset_phys(ds)->ds_num_children == 1 &&
519a7a845e4SSteven Hartland DS_IS_DEFER_DESTROY(ds));
520a7a845e4SSteven Hartland dsl_destroy_snapshot_sync_impl(ds, B_FALSE, tx);
521a7a845e4SSteven Hartland }
522a7a845e4SSteven Hartland dsl_dataset_rele(ds, FTAG);
523a7a845e4SSteven Hartland }
524a7a845e4SSteven Hartland }
525a7a845e4SSteven Hartland
526a7a845e4SSteven Hartland /*
527a7a845e4SSteven Hartland * The full semantics of this function are described in the comment above
528a7a845e4SSteven Hartland * lzc_release().
529a7a845e4SSteven Hartland *
530a7a845e4SSteven Hartland * To summarize:
531a7a845e4SSteven Hartland * Releases holds specified in the nvl holds.
532a7a845e4SSteven Hartland *
533a7a845e4SSteven Hartland * holds is nvl of snapname -> { holdname, ... }
534a7a845e4SSteven Hartland * errlist will be filled in with snapname -> error
535a7a845e4SSteven Hartland *
536a7a845e4SSteven Hartland * If tmpdp is not NULL the names for holds should be the dsobj's of snapshots,
537a7a845e4SSteven Hartland * otherwise they should be the names of shapshots.
538a7a845e4SSteven Hartland *
539a7a845e4SSteven Hartland * As a release may cause snapshots to be destroyed this trys to ensure they
540a7a845e4SSteven Hartland * aren't mounted.
541a7a845e4SSteven Hartland *
542a7a845e4SSteven Hartland * The release of non-existent holds are skipped.
543a7a845e4SSteven Hartland *
544a7a845e4SSteven Hartland * At least one hold must have been released for the this function to succeed
545a7a845e4SSteven Hartland * and return 0.
546a7a845e4SSteven Hartland */
547a7a845e4SSteven Hartland static int
dsl_dataset_user_release_impl(nvlist_t * holds,nvlist_t * errlist,dsl_pool_t * tmpdp)548a7a845e4SSteven Hartland dsl_dataset_user_release_impl(nvlist_t *holds, nvlist_t *errlist,
549a7a845e4SSteven Hartland dsl_pool_t *tmpdp)
550a7a845e4SSteven Hartland {
551a7a845e4SSteven Hartland dsl_dataset_user_release_arg_t ddura;
552a7a845e4SSteven Hartland nvpair_t *pair;
553a7a845e4SSteven Hartland char *pool;
554a7a845e4SSteven Hartland int error;
555a7a845e4SSteven Hartland
556a7a845e4SSteven Hartland pair = nvlist_next_nvpair(holds, NULL);
557a7a845e4SSteven Hartland if (pair == NULL)
558a7a845e4SSteven Hartland return (0);
559a7a845e4SSteven Hartland
560a7a845e4SSteven Hartland /*
561a7a845e4SSteven Hartland * The release may cause snapshots to be destroyed; make sure they
562a7a845e4SSteven Hartland * are not mounted.
563a7a845e4SSteven Hartland */
564a7a845e4SSteven Hartland if (tmpdp != NULL) {
565a7a845e4SSteven Hartland /* Temporary holds are specified by dsobj string. */
566a7a845e4SSteven Hartland ddura.ddura_holdfunc = dsl_dataset_hold_obj_string;
567a7a845e4SSteven Hartland pool = spa_name(tmpdp->dp_spa);
568a7a845e4SSteven Hartland #ifdef _KERNEL
569a7a845e4SSteven Hartland for (pair = nvlist_next_nvpair(holds, NULL); pair != NULL;
570a7a845e4SSteven Hartland pair = nvlist_next_nvpair(holds, pair)) {
571a7a845e4SSteven Hartland dsl_dataset_t *ds;
572a7a845e4SSteven Hartland
573c50d56f6SSteven Hartland dsl_pool_config_enter(tmpdp, FTAG);
574a7a845e4SSteven Hartland error = dsl_dataset_hold_obj_string(tmpdp,
575a7a845e4SSteven Hartland nvpair_name(pair), FTAG, &ds);
576a7a845e4SSteven Hartland if (error == 0) {
577*a1988827SMatthew Ahrens char name[ZFS_MAX_DATASET_NAME_LEN];
578a7a845e4SSteven Hartland dsl_dataset_name(ds, name);
579c50d56f6SSteven Hartland dsl_pool_config_exit(tmpdp, FTAG);
580a7a845e4SSteven Hartland dsl_dataset_rele(ds, FTAG);
581a7a845e4SSteven Hartland (void) zfs_unmount_snap(name);
582c50d56f6SSteven Hartland } else {
583a7a845e4SSteven Hartland dsl_pool_config_exit(tmpdp, FTAG);
584c50d56f6SSteven Hartland }
585c50d56f6SSteven Hartland }
586a7a845e4SSteven Hartland #endif
587a7a845e4SSteven Hartland } else {
588a7a845e4SSteven Hartland /* Non-temporary holds are specified by name. */
589a7a845e4SSteven Hartland ddura.ddura_holdfunc = dsl_dataset_hold;
590a7a845e4SSteven Hartland pool = nvpair_name(pair);
591a7a845e4SSteven Hartland #ifdef _KERNEL
592a7a845e4SSteven Hartland for (pair = nvlist_next_nvpair(holds, NULL); pair != NULL;
593a7a845e4SSteven Hartland pair = nvlist_next_nvpair(holds, pair)) {
594a7a845e4SSteven Hartland (void) zfs_unmount_snap(nvpair_name(pair));
595a7a845e4SSteven Hartland }
596a7a845e4SSteven Hartland #endif
597a7a845e4SSteven Hartland }
598a7a845e4SSteven Hartland
599a7a845e4SSteven Hartland ddura.ddura_holds = holds;
600a7a845e4SSteven Hartland ddura.ddura_errlist = errlist;
601a7a845e4SSteven Hartland ddura.ddura_todelete = fnvlist_alloc();
602a7a845e4SSteven Hartland ddura.ddura_chkholds = fnvlist_alloc();
603a7a845e4SSteven Hartland
604a7a845e4SSteven Hartland error = dsl_sync_task(pool, dsl_dataset_user_release_check,
6057d46dc6cSMatthew Ahrens dsl_dataset_user_release_sync, &ddura, 0, ZFS_SPACE_CHECK_NONE);
606a7a845e4SSteven Hartland fnvlist_free(ddura.ddura_todelete);
607a7a845e4SSteven Hartland fnvlist_free(ddura.ddura_chkholds);
608a7a845e4SSteven Hartland
609a7a845e4SSteven Hartland return (error);
610a7a845e4SSteven Hartland }
611a7a845e4SSteven Hartland
612a7a845e4SSteven Hartland /*
613a7a845e4SSteven Hartland * holds is nvl of snapname -> { holdname, ... }
614a7a845e4SSteven Hartland * errlist will be filled in with snapname -> error
615a7a845e4SSteven Hartland */
616a7a845e4SSteven Hartland int
dsl_dataset_user_release(nvlist_t * holds,nvlist_t * errlist)617a7a845e4SSteven Hartland dsl_dataset_user_release(nvlist_t *holds, nvlist_t *errlist)
618a7a845e4SSteven Hartland {
619a7a845e4SSteven Hartland return (dsl_dataset_user_release_impl(holds, errlist, NULL));
620a7a845e4SSteven Hartland }
621a7a845e4SSteven Hartland
622a7a845e4SSteven Hartland /*
623a7a845e4SSteven Hartland * holds is nvl of snapdsobj -> { holdname, ... }
624a7a845e4SSteven Hartland */
625a7a845e4SSteven Hartland void
dsl_dataset_user_release_tmp(struct dsl_pool * dp,nvlist_t * holds)626a7a845e4SSteven Hartland dsl_dataset_user_release_tmp(struct dsl_pool *dp, nvlist_t *holds)
627a7a845e4SSteven Hartland {
628a7a845e4SSteven Hartland ASSERT(dp != NULL);
629a7a845e4SSteven Hartland (void) dsl_dataset_user_release_impl(holds, NULL, dp);
630a7a845e4SSteven Hartland }
631a7a845e4SSteven Hartland
6323b2aab18SMatthew Ahrens int
dsl_dataset_get_holds(const char * dsname,nvlist_t * nvl)6333b2aab18SMatthew Ahrens dsl_dataset_get_holds(const char *dsname, nvlist_t *nvl)
6343b2aab18SMatthew Ahrens {
6353b2aab18SMatthew Ahrens dsl_pool_t *dp;
6363b2aab18SMatthew Ahrens dsl_dataset_t *ds;
6373b2aab18SMatthew Ahrens int err;
6383b2aab18SMatthew Ahrens
6393b2aab18SMatthew Ahrens err = dsl_pool_hold(dsname, FTAG, &dp);
6403b2aab18SMatthew Ahrens if (err != 0)
6413b2aab18SMatthew Ahrens return (err);
6423b2aab18SMatthew Ahrens err = dsl_dataset_hold(dp, dsname, FTAG, &ds);
6433b2aab18SMatthew Ahrens if (err != 0) {
6443b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG);
6453b2aab18SMatthew Ahrens return (err);
6463b2aab18SMatthew Ahrens }
6473b2aab18SMatthew Ahrens
648c1379625SJustin T. Gibbs if (dsl_dataset_phys(ds)->ds_userrefs_obj != 0) {
6493b2aab18SMatthew Ahrens zap_attribute_t *za;
6503b2aab18SMatthew Ahrens zap_cursor_t zc;
6513b2aab18SMatthew Ahrens
6523b2aab18SMatthew Ahrens za = kmem_alloc(sizeof (zap_attribute_t), KM_SLEEP);
6533b2aab18SMatthew Ahrens for (zap_cursor_init(&zc, ds->ds_dir->dd_pool->dp_meta_objset,
654c1379625SJustin T. Gibbs dsl_dataset_phys(ds)->ds_userrefs_obj);
6553b2aab18SMatthew Ahrens zap_cursor_retrieve(&zc, za) == 0;
6563b2aab18SMatthew Ahrens zap_cursor_advance(&zc)) {
6573b2aab18SMatthew Ahrens fnvlist_add_uint64(nvl, za->za_name,
6583b2aab18SMatthew Ahrens za->za_first_integer);
6593b2aab18SMatthew Ahrens }
6603b2aab18SMatthew Ahrens zap_cursor_fini(&zc);
6613b2aab18SMatthew Ahrens kmem_free(za, sizeof (zap_attribute_t));
6623b2aab18SMatthew Ahrens }
6633b2aab18SMatthew Ahrens dsl_dataset_rele(ds, FTAG);
6643b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG);
6653b2aab18SMatthew Ahrens return (0);
6663b2aab18SMatthew Ahrens }
667