1260154Sdelphij/*
2260154Sdelphij * CDDL HEADER START
3260154Sdelphij *
4260154Sdelphij * This file and its contents are supplied under the terms of the
5260154Sdelphij * Common Development and Distribution License ("CDDL"), version 1.0.
6260154Sdelphij * You may only use this file in accordance with the terms of version
7260154Sdelphij * 1.0 of the CDDL.
8260154Sdelphij *
9260154Sdelphij * A full copy of the text of the CDDL should have accompanied this
10260154Sdelphij * source.  A copy of the CDDL is also available via the Internet at
11260154Sdelphij * http://www.illumos.org/license/CDDL.
12260154Sdelphij *
13260154Sdelphij * CDDL HEADER END
14260154Sdelphij */
15321545Smav
16260154Sdelphij/*
17268473Sdelphij * Copyright (c) 2013, 2014 by Delphix. All rights reserved.
18321545Smav * Copyright 2017 Nexenta Systems, Inc.
19260154Sdelphij */
20260154Sdelphij
21260154Sdelphij#include <sys/zfs_context.h>
22260154Sdelphij#include <sys/dsl_dataset.h>
23260154Sdelphij#include <sys/dsl_dir.h>
24260154Sdelphij#include <sys/dsl_prop.h>
25260154Sdelphij#include <sys/dsl_synctask.h>
26260154Sdelphij#include <sys/dmu_impl.h>
27260154Sdelphij#include <sys/dmu_tx.h>
28260154Sdelphij#include <sys/arc.h>
29260154Sdelphij#include <sys/zap.h>
30260154Sdelphij#include <sys/zfeature.h>
31260154Sdelphij#include <sys/spa.h>
32260154Sdelphij#include <sys/dsl_bookmark.h>
33260154Sdelphij#include <zfs_namecheck.h>
34260154Sdelphij
35260154Sdelphijstatic int
36260154Sdelphijdsl_bookmark_hold_ds(dsl_pool_t *dp, const char *fullname,
37260154Sdelphij    dsl_dataset_t **dsp, void *tag, char **shortnamep)
38260154Sdelphij{
39307108Smav	char buf[ZFS_MAX_DATASET_NAME_LEN];
40260154Sdelphij	char *hashp;
41260154Sdelphij
42307108Smav	if (strlen(fullname) >= ZFS_MAX_DATASET_NAME_LEN)
43260154Sdelphij		return (SET_ERROR(ENAMETOOLONG));
44260154Sdelphij	hashp = strchr(fullname, '#');
45260154Sdelphij	if (hashp == NULL)
46260154Sdelphij		return (SET_ERROR(EINVAL));
47260154Sdelphij
48260154Sdelphij	*shortnamep = hashp + 1;
49260154Sdelphij	if (zfs_component_namecheck(*shortnamep, NULL, NULL))
50260154Sdelphij		return (SET_ERROR(EINVAL));
51260154Sdelphij	(void) strlcpy(buf, fullname, hashp - fullname + 1);
52260154Sdelphij	return (dsl_dataset_hold(dp, buf, tag, dsp));
53260154Sdelphij}
54260154Sdelphij
55260154Sdelphij/*
56260154Sdelphij * Returns ESRCH if bookmark is not found.
57260154Sdelphij */
58260154Sdelphijstatic int
59260154Sdelphijdsl_dataset_bmark_lookup(dsl_dataset_t *ds, const char *shortname,
60260154Sdelphij    zfs_bookmark_phys_t *bmark_phys)
61260154Sdelphij{
62260154Sdelphij	objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset;
63260154Sdelphij	uint64_t bmark_zapobj = ds->ds_bookmarks;
64321545Smav	matchtype_t mt = 0;
65260154Sdelphij	int err;
66260154Sdelphij
67260154Sdelphij	if (bmark_zapobj == 0)
68260154Sdelphij		return (SET_ERROR(ESRCH));
69260154Sdelphij
70275782Sdelphij	if (dsl_dataset_phys(ds)->ds_flags & DS_FLAG_CI_DATASET)
71321545Smav		mt = MT_NORMALIZE;
72260154Sdelphij
73260154Sdelphij	err = zap_lookup_norm(mos, bmark_zapobj, shortname, sizeof (uint64_t),
74260154Sdelphij	    sizeof (*bmark_phys) / sizeof (uint64_t), bmark_phys, mt,
75260154Sdelphij	    NULL, 0, NULL);
76260154Sdelphij
77260154Sdelphij	return (err == ENOENT ? ESRCH : err);
78260154Sdelphij}
79260154Sdelphij
80260154Sdelphij/*
81260154Sdelphij * If later_ds is non-NULL, this will return EXDEV if the the specified bookmark
82260154Sdelphij * does not represents an earlier point in later_ds's timeline.
83260154Sdelphij *
84260154Sdelphij * Returns ENOENT if the dataset containing the bookmark does not exist.
85260154Sdelphij * Returns ESRCH if the dataset exists but the bookmark was not found in it.
86260154Sdelphij */
87260154Sdelphijint
88260154Sdelphijdsl_bookmark_lookup(dsl_pool_t *dp, const char *fullname,
89260154Sdelphij    dsl_dataset_t *later_ds, zfs_bookmark_phys_t *bmp)
90260154Sdelphij{
91260154Sdelphij	char *shortname;
92260154Sdelphij	dsl_dataset_t *ds;
93260154Sdelphij	int error;
94260154Sdelphij
95260154Sdelphij	error = dsl_bookmark_hold_ds(dp, fullname, &ds, FTAG, &shortname);
96260154Sdelphij	if (error != 0)
97260154Sdelphij		return (error);
98260154Sdelphij
99260154Sdelphij	error = dsl_dataset_bmark_lookup(ds, shortname, bmp);
100260154Sdelphij	if (error == 0 && later_ds != NULL) {
101260154Sdelphij		if (!dsl_dataset_is_before(later_ds, ds, bmp->zbm_creation_txg))
102260154Sdelphij			error = SET_ERROR(EXDEV);
103260154Sdelphij	}
104260154Sdelphij	dsl_dataset_rele(ds, FTAG);
105260154Sdelphij	return (error);
106260154Sdelphij}
107260154Sdelphij
108260154Sdelphijtypedef struct dsl_bookmark_create_arg {
109260154Sdelphij	nvlist_t *dbca_bmarks;
110260154Sdelphij	nvlist_t *dbca_errors;
111260154Sdelphij} dsl_bookmark_create_arg_t;
112260154Sdelphij
113260154Sdelphijstatic int
114260154Sdelphijdsl_bookmark_create_check_impl(dsl_dataset_t *snapds, const char *bookmark_name,
115260154Sdelphij    dmu_tx_t *tx)
116260154Sdelphij{
117260154Sdelphij	dsl_pool_t *dp = dmu_tx_pool(tx);
118260154Sdelphij	dsl_dataset_t *bmark_fs;
119260154Sdelphij	char *shortname;
120260154Sdelphij	int error;
121260154Sdelphij	zfs_bookmark_phys_t bmark_phys;
122260154Sdelphij
123286575Smav	if (!snapds->ds_is_snapshot)
124260154Sdelphij		return (SET_ERROR(EINVAL));
125260154Sdelphij
126260154Sdelphij	error = dsl_bookmark_hold_ds(dp, bookmark_name,
127260154Sdelphij	    &bmark_fs, FTAG, &shortname);
128260154Sdelphij	if (error != 0)
129260154Sdelphij		return (error);
130260154Sdelphij
131260154Sdelphij	if (!dsl_dataset_is_before(bmark_fs, snapds, 0)) {
132260154Sdelphij		dsl_dataset_rele(bmark_fs, FTAG);
133260154Sdelphij		return (SET_ERROR(EINVAL));
134260154Sdelphij	}
135260154Sdelphij
136260154Sdelphij	error = dsl_dataset_bmark_lookup(bmark_fs, shortname,
137260154Sdelphij	    &bmark_phys);
138260154Sdelphij	dsl_dataset_rele(bmark_fs, FTAG);
139260154Sdelphij	if (error == 0)
140260154Sdelphij		return (SET_ERROR(EEXIST));
141260154Sdelphij	if (error == ESRCH)
142260154Sdelphij		return (0);
143260154Sdelphij	return (error);
144260154Sdelphij}
145260154Sdelphij
146260154Sdelphijstatic int
147260154Sdelphijdsl_bookmark_create_check(void *arg, dmu_tx_t *tx)
148260154Sdelphij{
149260154Sdelphij	dsl_bookmark_create_arg_t *dbca = arg;
150260154Sdelphij	dsl_pool_t *dp = dmu_tx_pool(tx);
151260154Sdelphij	int rv = 0;
152260154Sdelphij
153260154Sdelphij	if (!spa_feature_is_enabled(dp->dp_spa, SPA_FEATURE_BOOKMARKS))
154260154Sdelphij		return (SET_ERROR(ENOTSUP));
155260154Sdelphij
156260154Sdelphij	for (nvpair_t *pair = nvlist_next_nvpair(dbca->dbca_bmarks, NULL);
157260154Sdelphij	    pair != NULL; pair = nvlist_next_nvpair(dbca->dbca_bmarks, pair)) {
158260154Sdelphij		dsl_dataset_t *snapds;
159260154Sdelphij		int error;
160260154Sdelphij
161260154Sdelphij		/* note: validity of nvlist checked by ioctl layer */
162260154Sdelphij		error = dsl_dataset_hold(dp, fnvpair_value_string(pair),
163260154Sdelphij		    FTAG, &snapds);
164260154Sdelphij		if (error == 0) {
165260154Sdelphij			error = dsl_bookmark_create_check_impl(snapds,
166260154Sdelphij			    nvpair_name(pair), tx);
167260154Sdelphij			dsl_dataset_rele(snapds, FTAG);
168260154Sdelphij		}
169260154Sdelphij		if (error != 0) {
170260154Sdelphij			fnvlist_add_int32(dbca->dbca_errors,
171260154Sdelphij			    nvpair_name(pair), error);
172260154Sdelphij			rv = error;
173260154Sdelphij		}
174260154Sdelphij	}
175260154Sdelphij
176260154Sdelphij	return (rv);
177260154Sdelphij}
178260154Sdelphij
179260154Sdelphijstatic void
180260154Sdelphijdsl_bookmark_create_sync(void *arg, dmu_tx_t *tx)
181260154Sdelphij{
182260154Sdelphij	dsl_bookmark_create_arg_t *dbca = arg;
183260154Sdelphij	dsl_pool_t *dp = dmu_tx_pool(tx);
184260154Sdelphij	objset_t *mos = dp->dp_meta_objset;
185260154Sdelphij
186260154Sdelphij	ASSERT(spa_feature_is_enabled(dp->dp_spa, SPA_FEATURE_BOOKMARKS));
187260154Sdelphij
188260154Sdelphij	for (nvpair_t *pair = nvlist_next_nvpair(dbca->dbca_bmarks, NULL);
189260154Sdelphij	    pair != NULL; pair = nvlist_next_nvpair(dbca->dbca_bmarks, pair)) {
190260154Sdelphij		dsl_dataset_t *snapds, *bmark_fs;
191260154Sdelphij		zfs_bookmark_phys_t bmark_phys;
192260154Sdelphij		char *shortname;
193260154Sdelphij
194260154Sdelphij		VERIFY0(dsl_dataset_hold(dp, fnvpair_value_string(pair),
195260154Sdelphij		    FTAG, &snapds));
196260154Sdelphij		VERIFY0(dsl_bookmark_hold_ds(dp, nvpair_name(pair),
197260154Sdelphij		    &bmark_fs, FTAG, &shortname));
198260154Sdelphij		if (bmark_fs->ds_bookmarks == 0) {
199260154Sdelphij			bmark_fs->ds_bookmarks =
200260154Sdelphij			    zap_create_norm(mos, U8_TEXTPREP_TOUPPER,
201260154Sdelphij			    DMU_OTN_ZAP_METADATA, DMU_OT_NONE, 0, tx);
202260154Sdelphij			spa_feature_incr(dp->dp_spa, SPA_FEATURE_BOOKMARKS, tx);
203260154Sdelphij
204260154Sdelphij			dsl_dataset_zapify(bmark_fs, tx);
205260154Sdelphij			VERIFY0(zap_add(mos, bmark_fs->ds_object,
206260154Sdelphij			    DS_FIELD_BOOKMARK_NAMES,
207260154Sdelphij			    sizeof (bmark_fs->ds_bookmarks), 1,
208260154Sdelphij			    &bmark_fs->ds_bookmarks, tx));
209260154Sdelphij		}
210260154Sdelphij
211275782Sdelphij		bmark_phys.zbm_guid = dsl_dataset_phys(snapds)->ds_guid;
212275782Sdelphij		bmark_phys.zbm_creation_txg =
213275782Sdelphij		    dsl_dataset_phys(snapds)->ds_creation_txg;
214260154Sdelphij		bmark_phys.zbm_creation_time =
215275782Sdelphij		    dsl_dataset_phys(snapds)->ds_creation_time;
216260154Sdelphij
217260154Sdelphij		VERIFY0(zap_add(mos, bmark_fs->ds_bookmarks,
218260154Sdelphij		    shortname, sizeof (uint64_t),
219260154Sdelphij		    sizeof (zfs_bookmark_phys_t) / sizeof (uint64_t),
220260154Sdelphij		    &bmark_phys, tx));
221260154Sdelphij
222260154Sdelphij		spa_history_log_internal_ds(bmark_fs, "bookmark", tx,
223260154Sdelphij		    "name=%s creation_txg=%llu target_snap=%llu",
224260154Sdelphij		    shortname,
225260154Sdelphij		    (longlong_t)bmark_phys.zbm_creation_txg,
226260154Sdelphij		    (longlong_t)snapds->ds_object);
227260154Sdelphij
228260154Sdelphij		dsl_dataset_rele(bmark_fs, FTAG);
229260154Sdelphij		dsl_dataset_rele(snapds, FTAG);
230260154Sdelphij	}
231260154Sdelphij}
232260154Sdelphij
233260154Sdelphij/*
234260154Sdelphij * The bookmarks must all be in the same pool.
235260154Sdelphij */
236260154Sdelphijint
237260154Sdelphijdsl_bookmark_create(nvlist_t *bmarks, nvlist_t *errors)
238260154Sdelphij{
239260154Sdelphij	nvpair_t *pair;
240260154Sdelphij	dsl_bookmark_create_arg_t dbca;
241260154Sdelphij
242260154Sdelphij	pair = nvlist_next_nvpair(bmarks, NULL);
243260154Sdelphij	if (pair == NULL)
244260154Sdelphij		return (0);
245260154Sdelphij
246260154Sdelphij	dbca.dbca_bmarks = bmarks;
247260154Sdelphij	dbca.dbca_errors = errors;
248260154Sdelphij
249260154Sdelphij	return (dsl_sync_task(nvpair_name(pair), dsl_bookmark_create_check,
250268473Sdelphij	    dsl_bookmark_create_sync, &dbca,
251268473Sdelphij	    fnvlist_num_pairs(bmarks), ZFS_SPACE_CHECK_NORMAL));
252260154Sdelphij}
253260154Sdelphij
254260154Sdelphijint
255260154Sdelphijdsl_get_bookmarks_impl(dsl_dataset_t *ds, nvlist_t *props, nvlist_t *outnvl)
256260154Sdelphij{
257260154Sdelphij	int err = 0;
258260154Sdelphij	zap_cursor_t zc;
259260154Sdelphij	zap_attribute_t attr;
260260154Sdelphij	dsl_pool_t *dp = ds->ds_dir->dd_pool;
261260154Sdelphij
262260154Sdelphij	uint64_t bmark_zapobj = ds->ds_bookmarks;
263260154Sdelphij	if (bmark_zapobj == 0)
264260154Sdelphij		return (0);
265260154Sdelphij
266260154Sdelphij	for (zap_cursor_init(&zc, dp->dp_meta_objset, bmark_zapobj);
267260154Sdelphij	    zap_cursor_retrieve(&zc, &attr) == 0;
268260154Sdelphij	    zap_cursor_advance(&zc)) {
269260154Sdelphij		char *bmark_name = attr.za_name;
270260154Sdelphij		zfs_bookmark_phys_t bmark_phys;
271260154Sdelphij
272260154Sdelphij		err = dsl_dataset_bmark_lookup(ds, bmark_name, &bmark_phys);
273260154Sdelphij		ASSERT3U(err, !=, ENOENT);
274260154Sdelphij		if (err != 0)
275260154Sdelphij			break;
276260154Sdelphij
277260154Sdelphij		nvlist_t *out_props = fnvlist_alloc();
278260154Sdelphij		if (nvlist_exists(props,
279260154Sdelphij		    zfs_prop_to_name(ZFS_PROP_GUID))) {
280260154Sdelphij			dsl_prop_nvlist_add_uint64(out_props,
281260154Sdelphij			    ZFS_PROP_GUID, bmark_phys.zbm_guid);
282260154Sdelphij		}
283260154Sdelphij		if (nvlist_exists(props,
284260154Sdelphij		    zfs_prop_to_name(ZFS_PROP_CREATETXG))) {
285260154Sdelphij			dsl_prop_nvlist_add_uint64(out_props,
286260154Sdelphij			    ZFS_PROP_CREATETXG, bmark_phys.zbm_creation_txg);
287260154Sdelphij		}
288260154Sdelphij		if (nvlist_exists(props,
289260154Sdelphij		    zfs_prop_to_name(ZFS_PROP_CREATION))) {
290260154Sdelphij			dsl_prop_nvlist_add_uint64(out_props,
291260154Sdelphij			    ZFS_PROP_CREATION, bmark_phys.zbm_creation_time);
292260154Sdelphij		}
293260154Sdelphij
294260154Sdelphij		fnvlist_add_nvlist(outnvl, bmark_name, out_props);
295260154Sdelphij		fnvlist_free(out_props);
296260154Sdelphij	}
297260154Sdelphij	zap_cursor_fini(&zc);
298260154Sdelphij	return (err);
299260154Sdelphij}
300260154Sdelphij
301260154Sdelphij/*
302260154Sdelphij * Retrieve the bookmarks that exist in the specified dataset, and the
303260154Sdelphij * requested properties of each bookmark.
304260154Sdelphij *
305260154Sdelphij * The "props" nvlist specifies which properties are requested.
306260154Sdelphij * See lzc_get_bookmarks() for the list of valid properties.
307260154Sdelphij */
308260154Sdelphijint
309260154Sdelphijdsl_get_bookmarks(const char *dsname, nvlist_t *props, nvlist_t *outnvl)
310260154Sdelphij{
311260154Sdelphij	dsl_pool_t *dp;
312260154Sdelphij	dsl_dataset_t *ds;
313260154Sdelphij	int err;
314260154Sdelphij
315260154Sdelphij	err = dsl_pool_hold(dsname, FTAG, &dp);
316260154Sdelphij	if (err != 0)
317260154Sdelphij		return (err);
318260154Sdelphij	err = dsl_dataset_hold(dp, dsname, FTAG, &ds);
319260154Sdelphij	if (err != 0) {
320260154Sdelphij		dsl_pool_rele(dp, FTAG);
321260154Sdelphij		return (err);
322260154Sdelphij	}
323260154Sdelphij
324260154Sdelphij	err = dsl_get_bookmarks_impl(ds, props, outnvl);
325260154Sdelphij
326260154Sdelphij	dsl_dataset_rele(ds, FTAG);
327260154Sdelphij	dsl_pool_rele(dp, FTAG);
328260154Sdelphij	return (err);
329260154Sdelphij}
330260154Sdelphij
331260154Sdelphijtypedef struct dsl_bookmark_destroy_arg {
332260154Sdelphij	nvlist_t *dbda_bmarks;
333260154Sdelphij	nvlist_t *dbda_success;
334260154Sdelphij	nvlist_t *dbda_errors;
335260154Sdelphij} dsl_bookmark_destroy_arg_t;
336260154Sdelphij
337260154Sdelphijstatic int
338260154Sdelphijdsl_dataset_bookmark_remove(dsl_dataset_t *ds, const char *name, dmu_tx_t *tx)
339260154Sdelphij{
340260154Sdelphij	objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset;
341260154Sdelphij	uint64_t bmark_zapobj = ds->ds_bookmarks;
342321545Smav	matchtype_t mt = 0;
343260154Sdelphij
344275782Sdelphij	if (dsl_dataset_phys(ds)->ds_flags & DS_FLAG_CI_DATASET)
345321545Smav		mt = MT_NORMALIZE;
346260154Sdelphij
347260154Sdelphij	return (zap_remove_norm(mos, bmark_zapobj, name, mt, tx));
348260154Sdelphij}
349260154Sdelphij
350260154Sdelphijstatic int
351260154Sdelphijdsl_bookmark_destroy_check(void *arg, dmu_tx_t *tx)
352260154Sdelphij{
353260154Sdelphij	dsl_bookmark_destroy_arg_t *dbda = arg;
354260154Sdelphij	dsl_pool_t *dp = dmu_tx_pool(tx);
355260154Sdelphij	int rv = 0;
356260154Sdelphij
357323755Savg	ASSERT(nvlist_empty(dbda->dbda_success));
358323755Savg	ASSERT(nvlist_empty(dbda->dbda_errors));
359323755Savg
360260154Sdelphij	if (!spa_feature_is_enabled(dp->dp_spa, SPA_FEATURE_BOOKMARKS))
361260154Sdelphij		return (0);
362260154Sdelphij
363260154Sdelphij	for (nvpair_t *pair = nvlist_next_nvpair(dbda->dbda_bmarks, NULL);
364260154Sdelphij	    pair != NULL; pair = nvlist_next_nvpair(dbda->dbda_bmarks, pair)) {
365260154Sdelphij		const char *fullname = nvpair_name(pair);
366260154Sdelphij		dsl_dataset_t *ds;
367260154Sdelphij		zfs_bookmark_phys_t bm;
368260154Sdelphij		int error;
369260154Sdelphij		char *shortname;
370260154Sdelphij
371260154Sdelphij		error = dsl_bookmark_hold_ds(dp, fullname, &ds,
372260154Sdelphij		    FTAG, &shortname);
373260154Sdelphij		if (error == ENOENT) {
374260154Sdelphij			/* ignore it; the bookmark is "already destroyed" */
375260154Sdelphij			continue;
376260154Sdelphij		}
377260154Sdelphij		if (error == 0) {
378260154Sdelphij			error = dsl_dataset_bmark_lookup(ds, shortname, &bm);
379260154Sdelphij			dsl_dataset_rele(ds, FTAG);
380260154Sdelphij			if (error == ESRCH) {
381260154Sdelphij				/*
382260154Sdelphij				 * ignore it; the bookmark is
383260154Sdelphij				 * "already destroyed"
384260154Sdelphij				 */
385260154Sdelphij				continue;
386260154Sdelphij			}
387260154Sdelphij		}
388260154Sdelphij		if (error == 0) {
389323755Savg			if (dmu_tx_is_syncing(tx)) {
390323755Savg				fnvlist_add_boolean(dbda->dbda_success,
391323755Savg				    fullname);
392323755Savg			}
393260154Sdelphij		} else {
394260154Sdelphij			fnvlist_add_int32(dbda->dbda_errors, fullname, error);
395260154Sdelphij			rv = error;
396260154Sdelphij		}
397260154Sdelphij	}
398260154Sdelphij	return (rv);
399260154Sdelphij}
400260154Sdelphij
401260154Sdelphijstatic void
402260154Sdelphijdsl_bookmark_destroy_sync(void *arg, dmu_tx_t *tx)
403260154Sdelphij{
404260154Sdelphij	dsl_bookmark_destroy_arg_t *dbda = arg;
405260154Sdelphij	dsl_pool_t *dp = dmu_tx_pool(tx);
406260154Sdelphij	objset_t *mos = dp->dp_meta_objset;
407260154Sdelphij
408260154Sdelphij	for (nvpair_t *pair = nvlist_next_nvpair(dbda->dbda_success, NULL);
409260154Sdelphij	    pair != NULL; pair = nvlist_next_nvpair(dbda->dbda_success, pair)) {
410260154Sdelphij		dsl_dataset_t *ds;
411260154Sdelphij		char *shortname;
412260154Sdelphij		uint64_t zap_cnt;
413260154Sdelphij
414260154Sdelphij		VERIFY0(dsl_bookmark_hold_ds(dp, nvpair_name(pair),
415260154Sdelphij		    &ds, FTAG, &shortname));
416260154Sdelphij		VERIFY0(dsl_dataset_bookmark_remove(ds, shortname, tx));
417260154Sdelphij
418260154Sdelphij		/*
419260154Sdelphij		 * If all of this dataset's bookmarks have been destroyed,
420260154Sdelphij		 * free the zap object and decrement the feature's use count.
421260154Sdelphij		 */
422260154Sdelphij		VERIFY0(zap_count(mos, ds->ds_bookmarks,
423260154Sdelphij		    &zap_cnt));
424260154Sdelphij		if (zap_cnt == 0) {
425260154Sdelphij			dmu_buf_will_dirty(ds->ds_dbuf, tx);
426260154Sdelphij			VERIFY0(zap_destroy(mos, ds->ds_bookmarks, tx));
427260154Sdelphij			ds->ds_bookmarks = 0;
428260154Sdelphij			spa_feature_decr(dp->dp_spa, SPA_FEATURE_BOOKMARKS, tx);
429260154Sdelphij			VERIFY0(zap_remove(mos, ds->ds_object,
430260154Sdelphij			    DS_FIELD_BOOKMARK_NAMES, tx));
431260154Sdelphij		}
432260154Sdelphij
433260154Sdelphij		spa_history_log_internal_ds(ds, "remove bookmark", tx,
434260154Sdelphij		    "name=%s", shortname);
435260154Sdelphij
436260154Sdelphij		dsl_dataset_rele(ds, FTAG);
437260154Sdelphij	}
438260154Sdelphij}
439260154Sdelphij
440260154Sdelphij/*
441260154Sdelphij * The bookmarks must all be in the same pool.
442260154Sdelphij */
443260154Sdelphijint
444260154Sdelphijdsl_bookmark_destroy(nvlist_t *bmarks, nvlist_t *errors)
445260154Sdelphij{
446260154Sdelphij	int rv;
447260154Sdelphij	dsl_bookmark_destroy_arg_t dbda;
448260154Sdelphij	nvpair_t *pair = nvlist_next_nvpair(bmarks, NULL);
449260154Sdelphij	if (pair == NULL)
450260154Sdelphij		return (0);
451260154Sdelphij
452260154Sdelphij	dbda.dbda_bmarks = bmarks;
453260154Sdelphij	dbda.dbda_errors = errors;
454260154Sdelphij	dbda.dbda_success = fnvlist_alloc();
455260154Sdelphij
456260154Sdelphij	rv = dsl_sync_task(nvpair_name(pair), dsl_bookmark_destroy_check,
457268473Sdelphij	    dsl_bookmark_destroy_sync, &dbda, fnvlist_num_pairs(bmarks),
458268473Sdelphij	    ZFS_SPACE_CHECK_RESERVED);
459260154Sdelphij	fnvlist_free(dbda.dbda_success);
460260154Sdelphij	return (rv);
461260154Sdelphij}
462353759Savg
463353759Savgtypedef struct dsl_bookmark_rename_arg {
464353759Savg	const char *dbra_fsname;
465353759Savg	const char *dbra_oldname;
466353759Savg	const char *dbra_newname;
467353759Savg} dsl_bookmark_rename_arg_t;
468353759Savg
469353759Savgstatic int
470353759Savgdsl_bookmark_rename_check(void *arg, dmu_tx_t *tx)
471353759Savg{
472353759Savg	dsl_bookmark_rename_arg_t *dbra = arg;
473353759Savg	dsl_pool_t *dp = dmu_tx_pool(tx);
474353759Savg	dsl_dataset_t *ds;
475353759Savg	zfs_bookmark_phys_t bmark_phys;
476353759Savg	int error;
477353759Savg
478353759Savg	if (!spa_feature_is_enabled(dp->dp_spa, SPA_FEATURE_BOOKMARKS))
479353759Savg		return (SET_ERROR(ENOTSUP));
480353759Savg
481353759Savg	/* Check validity and the full length of the new bookmark name. */
482353759Savg	if (zfs_component_namecheck(dbra->dbra_newname, NULL, NULL))
483353759Savg		return (SET_ERROR(EINVAL));
484353759Savg	if (strlen(dbra->dbra_fsname) + strlen(dbra->dbra_newname) + 1 >=
485353759Savg	    ZFS_MAX_DATASET_NAME_LEN)
486353759Savg		return (SET_ERROR(ENAMETOOLONG));
487353759Savg
488353759Savg	error = dsl_dataset_hold(dp, dbra->dbra_fsname, FTAG, &ds);
489353759Savg	if (error != 0)
490353759Savg		return (error);
491353759Savg	if (ds->ds_is_snapshot) {
492353759Savg		dsl_dataset_rele(ds, FTAG);
493353759Savg		return (SET_ERROR(EINVAL));
494353759Savg	}
495353759Savg	error = dsl_dataset_bmark_lookup(ds, dbra->dbra_oldname, &bmark_phys);
496353759Savg	if (error != 0) {
497353759Savg		dsl_dataset_rele(ds, FTAG);
498353759Savg		return (error);
499353759Savg	}
500353759Savg
501353759Savg	error = dsl_dataset_bmark_lookup(ds, dbra->dbra_newname, &bmark_phys);
502353759Savg	dsl_dataset_rele(ds, FTAG);
503353759Savg	if (error == 0)
504353759Savg		return (SET_ERROR(EEXIST));
505353759Savg	if (error != ESRCH)
506353759Savg		return (error);
507353759Savg	return (0);
508353759Savg}
509353759Savg
510353759Savgstatic void
511353759Savgdsl_bookmark_rename_sync(void *arg, dmu_tx_t *tx)
512353759Savg{
513353759Savg	zfs_bookmark_phys_t bmark_phys;
514353759Savg	dsl_bookmark_rename_arg_t *dbra = arg;
515353759Savg	dsl_pool_t *dp = dmu_tx_pool(tx);
516353759Savg	objset_t *mos;
517353759Savg	dsl_dataset_t *ds;
518353759Savg	uint64_t bmark_zapobj;
519353759Savg	uint64_t int_size, num_ints;
520353759Savg	matchtype_t mt = 0;
521353759Savg	int error;
522353759Savg
523353759Savg	ASSERT(spa_feature_is_enabled(dp->dp_spa, SPA_FEATURE_BOOKMARKS));
524353759Savg	VERIFY0(dsl_dataset_hold(dp, dbra->dbra_fsname, FTAG, &ds));
525353759Savg
526353759Savg	mos = ds->ds_dir->dd_pool->dp_meta_objset;
527353759Savg	bmark_zapobj = ds->ds_bookmarks;
528353759Savg
529353759Savg	if (dsl_dataset_phys(ds)->ds_flags & DS_FLAG_CI_DATASET)
530353759Savg		mt = MT_NORMALIZE;
531353759Savg
532353759Savg	VERIFY0(zap_length(mos, bmark_zapobj, dbra->dbra_oldname,
533353759Savg	    &int_size, &num_ints));
534353759Savg	ASSERT3U(int_size, ==, sizeof (uint64_t));
535353759Savg	VERIFY0(zap_lookup_norm(mos, bmark_zapobj, dbra->dbra_oldname, int_size,
536353759Savg	    num_ints, &bmark_phys, mt, NULL, 0, NULL));
537353759Savg	VERIFY0(zap_remove_norm(mos, bmark_zapobj, dbra->dbra_oldname, mt, tx));
538353759Savg
539353759Savg	VERIFY0(zap_add(mos, bmark_zapobj, dbra->dbra_newname, int_size,
540353759Savg	    num_ints, &bmark_phys, tx));
541353759Savg
542353759Savg	spa_history_log_internal_ds(ds, "rename bookmark", tx,
543353759Savg	    "#%s -> #%s creation_txg=%llu",
544353759Savg	    dbra->dbra_oldname, dbra->dbra_newname,
545353759Savg	    (longlong_t)bmark_phys.zbm_creation_txg);
546353759Savg
547353759Savg	dsl_dataset_rele(ds, FTAG);
548353759Savg}
549353759Savg
550353759Savg/*
551353759Savg * The bookmarks must all be in the same pool.
552353759Savg */
553353759Savgint
554353759Savgdsl_bookmark_rename(const char *fsname, const char *oldbmark,
555353759Savg    const char *newbmark)
556353759Savg{
557353759Savg	dsl_bookmark_rename_arg_t dbra;
558353759Savg
559353759Savg	dbra.dbra_fsname = fsname;
560353759Savg	dbra.dbra_oldname = oldbmark;
561353759Savg	dbra.dbra_newname = newbmark;
562353759Savg
563353759Savg	return (dsl_sync_task(fsname, dsl_bookmark_rename_check,
564353759Savg	    dsl_bookmark_rename_sync, &dbra, 1, ZFS_SPACE_CHECK_NORMAL));
565353759Savg}
566353759Savg
567