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