]>
Commit | Line | Data |
---|---|---|
da536844 MA |
1 | /* |
2 | * CDDL HEADER START | |
3 | * | |
4 | * This file and its contents are supplied under the terms of the | |
5 | * Common Development and Distribution License ("CDDL"), version 1.0. | |
6 | * You may only use this file in accordance with the terms of version | |
7 | * 1.0 of the CDDL. | |
8 | * | |
9 | * A full copy of the text of the CDDL should have accompanied this | |
10 | * source. A copy of the CDDL is also available via the Internet at | |
11 | * http://www.illumos.org/license/CDDL. | |
12 | * | |
13 | * CDDL HEADER END | |
14 | */ | |
9b7b9cd3 | 15 | |
da536844 | 16 | /* |
3d45fdd6 | 17 | * Copyright (c) 2013, 2014 by Delphix. All rights reserved. |
9b7b9cd3 | 18 | * Copyright 2017 Nexenta Systems, Inc. |
da536844 MA |
19 | */ |
20 | ||
21 | #include <sys/zfs_context.h> | |
22 | #include <sys/dsl_dataset.h> | |
23 | #include <sys/dsl_dir.h> | |
24 | #include <sys/dsl_prop.h> | |
25 | #include <sys/dsl_synctask.h> | |
26 | #include <sys/dmu_impl.h> | |
27 | #include <sys/dmu_tx.h> | |
28 | #include <sys/arc.h> | |
29 | #include <sys/zap.h> | |
30 | #include <sys/zfeature.h> | |
31 | #include <sys/spa.h> | |
32 | #include <sys/dsl_bookmark.h> | |
33 | #include <zfs_namecheck.h> | |
34 | ||
35 | static int | |
36 | dsl_bookmark_hold_ds(dsl_pool_t *dp, const char *fullname, | |
37 | dsl_dataset_t **dsp, void *tag, char **shortnamep) | |
38 | { | |
eca7b760 | 39 | char buf[ZFS_MAX_DATASET_NAME_LEN]; |
da536844 MA |
40 | char *hashp; |
41 | ||
eca7b760 | 42 | if (strlen(fullname) >= ZFS_MAX_DATASET_NAME_LEN) |
da536844 MA |
43 | return (SET_ERROR(ENAMETOOLONG)); |
44 | hashp = strchr(fullname, '#'); | |
45 | if (hashp == NULL) | |
46 | return (SET_ERROR(EINVAL)); | |
47 | ||
48 | *shortnamep = hashp + 1; | |
49 | if (zfs_component_namecheck(*shortnamep, NULL, NULL)) | |
50 | return (SET_ERROR(EINVAL)); | |
51 | (void) strlcpy(buf, fullname, hashp - fullname + 1); | |
52 | return (dsl_dataset_hold(dp, buf, tag, dsp)); | |
53 | } | |
54 | ||
55 | /* | |
56 | * Returns ESRCH if bookmark is not found. | |
57 | */ | |
58 | static int | |
59 | dsl_dataset_bmark_lookup(dsl_dataset_t *ds, const char *shortname, | |
60 | zfs_bookmark_phys_t *bmark_phys) | |
61 | { | |
62 | objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset; | |
63 | uint64_t bmark_zapobj = ds->ds_bookmarks; | |
9b7b9cd3 | 64 | matchtype_t mt = 0; |
da536844 MA |
65 | int err; |
66 | ||
67 | if (bmark_zapobj == 0) | |
68 | return (SET_ERROR(ESRCH)); | |
69 | ||
d683ddbb | 70 | if (dsl_dataset_phys(ds)->ds_flags & DS_FLAG_CI_DATASET) |
9b7b9cd3 | 71 | mt = MT_NORMALIZE; |
da536844 | 72 | |
579ce7c5 TC |
73 | /* |
74 | * Zero out the bookmark in case the one stored on disk | |
75 | * is in an older, shorter format. | |
76 | */ | |
77 | bzero(bmark_phys, sizeof (*bmark_phys)); | |
78 | ||
da536844 MA |
79 | err = zap_lookup_norm(mos, bmark_zapobj, shortname, sizeof (uint64_t), |
80 | sizeof (*bmark_phys) / sizeof (uint64_t), bmark_phys, mt, | |
81 | NULL, 0, NULL); | |
82 | ||
83 | return (err == ENOENT ? ESRCH : err); | |
84 | } | |
85 | ||
86 | /* | |
87 | * If later_ds is non-NULL, this will return EXDEV if the the specified bookmark | |
88 | * does not represents an earlier point in later_ds's timeline. | |
89 | * | |
90 | * Returns ENOENT if the dataset containing the bookmark does not exist. | |
91 | * Returns ESRCH if the dataset exists but the bookmark was not found in it. | |
92 | */ | |
93 | int | |
94 | dsl_bookmark_lookup(dsl_pool_t *dp, const char *fullname, | |
95 | dsl_dataset_t *later_ds, zfs_bookmark_phys_t *bmp) | |
96 | { | |
97 | char *shortname; | |
98 | dsl_dataset_t *ds; | |
99 | int error; | |
100 | ||
101 | error = dsl_bookmark_hold_ds(dp, fullname, &ds, FTAG, &shortname); | |
102 | if (error != 0) | |
103 | return (error); | |
104 | ||
105 | error = dsl_dataset_bmark_lookup(ds, shortname, bmp); | |
106 | if (error == 0 && later_ds != NULL) { | |
107 | if (!dsl_dataset_is_before(later_ds, ds, bmp->zbm_creation_txg)) | |
108 | error = SET_ERROR(EXDEV); | |
109 | } | |
110 | dsl_dataset_rele(ds, FTAG); | |
111 | return (error); | |
112 | } | |
113 | ||
114 | typedef struct dsl_bookmark_create_arg { | |
115 | nvlist_t *dbca_bmarks; | |
116 | nvlist_t *dbca_errors; | |
117 | } dsl_bookmark_create_arg_t; | |
118 | ||
119 | static int | |
120 | dsl_bookmark_create_check_impl(dsl_dataset_t *snapds, const char *bookmark_name, | |
121 | dmu_tx_t *tx) | |
122 | { | |
123 | dsl_pool_t *dp = dmu_tx_pool(tx); | |
124 | dsl_dataset_t *bmark_fs; | |
125 | char *shortname; | |
126 | int error; | |
127 | zfs_bookmark_phys_t bmark_phys; | |
128 | ||
0c66c32d | 129 | if (!snapds->ds_is_snapshot) |
da536844 MA |
130 | return (SET_ERROR(EINVAL)); |
131 | ||
132 | error = dsl_bookmark_hold_ds(dp, bookmark_name, | |
133 | &bmark_fs, FTAG, &shortname); | |
134 | if (error != 0) | |
135 | return (error); | |
136 | ||
137 | if (!dsl_dataset_is_before(bmark_fs, snapds, 0)) { | |
138 | dsl_dataset_rele(bmark_fs, FTAG); | |
139 | return (SET_ERROR(EINVAL)); | |
140 | } | |
141 | ||
142 | error = dsl_dataset_bmark_lookup(bmark_fs, shortname, | |
143 | &bmark_phys); | |
144 | dsl_dataset_rele(bmark_fs, FTAG); | |
145 | if (error == 0) | |
146 | return (SET_ERROR(EEXIST)); | |
147 | if (error == ESRCH) | |
148 | return (0); | |
149 | return (error); | |
150 | } | |
151 | ||
152 | static int | |
153 | dsl_bookmark_create_check(void *arg, dmu_tx_t *tx) | |
154 | { | |
155 | dsl_bookmark_create_arg_t *dbca = arg; | |
156 | dsl_pool_t *dp = dmu_tx_pool(tx); | |
157 | int rv = 0; | |
da536844 MA |
158 | |
159 | if (!spa_feature_is_enabled(dp->dp_spa, SPA_FEATURE_BOOKMARKS)) | |
160 | return (SET_ERROR(ENOTSUP)); | |
161 | ||
1c27024e | 162 | for (nvpair_t *pair = nvlist_next_nvpair(dbca->dbca_bmarks, NULL); |
da536844 MA |
163 | pair != NULL; pair = nvlist_next_nvpair(dbca->dbca_bmarks, pair)) { |
164 | dsl_dataset_t *snapds; | |
165 | int error; | |
166 | ||
167 | /* note: validity of nvlist checked by ioctl layer */ | |
168 | error = dsl_dataset_hold(dp, fnvpair_value_string(pair), | |
169 | FTAG, &snapds); | |
170 | if (error == 0) { | |
171 | error = dsl_bookmark_create_check_impl(snapds, | |
172 | nvpair_name(pair), tx); | |
173 | dsl_dataset_rele(snapds, FTAG); | |
174 | } | |
175 | if (error != 0) { | |
176 | fnvlist_add_int32(dbca->dbca_errors, | |
177 | nvpair_name(pair), error); | |
178 | rv = error; | |
179 | } | |
180 | } | |
181 | ||
182 | return (rv); | |
183 | } | |
184 | ||
185 | static void | |
186 | dsl_bookmark_create_sync(void *arg, dmu_tx_t *tx) | |
187 | { | |
188 | dsl_bookmark_create_arg_t *dbca = arg; | |
189 | dsl_pool_t *dp = dmu_tx_pool(tx); | |
190 | objset_t *mos = dp->dp_meta_objset; | |
da536844 MA |
191 | |
192 | ASSERT(spa_feature_is_enabled(dp->dp_spa, SPA_FEATURE_BOOKMARKS)); | |
193 | ||
1c27024e | 194 | for (nvpair_t *pair = nvlist_next_nvpair(dbca->dbca_bmarks, NULL); |
da536844 MA |
195 | pair != NULL; pair = nvlist_next_nvpair(dbca->dbca_bmarks, pair)) { |
196 | dsl_dataset_t *snapds, *bmark_fs; | |
579ce7c5 | 197 | zfs_bookmark_phys_t bmark_phys = { 0 }; |
da536844 | 198 | char *shortname; |
579ce7c5 | 199 | uint32_t bmark_len = BOOKMARK_PHYS_SIZE_V1; |
da536844 MA |
200 | |
201 | VERIFY0(dsl_dataset_hold(dp, fnvpair_value_string(pair), | |
202 | FTAG, &snapds)); | |
203 | VERIFY0(dsl_bookmark_hold_ds(dp, nvpair_name(pair), | |
204 | &bmark_fs, FTAG, &shortname)); | |
205 | if (bmark_fs->ds_bookmarks == 0) { | |
206 | bmark_fs->ds_bookmarks = | |
207 | zap_create_norm(mos, U8_TEXTPREP_TOUPPER, | |
208 | DMU_OTN_ZAP_METADATA, DMU_OT_NONE, 0, tx); | |
209 | spa_feature_incr(dp->dp_spa, SPA_FEATURE_BOOKMARKS, tx); | |
210 | ||
211 | dsl_dataset_zapify(bmark_fs, tx); | |
212 | VERIFY0(zap_add(mos, bmark_fs->ds_object, | |
213 | DS_FIELD_BOOKMARK_NAMES, | |
214 | sizeof (bmark_fs->ds_bookmarks), 1, | |
215 | &bmark_fs->ds_bookmarks, tx)); | |
216 | } | |
217 | ||
d683ddbb JG |
218 | bmark_phys.zbm_guid = dsl_dataset_phys(snapds)->ds_guid; |
219 | bmark_phys.zbm_creation_txg = | |
220 | dsl_dataset_phys(snapds)->ds_creation_txg; | |
da536844 | 221 | bmark_phys.zbm_creation_time = |
d683ddbb | 222 | dsl_dataset_phys(snapds)->ds_creation_time; |
da536844 | 223 | |
f00ab3f2 TC |
224 | /* |
225 | * If the dataset is encrypted create a larger bookmark to | |
226 | * accommodate the IVset guid. The IVset guid was added | |
227 | * after the encryption feature to prevent a problem with | |
228 | * raw sends. If we encounter an encrypted dataset without | |
229 | * an IVset guid we fall back to a normal bookmark. | |
230 | */ | |
231 | if (snapds->ds_dir->dd_crypto_obj != 0 && | |
232 | spa_feature_is_enabled(dp->dp_spa, | |
233 | SPA_FEATURE_BOOKMARK_V2)) { | |
234 | int err = zap_lookup(mos, snapds->ds_object, | |
235 | DS_FIELD_IVSET_GUID, sizeof (uint64_t), 1, | |
236 | &bmark_phys.zbm_ivset_guid); | |
237 | if (err == 0) { | |
238 | bmark_len = BOOKMARK_PHYS_SIZE_V2; | |
239 | spa_feature_incr(dp->dp_spa, | |
240 | SPA_FEATURE_BOOKMARK_V2, tx); | |
241 | } | |
242 | } | |
243 | ||
da536844 MA |
244 | VERIFY0(zap_add(mos, bmark_fs->ds_bookmarks, |
245 | shortname, sizeof (uint64_t), | |
579ce7c5 | 246 | bmark_len / sizeof (uint64_t), &bmark_phys, tx)); |
da536844 MA |
247 | |
248 | spa_history_log_internal_ds(bmark_fs, "bookmark", tx, | |
249 | "name=%s creation_txg=%llu target_snap=%llu", | |
250 | shortname, | |
251 | (longlong_t)bmark_phys.zbm_creation_txg, | |
252 | (longlong_t)snapds->ds_object); | |
253 | ||
254 | dsl_dataset_rele(bmark_fs, FTAG); | |
255 | dsl_dataset_rele(snapds, FTAG); | |
256 | } | |
257 | } | |
258 | ||
259 | /* | |
260 | * The bookmarks must all be in the same pool. | |
261 | */ | |
262 | int | |
263 | dsl_bookmark_create(nvlist_t *bmarks, nvlist_t *errors) | |
264 | { | |
265 | nvpair_t *pair; | |
266 | dsl_bookmark_create_arg_t dbca; | |
267 | ||
268 | pair = nvlist_next_nvpair(bmarks, NULL); | |
269 | if (pair == NULL) | |
270 | return (0); | |
271 | ||
272 | dbca.dbca_bmarks = bmarks; | |
273 | dbca.dbca_errors = errors; | |
274 | ||
275 | return (dsl_sync_task(nvpair_name(pair), dsl_bookmark_create_check, | |
3d45fdd6 MA |
276 | dsl_bookmark_create_sync, &dbca, |
277 | fnvlist_num_pairs(bmarks), ZFS_SPACE_CHECK_NORMAL)); | |
da536844 MA |
278 | } |
279 | ||
280 | int | |
281 | dsl_get_bookmarks_impl(dsl_dataset_t *ds, nvlist_t *props, nvlist_t *outnvl) | |
282 | { | |
283 | int err = 0; | |
284 | zap_cursor_t zc; | |
285 | zap_attribute_t attr; | |
286 | dsl_pool_t *dp = ds->ds_dir->dd_pool; | |
287 | ||
288 | uint64_t bmark_zapobj = ds->ds_bookmarks; | |
289 | if (bmark_zapobj == 0) | |
290 | return (0); | |
291 | ||
292 | for (zap_cursor_init(&zc, dp->dp_meta_objset, bmark_zapobj); | |
293 | zap_cursor_retrieve(&zc, &attr) == 0; | |
294 | zap_cursor_advance(&zc)) { | |
da536844 | 295 | char *bmark_name = attr.za_name; |
f00ab3f2 | 296 | zfs_bookmark_phys_t bmark_phys = { 0 }; |
da536844 MA |
297 | |
298 | err = dsl_dataset_bmark_lookup(ds, bmark_name, &bmark_phys); | |
299 | ASSERT3U(err, !=, ENOENT); | |
300 | if (err != 0) | |
301 | break; | |
302 | ||
1c27024e | 303 | nvlist_t *out_props = fnvlist_alloc(); |
da536844 MA |
304 | if (nvlist_exists(props, |
305 | zfs_prop_to_name(ZFS_PROP_GUID))) { | |
306 | dsl_prop_nvlist_add_uint64(out_props, | |
307 | ZFS_PROP_GUID, bmark_phys.zbm_guid); | |
308 | } | |
309 | if (nvlist_exists(props, | |
310 | zfs_prop_to_name(ZFS_PROP_CREATETXG))) { | |
311 | dsl_prop_nvlist_add_uint64(out_props, | |
312 | ZFS_PROP_CREATETXG, bmark_phys.zbm_creation_txg); | |
313 | } | |
314 | if (nvlist_exists(props, | |
315 | zfs_prop_to_name(ZFS_PROP_CREATION))) { | |
316 | dsl_prop_nvlist_add_uint64(out_props, | |
317 | ZFS_PROP_CREATION, bmark_phys.zbm_creation_time); | |
318 | } | |
f00ab3f2 TC |
319 | if (nvlist_exists(props, |
320 | zfs_prop_to_name(ZFS_PROP_IVSET_GUID))) { | |
321 | dsl_prop_nvlist_add_uint64(out_props, | |
322 | ZFS_PROP_IVSET_GUID, bmark_phys.zbm_ivset_guid); | |
323 | } | |
da536844 MA |
324 | |
325 | fnvlist_add_nvlist(outnvl, bmark_name, out_props); | |
326 | fnvlist_free(out_props); | |
327 | } | |
328 | zap_cursor_fini(&zc); | |
329 | return (err); | |
330 | } | |
331 | ||
332 | /* | |
333 | * Retrieve the bookmarks that exist in the specified dataset, and the | |
334 | * requested properties of each bookmark. | |
335 | * | |
336 | * The "props" nvlist specifies which properties are requested. | |
337 | * See lzc_get_bookmarks() for the list of valid properties. | |
338 | */ | |
339 | int | |
340 | dsl_get_bookmarks(const char *dsname, nvlist_t *props, nvlist_t *outnvl) | |
341 | { | |
342 | dsl_pool_t *dp; | |
343 | dsl_dataset_t *ds; | |
344 | int err; | |
345 | ||
346 | err = dsl_pool_hold(dsname, FTAG, &dp); | |
347 | if (err != 0) | |
348 | return (err); | |
349 | err = dsl_dataset_hold(dp, dsname, FTAG, &ds); | |
350 | if (err != 0) { | |
351 | dsl_pool_rele(dp, FTAG); | |
352 | return (err); | |
353 | } | |
354 | ||
355 | err = dsl_get_bookmarks_impl(ds, props, outnvl); | |
356 | ||
357 | dsl_dataset_rele(ds, FTAG); | |
358 | dsl_pool_rele(dp, FTAG); | |
359 | return (err); | |
360 | } | |
361 | ||
362 | typedef struct dsl_bookmark_destroy_arg { | |
363 | nvlist_t *dbda_bmarks; | |
364 | nvlist_t *dbda_success; | |
365 | nvlist_t *dbda_errors; | |
366 | } dsl_bookmark_destroy_arg_t; | |
367 | ||
368 | static int | |
369 | dsl_dataset_bookmark_remove(dsl_dataset_t *ds, const char *name, dmu_tx_t *tx) | |
370 | { | |
f00ab3f2 | 371 | int err; |
da536844 MA |
372 | objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset; |
373 | uint64_t bmark_zapobj = ds->ds_bookmarks; | |
9b7b9cd3 | 374 | matchtype_t mt = 0; |
f00ab3f2 | 375 | uint64_t int_size, num_ints; |
da536844 | 376 | |
d683ddbb | 377 | if (dsl_dataset_phys(ds)->ds_flags & DS_FLAG_CI_DATASET) |
9b7b9cd3 | 378 | mt = MT_NORMALIZE; |
da536844 | 379 | |
f00ab3f2 TC |
380 | err = zap_length(mos, bmark_zapobj, name, &int_size, &num_ints); |
381 | if (err != 0) | |
382 | return (err); | |
383 | ||
384 | ASSERT3U(int_size, ==, sizeof (uint64_t)); | |
385 | ||
386 | if (num_ints * int_size > BOOKMARK_PHYS_SIZE_V1) { | |
387 | spa_feature_decr(dmu_objset_spa(mos), | |
388 | SPA_FEATURE_BOOKMARK_V2, tx); | |
389 | } | |
390 | ||
da536844 MA |
391 | return (zap_remove_norm(mos, bmark_zapobj, name, mt, tx)); |
392 | } | |
393 | ||
394 | static int | |
395 | dsl_bookmark_destroy_check(void *arg, dmu_tx_t *tx) | |
396 | { | |
397 | dsl_bookmark_destroy_arg_t *dbda = arg; | |
398 | dsl_pool_t *dp = dmu_tx_pool(tx); | |
399 | int rv = 0; | |
da536844 | 400 | |
c6f6767e MA |
401 | ASSERT(nvlist_empty(dbda->dbda_success)); |
402 | ASSERT(nvlist_empty(dbda->dbda_errors)); | |
403 | ||
da536844 MA |
404 | if (!spa_feature_is_enabled(dp->dp_spa, SPA_FEATURE_BOOKMARKS)) |
405 | return (0); | |
406 | ||
1c27024e | 407 | for (nvpair_t *pair = nvlist_next_nvpair(dbda->dbda_bmarks, NULL); |
da536844 MA |
408 | pair != NULL; pair = nvlist_next_nvpair(dbda->dbda_bmarks, pair)) { |
409 | const char *fullname = nvpair_name(pair); | |
410 | dsl_dataset_t *ds; | |
411 | zfs_bookmark_phys_t bm; | |
412 | int error; | |
413 | char *shortname; | |
414 | ||
415 | error = dsl_bookmark_hold_ds(dp, fullname, &ds, | |
416 | FTAG, &shortname); | |
417 | if (error == ENOENT) { | |
418 | /* ignore it; the bookmark is "already destroyed" */ | |
419 | continue; | |
420 | } | |
421 | if (error == 0) { | |
422 | error = dsl_dataset_bmark_lookup(ds, shortname, &bm); | |
423 | dsl_dataset_rele(ds, FTAG); | |
424 | if (error == ESRCH) { | |
425 | /* | |
426 | * ignore it; the bookmark is | |
427 | * "already destroyed" | |
428 | */ | |
429 | continue; | |
430 | } | |
431 | } | |
432 | if (error == 0) { | |
c6f6767e MA |
433 | if (dmu_tx_is_syncing(tx)) { |
434 | fnvlist_add_boolean(dbda->dbda_success, | |
435 | fullname); | |
436 | } | |
da536844 MA |
437 | } else { |
438 | fnvlist_add_int32(dbda->dbda_errors, fullname, error); | |
439 | rv = error; | |
440 | } | |
441 | } | |
442 | return (rv); | |
443 | } | |
444 | ||
445 | static void | |
446 | dsl_bookmark_destroy_sync(void *arg, dmu_tx_t *tx) | |
447 | { | |
448 | dsl_bookmark_destroy_arg_t *dbda = arg; | |
449 | dsl_pool_t *dp = dmu_tx_pool(tx); | |
450 | objset_t *mos = dp->dp_meta_objset; | |
da536844 | 451 | |
1c27024e | 452 | for (nvpair_t *pair = nvlist_next_nvpair(dbda->dbda_success, NULL); |
da536844 MA |
453 | pair != NULL; pair = nvlist_next_nvpair(dbda->dbda_success, pair)) { |
454 | dsl_dataset_t *ds; | |
455 | char *shortname; | |
456 | uint64_t zap_cnt; | |
457 | ||
458 | VERIFY0(dsl_bookmark_hold_ds(dp, nvpair_name(pair), | |
459 | &ds, FTAG, &shortname)); | |
460 | VERIFY0(dsl_dataset_bookmark_remove(ds, shortname, tx)); | |
461 | ||
462 | /* | |
463 | * If all of this dataset's bookmarks have been destroyed, | |
464 | * free the zap object and decrement the feature's use count. | |
465 | */ | |
466 | VERIFY0(zap_count(mos, ds->ds_bookmarks, | |
467 | &zap_cnt)); | |
468 | if (zap_cnt == 0) { | |
469 | dmu_buf_will_dirty(ds->ds_dbuf, tx); | |
470 | VERIFY0(zap_destroy(mos, ds->ds_bookmarks, tx)); | |
471 | ds->ds_bookmarks = 0; | |
472 | spa_feature_decr(dp->dp_spa, SPA_FEATURE_BOOKMARKS, tx); | |
473 | VERIFY0(zap_remove(mos, ds->ds_object, | |
474 | DS_FIELD_BOOKMARK_NAMES, tx)); | |
475 | } | |
476 | ||
477 | spa_history_log_internal_ds(ds, "remove bookmark", tx, | |
478 | "name=%s", shortname); | |
479 | ||
480 | dsl_dataset_rele(ds, FTAG); | |
481 | } | |
482 | } | |
483 | ||
484 | /* | |
485 | * The bookmarks must all be in the same pool. | |
486 | */ | |
487 | int | |
488 | dsl_bookmark_destroy(nvlist_t *bmarks, nvlist_t *errors) | |
489 | { | |
490 | int rv; | |
491 | dsl_bookmark_destroy_arg_t dbda; | |
492 | nvpair_t *pair = nvlist_next_nvpair(bmarks, NULL); | |
493 | if (pair == NULL) | |
494 | return (0); | |
495 | ||
496 | dbda.dbda_bmarks = bmarks; | |
497 | dbda.dbda_errors = errors; | |
498 | dbda.dbda_success = fnvlist_alloc(); | |
499 | ||
500 | rv = dsl_sync_task(nvpair_name(pair), dsl_bookmark_destroy_check, | |
3d45fdd6 MA |
501 | dsl_bookmark_destroy_sync, &dbda, fnvlist_num_pairs(bmarks), |
502 | ZFS_SPACE_CHECK_RESERVED); | |
da536844 MA |
503 | fnvlist_free(dbda.dbda_success); |
504 | return (rv); | |
505 | } |