1260154Sdelphij/* 2260154Sdelphij * CDDL HEADER START 3260154Sdelphij * 4260154Sdelphij * This file and its contents are supplied under the terms of the 5260154Sdelphij * Common Development and Distribution License ("CDDL"), version 1.0. 6260154Sdelphij * You may only use this file in accordance with the terms of version 7260154Sdelphij * 1.0 of the CDDL. 8260154Sdelphij * 9260154Sdelphij * A full copy of the text of the CDDL should have accompanied this 10260154Sdelphij * source. A copy of the CDDL is also available via the Internet at 11260154Sdelphij * http://www.illumos.org/license/CDDL. 12260154Sdelphij * 13260154Sdelphij * CDDL HEADER END 14260154Sdelphij */ 15260154Sdelphij/* 16260154Sdelphij * Copyright (c) 2013 by Delphix. All rights reserved. 17260154Sdelphij */ 18260154Sdelphij 19260154Sdelphij#include <sys/zfs_context.h> 20260154Sdelphij#include <sys/dsl_dataset.h> 21260154Sdelphij#include <sys/dsl_dir.h> 22260154Sdelphij#include <sys/dsl_prop.h> 23260154Sdelphij#include <sys/dsl_synctask.h> 24260154Sdelphij#include <sys/dmu_impl.h> 25260154Sdelphij#include <sys/dmu_tx.h> 26260154Sdelphij#include <sys/arc.h> 27260154Sdelphij#include <sys/zap.h> 28260154Sdelphij#include <sys/zfeature.h> 29260154Sdelphij#include <sys/spa.h> 30260154Sdelphij#include <sys/dsl_bookmark.h> 31260154Sdelphij#include <zfs_namecheck.h> 32260154Sdelphij 33260154Sdelphijstatic int 34260154Sdelphijdsl_bookmark_hold_ds(dsl_pool_t *dp, const char *fullname, 35260154Sdelphij dsl_dataset_t **dsp, void *tag, char **shortnamep) 36260154Sdelphij{ 37260154Sdelphij char buf[MAXNAMELEN]; 38260154Sdelphij char *hashp; 39260154Sdelphij 40260154Sdelphij if (strlen(fullname) >= MAXNAMELEN) 41260154Sdelphij return (SET_ERROR(ENAMETOOLONG)); 42260154Sdelphij hashp = strchr(fullname, '#'); 43260154Sdelphij if (hashp == NULL) 44260154Sdelphij return (SET_ERROR(EINVAL)); 45260154Sdelphij 46260154Sdelphij *shortnamep = hashp + 1; 47260154Sdelphij if (zfs_component_namecheck(*shortnamep, NULL, NULL)) 48260154Sdelphij return (SET_ERROR(EINVAL)); 49260154Sdelphij (void) strlcpy(buf, fullname, hashp - fullname + 1); 50260154Sdelphij return (dsl_dataset_hold(dp, buf, tag, dsp)); 51260154Sdelphij} 52260154Sdelphij 53260154Sdelphij/* 54260154Sdelphij * Returns ESRCH if bookmark is not found. 55260154Sdelphij */ 56260154Sdelphijstatic int 57260154Sdelphijdsl_dataset_bmark_lookup(dsl_dataset_t *ds, const char *shortname, 58260154Sdelphij zfs_bookmark_phys_t *bmark_phys) 59260154Sdelphij{ 60260154Sdelphij objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset; 61260154Sdelphij uint64_t bmark_zapobj = ds->ds_bookmarks; 62260154Sdelphij matchtype_t mt; 63260154Sdelphij int err; 64260154Sdelphij 65260154Sdelphij if (bmark_zapobj == 0) 66260154Sdelphij return (SET_ERROR(ESRCH)); 67260154Sdelphij 68260154Sdelphij if (ds->ds_phys->ds_flags & DS_FLAG_CI_DATASET) 69260154Sdelphij mt = MT_FIRST; 70260154Sdelphij else 71260154Sdelphij mt = MT_EXACT; 72260154Sdelphij 73260154Sdelphij err = zap_lookup_norm(mos, bmark_zapobj, shortname, sizeof (uint64_t), 74260154Sdelphij sizeof (*bmark_phys) / sizeof (uint64_t), bmark_phys, mt, 75260154Sdelphij NULL, 0, NULL); 76260154Sdelphij 77260154Sdelphij return (err == ENOENT ? ESRCH : err); 78260154Sdelphij} 79260154Sdelphij 80260154Sdelphij/* 81260154Sdelphij * If later_ds is non-NULL, this will return EXDEV if the the specified bookmark 82260154Sdelphij * does not represents an earlier point in later_ds's timeline. 83260154Sdelphij * 84260154Sdelphij * Returns ENOENT if the dataset containing the bookmark does not exist. 85260154Sdelphij * Returns ESRCH if the dataset exists but the bookmark was not found in it. 86260154Sdelphij */ 87260154Sdelphijint 88260154Sdelphijdsl_bookmark_lookup(dsl_pool_t *dp, const char *fullname, 89260154Sdelphij dsl_dataset_t *later_ds, zfs_bookmark_phys_t *bmp) 90260154Sdelphij{ 91260154Sdelphij char *shortname; 92260154Sdelphij dsl_dataset_t *ds; 93260154Sdelphij int error; 94260154Sdelphij 95260154Sdelphij error = dsl_bookmark_hold_ds(dp, fullname, &ds, FTAG, &shortname); 96260154Sdelphij if (error != 0) 97260154Sdelphij return (error); 98260154Sdelphij 99260154Sdelphij error = dsl_dataset_bmark_lookup(ds, shortname, bmp); 100260154Sdelphij if (error == 0 && later_ds != NULL) { 101260154Sdelphij if (!dsl_dataset_is_before(later_ds, ds, bmp->zbm_creation_txg)) 102260154Sdelphij error = SET_ERROR(EXDEV); 103260154Sdelphij } 104260154Sdelphij dsl_dataset_rele(ds, FTAG); 105260154Sdelphij return (error); 106260154Sdelphij} 107260154Sdelphij 108260154Sdelphijtypedef struct dsl_bookmark_create_arg { 109260154Sdelphij nvlist_t *dbca_bmarks; 110260154Sdelphij nvlist_t *dbca_errors; 111260154Sdelphij} dsl_bookmark_create_arg_t; 112260154Sdelphij 113260154Sdelphijstatic int 114260154Sdelphijdsl_bookmark_create_check_impl(dsl_dataset_t *snapds, const char *bookmark_name, 115260154Sdelphij dmu_tx_t *tx) 116260154Sdelphij{ 117260154Sdelphij dsl_pool_t *dp = dmu_tx_pool(tx); 118260154Sdelphij dsl_dataset_t *bmark_fs; 119260154Sdelphij char *shortname; 120260154Sdelphij int error; 121260154Sdelphij zfs_bookmark_phys_t bmark_phys; 122260154Sdelphij 123260154Sdelphij if (!dsl_dataset_is_snapshot(snapds)) 124260154Sdelphij return (SET_ERROR(EINVAL)); 125260154Sdelphij 126260154Sdelphij error = dsl_bookmark_hold_ds(dp, bookmark_name, 127260154Sdelphij &bmark_fs, FTAG, &shortname); 128260154Sdelphij if (error != 0) 129260154Sdelphij return (error); 130260154Sdelphij 131260154Sdelphij if (!dsl_dataset_is_before(bmark_fs, snapds, 0)) { 132260154Sdelphij dsl_dataset_rele(bmark_fs, FTAG); 133260154Sdelphij return (SET_ERROR(EINVAL)); 134260154Sdelphij } 135260154Sdelphij 136260154Sdelphij error = dsl_dataset_bmark_lookup(bmark_fs, shortname, 137260154Sdelphij &bmark_phys); 138260154Sdelphij dsl_dataset_rele(bmark_fs, FTAG); 139260154Sdelphij if (error == 0) 140260154Sdelphij return (SET_ERROR(EEXIST)); 141260154Sdelphij if (error == ESRCH) 142260154Sdelphij return (0); 143260154Sdelphij return (error); 144260154Sdelphij} 145260154Sdelphij 146260154Sdelphijstatic int 147260154Sdelphijdsl_bookmark_create_check(void *arg, dmu_tx_t *tx) 148260154Sdelphij{ 149260154Sdelphij dsl_bookmark_create_arg_t *dbca = arg; 150260154Sdelphij dsl_pool_t *dp = dmu_tx_pool(tx); 151260154Sdelphij int rv = 0; 152260154Sdelphij 153260154Sdelphij if (!spa_feature_is_enabled(dp->dp_spa, SPA_FEATURE_BOOKMARKS)) 154260154Sdelphij return (SET_ERROR(ENOTSUP)); 155260154Sdelphij 156260154Sdelphij for (nvpair_t *pair = nvlist_next_nvpair(dbca->dbca_bmarks, NULL); 157260154Sdelphij pair != NULL; pair = nvlist_next_nvpair(dbca->dbca_bmarks, pair)) { 158260154Sdelphij dsl_dataset_t *snapds; 159260154Sdelphij int error; 160260154Sdelphij 161260154Sdelphij /* note: validity of nvlist checked by ioctl layer */ 162260154Sdelphij error = dsl_dataset_hold(dp, fnvpair_value_string(pair), 163260154Sdelphij FTAG, &snapds); 164260154Sdelphij if (error == 0) { 165260154Sdelphij error = dsl_bookmark_create_check_impl(snapds, 166260154Sdelphij nvpair_name(pair), tx); 167260154Sdelphij dsl_dataset_rele(snapds, FTAG); 168260154Sdelphij } 169260154Sdelphij if (error != 0) { 170260154Sdelphij fnvlist_add_int32(dbca->dbca_errors, 171260154Sdelphij nvpair_name(pair), error); 172260154Sdelphij rv = error; 173260154Sdelphij } 174260154Sdelphij } 175260154Sdelphij 176260154Sdelphij return (rv); 177260154Sdelphij} 178260154Sdelphij 179260154Sdelphijstatic void 180260154Sdelphijdsl_bookmark_create_sync(void *arg, dmu_tx_t *tx) 181260154Sdelphij{ 182260154Sdelphij dsl_bookmark_create_arg_t *dbca = arg; 183260154Sdelphij dsl_pool_t *dp = dmu_tx_pool(tx); 184260154Sdelphij objset_t *mos = dp->dp_meta_objset; 185260154Sdelphij 186260154Sdelphij ASSERT(spa_feature_is_enabled(dp->dp_spa, SPA_FEATURE_BOOKMARKS)); 187260154Sdelphij 188260154Sdelphij for (nvpair_t *pair = nvlist_next_nvpair(dbca->dbca_bmarks, NULL); 189260154Sdelphij pair != NULL; pair = nvlist_next_nvpair(dbca->dbca_bmarks, pair)) { 190260154Sdelphij dsl_dataset_t *snapds, *bmark_fs; 191260154Sdelphij zfs_bookmark_phys_t bmark_phys; 192260154Sdelphij char *shortname; 193260154Sdelphij 194260154Sdelphij VERIFY0(dsl_dataset_hold(dp, fnvpair_value_string(pair), 195260154Sdelphij FTAG, &snapds)); 196260154Sdelphij VERIFY0(dsl_bookmark_hold_ds(dp, nvpair_name(pair), 197260154Sdelphij &bmark_fs, FTAG, &shortname)); 198260154Sdelphij if (bmark_fs->ds_bookmarks == 0) { 199260154Sdelphij bmark_fs->ds_bookmarks = 200260154Sdelphij zap_create_norm(mos, U8_TEXTPREP_TOUPPER, 201260154Sdelphij DMU_OTN_ZAP_METADATA, DMU_OT_NONE, 0, tx); 202260154Sdelphij spa_feature_incr(dp->dp_spa, SPA_FEATURE_BOOKMARKS, tx); 203260154Sdelphij 204260154Sdelphij dsl_dataset_zapify(bmark_fs, tx); 205260154Sdelphij VERIFY0(zap_add(mos, bmark_fs->ds_object, 206260154Sdelphij DS_FIELD_BOOKMARK_NAMES, 207260154Sdelphij sizeof (bmark_fs->ds_bookmarks), 1, 208260154Sdelphij &bmark_fs->ds_bookmarks, tx)); 209260154Sdelphij } 210260154Sdelphij 211260154Sdelphij bmark_phys.zbm_guid = snapds->ds_phys->ds_guid; 212260154Sdelphij bmark_phys.zbm_creation_txg = snapds->ds_phys->ds_creation_txg; 213260154Sdelphij bmark_phys.zbm_creation_time = 214260154Sdelphij snapds->ds_phys->ds_creation_time; 215260154Sdelphij 216260154Sdelphij VERIFY0(zap_add(mos, bmark_fs->ds_bookmarks, 217260154Sdelphij shortname, sizeof (uint64_t), 218260154Sdelphij sizeof (zfs_bookmark_phys_t) / sizeof (uint64_t), 219260154Sdelphij &bmark_phys, tx)); 220260154Sdelphij 221260154Sdelphij spa_history_log_internal_ds(bmark_fs, "bookmark", tx, 222260154Sdelphij "name=%s creation_txg=%llu target_snap=%llu", 223260154Sdelphij shortname, 224260154Sdelphij (longlong_t)bmark_phys.zbm_creation_txg, 225260154Sdelphij (longlong_t)snapds->ds_object); 226260154Sdelphij 227260154Sdelphij dsl_dataset_rele(bmark_fs, FTAG); 228260154Sdelphij dsl_dataset_rele(snapds, FTAG); 229260154Sdelphij } 230260154Sdelphij} 231260154Sdelphij 232260154Sdelphij/* 233260154Sdelphij * The bookmarks must all be in the same pool. 234260154Sdelphij */ 235260154Sdelphijint 236260154Sdelphijdsl_bookmark_create(nvlist_t *bmarks, nvlist_t *errors) 237260154Sdelphij{ 238260154Sdelphij nvpair_t *pair; 239260154Sdelphij dsl_bookmark_create_arg_t dbca; 240260154Sdelphij 241260154Sdelphij pair = nvlist_next_nvpair(bmarks, NULL); 242260154Sdelphij if (pair == NULL) 243260154Sdelphij return (0); 244260154Sdelphij 245260154Sdelphij dbca.dbca_bmarks = bmarks; 246260154Sdelphij dbca.dbca_errors = errors; 247260154Sdelphij 248260154Sdelphij return (dsl_sync_task(nvpair_name(pair), dsl_bookmark_create_check, 249260154Sdelphij dsl_bookmark_create_sync, &dbca, fnvlist_num_pairs(bmarks))); 250260154Sdelphij} 251260154Sdelphij 252260154Sdelphijint 253260154Sdelphijdsl_get_bookmarks_impl(dsl_dataset_t *ds, nvlist_t *props, nvlist_t *outnvl) 254260154Sdelphij{ 255260154Sdelphij int err = 0; 256260154Sdelphij zap_cursor_t zc; 257260154Sdelphij zap_attribute_t attr; 258260154Sdelphij dsl_pool_t *dp = ds->ds_dir->dd_pool; 259260154Sdelphij 260260154Sdelphij uint64_t bmark_zapobj = ds->ds_bookmarks; 261260154Sdelphij if (bmark_zapobj == 0) 262260154Sdelphij return (0); 263260154Sdelphij 264260154Sdelphij for (zap_cursor_init(&zc, dp->dp_meta_objset, bmark_zapobj); 265260154Sdelphij zap_cursor_retrieve(&zc, &attr) == 0; 266260154Sdelphij zap_cursor_advance(&zc)) { 267260154Sdelphij char *bmark_name = attr.za_name; 268260154Sdelphij zfs_bookmark_phys_t bmark_phys; 269260154Sdelphij 270260154Sdelphij err = dsl_dataset_bmark_lookup(ds, bmark_name, &bmark_phys); 271260154Sdelphij ASSERT3U(err, !=, ENOENT); 272260154Sdelphij if (err != 0) 273260154Sdelphij break; 274260154Sdelphij 275260154Sdelphij nvlist_t *out_props = fnvlist_alloc(); 276260154Sdelphij if (nvlist_exists(props, 277260154Sdelphij zfs_prop_to_name(ZFS_PROP_GUID))) { 278260154Sdelphij dsl_prop_nvlist_add_uint64(out_props, 279260154Sdelphij ZFS_PROP_GUID, bmark_phys.zbm_guid); 280260154Sdelphij } 281260154Sdelphij if (nvlist_exists(props, 282260154Sdelphij zfs_prop_to_name(ZFS_PROP_CREATETXG))) { 283260154Sdelphij dsl_prop_nvlist_add_uint64(out_props, 284260154Sdelphij ZFS_PROP_CREATETXG, bmark_phys.zbm_creation_txg); 285260154Sdelphij } 286260154Sdelphij if (nvlist_exists(props, 287260154Sdelphij zfs_prop_to_name(ZFS_PROP_CREATION))) { 288260154Sdelphij dsl_prop_nvlist_add_uint64(out_props, 289260154Sdelphij ZFS_PROP_CREATION, bmark_phys.zbm_creation_time); 290260154Sdelphij } 291260154Sdelphij 292260154Sdelphij fnvlist_add_nvlist(outnvl, bmark_name, out_props); 293260154Sdelphij fnvlist_free(out_props); 294260154Sdelphij } 295260154Sdelphij zap_cursor_fini(&zc); 296260154Sdelphij return (err); 297260154Sdelphij} 298260154Sdelphij 299260154Sdelphij/* 300260154Sdelphij * Retrieve the bookmarks that exist in the specified dataset, and the 301260154Sdelphij * requested properties of each bookmark. 302260154Sdelphij * 303260154Sdelphij * The "props" nvlist specifies which properties are requested. 304260154Sdelphij * See lzc_get_bookmarks() for the list of valid properties. 305260154Sdelphij */ 306260154Sdelphijint 307260154Sdelphijdsl_get_bookmarks(const char *dsname, nvlist_t *props, nvlist_t *outnvl) 308260154Sdelphij{ 309260154Sdelphij dsl_pool_t *dp; 310260154Sdelphij dsl_dataset_t *ds; 311260154Sdelphij int err; 312260154Sdelphij 313260154Sdelphij err = dsl_pool_hold(dsname, FTAG, &dp); 314260154Sdelphij if (err != 0) 315260154Sdelphij return (err); 316260154Sdelphij err = dsl_dataset_hold(dp, dsname, FTAG, &ds); 317260154Sdelphij if (err != 0) { 318260154Sdelphij dsl_pool_rele(dp, FTAG); 319260154Sdelphij return (err); 320260154Sdelphij } 321260154Sdelphij 322260154Sdelphij err = dsl_get_bookmarks_impl(ds, props, outnvl); 323260154Sdelphij 324260154Sdelphij dsl_dataset_rele(ds, FTAG); 325260154Sdelphij dsl_pool_rele(dp, FTAG); 326260154Sdelphij return (err); 327260154Sdelphij} 328260154Sdelphij 329260154Sdelphijtypedef struct dsl_bookmark_destroy_arg { 330260154Sdelphij nvlist_t *dbda_bmarks; 331260154Sdelphij nvlist_t *dbda_success; 332260154Sdelphij nvlist_t *dbda_errors; 333260154Sdelphij} dsl_bookmark_destroy_arg_t; 334260154Sdelphij 335260154Sdelphijstatic int 336260154Sdelphijdsl_dataset_bookmark_remove(dsl_dataset_t *ds, const char *name, dmu_tx_t *tx) 337260154Sdelphij{ 338260154Sdelphij objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset; 339260154Sdelphij uint64_t bmark_zapobj = ds->ds_bookmarks; 340260154Sdelphij matchtype_t mt; 341260154Sdelphij 342260154Sdelphij if (ds->ds_phys->ds_flags & DS_FLAG_CI_DATASET) 343260154Sdelphij mt = MT_FIRST; 344260154Sdelphij else 345260154Sdelphij mt = MT_EXACT; 346260154Sdelphij 347260154Sdelphij return (zap_remove_norm(mos, bmark_zapobj, name, mt, tx)); 348260154Sdelphij} 349260154Sdelphij 350260154Sdelphijstatic int 351260154Sdelphijdsl_bookmark_destroy_check(void *arg, dmu_tx_t *tx) 352260154Sdelphij{ 353260154Sdelphij dsl_bookmark_destroy_arg_t *dbda = arg; 354260154Sdelphij dsl_pool_t *dp = dmu_tx_pool(tx); 355260154Sdelphij int rv = 0; 356260154Sdelphij 357260154Sdelphij if (!spa_feature_is_enabled(dp->dp_spa, SPA_FEATURE_BOOKMARKS)) 358260154Sdelphij return (0); 359260154Sdelphij 360260154Sdelphij for (nvpair_t *pair = nvlist_next_nvpair(dbda->dbda_bmarks, NULL); 361260154Sdelphij pair != NULL; pair = nvlist_next_nvpair(dbda->dbda_bmarks, pair)) { 362260154Sdelphij const char *fullname = nvpair_name(pair); 363260154Sdelphij dsl_dataset_t *ds; 364260154Sdelphij zfs_bookmark_phys_t bm; 365260154Sdelphij int error; 366260154Sdelphij char *shortname; 367260154Sdelphij 368260154Sdelphij error = dsl_bookmark_hold_ds(dp, fullname, &ds, 369260154Sdelphij FTAG, &shortname); 370260154Sdelphij if (error == ENOENT) { 371260154Sdelphij /* ignore it; the bookmark is "already destroyed" */ 372260154Sdelphij continue; 373260154Sdelphij } 374260154Sdelphij if (error == 0) { 375260154Sdelphij error = dsl_dataset_bmark_lookup(ds, shortname, &bm); 376260154Sdelphij dsl_dataset_rele(ds, FTAG); 377260154Sdelphij if (error == ESRCH) { 378260154Sdelphij /* 379260154Sdelphij * ignore it; the bookmark is 380260154Sdelphij * "already destroyed" 381260154Sdelphij */ 382260154Sdelphij continue; 383260154Sdelphij } 384260154Sdelphij } 385260154Sdelphij if (error == 0) { 386260154Sdelphij fnvlist_add_boolean(dbda->dbda_success, fullname); 387260154Sdelphij } else { 388260154Sdelphij fnvlist_add_int32(dbda->dbda_errors, fullname, error); 389260154Sdelphij rv = error; 390260154Sdelphij } 391260154Sdelphij } 392260154Sdelphij return (rv); 393260154Sdelphij} 394260154Sdelphij 395260154Sdelphijstatic void 396260154Sdelphijdsl_bookmark_destroy_sync(void *arg, dmu_tx_t *tx) 397260154Sdelphij{ 398260154Sdelphij dsl_bookmark_destroy_arg_t *dbda = arg; 399260154Sdelphij dsl_pool_t *dp = dmu_tx_pool(tx); 400260154Sdelphij objset_t *mos = dp->dp_meta_objset; 401260154Sdelphij 402260154Sdelphij for (nvpair_t *pair = nvlist_next_nvpair(dbda->dbda_success, NULL); 403260154Sdelphij pair != NULL; pair = nvlist_next_nvpair(dbda->dbda_success, pair)) { 404260154Sdelphij dsl_dataset_t *ds; 405260154Sdelphij char *shortname; 406260154Sdelphij uint64_t zap_cnt; 407260154Sdelphij 408260154Sdelphij VERIFY0(dsl_bookmark_hold_ds(dp, nvpair_name(pair), 409260154Sdelphij &ds, FTAG, &shortname)); 410260154Sdelphij VERIFY0(dsl_dataset_bookmark_remove(ds, shortname, tx)); 411260154Sdelphij 412260154Sdelphij /* 413260154Sdelphij * If all of this dataset's bookmarks have been destroyed, 414260154Sdelphij * free the zap object and decrement the feature's use count. 415260154Sdelphij */ 416260154Sdelphij VERIFY0(zap_count(mos, ds->ds_bookmarks, 417260154Sdelphij &zap_cnt)); 418260154Sdelphij if (zap_cnt == 0) { 419260154Sdelphij dmu_buf_will_dirty(ds->ds_dbuf, tx); 420260154Sdelphij VERIFY0(zap_destroy(mos, ds->ds_bookmarks, tx)); 421260154Sdelphij ds->ds_bookmarks = 0; 422260154Sdelphij spa_feature_decr(dp->dp_spa, SPA_FEATURE_BOOKMARKS, tx); 423260154Sdelphij VERIFY0(zap_remove(mos, ds->ds_object, 424260154Sdelphij DS_FIELD_BOOKMARK_NAMES, tx)); 425260154Sdelphij } 426260154Sdelphij 427260154Sdelphij spa_history_log_internal_ds(ds, "remove bookmark", tx, 428260154Sdelphij "name=%s", shortname); 429260154Sdelphij 430260154Sdelphij dsl_dataset_rele(ds, FTAG); 431260154Sdelphij } 432260154Sdelphij} 433260154Sdelphij 434260154Sdelphij/* 435260154Sdelphij * The bookmarks must all be in the same pool. 436260154Sdelphij */ 437260154Sdelphijint 438260154Sdelphijdsl_bookmark_destroy(nvlist_t *bmarks, nvlist_t *errors) 439260154Sdelphij{ 440260154Sdelphij int rv; 441260154Sdelphij dsl_bookmark_destroy_arg_t dbda; 442260154Sdelphij nvpair_t *pair = nvlist_next_nvpair(bmarks, NULL); 443260154Sdelphij if (pair == NULL) 444260154Sdelphij return (0); 445260154Sdelphij 446260154Sdelphij dbda.dbda_bmarks = bmarks; 447260154Sdelphij dbda.dbda_errors = errors; 448260154Sdelphij dbda.dbda_success = fnvlist_alloc(); 449260154Sdelphij 450260154Sdelphij rv = dsl_sync_task(nvpair_name(pair), dsl_bookmark_destroy_check, 451260154Sdelphij dsl_bookmark_destroy_sync, &dbda, fnvlist_num_pairs(bmarks)); 452260154Sdelphij fnvlist_free(dbda.dbda_success); 453260154Sdelphij return (rv); 454260154Sdelphij} 455