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