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