Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/contrib/openzfs/module/zfs/dsl_userhold.c
48383 views
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
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
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
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
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
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
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
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
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
VERIFY0(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
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
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
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
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
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
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
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
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
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
694