178f17100SMatthew Ahrens /* 278f17100SMatthew Ahrens * CDDL HEADER START 378f17100SMatthew Ahrens * 478f17100SMatthew Ahrens * This file and its contents are supplied under the terms of the 578f17100SMatthew Ahrens * Common Development and Distribution License ("CDDL"), version 1.0. 678f17100SMatthew Ahrens * You may only use this file in accordance with the terms of version 778f17100SMatthew Ahrens * 1.0 of the CDDL. 878f17100SMatthew Ahrens * 978f17100SMatthew Ahrens * A full copy of the text of the CDDL should have accompanied this 1078f17100SMatthew Ahrens * source. A copy of the CDDL is also available via the Internet at 1178f17100SMatthew Ahrens * http://www.illumos.org/license/CDDL. 1278f17100SMatthew Ahrens * 1378f17100SMatthew Ahrens * CDDL HEADER END 1478f17100SMatthew Ahrens */ 1578f17100SMatthew Ahrens /* 167d46dc6cSMatthew Ahrens * Copyright (c) 2013, 2014 by Delphix. All rights reserved. 1778f17100SMatthew Ahrens */ 1878f17100SMatthew Ahrens 1978f17100SMatthew Ahrens #include <sys/zfs_context.h> 2078f17100SMatthew Ahrens #include <sys/dsl_dataset.h> 2178f17100SMatthew Ahrens #include <sys/dsl_dir.h> 2278f17100SMatthew Ahrens #include <sys/dsl_prop.h> 2378f17100SMatthew Ahrens #include <sys/dsl_synctask.h> 2478f17100SMatthew Ahrens #include <sys/dmu_impl.h> 2578f17100SMatthew Ahrens #include <sys/dmu_tx.h> 2678f17100SMatthew Ahrens #include <sys/arc.h> 2778f17100SMatthew Ahrens #include <sys/zap.h> 2878f17100SMatthew Ahrens #include <sys/zfeature.h> 2978f17100SMatthew Ahrens #include <sys/spa.h> 3078f17100SMatthew Ahrens #include <sys/dsl_bookmark.h> 3178f17100SMatthew Ahrens #include <zfs_namecheck.h> 3278f17100SMatthew Ahrens 3378f17100SMatthew Ahrens static int 3478f17100SMatthew Ahrens dsl_bookmark_hold_ds(dsl_pool_t *dp, const char *fullname, 3578f17100SMatthew Ahrens dsl_dataset_t **dsp, void *tag, char **shortnamep) 3678f17100SMatthew Ahrens { 3740a5c998SMatthew Ahrens char buf[ZFS_MAX_DATASET_NAME_LEN]; 3878f17100SMatthew Ahrens char *hashp; 3978f17100SMatthew Ahrens 4040a5c998SMatthew Ahrens if (strlen(fullname) >= ZFS_MAX_DATASET_NAME_LEN) 4178f17100SMatthew Ahrens return (SET_ERROR(ENAMETOOLONG)); 4278f17100SMatthew Ahrens hashp = strchr(fullname, '#'); 4378f17100SMatthew Ahrens if (hashp == NULL) 4478f17100SMatthew Ahrens return (SET_ERROR(EINVAL)); 4578f17100SMatthew Ahrens 4678f17100SMatthew Ahrens *shortnamep = hashp + 1; 4778f17100SMatthew Ahrens if (zfs_component_namecheck(*shortnamep, NULL, NULL)) 4878f17100SMatthew Ahrens return (SET_ERROR(EINVAL)); 4978f17100SMatthew Ahrens (void) strlcpy(buf, fullname, hashp - fullname + 1); 5078f17100SMatthew Ahrens return (dsl_dataset_hold(dp, buf, tag, dsp)); 5178f17100SMatthew Ahrens } 5278f17100SMatthew Ahrens 5378f17100SMatthew Ahrens /* 5478f17100SMatthew Ahrens * Returns ESRCH if bookmark is not found. 5578f17100SMatthew Ahrens */ 5678f17100SMatthew Ahrens static int 5778f17100SMatthew Ahrens dsl_dataset_bmark_lookup(dsl_dataset_t *ds, const char *shortname, 5878f17100SMatthew Ahrens zfs_bookmark_phys_t *bmark_phys) 5978f17100SMatthew Ahrens { 6078f17100SMatthew Ahrens objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset; 6178f17100SMatthew Ahrens uint64_t bmark_zapobj = ds->ds_bookmarks; 6278f17100SMatthew Ahrens matchtype_t mt; 6378f17100SMatthew Ahrens int err; 6478f17100SMatthew Ahrens 6578f17100SMatthew Ahrens if (bmark_zapobj == 0) 6678f17100SMatthew Ahrens return (SET_ERROR(ESRCH)); 6778f17100SMatthew Ahrens 68c1379625SJustin T. Gibbs if (dsl_dataset_phys(ds)->ds_flags & DS_FLAG_CI_DATASET) 6978f17100SMatthew Ahrens mt = MT_FIRST; 7078f17100SMatthew Ahrens else 7178f17100SMatthew Ahrens mt = MT_EXACT; 7278f17100SMatthew Ahrens 7378f17100SMatthew Ahrens err = zap_lookup_norm(mos, bmark_zapobj, shortname, sizeof (uint64_t), 7478f17100SMatthew Ahrens sizeof (*bmark_phys) / sizeof (uint64_t), bmark_phys, mt, 7578f17100SMatthew Ahrens NULL, 0, NULL); 7678f17100SMatthew Ahrens 7778f17100SMatthew Ahrens return (err == ENOENT ? ESRCH : err); 7878f17100SMatthew Ahrens } 7978f17100SMatthew Ahrens 8078f17100SMatthew Ahrens /* 8178f17100SMatthew Ahrens * If later_ds is non-NULL, this will return EXDEV if the the specified bookmark 8278f17100SMatthew Ahrens * does not represents an earlier point in later_ds's timeline. 8378f17100SMatthew Ahrens * 8478f17100SMatthew Ahrens * Returns ENOENT if the dataset containing the bookmark does not exist. 8578f17100SMatthew Ahrens * Returns ESRCH if the dataset exists but the bookmark was not found in it. 8678f17100SMatthew Ahrens */ 8778f17100SMatthew Ahrens int 8878f17100SMatthew Ahrens dsl_bookmark_lookup(dsl_pool_t *dp, const char *fullname, 8978f17100SMatthew Ahrens dsl_dataset_t *later_ds, zfs_bookmark_phys_t *bmp) 9078f17100SMatthew Ahrens { 9178f17100SMatthew Ahrens char *shortname; 9278f17100SMatthew Ahrens dsl_dataset_t *ds; 9378f17100SMatthew Ahrens int error; 9478f17100SMatthew Ahrens 9578f17100SMatthew Ahrens error = dsl_bookmark_hold_ds(dp, fullname, &ds, FTAG, &shortname); 9678f17100SMatthew Ahrens if (error != 0) 9778f17100SMatthew Ahrens return (error); 9878f17100SMatthew Ahrens 9978f17100SMatthew Ahrens error = dsl_dataset_bmark_lookup(ds, shortname, bmp); 10078f17100SMatthew Ahrens if (error == 0 && later_ds != NULL) { 10178f17100SMatthew Ahrens if (!dsl_dataset_is_before(later_ds, ds, bmp->zbm_creation_txg)) 10278f17100SMatthew Ahrens error = SET_ERROR(EXDEV); 10378f17100SMatthew Ahrens } 10478f17100SMatthew Ahrens dsl_dataset_rele(ds, FTAG); 10578f17100SMatthew Ahrens return (error); 10678f17100SMatthew Ahrens } 10778f17100SMatthew Ahrens 10878f17100SMatthew Ahrens typedef struct dsl_bookmark_create_arg { 10978f17100SMatthew Ahrens nvlist_t *dbca_bmarks; 11078f17100SMatthew Ahrens nvlist_t *dbca_errors; 11178f17100SMatthew Ahrens } dsl_bookmark_create_arg_t; 11278f17100SMatthew Ahrens 11378f17100SMatthew Ahrens static int 11478f17100SMatthew Ahrens dsl_bookmark_create_check_impl(dsl_dataset_t *snapds, const char *bookmark_name, 11578f17100SMatthew Ahrens dmu_tx_t *tx) 11678f17100SMatthew Ahrens { 11778f17100SMatthew Ahrens dsl_pool_t *dp = dmu_tx_pool(tx); 11878f17100SMatthew Ahrens dsl_dataset_t *bmark_fs; 11978f17100SMatthew Ahrens char *shortname; 12078f17100SMatthew Ahrens int error; 12178f17100SMatthew Ahrens zfs_bookmark_phys_t bmark_phys; 12278f17100SMatthew Ahrens 123bc9014e6SJustin Gibbs if (!snapds->ds_is_snapshot) 12478f17100SMatthew Ahrens return (SET_ERROR(EINVAL)); 12578f17100SMatthew Ahrens 12678f17100SMatthew Ahrens error = dsl_bookmark_hold_ds(dp, bookmark_name, 12778f17100SMatthew Ahrens &bmark_fs, FTAG, &shortname); 12878f17100SMatthew Ahrens if (error != 0) 12978f17100SMatthew Ahrens return (error); 13078f17100SMatthew Ahrens 13178f17100SMatthew Ahrens if (!dsl_dataset_is_before(bmark_fs, snapds, 0)) { 13278f17100SMatthew Ahrens dsl_dataset_rele(bmark_fs, FTAG); 13378f17100SMatthew Ahrens return (SET_ERROR(EINVAL)); 13478f17100SMatthew Ahrens } 13578f17100SMatthew Ahrens 13678f17100SMatthew Ahrens error = dsl_dataset_bmark_lookup(bmark_fs, shortname, 13778f17100SMatthew Ahrens &bmark_phys); 13878f17100SMatthew Ahrens dsl_dataset_rele(bmark_fs, FTAG); 13978f17100SMatthew Ahrens if (error == 0) 14078f17100SMatthew Ahrens return (SET_ERROR(EEXIST)); 14178f17100SMatthew Ahrens if (error == ESRCH) 14278f17100SMatthew Ahrens return (0); 14378f17100SMatthew Ahrens return (error); 14478f17100SMatthew Ahrens } 14578f17100SMatthew Ahrens 14678f17100SMatthew Ahrens static int 14778f17100SMatthew Ahrens dsl_bookmark_create_check(void *arg, dmu_tx_t *tx) 14878f17100SMatthew Ahrens { 14978f17100SMatthew Ahrens dsl_bookmark_create_arg_t *dbca = arg; 15078f17100SMatthew Ahrens dsl_pool_t *dp = dmu_tx_pool(tx); 15178f17100SMatthew Ahrens int rv = 0; 15278f17100SMatthew Ahrens 15378f17100SMatthew Ahrens if (!spa_feature_is_enabled(dp->dp_spa, SPA_FEATURE_BOOKMARKS)) 15478f17100SMatthew Ahrens return (SET_ERROR(ENOTSUP)); 15578f17100SMatthew Ahrens 15678f17100SMatthew Ahrens for (nvpair_t *pair = nvlist_next_nvpair(dbca->dbca_bmarks, NULL); 15778f17100SMatthew Ahrens pair != NULL; pair = nvlist_next_nvpair(dbca->dbca_bmarks, pair)) { 15878f17100SMatthew Ahrens dsl_dataset_t *snapds; 15978f17100SMatthew Ahrens int error; 16078f17100SMatthew Ahrens 16178f17100SMatthew Ahrens /* note: validity of nvlist checked by ioctl layer */ 16278f17100SMatthew Ahrens error = dsl_dataset_hold(dp, fnvpair_value_string(pair), 16378f17100SMatthew Ahrens FTAG, &snapds); 16478f17100SMatthew Ahrens if (error == 0) { 16578f17100SMatthew Ahrens error = dsl_bookmark_create_check_impl(snapds, 16678f17100SMatthew Ahrens nvpair_name(pair), tx); 16778f17100SMatthew Ahrens dsl_dataset_rele(snapds, FTAG); 16878f17100SMatthew Ahrens } 16978f17100SMatthew Ahrens if (error != 0) { 17078f17100SMatthew Ahrens fnvlist_add_int32(dbca->dbca_errors, 17178f17100SMatthew Ahrens nvpair_name(pair), error); 17278f17100SMatthew Ahrens rv = error; 17378f17100SMatthew Ahrens } 17478f17100SMatthew Ahrens } 17578f17100SMatthew Ahrens 17678f17100SMatthew Ahrens return (rv); 17778f17100SMatthew Ahrens } 17878f17100SMatthew Ahrens 17978f17100SMatthew Ahrens static void 18078f17100SMatthew Ahrens dsl_bookmark_create_sync(void *arg, dmu_tx_t *tx) 18178f17100SMatthew Ahrens { 18278f17100SMatthew Ahrens dsl_bookmark_create_arg_t *dbca = arg; 18378f17100SMatthew Ahrens dsl_pool_t *dp = dmu_tx_pool(tx); 18478f17100SMatthew Ahrens objset_t *mos = dp->dp_meta_objset; 18578f17100SMatthew Ahrens 18678f17100SMatthew Ahrens ASSERT(spa_feature_is_enabled(dp->dp_spa, SPA_FEATURE_BOOKMARKS)); 18778f17100SMatthew Ahrens 18878f17100SMatthew Ahrens for (nvpair_t *pair = nvlist_next_nvpair(dbca->dbca_bmarks, NULL); 18978f17100SMatthew Ahrens pair != NULL; pair = nvlist_next_nvpair(dbca->dbca_bmarks, pair)) { 19078f17100SMatthew Ahrens dsl_dataset_t *snapds, *bmark_fs; 19178f17100SMatthew Ahrens zfs_bookmark_phys_t bmark_phys; 19278f17100SMatthew Ahrens char *shortname; 19378f17100SMatthew Ahrens 19478f17100SMatthew Ahrens VERIFY0(dsl_dataset_hold(dp, fnvpair_value_string(pair), 19578f17100SMatthew Ahrens FTAG, &snapds)); 19678f17100SMatthew Ahrens VERIFY0(dsl_bookmark_hold_ds(dp, nvpair_name(pair), 19778f17100SMatthew Ahrens &bmark_fs, FTAG, &shortname)); 19878f17100SMatthew Ahrens if (bmark_fs->ds_bookmarks == 0) { 19978f17100SMatthew Ahrens bmark_fs->ds_bookmarks = 20078f17100SMatthew Ahrens zap_create_norm(mos, U8_TEXTPREP_TOUPPER, 20178f17100SMatthew Ahrens DMU_OTN_ZAP_METADATA, DMU_OT_NONE, 0, tx); 20278f17100SMatthew Ahrens spa_feature_incr(dp->dp_spa, SPA_FEATURE_BOOKMARKS, tx); 20378f17100SMatthew Ahrens 20478f17100SMatthew Ahrens dsl_dataset_zapify(bmark_fs, tx); 20578f17100SMatthew Ahrens VERIFY0(zap_add(mos, bmark_fs->ds_object, 20678f17100SMatthew Ahrens DS_FIELD_BOOKMARK_NAMES, 20778f17100SMatthew Ahrens sizeof (bmark_fs->ds_bookmarks), 1, 20878f17100SMatthew Ahrens &bmark_fs->ds_bookmarks, tx)); 20978f17100SMatthew Ahrens } 21078f17100SMatthew Ahrens 211c1379625SJustin T. Gibbs bmark_phys.zbm_guid = dsl_dataset_phys(snapds)->ds_guid; 212c1379625SJustin T. Gibbs bmark_phys.zbm_creation_txg = 213c1379625SJustin T. Gibbs dsl_dataset_phys(snapds)->ds_creation_txg; 21478f17100SMatthew Ahrens bmark_phys.zbm_creation_time = 215c1379625SJustin T. Gibbs dsl_dataset_phys(snapds)->ds_creation_time; 21678f17100SMatthew Ahrens 21778f17100SMatthew Ahrens VERIFY0(zap_add(mos, bmark_fs->ds_bookmarks, 21878f17100SMatthew Ahrens shortname, sizeof (uint64_t), 21978f17100SMatthew Ahrens sizeof (zfs_bookmark_phys_t) / sizeof (uint64_t), 22078f17100SMatthew Ahrens &bmark_phys, tx)); 22178f17100SMatthew Ahrens 22278f17100SMatthew Ahrens spa_history_log_internal_ds(bmark_fs, "bookmark", tx, 22378f17100SMatthew Ahrens "name=%s creation_txg=%llu target_snap=%llu", 22478f17100SMatthew Ahrens shortname, 22578f17100SMatthew Ahrens (longlong_t)bmark_phys.zbm_creation_txg, 22678f17100SMatthew Ahrens (longlong_t)snapds->ds_object); 22778f17100SMatthew Ahrens 22878f17100SMatthew Ahrens dsl_dataset_rele(bmark_fs, FTAG); 22978f17100SMatthew Ahrens dsl_dataset_rele(snapds, FTAG); 23078f17100SMatthew Ahrens } 23178f17100SMatthew Ahrens } 23278f17100SMatthew Ahrens 23378f17100SMatthew Ahrens /* 23478f17100SMatthew Ahrens * The bookmarks must all be in the same pool. 23578f17100SMatthew Ahrens */ 23678f17100SMatthew Ahrens int 23778f17100SMatthew Ahrens dsl_bookmark_create(nvlist_t *bmarks, nvlist_t *errors) 23878f17100SMatthew Ahrens { 23978f17100SMatthew Ahrens nvpair_t *pair; 24078f17100SMatthew Ahrens dsl_bookmark_create_arg_t dbca; 24178f17100SMatthew Ahrens 24278f17100SMatthew Ahrens pair = nvlist_next_nvpair(bmarks, NULL); 24378f17100SMatthew Ahrens if (pair == NULL) 24478f17100SMatthew Ahrens return (0); 24578f17100SMatthew Ahrens 24678f17100SMatthew Ahrens dbca.dbca_bmarks = bmarks; 24778f17100SMatthew Ahrens dbca.dbca_errors = errors; 24878f17100SMatthew Ahrens 24978f17100SMatthew Ahrens return (dsl_sync_task(nvpair_name(pair), dsl_bookmark_create_check, 2507d46dc6cSMatthew Ahrens dsl_bookmark_create_sync, &dbca, 2517d46dc6cSMatthew Ahrens fnvlist_num_pairs(bmarks), ZFS_SPACE_CHECK_NORMAL)); 25278f17100SMatthew Ahrens } 25378f17100SMatthew Ahrens 25478f17100SMatthew Ahrens int 25578f17100SMatthew Ahrens dsl_get_bookmarks_impl(dsl_dataset_t *ds, nvlist_t *props, nvlist_t *outnvl) 25678f17100SMatthew Ahrens { 25778f17100SMatthew Ahrens int err = 0; 25878f17100SMatthew Ahrens zap_cursor_t zc; 25978f17100SMatthew Ahrens zap_attribute_t attr; 26078f17100SMatthew Ahrens dsl_pool_t *dp = ds->ds_dir->dd_pool; 26178f17100SMatthew Ahrens 26278f17100SMatthew Ahrens uint64_t bmark_zapobj = ds->ds_bookmarks; 26378f17100SMatthew Ahrens if (bmark_zapobj == 0) 26478f17100SMatthew Ahrens return (0); 26578f17100SMatthew Ahrens 26678f17100SMatthew Ahrens for (zap_cursor_init(&zc, dp->dp_meta_objset, bmark_zapobj); 26778f17100SMatthew Ahrens zap_cursor_retrieve(&zc, &attr) == 0; 26878f17100SMatthew Ahrens zap_cursor_advance(&zc)) { 26978f17100SMatthew Ahrens char *bmark_name = attr.za_name; 27078f17100SMatthew Ahrens zfs_bookmark_phys_t bmark_phys; 27178f17100SMatthew Ahrens 27278f17100SMatthew Ahrens err = dsl_dataset_bmark_lookup(ds, bmark_name, &bmark_phys); 27378f17100SMatthew Ahrens ASSERT3U(err, !=, ENOENT); 27478f17100SMatthew Ahrens if (err != 0) 27578f17100SMatthew Ahrens break; 27678f17100SMatthew Ahrens 27778f17100SMatthew Ahrens nvlist_t *out_props = fnvlist_alloc(); 27878f17100SMatthew Ahrens if (nvlist_exists(props, 27978f17100SMatthew Ahrens zfs_prop_to_name(ZFS_PROP_GUID))) { 28078f17100SMatthew Ahrens dsl_prop_nvlist_add_uint64(out_props, 28178f17100SMatthew Ahrens ZFS_PROP_GUID, bmark_phys.zbm_guid); 28278f17100SMatthew Ahrens } 28378f17100SMatthew Ahrens if (nvlist_exists(props, 28478f17100SMatthew Ahrens zfs_prop_to_name(ZFS_PROP_CREATETXG))) { 28578f17100SMatthew Ahrens dsl_prop_nvlist_add_uint64(out_props, 28678f17100SMatthew Ahrens ZFS_PROP_CREATETXG, bmark_phys.zbm_creation_txg); 28778f17100SMatthew Ahrens } 28878f17100SMatthew Ahrens if (nvlist_exists(props, 28978f17100SMatthew Ahrens zfs_prop_to_name(ZFS_PROP_CREATION))) { 29078f17100SMatthew Ahrens dsl_prop_nvlist_add_uint64(out_props, 29178f17100SMatthew Ahrens ZFS_PROP_CREATION, bmark_phys.zbm_creation_time); 29278f17100SMatthew Ahrens } 29378f17100SMatthew Ahrens 29478f17100SMatthew Ahrens fnvlist_add_nvlist(outnvl, bmark_name, out_props); 29578f17100SMatthew Ahrens fnvlist_free(out_props); 29678f17100SMatthew Ahrens } 29778f17100SMatthew Ahrens zap_cursor_fini(&zc); 29878f17100SMatthew Ahrens return (err); 29978f17100SMatthew Ahrens } 30078f17100SMatthew Ahrens 30178f17100SMatthew Ahrens /* 30278f17100SMatthew Ahrens * Retrieve the bookmarks that exist in the specified dataset, and the 30378f17100SMatthew Ahrens * requested properties of each bookmark. 30478f17100SMatthew Ahrens * 30578f17100SMatthew Ahrens * The "props" nvlist specifies which properties are requested. 30678f17100SMatthew Ahrens * See lzc_get_bookmarks() for the list of valid properties. 30778f17100SMatthew Ahrens */ 30878f17100SMatthew Ahrens int 30978f17100SMatthew Ahrens dsl_get_bookmarks(const char *dsname, nvlist_t *props, nvlist_t *outnvl) 31078f17100SMatthew Ahrens { 31178f17100SMatthew Ahrens dsl_pool_t *dp; 31278f17100SMatthew Ahrens dsl_dataset_t *ds; 31378f17100SMatthew Ahrens int err; 31478f17100SMatthew Ahrens 31578f17100SMatthew Ahrens err = dsl_pool_hold(dsname, FTAG, &dp); 31678f17100SMatthew Ahrens if (err != 0) 31778f17100SMatthew Ahrens return (err); 31878f17100SMatthew Ahrens err = dsl_dataset_hold(dp, dsname, FTAG, &ds); 31978f17100SMatthew Ahrens if (err != 0) { 32078f17100SMatthew Ahrens dsl_pool_rele(dp, FTAG); 32178f17100SMatthew Ahrens return (err); 32278f17100SMatthew Ahrens } 32378f17100SMatthew Ahrens 32478f17100SMatthew Ahrens err = dsl_get_bookmarks_impl(ds, props, outnvl); 32578f17100SMatthew Ahrens 32678f17100SMatthew Ahrens dsl_dataset_rele(ds, FTAG); 32778f17100SMatthew Ahrens dsl_pool_rele(dp, FTAG); 32878f17100SMatthew Ahrens return (err); 32978f17100SMatthew Ahrens } 33078f17100SMatthew Ahrens 33178f17100SMatthew Ahrens typedef struct dsl_bookmark_destroy_arg { 33278f17100SMatthew Ahrens nvlist_t *dbda_bmarks; 33378f17100SMatthew Ahrens nvlist_t *dbda_success; 33478f17100SMatthew Ahrens nvlist_t *dbda_errors; 33578f17100SMatthew Ahrens } dsl_bookmark_destroy_arg_t; 33678f17100SMatthew Ahrens 33778f17100SMatthew Ahrens static int 33878f17100SMatthew Ahrens dsl_dataset_bookmark_remove(dsl_dataset_t *ds, const char *name, dmu_tx_t *tx) 33978f17100SMatthew Ahrens { 34078f17100SMatthew Ahrens objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset; 34178f17100SMatthew Ahrens uint64_t bmark_zapobj = ds->ds_bookmarks; 34278f17100SMatthew Ahrens matchtype_t mt; 34378f17100SMatthew Ahrens 344c1379625SJustin T. Gibbs if (dsl_dataset_phys(ds)->ds_flags & DS_FLAG_CI_DATASET) 34578f17100SMatthew Ahrens mt = MT_FIRST; 34678f17100SMatthew Ahrens else 34778f17100SMatthew Ahrens mt = MT_EXACT; 34878f17100SMatthew Ahrens 34978f17100SMatthew Ahrens return (zap_remove_norm(mos, bmark_zapobj, name, mt, tx)); 35078f17100SMatthew Ahrens } 35178f17100SMatthew Ahrens 35278f17100SMatthew Ahrens static int 35378f17100SMatthew Ahrens dsl_bookmark_destroy_check(void *arg, dmu_tx_t *tx) 35478f17100SMatthew Ahrens { 35578f17100SMatthew Ahrens dsl_bookmark_destroy_arg_t *dbda = arg; 35678f17100SMatthew Ahrens dsl_pool_t *dp = dmu_tx_pool(tx); 35778f17100SMatthew Ahrens int rv = 0; 35878f17100SMatthew Ahrens 359*4ae98e8eSMatthew Ahrens ASSERT(nvlist_empty(dbda->dbda_success)); 360*4ae98e8eSMatthew Ahrens ASSERT(nvlist_empty(dbda->dbda_errors)); 361*4ae98e8eSMatthew Ahrens 36278f17100SMatthew Ahrens if (!spa_feature_is_enabled(dp->dp_spa, SPA_FEATURE_BOOKMARKS)) 36378f17100SMatthew Ahrens return (0); 36478f17100SMatthew Ahrens 36578f17100SMatthew Ahrens for (nvpair_t *pair = nvlist_next_nvpair(dbda->dbda_bmarks, NULL); 36678f17100SMatthew Ahrens pair != NULL; pair = nvlist_next_nvpair(dbda->dbda_bmarks, pair)) { 36778f17100SMatthew Ahrens const char *fullname = nvpair_name(pair); 36878f17100SMatthew Ahrens dsl_dataset_t *ds; 36978f17100SMatthew Ahrens zfs_bookmark_phys_t bm; 37078f17100SMatthew Ahrens int error; 37178f17100SMatthew Ahrens char *shortname; 37278f17100SMatthew Ahrens 37378f17100SMatthew Ahrens error = dsl_bookmark_hold_ds(dp, fullname, &ds, 37478f17100SMatthew Ahrens FTAG, &shortname); 37578f17100SMatthew Ahrens if (error == ENOENT) { 37678f17100SMatthew Ahrens /* ignore it; the bookmark is "already destroyed" */ 37778f17100SMatthew Ahrens continue; 37878f17100SMatthew Ahrens } 37978f17100SMatthew Ahrens if (error == 0) { 38078f17100SMatthew Ahrens error = dsl_dataset_bmark_lookup(ds, shortname, &bm); 38178f17100SMatthew Ahrens dsl_dataset_rele(ds, FTAG); 38278f17100SMatthew Ahrens if (error == ESRCH) { 38378f17100SMatthew Ahrens /* 38478f17100SMatthew Ahrens * ignore it; the bookmark is 38578f17100SMatthew Ahrens * "already destroyed" 38678f17100SMatthew Ahrens */ 38778f17100SMatthew Ahrens continue; 38878f17100SMatthew Ahrens } 38978f17100SMatthew Ahrens } 39078f17100SMatthew Ahrens if (error == 0) { 391*4ae98e8eSMatthew Ahrens if (dmu_tx_is_syncing(tx)) { 392*4ae98e8eSMatthew Ahrens fnvlist_add_boolean(dbda->dbda_success, 393*4ae98e8eSMatthew Ahrens fullname); 394*4ae98e8eSMatthew Ahrens } 39578f17100SMatthew Ahrens } else { 39678f17100SMatthew Ahrens fnvlist_add_int32(dbda->dbda_errors, fullname, error); 39778f17100SMatthew Ahrens rv = error; 39878f17100SMatthew Ahrens } 39978f17100SMatthew Ahrens } 40078f17100SMatthew Ahrens return (rv); 40178f17100SMatthew Ahrens } 40278f17100SMatthew Ahrens 40378f17100SMatthew Ahrens static void 40478f17100SMatthew Ahrens dsl_bookmark_destroy_sync(void *arg, dmu_tx_t *tx) 40578f17100SMatthew Ahrens { 40678f17100SMatthew Ahrens dsl_bookmark_destroy_arg_t *dbda = arg; 40778f17100SMatthew Ahrens dsl_pool_t *dp = dmu_tx_pool(tx); 40878f17100SMatthew Ahrens objset_t *mos = dp->dp_meta_objset; 40978f17100SMatthew Ahrens 41078f17100SMatthew Ahrens for (nvpair_t *pair = nvlist_next_nvpair(dbda->dbda_success, NULL); 41178f17100SMatthew Ahrens pair != NULL; pair = nvlist_next_nvpair(dbda->dbda_success, pair)) { 41278f17100SMatthew Ahrens dsl_dataset_t *ds; 41378f17100SMatthew Ahrens char *shortname; 41478f17100SMatthew Ahrens uint64_t zap_cnt; 41578f17100SMatthew Ahrens 41678f17100SMatthew Ahrens VERIFY0(dsl_bookmark_hold_ds(dp, nvpair_name(pair), 41778f17100SMatthew Ahrens &ds, FTAG, &shortname)); 41878f17100SMatthew Ahrens VERIFY0(dsl_dataset_bookmark_remove(ds, shortname, tx)); 41978f17100SMatthew Ahrens 42078f17100SMatthew Ahrens /* 42178f17100SMatthew Ahrens * If all of this dataset's bookmarks have been destroyed, 42278f17100SMatthew Ahrens * free the zap object and decrement the feature's use count. 42378f17100SMatthew Ahrens */ 42478f17100SMatthew Ahrens VERIFY0(zap_count(mos, ds->ds_bookmarks, 42578f17100SMatthew Ahrens &zap_cnt)); 42678f17100SMatthew Ahrens if (zap_cnt == 0) { 42778f17100SMatthew Ahrens dmu_buf_will_dirty(ds->ds_dbuf, tx); 42878f17100SMatthew Ahrens VERIFY0(zap_destroy(mos, ds->ds_bookmarks, tx)); 42978f17100SMatthew Ahrens ds->ds_bookmarks = 0; 43078f17100SMatthew Ahrens spa_feature_decr(dp->dp_spa, SPA_FEATURE_BOOKMARKS, tx); 43178f17100SMatthew Ahrens VERIFY0(zap_remove(mos, ds->ds_object, 43278f17100SMatthew Ahrens DS_FIELD_BOOKMARK_NAMES, tx)); 43378f17100SMatthew Ahrens } 43478f17100SMatthew Ahrens 43578f17100SMatthew Ahrens spa_history_log_internal_ds(ds, "remove bookmark", tx, 43678f17100SMatthew Ahrens "name=%s", shortname); 43778f17100SMatthew Ahrens 43878f17100SMatthew Ahrens dsl_dataset_rele(ds, FTAG); 43978f17100SMatthew Ahrens } 44078f17100SMatthew Ahrens } 44178f17100SMatthew Ahrens 44278f17100SMatthew Ahrens /* 44378f17100SMatthew Ahrens * The bookmarks must all be in the same pool. 44478f17100SMatthew Ahrens */ 44578f17100SMatthew Ahrens int 44678f17100SMatthew Ahrens dsl_bookmark_destroy(nvlist_t *bmarks, nvlist_t *errors) 44778f17100SMatthew Ahrens { 44878f17100SMatthew Ahrens int rv; 44978f17100SMatthew Ahrens dsl_bookmark_destroy_arg_t dbda; 45078f17100SMatthew Ahrens nvpair_t *pair = nvlist_next_nvpair(bmarks, NULL); 45178f17100SMatthew Ahrens if (pair == NULL) 45278f17100SMatthew Ahrens return (0); 45378f17100SMatthew Ahrens 45478f17100SMatthew Ahrens dbda.dbda_bmarks = bmarks; 45578f17100SMatthew Ahrens dbda.dbda_errors = errors; 45678f17100SMatthew Ahrens dbda.dbda_success = fnvlist_alloc(); 45778f17100SMatthew Ahrens 45878f17100SMatthew Ahrens rv = dsl_sync_task(nvpair_name(pair), dsl_bookmark_destroy_check, 4597d46dc6cSMatthew Ahrens dsl_bookmark_destroy_sync, &dbda, fnvlist_num_pairs(bmarks), 4607d46dc6cSMatthew Ahrens ZFS_SPACE_CHECK_RESERVED); 46178f17100SMatthew Ahrens fnvlist_free(dbda.dbda_success); 46278f17100SMatthew Ahrens return (rv); 46378f17100SMatthew Ahrens } 464