xref: /illumos-gate/usr/src/uts/common/fs/zfs/dsl_bookmark.c (revision 9adfa60d484ce2435f5af77cc99dcd4e692b6660)
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 {
37*9adfa60dSMatthew Ahrens 	char buf[ZFS_MAX_DATASET_NAME_LEN];
3878f17100SMatthew Ahrens 	char *hashp;
3978f17100SMatthew Ahrens 
40*9adfa60dSMatthew 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 
35978f17100SMatthew Ahrens 	if (!spa_feature_is_enabled(dp->dp_spa, SPA_FEATURE_BOOKMARKS))
36078f17100SMatthew Ahrens 		return (0);
36178f17100SMatthew Ahrens 
36278f17100SMatthew Ahrens 	for (nvpair_t *pair = nvlist_next_nvpair(dbda->dbda_bmarks, NULL);
36378f17100SMatthew Ahrens 	    pair != NULL; pair = nvlist_next_nvpair(dbda->dbda_bmarks, pair)) {
36478f17100SMatthew Ahrens 		const char *fullname = nvpair_name(pair);
36578f17100SMatthew Ahrens 		dsl_dataset_t *ds;
36678f17100SMatthew Ahrens 		zfs_bookmark_phys_t bm;
36778f17100SMatthew Ahrens 		int error;
36878f17100SMatthew Ahrens 		char *shortname;
36978f17100SMatthew Ahrens 
37078f17100SMatthew Ahrens 		error = dsl_bookmark_hold_ds(dp, fullname, &ds,
37178f17100SMatthew Ahrens 		    FTAG, &shortname);
37278f17100SMatthew Ahrens 		if (error == ENOENT) {
37378f17100SMatthew Ahrens 			/* ignore it; the bookmark is "already destroyed" */
37478f17100SMatthew Ahrens 			continue;
37578f17100SMatthew Ahrens 		}
37678f17100SMatthew Ahrens 		if (error == 0) {
37778f17100SMatthew Ahrens 			error = dsl_dataset_bmark_lookup(ds, shortname, &bm);
37878f17100SMatthew Ahrens 			dsl_dataset_rele(ds, FTAG);
37978f17100SMatthew Ahrens 			if (error == ESRCH) {
38078f17100SMatthew Ahrens 				/*
38178f17100SMatthew Ahrens 				 * ignore it; the bookmark is
38278f17100SMatthew Ahrens 				 * "already destroyed"
38378f17100SMatthew Ahrens 				 */
38478f17100SMatthew Ahrens 				continue;
38578f17100SMatthew Ahrens 			}
38678f17100SMatthew Ahrens 		}
38778f17100SMatthew Ahrens 		if (error == 0) {
38878f17100SMatthew Ahrens 			fnvlist_add_boolean(dbda->dbda_success, fullname);
38978f17100SMatthew Ahrens 		} else {
39078f17100SMatthew Ahrens 			fnvlist_add_int32(dbda->dbda_errors, fullname, error);
39178f17100SMatthew Ahrens 			rv = error;
39278f17100SMatthew Ahrens 		}
39378f17100SMatthew Ahrens 	}
39478f17100SMatthew Ahrens 	return (rv);
39578f17100SMatthew Ahrens }
39678f17100SMatthew Ahrens 
39778f17100SMatthew Ahrens static void
39878f17100SMatthew Ahrens dsl_bookmark_destroy_sync(void *arg, dmu_tx_t *tx)
39978f17100SMatthew Ahrens {
40078f17100SMatthew Ahrens 	dsl_bookmark_destroy_arg_t *dbda = arg;
40178f17100SMatthew Ahrens 	dsl_pool_t *dp = dmu_tx_pool(tx);
40278f17100SMatthew Ahrens 	objset_t *mos = dp->dp_meta_objset;
40378f17100SMatthew Ahrens 
40478f17100SMatthew Ahrens 	for (nvpair_t *pair = nvlist_next_nvpair(dbda->dbda_success, NULL);
40578f17100SMatthew Ahrens 	    pair != NULL; pair = nvlist_next_nvpair(dbda->dbda_success, pair)) {
40678f17100SMatthew Ahrens 		dsl_dataset_t *ds;
40778f17100SMatthew Ahrens 		char *shortname;
40878f17100SMatthew Ahrens 		uint64_t zap_cnt;
40978f17100SMatthew Ahrens 
41078f17100SMatthew Ahrens 		VERIFY0(dsl_bookmark_hold_ds(dp, nvpair_name(pair),
41178f17100SMatthew Ahrens 		    &ds, FTAG, &shortname));
41278f17100SMatthew Ahrens 		VERIFY0(dsl_dataset_bookmark_remove(ds, shortname, tx));
41378f17100SMatthew Ahrens 
41478f17100SMatthew Ahrens 		/*
41578f17100SMatthew Ahrens 		 * If all of this dataset's bookmarks have been destroyed,
41678f17100SMatthew Ahrens 		 * free the zap object and decrement the feature's use count.
41778f17100SMatthew Ahrens 		 */
41878f17100SMatthew Ahrens 		VERIFY0(zap_count(mos, ds->ds_bookmarks,
41978f17100SMatthew Ahrens 		    &zap_cnt));
42078f17100SMatthew Ahrens 		if (zap_cnt == 0) {
42178f17100SMatthew Ahrens 			dmu_buf_will_dirty(ds->ds_dbuf, tx);
42278f17100SMatthew Ahrens 			VERIFY0(zap_destroy(mos, ds->ds_bookmarks, tx));
42378f17100SMatthew Ahrens 			ds->ds_bookmarks = 0;
42478f17100SMatthew Ahrens 			spa_feature_decr(dp->dp_spa, SPA_FEATURE_BOOKMARKS, tx);
42578f17100SMatthew Ahrens 			VERIFY0(zap_remove(mos, ds->ds_object,
42678f17100SMatthew Ahrens 			    DS_FIELD_BOOKMARK_NAMES, tx));
42778f17100SMatthew Ahrens 		}
42878f17100SMatthew Ahrens 
42978f17100SMatthew Ahrens 		spa_history_log_internal_ds(ds, "remove bookmark", tx,
43078f17100SMatthew Ahrens 		    "name=%s", shortname);
43178f17100SMatthew Ahrens 
43278f17100SMatthew Ahrens 		dsl_dataset_rele(ds, FTAG);
43378f17100SMatthew Ahrens 	}
43478f17100SMatthew Ahrens }
43578f17100SMatthew Ahrens 
43678f17100SMatthew Ahrens /*
43778f17100SMatthew Ahrens  * The bookmarks must all be in the same pool.
43878f17100SMatthew Ahrens  */
43978f17100SMatthew Ahrens int
44078f17100SMatthew Ahrens dsl_bookmark_destroy(nvlist_t *bmarks, nvlist_t *errors)
44178f17100SMatthew Ahrens {
44278f17100SMatthew Ahrens 	int rv;
44378f17100SMatthew Ahrens 	dsl_bookmark_destroy_arg_t dbda;
44478f17100SMatthew Ahrens 	nvpair_t *pair = nvlist_next_nvpair(bmarks, NULL);
44578f17100SMatthew Ahrens 	if (pair == NULL)
44678f17100SMatthew Ahrens 		return (0);
44778f17100SMatthew Ahrens 
44878f17100SMatthew Ahrens 	dbda.dbda_bmarks = bmarks;
44978f17100SMatthew Ahrens 	dbda.dbda_errors = errors;
45078f17100SMatthew Ahrens 	dbda.dbda_success = fnvlist_alloc();
45178f17100SMatthew Ahrens 
45278f17100SMatthew Ahrens 	rv = dsl_sync_task(nvpair_name(pair), dsl_bookmark_destroy_check,
4537d46dc6cSMatthew Ahrens 	    dsl_bookmark_destroy_sync, &dbda, fnvlist_num_pairs(bmarks),
4547d46dc6cSMatthew Ahrens 	    ZFS_SPACE_CHECK_RESERVED);
45578f17100SMatthew Ahrens 	fnvlist_free(dbda.dbda_success);
45678f17100SMatthew Ahrens 	return (rv);
45778f17100SMatthew Ahrens }
458