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