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