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  */
151c17160aSKevin Crowe 
1678f17100SMatthew Ahrens /*
177d46dc6cSMatthew Ahrens  * Copyright (c) 2013, 2014 by Delphix. All rights reserved.
181c17160aSKevin Crowe  * Copyright 2017 Nexenta Systems, Inc.
1978f17100SMatthew Ahrens  */
2078f17100SMatthew Ahrens 
2178f17100SMatthew Ahrens #include <sys/zfs_context.h>
2278f17100SMatthew Ahrens #include <sys/dsl_dataset.h>
2378f17100SMatthew Ahrens #include <sys/dsl_dir.h>
2478f17100SMatthew Ahrens #include <sys/dsl_prop.h>
2578f17100SMatthew Ahrens #include <sys/dsl_synctask.h>
2678f17100SMatthew Ahrens #include <sys/dmu_impl.h>
2778f17100SMatthew Ahrens #include <sys/dmu_tx.h>
2878f17100SMatthew Ahrens #include <sys/arc.h>
2978f17100SMatthew Ahrens #include <sys/zap.h>
3078f17100SMatthew Ahrens #include <sys/zfeature.h>
3178f17100SMatthew Ahrens #include <sys/spa.h>
3278f17100SMatthew Ahrens #include <sys/dsl_bookmark.h>
3378f17100SMatthew Ahrens #include <zfs_namecheck.h>
3478f17100SMatthew Ahrens 
3578f17100SMatthew Ahrens static int
dsl_bookmark_hold_ds(dsl_pool_t * dp,const char * fullname,dsl_dataset_t ** dsp,void * tag,char ** shortnamep)3678f17100SMatthew Ahrens dsl_bookmark_hold_ds(dsl_pool_t *dp, const char *fullname,
3778f17100SMatthew Ahrens     dsl_dataset_t **dsp, void *tag, char **shortnamep)
3878f17100SMatthew Ahrens {
399adfa60dSMatthew Ahrens 	char buf[ZFS_MAX_DATASET_NAME_LEN];
4078f17100SMatthew Ahrens 	char *hashp;
4178f17100SMatthew Ahrens 
429adfa60dSMatthew Ahrens 	if (strlen(fullname) >= ZFS_MAX_DATASET_NAME_LEN)
4378f17100SMatthew Ahrens 		return (SET_ERROR(ENAMETOOLONG));
4478f17100SMatthew Ahrens 	hashp = strchr(fullname, '#');
4578f17100SMatthew Ahrens 	if (hashp == NULL)
4678f17100SMatthew Ahrens 		return (SET_ERROR(EINVAL));
4778f17100SMatthew Ahrens 
4878f17100SMatthew Ahrens 	*shortnamep = hashp + 1;
4978f17100SMatthew Ahrens 	if (zfs_component_namecheck(*shortnamep, NULL, NULL))
5078f17100SMatthew Ahrens 		return (SET_ERROR(EINVAL));
5178f17100SMatthew Ahrens 	(void) strlcpy(buf, fullname, hashp - fullname + 1);
5278f17100SMatthew Ahrens 	return (dsl_dataset_hold(dp, buf, tag, dsp));
5378f17100SMatthew Ahrens }
5478f17100SMatthew Ahrens 
5578f17100SMatthew Ahrens /*
5678f17100SMatthew Ahrens  * Returns ESRCH if bookmark is not found.
5778f17100SMatthew Ahrens  */
5878f17100SMatthew Ahrens static int
dsl_dataset_bmark_lookup(dsl_dataset_t * ds,const char * shortname,zfs_bookmark_phys_t * bmark_phys)5978f17100SMatthew Ahrens dsl_dataset_bmark_lookup(dsl_dataset_t *ds, const char *shortname,
6078f17100SMatthew Ahrens     zfs_bookmark_phys_t *bmark_phys)
6178f17100SMatthew Ahrens {
6278f17100SMatthew Ahrens 	objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset;
6378f17100SMatthew Ahrens 	uint64_t bmark_zapobj = ds->ds_bookmarks;
641c17160aSKevin Crowe 	matchtype_t mt = 0;
6578f17100SMatthew Ahrens 	int err;
6678f17100SMatthew Ahrens 
6778f17100SMatthew Ahrens 	if (bmark_zapobj == 0)
6878f17100SMatthew Ahrens 		return (SET_ERROR(ESRCH));
6978f17100SMatthew Ahrens 
70c1379625SJustin T. Gibbs 	if (dsl_dataset_phys(ds)->ds_flags & DS_FLAG_CI_DATASET)
711c17160aSKevin Crowe 		mt = MT_NORMALIZE;
7278f17100SMatthew Ahrens 
73*eb633035STom Caputi 	/*
74*eb633035STom Caputi 	 * Zero out the bookmark in case the one stored on disk
75*eb633035STom Caputi 	 * is in an older, shorter format.
76*eb633035STom Caputi 	 */
77*eb633035STom Caputi 	bzero(bmark_phys, sizeof (*bmark_phys));
78*eb633035STom Caputi 
7978f17100SMatthew Ahrens 	err = zap_lookup_norm(mos, bmark_zapobj, shortname, sizeof (uint64_t),
8078f17100SMatthew Ahrens 	    sizeof (*bmark_phys) / sizeof (uint64_t), bmark_phys, mt,
8178f17100SMatthew Ahrens 	    NULL, 0, NULL);
8278f17100SMatthew Ahrens 
8378f17100SMatthew Ahrens 	return (err == ENOENT ? ESRCH : err);
8478f17100SMatthew Ahrens }
8578f17100SMatthew Ahrens 
8678f17100SMatthew Ahrens /*
8778f17100SMatthew Ahrens  * If later_ds is non-NULL, this will return EXDEV if the the specified bookmark
8878f17100SMatthew Ahrens  * does not represents an earlier point in later_ds's timeline.
8978f17100SMatthew Ahrens  *
9078f17100SMatthew Ahrens  * Returns ENOENT if the dataset containing the bookmark does not exist.
9178f17100SMatthew Ahrens  * Returns ESRCH if the dataset exists but the bookmark was not found in it.
9278f17100SMatthew Ahrens  */
9378f17100SMatthew Ahrens int
dsl_bookmark_lookup(dsl_pool_t * dp,const char * fullname,dsl_dataset_t * later_ds,zfs_bookmark_phys_t * bmp)9478f17100SMatthew Ahrens dsl_bookmark_lookup(dsl_pool_t *dp, const char *fullname,
9578f17100SMatthew Ahrens     dsl_dataset_t *later_ds, zfs_bookmark_phys_t *bmp)
9678f17100SMatthew Ahrens {
9778f17100SMatthew Ahrens 	char *shortname;
9878f17100SMatthew Ahrens 	dsl_dataset_t *ds;
9978f17100SMatthew Ahrens 	int error;
10078f17100SMatthew Ahrens 
10178f17100SMatthew Ahrens 	error = dsl_bookmark_hold_ds(dp, fullname, &ds, FTAG, &shortname);
10278f17100SMatthew Ahrens 	if (error != 0)
10378f17100SMatthew Ahrens 		return (error);
10478f17100SMatthew Ahrens 
10578f17100SMatthew Ahrens 	error = dsl_dataset_bmark_lookup(ds, shortname, bmp);
10678f17100SMatthew Ahrens 	if (error == 0 && later_ds != NULL) {
10778f17100SMatthew Ahrens 		if (!dsl_dataset_is_before(later_ds, ds, bmp->zbm_creation_txg))
10878f17100SMatthew Ahrens 			error = SET_ERROR(EXDEV);
10978f17100SMatthew Ahrens 	}
11078f17100SMatthew Ahrens 	dsl_dataset_rele(ds, FTAG);
11178f17100SMatthew Ahrens 	return (error);
11278f17100SMatthew Ahrens }
11378f17100SMatthew Ahrens 
11478f17100SMatthew Ahrens typedef struct dsl_bookmark_create_arg {
11578f17100SMatthew Ahrens 	nvlist_t *dbca_bmarks;
11678f17100SMatthew Ahrens 	nvlist_t *dbca_errors;
11778f17100SMatthew Ahrens } dsl_bookmark_create_arg_t;
11878f17100SMatthew Ahrens 
11978f17100SMatthew Ahrens static int
dsl_bookmark_create_check_impl(dsl_dataset_t * snapds,const char * bookmark_name,dmu_tx_t * tx)12078f17100SMatthew Ahrens dsl_bookmark_create_check_impl(dsl_dataset_t *snapds, const char *bookmark_name,
12178f17100SMatthew Ahrens     dmu_tx_t *tx)
12278f17100SMatthew Ahrens {
12378f17100SMatthew Ahrens 	dsl_pool_t *dp = dmu_tx_pool(tx);
12478f17100SMatthew Ahrens 	dsl_dataset_t *bmark_fs;
12578f17100SMatthew Ahrens 	char *shortname;
12678f17100SMatthew Ahrens 	int error;
12778f17100SMatthew Ahrens 	zfs_bookmark_phys_t bmark_phys;
12878f17100SMatthew Ahrens 
129bc9014e6SJustin Gibbs 	if (!snapds->ds_is_snapshot)
13078f17100SMatthew Ahrens 		return (SET_ERROR(EINVAL));
13178f17100SMatthew Ahrens 
13278f17100SMatthew Ahrens 	error = dsl_bookmark_hold_ds(dp, bookmark_name,
13378f17100SMatthew Ahrens 	    &bmark_fs, FTAG, &shortname);
13478f17100SMatthew Ahrens 	if (error != 0)
13578f17100SMatthew Ahrens 		return (error);
13678f17100SMatthew Ahrens 
13778f17100SMatthew Ahrens 	if (!dsl_dataset_is_before(bmark_fs, snapds, 0)) {
13878f17100SMatthew Ahrens 		dsl_dataset_rele(bmark_fs, FTAG);
13978f17100SMatthew Ahrens 		return (SET_ERROR(EINVAL));
14078f17100SMatthew Ahrens 	}
14178f17100SMatthew Ahrens 
14278f17100SMatthew Ahrens 	error = dsl_dataset_bmark_lookup(bmark_fs, shortname,
14378f17100SMatthew Ahrens 	    &bmark_phys);
14478f17100SMatthew Ahrens 	dsl_dataset_rele(bmark_fs, FTAG);
14578f17100SMatthew Ahrens 	if (error == 0)
14678f17100SMatthew Ahrens 		return (SET_ERROR(EEXIST));
14778f17100SMatthew Ahrens 	if (error == ESRCH)
14878f17100SMatthew Ahrens 		return (0);
14978f17100SMatthew Ahrens 	return (error);
15078f17100SMatthew Ahrens }
15178f17100SMatthew Ahrens 
15278f17100SMatthew Ahrens static int
dsl_bookmark_create_check(void * arg,dmu_tx_t * tx)15378f17100SMatthew Ahrens dsl_bookmark_create_check(void *arg, dmu_tx_t *tx)
15478f17100SMatthew Ahrens {
15578f17100SMatthew Ahrens 	dsl_bookmark_create_arg_t *dbca = arg;
15678f17100SMatthew Ahrens 	dsl_pool_t *dp = dmu_tx_pool(tx);
15778f17100SMatthew Ahrens 	int rv = 0;
15878f17100SMatthew Ahrens 
15978f17100SMatthew Ahrens 	if (!spa_feature_is_enabled(dp->dp_spa, SPA_FEATURE_BOOKMARKS))
16078f17100SMatthew Ahrens 		return (SET_ERROR(ENOTSUP));
16178f17100SMatthew Ahrens 
16278f17100SMatthew Ahrens 	for (nvpair_t *pair = nvlist_next_nvpair(dbca->dbca_bmarks, NULL);
16378f17100SMatthew Ahrens 	    pair != NULL; pair = nvlist_next_nvpair(dbca->dbca_bmarks, pair)) {
16478f17100SMatthew Ahrens 		dsl_dataset_t *snapds;
16578f17100SMatthew Ahrens 		int error;
16678f17100SMatthew Ahrens 
16778f17100SMatthew Ahrens 		/* note: validity of nvlist checked by ioctl layer */
16878f17100SMatthew Ahrens 		error = dsl_dataset_hold(dp, fnvpair_value_string(pair),
16978f17100SMatthew Ahrens 		    FTAG, &snapds);
17078f17100SMatthew Ahrens 		if (error == 0) {
17178f17100SMatthew Ahrens 			error = dsl_bookmark_create_check_impl(snapds,
17278f17100SMatthew Ahrens 			    nvpair_name(pair), tx);
17378f17100SMatthew Ahrens 			dsl_dataset_rele(snapds, FTAG);
17478f17100SMatthew Ahrens 		}
17578f17100SMatthew Ahrens 		if (error != 0) {
17678f17100SMatthew Ahrens 			fnvlist_add_int32(dbca->dbca_errors,
17778f17100SMatthew Ahrens 			    nvpair_name(pair), error);
17878f17100SMatthew Ahrens 			rv = error;
17978f17100SMatthew Ahrens 		}
18078f17100SMatthew Ahrens 	}
18178f17100SMatthew Ahrens 
18278f17100SMatthew Ahrens 	return (rv);
18378f17100SMatthew Ahrens }
18478f17100SMatthew Ahrens 
18578f17100SMatthew Ahrens static void
dsl_bookmark_create_sync(void * arg,dmu_tx_t * tx)18678f17100SMatthew Ahrens dsl_bookmark_create_sync(void *arg, dmu_tx_t *tx)
18778f17100SMatthew Ahrens {
18878f17100SMatthew Ahrens 	dsl_bookmark_create_arg_t *dbca = arg;
18978f17100SMatthew Ahrens 	dsl_pool_t *dp = dmu_tx_pool(tx);
19078f17100SMatthew Ahrens 	objset_t *mos = dp->dp_meta_objset;
19178f17100SMatthew Ahrens 
19278f17100SMatthew Ahrens 	ASSERT(spa_feature_is_enabled(dp->dp_spa, SPA_FEATURE_BOOKMARKS));
19378f17100SMatthew Ahrens 
19478f17100SMatthew Ahrens 	for (nvpair_t *pair = nvlist_next_nvpair(dbca->dbca_bmarks, NULL);
19578f17100SMatthew Ahrens 	    pair != NULL; pair = nvlist_next_nvpair(dbca->dbca_bmarks, pair)) {
19678f17100SMatthew Ahrens 		dsl_dataset_t *snapds, *bmark_fs;
197*eb633035STom Caputi 		zfs_bookmark_phys_t bmark_phys = { 0 };
19878f17100SMatthew Ahrens 		char *shortname;
199*eb633035STom Caputi 		uint32_t bmark_len = BOOKMARK_PHYS_SIZE_V1;
20078f17100SMatthew Ahrens 
20178f17100SMatthew Ahrens 		VERIFY0(dsl_dataset_hold(dp, fnvpair_value_string(pair),
20278f17100SMatthew Ahrens 		    FTAG, &snapds));
20378f17100SMatthew Ahrens 		VERIFY0(dsl_bookmark_hold_ds(dp, nvpair_name(pair),
20478f17100SMatthew Ahrens 		    &bmark_fs, FTAG, &shortname));
20578f17100SMatthew Ahrens 		if (bmark_fs->ds_bookmarks == 0) {
20678f17100SMatthew Ahrens 			bmark_fs->ds_bookmarks =
20778f17100SMatthew Ahrens 			    zap_create_norm(mos, U8_TEXTPREP_TOUPPER,
20878f17100SMatthew Ahrens 			    DMU_OTN_ZAP_METADATA, DMU_OT_NONE, 0, tx);
20978f17100SMatthew Ahrens 			spa_feature_incr(dp->dp_spa, SPA_FEATURE_BOOKMARKS, tx);
21078f17100SMatthew Ahrens 
21178f17100SMatthew Ahrens 			dsl_dataset_zapify(bmark_fs, tx);
21278f17100SMatthew Ahrens 			VERIFY0(zap_add(mos, bmark_fs->ds_object,
21378f17100SMatthew Ahrens 			    DS_FIELD_BOOKMARK_NAMES,
21478f17100SMatthew Ahrens 			    sizeof (bmark_fs->ds_bookmarks), 1,
21578f17100SMatthew Ahrens 			    &bmark_fs->ds_bookmarks, tx));
21678f17100SMatthew Ahrens 		}
21778f17100SMatthew Ahrens 
218c1379625SJustin T. Gibbs 		bmark_phys.zbm_guid = dsl_dataset_phys(snapds)->ds_guid;
219c1379625SJustin T. Gibbs 		bmark_phys.zbm_creation_txg =
220c1379625SJustin T. Gibbs 		    dsl_dataset_phys(snapds)->ds_creation_txg;
22178f17100SMatthew Ahrens 		bmark_phys.zbm_creation_time =
222c1379625SJustin T. Gibbs 		    dsl_dataset_phys(snapds)->ds_creation_time;
22378f17100SMatthew Ahrens 
224*eb633035STom Caputi 		/*
225*eb633035STom Caputi 		 * If the dataset is encrypted create a larger bookmark to
226*eb633035STom Caputi 		 * accommodate the IVset guid. The IVset guid was added
227*eb633035STom Caputi 		 * after the encryption feature to prevent a problem with
228*eb633035STom Caputi 		 * raw sends. If we encounter an encrypted dataset without
229*eb633035STom Caputi 		 * an IVset guid we fall back to a normal bookmark.
230*eb633035STom Caputi 		 */
231*eb633035STom Caputi 		if (snapds->ds_dir->dd_crypto_obj != 0 &&
232*eb633035STom Caputi 		    spa_feature_is_enabled(dp->dp_spa,
233*eb633035STom Caputi 		    SPA_FEATURE_BOOKMARK_V2)) {
234*eb633035STom Caputi 			int err = zap_lookup(mos, snapds->ds_object,
235*eb633035STom Caputi 			    DS_FIELD_IVSET_GUID, sizeof (uint64_t), 1,
236*eb633035STom Caputi 			    &bmark_phys.zbm_ivset_guid);
237*eb633035STom Caputi 			if (err == 0) {
238*eb633035STom Caputi 				bmark_len = BOOKMARK_PHYS_SIZE_V2;
239*eb633035STom Caputi 				spa_feature_incr(dp->dp_spa,
240*eb633035STom Caputi 				    SPA_FEATURE_BOOKMARK_V2, tx);
241*eb633035STom Caputi 			}
242*eb633035STom Caputi 		}
243*eb633035STom Caputi 
24478f17100SMatthew Ahrens 		VERIFY0(zap_add(mos, bmark_fs->ds_bookmarks,
24578f17100SMatthew Ahrens 		    shortname, sizeof (uint64_t),
246*eb633035STom Caputi 		    bmark_len / sizeof (uint64_t), &bmark_phys, tx));
24778f17100SMatthew Ahrens 
24878f17100SMatthew Ahrens 		spa_history_log_internal_ds(bmark_fs, "bookmark", tx,
24978f17100SMatthew Ahrens 		    "name=%s creation_txg=%llu target_snap=%llu",
25078f17100SMatthew Ahrens 		    shortname,
25178f17100SMatthew Ahrens 		    (longlong_t)bmark_phys.zbm_creation_txg,
25278f17100SMatthew Ahrens 		    (longlong_t)snapds->ds_object);
25378f17100SMatthew Ahrens 
25478f17100SMatthew Ahrens 		dsl_dataset_rele(bmark_fs, FTAG);
25578f17100SMatthew Ahrens 		dsl_dataset_rele(snapds, FTAG);
25678f17100SMatthew Ahrens 	}
25778f17100SMatthew Ahrens }
25878f17100SMatthew Ahrens 
25978f17100SMatthew Ahrens /*
26078f17100SMatthew Ahrens  * The bookmarks must all be in the same pool.
26178f17100SMatthew Ahrens  */
26278f17100SMatthew Ahrens int
dsl_bookmark_create(nvlist_t * bmarks,nvlist_t * errors)26378f17100SMatthew Ahrens dsl_bookmark_create(nvlist_t *bmarks, nvlist_t *errors)
26478f17100SMatthew Ahrens {
26578f17100SMatthew Ahrens 	nvpair_t *pair;
26678f17100SMatthew Ahrens 	dsl_bookmark_create_arg_t dbca;
26778f17100SMatthew Ahrens 
26878f17100SMatthew Ahrens 	pair = nvlist_next_nvpair(bmarks, NULL);
26978f17100SMatthew Ahrens 	if (pair == NULL)
27078f17100SMatthew Ahrens 		return (0);
27178f17100SMatthew Ahrens 
27278f17100SMatthew Ahrens 	dbca.dbca_bmarks = bmarks;
27378f17100SMatthew Ahrens 	dbca.dbca_errors = errors;
27478f17100SMatthew Ahrens 
27578f17100SMatthew Ahrens 	return (dsl_sync_task(nvpair_name(pair), dsl_bookmark_create_check,
2767d46dc6cSMatthew Ahrens 	    dsl_bookmark_create_sync, &dbca,
2777d46dc6cSMatthew Ahrens 	    fnvlist_num_pairs(bmarks), ZFS_SPACE_CHECK_NORMAL));
27878f17100SMatthew Ahrens }
27978f17100SMatthew Ahrens 
28078f17100SMatthew Ahrens int
dsl_get_bookmarks_impl(dsl_dataset_t * ds,nvlist_t * props,nvlist_t * outnvl)28178f17100SMatthew Ahrens dsl_get_bookmarks_impl(dsl_dataset_t *ds, nvlist_t *props, nvlist_t *outnvl)
28278f17100SMatthew Ahrens {
28378f17100SMatthew Ahrens 	int err = 0;
28478f17100SMatthew Ahrens 	zap_cursor_t zc;
28578f17100SMatthew Ahrens 	zap_attribute_t attr;
28678f17100SMatthew Ahrens 	dsl_pool_t *dp = ds->ds_dir->dd_pool;
28778f17100SMatthew Ahrens 
28878f17100SMatthew Ahrens 	uint64_t bmark_zapobj = ds->ds_bookmarks;
28978f17100SMatthew Ahrens 	if (bmark_zapobj == 0)
29078f17100SMatthew Ahrens 		return (0);
29178f17100SMatthew Ahrens 
29278f17100SMatthew Ahrens 	for (zap_cursor_init(&zc, dp->dp_meta_objset, bmark_zapobj);
29378f17100SMatthew Ahrens 	    zap_cursor_retrieve(&zc, &attr) == 0;
29478f17100SMatthew Ahrens 	    zap_cursor_advance(&zc)) {
29578f17100SMatthew Ahrens 		char *bmark_name = attr.za_name;
296*eb633035STom Caputi 		zfs_bookmark_phys_t bmark_phys = { 0 };
29778f17100SMatthew Ahrens 
29878f17100SMatthew Ahrens 		err = dsl_dataset_bmark_lookup(ds, bmark_name, &bmark_phys);
29978f17100SMatthew Ahrens 		ASSERT3U(err, !=, ENOENT);
30078f17100SMatthew Ahrens 		if (err != 0)
30178f17100SMatthew Ahrens 			break;
30278f17100SMatthew Ahrens 
30378f17100SMatthew Ahrens 		nvlist_t *out_props = fnvlist_alloc();
30478f17100SMatthew Ahrens 		if (nvlist_exists(props,
30578f17100SMatthew Ahrens 		    zfs_prop_to_name(ZFS_PROP_GUID))) {
30678f17100SMatthew Ahrens 			dsl_prop_nvlist_add_uint64(out_props,
30778f17100SMatthew Ahrens 			    ZFS_PROP_GUID, bmark_phys.zbm_guid);
30878f17100SMatthew Ahrens 		}
30978f17100SMatthew Ahrens 		if (nvlist_exists(props,
31078f17100SMatthew Ahrens 		    zfs_prop_to_name(ZFS_PROP_CREATETXG))) {
31178f17100SMatthew Ahrens 			dsl_prop_nvlist_add_uint64(out_props,
31278f17100SMatthew Ahrens 			    ZFS_PROP_CREATETXG, bmark_phys.zbm_creation_txg);
31378f17100SMatthew Ahrens 		}
31478f17100SMatthew Ahrens 		if (nvlist_exists(props,
31578f17100SMatthew Ahrens 		    zfs_prop_to_name(ZFS_PROP_CREATION))) {
31678f17100SMatthew Ahrens 			dsl_prop_nvlist_add_uint64(out_props,
31778f17100SMatthew Ahrens 			    ZFS_PROP_CREATION, bmark_phys.zbm_creation_time);
31878f17100SMatthew Ahrens 		}
319*eb633035STom Caputi 		if (nvlist_exists(props,
320*eb633035STom Caputi 		    zfs_prop_to_name(ZFS_PROP_IVSET_GUID))) {
321*eb633035STom Caputi 			dsl_prop_nvlist_add_uint64(out_props,
322*eb633035STom Caputi 			    ZFS_PROP_IVSET_GUID, bmark_phys.zbm_ivset_guid);
323*eb633035STom Caputi 		}
32478f17100SMatthew Ahrens 
32578f17100SMatthew Ahrens 		fnvlist_add_nvlist(outnvl, bmark_name, out_props);
32678f17100SMatthew Ahrens 		fnvlist_free(out_props);
32778f17100SMatthew Ahrens 	}
32878f17100SMatthew Ahrens 	zap_cursor_fini(&zc);
32978f17100SMatthew Ahrens 	return (err);
33078f17100SMatthew Ahrens }
33178f17100SMatthew Ahrens 
33278f17100SMatthew Ahrens /*
33378f17100SMatthew Ahrens  * Retrieve the bookmarks that exist in the specified dataset, and the
33478f17100SMatthew Ahrens  * requested properties of each bookmark.
33578f17100SMatthew Ahrens  *
33678f17100SMatthew Ahrens  * The "props" nvlist specifies which properties are requested.
33778f17100SMatthew Ahrens  * See lzc_get_bookmarks() for the list of valid properties.
33878f17100SMatthew Ahrens  */
33978f17100SMatthew Ahrens int
dsl_get_bookmarks(const char * dsname,nvlist_t * props,nvlist_t * outnvl)34078f17100SMatthew Ahrens dsl_get_bookmarks(const char *dsname, nvlist_t *props, nvlist_t *outnvl)
34178f17100SMatthew Ahrens {
34278f17100SMatthew Ahrens 	dsl_pool_t *dp;
34378f17100SMatthew Ahrens 	dsl_dataset_t *ds;
34478f17100SMatthew Ahrens 	int err;
34578f17100SMatthew Ahrens 
34678f17100SMatthew Ahrens 	err = dsl_pool_hold(dsname, FTAG, &dp);
34778f17100SMatthew Ahrens 	if (err != 0)
34878f17100SMatthew Ahrens 		return (err);
34978f17100SMatthew Ahrens 	err = dsl_dataset_hold(dp, dsname, FTAG, &ds);
35078f17100SMatthew Ahrens 	if (err != 0) {
35178f17100SMatthew Ahrens 		dsl_pool_rele(dp, FTAG);
35278f17100SMatthew Ahrens 		return (err);
35378f17100SMatthew Ahrens 	}
35478f17100SMatthew Ahrens 
35578f17100SMatthew Ahrens 	err = dsl_get_bookmarks_impl(ds, props, outnvl);
35678f17100SMatthew Ahrens 
35778f17100SMatthew Ahrens 	dsl_dataset_rele(ds, FTAG);
35878f17100SMatthew Ahrens 	dsl_pool_rele(dp, FTAG);
35978f17100SMatthew Ahrens 	return (err);
36078f17100SMatthew Ahrens }
36178f17100SMatthew Ahrens 
36278f17100SMatthew Ahrens typedef struct dsl_bookmark_destroy_arg {
36378f17100SMatthew Ahrens 	nvlist_t *dbda_bmarks;
36478f17100SMatthew Ahrens 	nvlist_t *dbda_success;
36578f17100SMatthew Ahrens 	nvlist_t *dbda_errors;
36678f17100SMatthew Ahrens } dsl_bookmark_destroy_arg_t;
36778f17100SMatthew Ahrens 
36878f17100SMatthew Ahrens static int
dsl_dataset_bookmark_remove(dsl_dataset_t * ds,const char * name,dmu_tx_t * tx)36978f17100SMatthew Ahrens dsl_dataset_bookmark_remove(dsl_dataset_t *ds, const char *name, dmu_tx_t *tx)
37078f17100SMatthew Ahrens {
371*eb633035STom Caputi 	int err;
37278f17100SMatthew Ahrens 	objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset;
37378f17100SMatthew Ahrens 	uint64_t bmark_zapobj = ds->ds_bookmarks;
3741c17160aSKevin Crowe 	matchtype_t mt = 0;
375*eb633035STom Caputi 	uint64_t int_size, num_ints;
37678f17100SMatthew Ahrens 
377c1379625SJustin T. Gibbs 	if (dsl_dataset_phys(ds)->ds_flags & DS_FLAG_CI_DATASET)
3781c17160aSKevin Crowe 		mt = MT_NORMALIZE;
37978f17100SMatthew Ahrens 
380*eb633035STom Caputi 	err = zap_length(mos, bmark_zapobj, name, &int_size, &num_ints);
381*eb633035STom Caputi 	if (err != 0)
382*eb633035STom Caputi 		return (err);
383*eb633035STom Caputi 
384*eb633035STom Caputi 	ASSERT3U(int_size, ==, sizeof (uint64_t));
385*eb633035STom Caputi 
386*eb633035STom Caputi 	if (num_ints * int_size > BOOKMARK_PHYS_SIZE_V1) {
387*eb633035STom Caputi 		spa_feature_decr(dmu_objset_spa(mos),
388*eb633035STom Caputi 		    SPA_FEATURE_BOOKMARK_V2, tx);
389*eb633035STom Caputi 	}
390*eb633035STom Caputi 
39178f17100SMatthew Ahrens 	return (zap_remove_norm(mos, bmark_zapobj, name, mt, tx));
39278f17100SMatthew Ahrens }
39378f17100SMatthew Ahrens 
39478f17100SMatthew Ahrens static int
dsl_bookmark_destroy_check(void * arg,dmu_tx_t * tx)39578f17100SMatthew Ahrens dsl_bookmark_destroy_check(void *arg, dmu_tx_t *tx)
39678f17100SMatthew Ahrens {
39778f17100SMatthew Ahrens 	dsl_bookmark_destroy_arg_t *dbda = arg;
39878f17100SMatthew Ahrens 	dsl_pool_t *dp = dmu_tx_pool(tx);
39978f17100SMatthew Ahrens 	int rv = 0;
40078f17100SMatthew Ahrens 
40142418f9eSMatthew Ahrens 	ASSERT(nvlist_empty(dbda->dbda_success));
40242418f9eSMatthew Ahrens 	ASSERT(nvlist_empty(dbda->dbda_errors));
40342418f9eSMatthew Ahrens 
40478f17100SMatthew Ahrens 	if (!spa_feature_is_enabled(dp->dp_spa, SPA_FEATURE_BOOKMARKS))
40578f17100SMatthew Ahrens 		return (0);
40678f17100SMatthew Ahrens 
40778f17100SMatthew Ahrens 	for (nvpair_t *pair = nvlist_next_nvpair(dbda->dbda_bmarks, NULL);
40878f17100SMatthew Ahrens 	    pair != NULL; pair = nvlist_next_nvpair(dbda->dbda_bmarks, pair)) {
40978f17100SMatthew Ahrens 		const char *fullname = nvpair_name(pair);
41078f17100SMatthew Ahrens 		dsl_dataset_t *ds;
41178f17100SMatthew Ahrens 		zfs_bookmark_phys_t bm;
41278f17100SMatthew Ahrens 		int error;
41378f17100SMatthew Ahrens 		char *shortname;
41478f17100SMatthew Ahrens 
41578f17100SMatthew Ahrens 		error = dsl_bookmark_hold_ds(dp, fullname, &ds,
41678f17100SMatthew Ahrens 		    FTAG, &shortname);
41778f17100SMatthew Ahrens 		if (error == ENOENT) {
41878f17100SMatthew Ahrens 			/* ignore it; the bookmark is "already destroyed" */
41978f17100SMatthew Ahrens 			continue;
42078f17100SMatthew Ahrens 		}
42178f17100SMatthew Ahrens 		if (error == 0) {
42278f17100SMatthew Ahrens 			error = dsl_dataset_bmark_lookup(ds, shortname, &bm);
42378f17100SMatthew Ahrens 			dsl_dataset_rele(ds, FTAG);
42478f17100SMatthew Ahrens 			if (error == ESRCH) {
42578f17100SMatthew Ahrens 				/*
42678f17100SMatthew Ahrens 				 * ignore it; the bookmark is
42778f17100SMatthew Ahrens 				 * "already destroyed"
42878f17100SMatthew Ahrens 				 */
42978f17100SMatthew Ahrens 				continue;
43078f17100SMatthew Ahrens 			}
43178f17100SMatthew Ahrens 		}
43278f17100SMatthew Ahrens 		if (error == 0) {
43342418f9eSMatthew Ahrens 			if (dmu_tx_is_syncing(tx)) {
43442418f9eSMatthew Ahrens 				fnvlist_add_boolean(dbda->dbda_success,
43542418f9eSMatthew Ahrens 				    fullname);
43642418f9eSMatthew Ahrens 			}
43778f17100SMatthew Ahrens 		} else {
43878f17100SMatthew Ahrens 			fnvlist_add_int32(dbda->dbda_errors, fullname, error);
43978f17100SMatthew Ahrens 			rv = error;
44078f17100SMatthew Ahrens 		}
44178f17100SMatthew Ahrens 	}
44278f17100SMatthew Ahrens 	return (rv);
44378f17100SMatthew Ahrens }
44478f17100SMatthew Ahrens 
44578f17100SMatthew Ahrens static void
dsl_bookmark_destroy_sync(void * arg,dmu_tx_t * tx)44678f17100SMatthew Ahrens dsl_bookmark_destroy_sync(void *arg, dmu_tx_t *tx)
44778f17100SMatthew Ahrens {
44878f17100SMatthew Ahrens 	dsl_bookmark_destroy_arg_t *dbda = arg;
44978f17100SMatthew Ahrens 	dsl_pool_t *dp = dmu_tx_pool(tx);
45078f17100SMatthew Ahrens 	objset_t *mos = dp->dp_meta_objset;
45178f17100SMatthew Ahrens 
45278f17100SMatthew Ahrens 	for (nvpair_t *pair = nvlist_next_nvpair(dbda->dbda_success, NULL);
45378f17100SMatthew Ahrens 	    pair != NULL; pair = nvlist_next_nvpair(dbda->dbda_success, pair)) {
45478f17100SMatthew Ahrens 		dsl_dataset_t *ds;
45578f17100SMatthew Ahrens 		char *shortname;
45678f17100SMatthew Ahrens 		uint64_t zap_cnt;
45778f17100SMatthew Ahrens 
45878f17100SMatthew Ahrens 		VERIFY0(dsl_bookmark_hold_ds(dp, nvpair_name(pair),
45978f17100SMatthew Ahrens 		    &ds, FTAG, &shortname));
46078f17100SMatthew Ahrens 		VERIFY0(dsl_dataset_bookmark_remove(ds, shortname, tx));
46178f17100SMatthew Ahrens 
46278f17100SMatthew Ahrens 		/*
46378f17100SMatthew Ahrens 		 * If all of this dataset's bookmarks have been destroyed,
46478f17100SMatthew Ahrens 		 * free the zap object and decrement the feature's use count.
46578f17100SMatthew Ahrens 		 */
46678f17100SMatthew Ahrens 		VERIFY0(zap_count(mos, ds->ds_bookmarks,
46778f17100SMatthew Ahrens 		    &zap_cnt));
46878f17100SMatthew Ahrens 		if (zap_cnt == 0) {
46978f17100SMatthew Ahrens 			dmu_buf_will_dirty(ds->ds_dbuf, tx);
47078f17100SMatthew Ahrens 			VERIFY0(zap_destroy(mos, ds->ds_bookmarks, tx));
47178f17100SMatthew Ahrens 			ds->ds_bookmarks = 0;
47278f17100SMatthew Ahrens 			spa_feature_decr(dp->dp_spa, SPA_FEATURE_BOOKMARKS, tx);
47378f17100SMatthew Ahrens 			VERIFY0(zap_remove(mos, ds->ds_object,
47478f17100SMatthew Ahrens 			    DS_FIELD_BOOKMARK_NAMES, tx));
47578f17100SMatthew Ahrens 		}
47678f17100SMatthew Ahrens 
47778f17100SMatthew Ahrens 		spa_history_log_internal_ds(ds, "remove bookmark", tx,
47878f17100SMatthew Ahrens 		    "name=%s", shortname);
47978f17100SMatthew Ahrens 
48078f17100SMatthew Ahrens 		dsl_dataset_rele(ds, FTAG);
48178f17100SMatthew Ahrens 	}
48278f17100SMatthew Ahrens }
48378f17100SMatthew Ahrens 
48478f17100SMatthew Ahrens /*
48578f17100SMatthew Ahrens  * The bookmarks must all be in the same pool.
48678f17100SMatthew Ahrens  */
48778f17100SMatthew Ahrens int
dsl_bookmark_destroy(nvlist_t * bmarks,nvlist_t * errors)48878f17100SMatthew Ahrens dsl_bookmark_destroy(nvlist_t *bmarks, nvlist_t *errors)
48978f17100SMatthew Ahrens {
49078f17100SMatthew Ahrens 	int rv;
49178f17100SMatthew Ahrens 	dsl_bookmark_destroy_arg_t dbda;
49278f17100SMatthew Ahrens 	nvpair_t *pair = nvlist_next_nvpair(bmarks, NULL);
49378f17100SMatthew Ahrens 	if (pair == NULL)
49478f17100SMatthew Ahrens 		return (0);
49578f17100SMatthew Ahrens 
49678f17100SMatthew Ahrens 	dbda.dbda_bmarks = bmarks;
49778f17100SMatthew Ahrens 	dbda.dbda_errors = errors;
49878f17100SMatthew Ahrens 	dbda.dbda_success = fnvlist_alloc();
49978f17100SMatthew Ahrens 
50078f17100SMatthew Ahrens 	rv = dsl_sync_task(nvpair_name(pair), dsl_bookmark_destroy_check,
5017d46dc6cSMatthew Ahrens 	    dsl_bookmark_destroy_sync, &dbda, fnvlist_num_pairs(bmarks),
5027d46dc6cSMatthew Ahrens 	    ZFS_SPACE_CHECK_RESERVED);
50378f17100SMatthew Ahrens 	fnvlist_free(dbda.dbda_success);
50478f17100SMatthew Ahrens 	return (rv);
50578f17100SMatthew Ahrens }
506