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