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