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