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 */ 15321545Smav 16260154Sdelphij/* 17268473Sdelphij * Copyright (c) 2013, 2014 by Delphix. All rights reserved. 18321545Smav * Copyright 2017 Nexenta Systems, Inc. 19260154Sdelphij */ 20260154Sdelphij 21260154Sdelphij#include <sys/zfs_context.h> 22260154Sdelphij#include <sys/dsl_dataset.h> 23260154Sdelphij#include <sys/dsl_dir.h> 24260154Sdelphij#include <sys/dsl_prop.h> 25260154Sdelphij#include <sys/dsl_synctask.h> 26260154Sdelphij#include <sys/dmu_impl.h> 27260154Sdelphij#include <sys/dmu_tx.h> 28260154Sdelphij#include <sys/arc.h> 29260154Sdelphij#include <sys/zap.h> 30260154Sdelphij#include <sys/zfeature.h> 31260154Sdelphij#include <sys/spa.h> 32260154Sdelphij#include <sys/dsl_bookmark.h> 33260154Sdelphij#include <zfs_namecheck.h> 34260154Sdelphij 35260154Sdelphijstatic int 36260154Sdelphijdsl_bookmark_hold_ds(dsl_pool_t *dp, const char *fullname, 37260154Sdelphij dsl_dataset_t **dsp, void *tag, char **shortnamep) 38260154Sdelphij{ 39307108Smav char buf[ZFS_MAX_DATASET_NAME_LEN]; 40260154Sdelphij char *hashp; 41260154Sdelphij 42307108Smav if (strlen(fullname) >= ZFS_MAX_DATASET_NAME_LEN) 43260154Sdelphij return (SET_ERROR(ENAMETOOLONG)); 44260154Sdelphij hashp = strchr(fullname, '#'); 45260154Sdelphij if (hashp == NULL) 46260154Sdelphij return (SET_ERROR(EINVAL)); 47260154Sdelphij 48260154Sdelphij *shortnamep = hashp + 1; 49260154Sdelphij if (zfs_component_namecheck(*shortnamep, NULL, NULL)) 50260154Sdelphij return (SET_ERROR(EINVAL)); 51260154Sdelphij (void) strlcpy(buf, fullname, hashp - fullname + 1); 52260154Sdelphij return (dsl_dataset_hold(dp, buf, tag, dsp)); 53260154Sdelphij} 54260154Sdelphij 55260154Sdelphij/* 56260154Sdelphij * Returns ESRCH if bookmark is not found. 57260154Sdelphij */ 58260154Sdelphijstatic int 59260154Sdelphijdsl_dataset_bmark_lookup(dsl_dataset_t *ds, const char *shortname, 60260154Sdelphij zfs_bookmark_phys_t *bmark_phys) 61260154Sdelphij{ 62260154Sdelphij objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset; 63260154Sdelphij uint64_t bmark_zapobj = ds->ds_bookmarks; 64321545Smav matchtype_t mt = 0; 65260154Sdelphij int err; 66260154Sdelphij 67260154Sdelphij if (bmark_zapobj == 0) 68260154Sdelphij return (SET_ERROR(ESRCH)); 69260154Sdelphij 70275782Sdelphij if (dsl_dataset_phys(ds)->ds_flags & DS_FLAG_CI_DATASET) 71321545Smav mt = MT_NORMALIZE; 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 123286575Smav if (!snapds->ds_is_snapshot) 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 211275782Sdelphij bmark_phys.zbm_guid = dsl_dataset_phys(snapds)->ds_guid; 212275782Sdelphij bmark_phys.zbm_creation_txg = 213275782Sdelphij dsl_dataset_phys(snapds)->ds_creation_txg; 214260154Sdelphij bmark_phys.zbm_creation_time = 215275782Sdelphij dsl_dataset_phys(snapds)->ds_creation_time; 216260154Sdelphij 217260154Sdelphij VERIFY0(zap_add(mos, bmark_fs->ds_bookmarks, 218260154Sdelphij shortname, sizeof (uint64_t), 219260154Sdelphij sizeof (zfs_bookmark_phys_t) / sizeof (uint64_t), 220260154Sdelphij &bmark_phys, tx)); 221260154Sdelphij 222260154Sdelphij spa_history_log_internal_ds(bmark_fs, "bookmark", tx, 223260154Sdelphij "name=%s creation_txg=%llu target_snap=%llu", 224260154Sdelphij shortname, 225260154Sdelphij (longlong_t)bmark_phys.zbm_creation_txg, 226260154Sdelphij (longlong_t)snapds->ds_object); 227260154Sdelphij 228260154Sdelphij dsl_dataset_rele(bmark_fs, FTAG); 229260154Sdelphij dsl_dataset_rele(snapds, FTAG); 230260154Sdelphij } 231260154Sdelphij} 232260154Sdelphij 233260154Sdelphij/* 234260154Sdelphij * The bookmarks must all be in the same pool. 235260154Sdelphij */ 236260154Sdelphijint 237260154Sdelphijdsl_bookmark_create(nvlist_t *bmarks, nvlist_t *errors) 238260154Sdelphij{ 239260154Sdelphij nvpair_t *pair; 240260154Sdelphij dsl_bookmark_create_arg_t dbca; 241260154Sdelphij 242260154Sdelphij pair = nvlist_next_nvpair(bmarks, NULL); 243260154Sdelphij if (pair == NULL) 244260154Sdelphij return (0); 245260154Sdelphij 246260154Sdelphij dbca.dbca_bmarks = bmarks; 247260154Sdelphij dbca.dbca_errors = errors; 248260154Sdelphij 249260154Sdelphij return (dsl_sync_task(nvpair_name(pair), dsl_bookmark_create_check, 250268473Sdelphij dsl_bookmark_create_sync, &dbca, 251268473Sdelphij fnvlist_num_pairs(bmarks), ZFS_SPACE_CHECK_NORMAL)); 252260154Sdelphij} 253260154Sdelphij 254260154Sdelphijint 255260154Sdelphijdsl_get_bookmarks_impl(dsl_dataset_t *ds, nvlist_t *props, nvlist_t *outnvl) 256260154Sdelphij{ 257260154Sdelphij int err = 0; 258260154Sdelphij zap_cursor_t zc; 259260154Sdelphij zap_attribute_t attr; 260260154Sdelphij dsl_pool_t *dp = ds->ds_dir->dd_pool; 261260154Sdelphij 262260154Sdelphij uint64_t bmark_zapobj = ds->ds_bookmarks; 263260154Sdelphij if (bmark_zapobj == 0) 264260154Sdelphij return (0); 265260154Sdelphij 266260154Sdelphij for (zap_cursor_init(&zc, dp->dp_meta_objset, bmark_zapobj); 267260154Sdelphij zap_cursor_retrieve(&zc, &attr) == 0; 268260154Sdelphij zap_cursor_advance(&zc)) { 269260154Sdelphij char *bmark_name = attr.za_name; 270260154Sdelphij zfs_bookmark_phys_t bmark_phys; 271260154Sdelphij 272260154Sdelphij err = dsl_dataset_bmark_lookup(ds, bmark_name, &bmark_phys); 273260154Sdelphij ASSERT3U(err, !=, ENOENT); 274260154Sdelphij if (err != 0) 275260154Sdelphij break; 276260154Sdelphij 277260154Sdelphij nvlist_t *out_props = fnvlist_alloc(); 278260154Sdelphij if (nvlist_exists(props, 279260154Sdelphij zfs_prop_to_name(ZFS_PROP_GUID))) { 280260154Sdelphij dsl_prop_nvlist_add_uint64(out_props, 281260154Sdelphij ZFS_PROP_GUID, bmark_phys.zbm_guid); 282260154Sdelphij } 283260154Sdelphij if (nvlist_exists(props, 284260154Sdelphij zfs_prop_to_name(ZFS_PROP_CREATETXG))) { 285260154Sdelphij dsl_prop_nvlist_add_uint64(out_props, 286260154Sdelphij ZFS_PROP_CREATETXG, bmark_phys.zbm_creation_txg); 287260154Sdelphij } 288260154Sdelphij if (nvlist_exists(props, 289260154Sdelphij zfs_prop_to_name(ZFS_PROP_CREATION))) { 290260154Sdelphij dsl_prop_nvlist_add_uint64(out_props, 291260154Sdelphij ZFS_PROP_CREATION, bmark_phys.zbm_creation_time); 292260154Sdelphij } 293260154Sdelphij 294260154Sdelphij fnvlist_add_nvlist(outnvl, bmark_name, out_props); 295260154Sdelphij fnvlist_free(out_props); 296260154Sdelphij } 297260154Sdelphij zap_cursor_fini(&zc); 298260154Sdelphij return (err); 299260154Sdelphij} 300260154Sdelphij 301260154Sdelphij/* 302260154Sdelphij * Retrieve the bookmarks that exist in the specified dataset, and the 303260154Sdelphij * requested properties of each bookmark. 304260154Sdelphij * 305260154Sdelphij * The "props" nvlist specifies which properties are requested. 306260154Sdelphij * See lzc_get_bookmarks() for the list of valid properties. 307260154Sdelphij */ 308260154Sdelphijint 309260154Sdelphijdsl_get_bookmarks(const char *dsname, nvlist_t *props, nvlist_t *outnvl) 310260154Sdelphij{ 311260154Sdelphij dsl_pool_t *dp; 312260154Sdelphij dsl_dataset_t *ds; 313260154Sdelphij int err; 314260154Sdelphij 315260154Sdelphij err = dsl_pool_hold(dsname, FTAG, &dp); 316260154Sdelphij if (err != 0) 317260154Sdelphij return (err); 318260154Sdelphij err = dsl_dataset_hold(dp, dsname, FTAG, &ds); 319260154Sdelphij if (err != 0) { 320260154Sdelphij dsl_pool_rele(dp, FTAG); 321260154Sdelphij return (err); 322260154Sdelphij } 323260154Sdelphij 324260154Sdelphij err = dsl_get_bookmarks_impl(ds, props, outnvl); 325260154Sdelphij 326260154Sdelphij dsl_dataset_rele(ds, FTAG); 327260154Sdelphij dsl_pool_rele(dp, FTAG); 328260154Sdelphij return (err); 329260154Sdelphij} 330260154Sdelphij 331260154Sdelphijtypedef struct dsl_bookmark_destroy_arg { 332260154Sdelphij nvlist_t *dbda_bmarks; 333260154Sdelphij nvlist_t *dbda_success; 334260154Sdelphij nvlist_t *dbda_errors; 335260154Sdelphij} dsl_bookmark_destroy_arg_t; 336260154Sdelphij 337260154Sdelphijstatic int 338260154Sdelphijdsl_dataset_bookmark_remove(dsl_dataset_t *ds, const char *name, dmu_tx_t *tx) 339260154Sdelphij{ 340260154Sdelphij objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset; 341260154Sdelphij uint64_t bmark_zapobj = ds->ds_bookmarks; 342321545Smav matchtype_t mt = 0; 343260154Sdelphij 344275782Sdelphij if (dsl_dataset_phys(ds)->ds_flags & DS_FLAG_CI_DATASET) 345321545Smav mt = MT_NORMALIZE; 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 357323755Savg ASSERT(nvlist_empty(dbda->dbda_success)); 358323755Savg ASSERT(nvlist_empty(dbda->dbda_errors)); 359323755Savg 360260154Sdelphij if (!spa_feature_is_enabled(dp->dp_spa, SPA_FEATURE_BOOKMARKS)) 361260154Sdelphij return (0); 362260154Sdelphij 363260154Sdelphij for (nvpair_t *pair = nvlist_next_nvpair(dbda->dbda_bmarks, NULL); 364260154Sdelphij pair != NULL; pair = nvlist_next_nvpair(dbda->dbda_bmarks, pair)) { 365260154Sdelphij const char *fullname = nvpair_name(pair); 366260154Sdelphij dsl_dataset_t *ds; 367260154Sdelphij zfs_bookmark_phys_t bm; 368260154Sdelphij int error; 369260154Sdelphij char *shortname; 370260154Sdelphij 371260154Sdelphij error = dsl_bookmark_hold_ds(dp, fullname, &ds, 372260154Sdelphij FTAG, &shortname); 373260154Sdelphij if (error == ENOENT) { 374260154Sdelphij /* ignore it; the bookmark is "already destroyed" */ 375260154Sdelphij continue; 376260154Sdelphij } 377260154Sdelphij if (error == 0) { 378260154Sdelphij error = dsl_dataset_bmark_lookup(ds, shortname, &bm); 379260154Sdelphij dsl_dataset_rele(ds, FTAG); 380260154Sdelphij if (error == ESRCH) { 381260154Sdelphij /* 382260154Sdelphij * ignore it; the bookmark is 383260154Sdelphij * "already destroyed" 384260154Sdelphij */ 385260154Sdelphij continue; 386260154Sdelphij } 387260154Sdelphij } 388260154Sdelphij if (error == 0) { 389323755Savg if (dmu_tx_is_syncing(tx)) { 390323755Savg fnvlist_add_boolean(dbda->dbda_success, 391323755Savg fullname); 392323755Savg } 393260154Sdelphij } else { 394260154Sdelphij fnvlist_add_int32(dbda->dbda_errors, fullname, error); 395260154Sdelphij rv = error; 396260154Sdelphij } 397260154Sdelphij } 398260154Sdelphij return (rv); 399260154Sdelphij} 400260154Sdelphij 401260154Sdelphijstatic void 402260154Sdelphijdsl_bookmark_destroy_sync(void *arg, dmu_tx_t *tx) 403260154Sdelphij{ 404260154Sdelphij dsl_bookmark_destroy_arg_t *dbda = arg; 405260154Sdelphij dsl_pool_t *dp = dmu_tx_pool(tx); 406260154Sdelphij objset_t *mos = dp->dp_meta_objset; 407260154Sdelphij 408260154Sdelphij for (nvpair_t *pair = nvlist_next_nvpair(dbda->dbda_success, NULL); 409260154Sdelphij pair != NULL; pair = nvlist_next_nvpair(dbda->dbda_success, pair)) { 410260154Sdelphij dsl_dataset_t *ds; 411260154Sdelphij char *shortname; 412260154Sdelphij uint64_t zap_cnt; 413260154Sdelphij 414260154Sdelphij VERIFY0(dsl_bookmark_hold_ds(dp, nvpair_name(pair), 415260154Sdelphij &ds, FTAG, &shortname)); 416260154Sdelphij VERIFY0(dsl_dataset_bookmark_remove(ds, shortname, tx)); 417260154Sdelphij 418260154Sdelphij /* 419260154Sdelphij * If all of this dataset's bookmarks have been destroyed, 420260154Sdelphij * free the zap object and decrement the feature's use count. 421260154Sdelphij */ 422260154Sdelphij VERIFY0(zap_count(mos, ds->ds_bookmarks, 423260154Sdelphij &zap_cnt)); 424260154Sdelphij if (zap_cnt == 0) { 425260154Sdelphij dmu_buf_will_dirty(ds->ds_dbuf, tx); 426260154Sdelphij VERIFY0(zap_destroy(mos, ds->ds_bookmarks, tx)); 427260154Sdelphij ds->ds_bookmarks = 0; 428260154Sdelphij spa_feature_decr(dp->dp_spa, SPA_FEATURE_BOOKMARKS, tx); 429260154Sdelphij VERIFY0(zap_remove(mos, ds->ds_object, 430260154Sdelphij DS_FIELD_BOOKMARK_NAMES, tx)); 431260154Sdelphij } 432260154Sdelphij 433260154Sdelphij spa_history_log_internal_ds(ds, "remove bookmark", tx, 434260154Sdelphij "name=%s", shortname); 435260154Sdelphij 436260154Sdelphij dsl_dataset_rele(ds, FTAG); 437260154Sdelphij } 438260154Sdelphij} 439260154Sdelphij 440260154Sdelphij/* 441260154Sdelphij * The bookmarks must all be in the same pool. 442260154Sdelphij */ 443260154Sdelphijint 444260154Sdelphijdsl_bookmark_destroy(nvlist_t *bmarks, nvlist_t *errors) 445260154Sdelphij{ 446260154Sdelphij int rv; 447260154Sdelphij dsl_bookmark_destroy_arg_t dbda; 448260154Sdelphij nvpair_t *pair = nvlist_next_nvpair(bmarks, NULL); 449260154Sdelphij if (pair == NULL) 450260154Sdelphij return (0); 451260154Sdelphij 452260154Sdelphij dbda.dbda_bmarks = bmarks; 453260154Sdelphij dbda.dbda_errors = errors; 454260154Sdelphij dbda.dbda_success = fnvlist_alloc(); 455260154Sdelphij 456260154Sdelphij rv = dsl_sync_task(nvpair_name(pair), dsl_bookmark_destroy_check, 457268473Sdelphij dsl_bookmark_destroy_sync, &dbda, fnvlist_num_pairs(bmarks), 458268473Sdelphij ZFS_SPACE_CHECK_RESERVED); 459260154Sdelphij fnvlist_free(dbda.dbda_success); 460260154Sdelphij return (rv); 461260154Sdelphij} 462353759Savg 463353759Savgtypedef struct dsl_bookmark_rename_arg { 464353759Savg const char *dbra_fsname; 465353759Savg const char *dbra_oldname; 466353759Savg const char *dbra_newname; 467353759Savg} dsl_bookmark_rename_arg_t; 468353759Savg 469353759Savgstatic int 470353759Savgdsl_bookmark_rename_check(void *arg, dmu_tx_t *tx) 471353759Savg{ 472353759Savg dsl_bookmark_rename_arg_t *dbra = arg; 473353759Savg dsl_pool_t *dp = dmu_tx_pool(tx); 474353759Savg dsl_dataset_t *ds; 475353759Savg zfs_bookmark_phys_t bmark_phys; 476353759Savg int error; 477353759Savg 478353759Savg if (!spa_feature_is_enabled(dp->dp_spa, SPA_FEATURE_BOOKMARKS)) 479353759Savg return (SET_ERROR(ENOTSUP)); 480353759Savg 481353759Savg /* Check validity and the full length of the new bookmark name. */ 482353759Savg if (zfs_component_namecheck(dbra->dbra_newname, NULL, NULL)) 483353759Savg return (SET_ERROR(EINVAL)); 484353759Savg if (strlen(dbra->dbra_fsname) + strlen(dbra->dbra_newname) + 1 >= 485353759Savg ZFS_MAX_DATASET_NAME_LEN) 486353759Savg return (SET_ERROR(ENAMETOOLONG)); 487353759Savg 488353759Savg error = dsl_dataset_hold(dp, dbra->dbra_fsname, FTAG, &ds); 489353759Savg if (error != 0) 490353759Savg return (error); 491353759Savg if (ds->ds_is_snapshot) { 492353759Savg dsl_dataset_rele(ds, FTAG); 493353759Savg return (SET_ERROR(EINVAL)); 494353759Savg } 495353759Savg error = dsl_dataset_bmark_lookup(ds, dbra->dbra_oldname, &bmark_phys); 496353759Savg if (error != 0) { 497353759Savg dsl_dataset_rele(ds, FTAG); 498353759Savg return (error); 499353759Savg } 500353759Savg 501353759Savg error = dsl_dataset_bmark_lookup(ds, dbra->dbra_newname, &bmark_phys); 502353759Savg dsl_dataset_rele(ds, FTAG); 503353759Savg if (error == 0) 504353759Savg return (SET_ERROR(EEXIST)); 505353759Savg if (error != ESRCH) 506353759Savg return (error); 507353759Savg return (0); 508353759Savg} 509353759Savg 510353759Savgstatic void 511353759Savgdsl_bookmark_rename_sync(void *arg, dmu_tx_t *tx) 512353759Savg{ 513353759Savg zfs_bookmark_phys_t bmark_phys; 514353759Savg dsl_bookmark_rename_arg_t *dbra = arg; 515353759Savg dsl_pool_t *dp = dmu_tx_pool(tx); 516353759Savg objset_t *mos; 517353759Savg dsl_dataset_t *ds; 518353759Savg uint64_t bmark_zapobj; 519353759Savg uint64_t int_size, num_ints; 520353759Savg matchtype_t mt = 0; 521353759Savg int error; 522353759Savg 523353759Savg ASSERT(spa_feature_is_enabled(dp->dp_spa, SPA_FEATURE_BOOKMARKS)); 524353759Savg VERIFY0(dsl_dataset_hold(dp, dbra->dbra_fsname, FTAG, &ds)); 525353759Savg 526353759Savg mos = ds->ds_dir->dd_pool->dp_meta_objset; 527353759Savg bmark_zapobj = ds->ds_bookmarks; 528353759Savg 529353759Savg if (dsl_dataset_phys(ds)->ds_flags & DS_FLAG_CI_DATASET) 530353759Savg mt = MT_NORMALIZE; 531353759Savg 532353759Savg VERIFY0(zap_length(mos, bmark_zapobj, dbra->dbra_oldname, 533353759Savg &int_size, &num_ints)); 534353759Savg ASSERT3U(int_size, ==, sizeof (uint64_t)); 535353759Savg VERIFY0(zap_lookup_norm(mos, bmark_zapobj, dbra->dbra_oldname, int_size, 536353759Savg num_ints, &bmark_phys, mt, NULL, 0, NULL)); 537353759Savg VERIFY0(zap_remove_norm(mos, bmark_zapobj, dbra->dbra_oldname, mt, tx)); 538353759Savg 539353759Savg VERIFY0(zap_add(mos, bmark_zapobj, dbra->dbra_newname, int_size, 540353759Savg num_ints, &bmark_phys, tx)); 541353759Savg 542353759Savg spa_history_log_internal_ds(ds, "rename bookmark", tx, 543353759Savg "#%s -> #%s creation_txg=%llu", 544353759Savg dbra->dbra_oldname, dbra->dbra_newname, 545353759Savg (longlong_t)bmark_phys.zbm_creation_txg); 546353759Savg 547353759Savg dsl_dataset_rele(ds, FTAG); 548353759Savg} 549353759Savg 550353759Savg/* 551353759Savg * The bookmarks must all be in the same pool. 552353759Savg */ 553353759Savgint 554353759Savgdsl_bookmark_rename(const char *fsname, const char *oldbmark, 555353759Savg const char *newbmark) 556353759Savg{ 557353759Savg dsl_bookmark_rename_arg_t dbra; 558353759Savg 559353759Savg dbra.dbra_fsname = fsname; 560353759Savg dbra.dbra_oldname = oldbmark; 561353759Savg dbra.dbra_newname = newbmark; 562353759Savg 563353759Savg return (dsl_sync_task(fsname, dsl_bookmark_rename_check, 564353759Savg dsl_bookmark_rename_sync, &dbra, 1, ZFS_SPACE_CHECK_NORMAL)); 565353759Savg} 566353759Savg 567