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
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.
17 * Copyright (c) 2013, 2014 by Delphix. All rights reserved.
18 * Copyright 2017 Nexenta Systems, Inc.
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>
30 #include <sys/zfeature.h>
32 #include <sys/dsl_bookmark.h>
33 #include <zfs_namecheck.h>
36 dsl_bookmark_hold_ds(dsl_pool_t *dp, const char *fullname,
37 dsl_dataset_t **dsp, void *tag, char **shortnamep)
39 char buf[ZFS_MAX_DATASET_NAME_LEN];
42 if (strlen(fullname) >= ZFS_MAX_DATASET_NAME_LEN)
43 return (SET_ERROR(ENAMETOOLONG));
44 hashp = strchr(fullname, '#');
46 return (SET_ERROR(EINVAL));
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));
56 * Returns ESRCH if bookmark is not found.
59 dsl_dataset_bmark_lookup(dsl_dataset_t *ds, const char *shortname,
60 zfs_bookmark_phys_t *bmark_phys)
62 objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset;
63 uint64_t bmark_zapobj = ds->ds_bookmarks;
67 if (bmark_zapobj == 0)
68 return (SET_ERROR(ESRCH));
70 if (dsl_dataset_phys(ds)->ds_flags & DS_FLAG_CI_DATASET)
73 err = zap_lookup_norm(mos, bmark_zapobj, shortname, sizeof (uint64_t),
74 sizeof (*bmark_phys) / sizeof (uint64_t), bmark_phys, mt,
77 return (err == ENOENT ? ESRCH : err);
81 * If later_ds is non-NULL, this will return EXDEV if the the specified bookmark
82 * does not represents an earlier point in later_ds's timeline.
84 * Returns ENOENT if the dataset containing the bookmark does not exist.
85 * Returns ESRCH if the dataset exists but the bookmark was not found in it.
88 dsl_bookmark_lookup(dsl_pool_t *dp, const char *fullname,
89 dsl_dataset_t *later_ds, zfs_bookmark_phys_t *bmp)
95 error = dsl_bookmark_hold_ds(dp, fullname, &ds, FTAG, &shortname);
99 error = dsl_dataset_bmark_lookup(ds, shortname, bmp);
100 if (error == 0 && later_ds != NULL) {
101 if (!dsl_dataset_is_before(later_ds, ds, bmp->zbm_creation_txg))
102 error = SET_ERROR(EXDEV);
104 dsl_dataset_rele(ds, FTAG);
108 typedef struct dsl_bookmark_create_arg {
109 nvlist_t *dbca_bmarks;
110 nvlist_t *dbca_errors;
111 } dsl_bookmark_create_arg_t;
114 dsl_bookmark_create_check_impl(dsl_dataset_t *snapds, const char *bookmark_name,
117 dsl_pool_t *dp = dmu_tx_pool(tx);
118 dsl_dataset_t *bmark_fs;
121 zfs_bookmark_phys_t bmark_phys;
123 if (!snapds->ds_is_snapshot)
124 return (SET_ERROR(EINVAL));
126 error = dsl_bookmark_hold_ds(dp, bookmark_name,
127 &bmark_fs, FTAG, &shortname);
131 if (!dsl_dataset_is_before(bmark_fs, snapds, 0)) {
132 dsl_dataset_rele(bmark_fs, FTAG);
133 return (SET_ERROR(EINVAL));
136 error = dsl_dataset_bmark_lookup(bmark_fs, shortname,
138 dsl_dataset_rele(bmark_fs, FTAG);
140 return (SET_ERROR(EEXIST));
147 dsl_bookmark_create_check(void *arg, dmu_tx_t *tx)
149 dsl_bookmark_create_arg_t *dbca = arg;
150 dsl_pool_t *dp = dmu_tx_pool(tx);
153 if (!spa_feature_is_enabled(dp->dp_spa, SPA_FEATURE_BOOKMARKS))
154 return (SET_ERROR(ENOTSUP));
156 for (nvpair_t *pair = nvlist_next_nvpair(dbca->dbca_bmarks, NULL);
157 pair != NULL; pair = nvlist_next_nvpair(dbca->dbca_bmarks, pair)) {
158 dsl_dataset_t *snapds;
161 /* note: validity of nvlist checked by ioctl layer */
162 error = dsl_dataset_hold(dp, fnvpair_value_string(pair),
165 error = dsl_bookmark_create_check_impl(snapds,
166 nvpair_name(pair), tx);
167 dsl_dataset_rele(snapds, FTAG);
170 fnvlist_add_int32(dbca->dbca_errors,
171 nvpair_name(pair), error);
180 dsl_bookmark_create_sync(void *arg, dmu_tx_t *tx)
182 dsl_bookmark_create_arg_t *dbca = arg;
183 dsl_pool_t *dp = dmu_tx_pool(tx);
184 objset_t *mos = dp->dp_meta_objset;
186 ASSERT(spa_feature_is_enabled(dp->dp_spa, SPA_FEATURE_BOOKMARKS));
188 for (nvpair_t *pair = nvlist_next_nvpair(dbca->dbca_bmarks, NULL);
189 pair != NULL; pair = nvlist_next_nvpair(dbca->dbca_bmarks, pair)) {
190 dsl_dataset_t *snapds, *bmark_fs;
191 zfs_bookmark_phys_t bmark_phys;
194 VERIFY0(dsl_dataset_hold(dp, fnvpair_value_string(pair),
196 VERIFY0(dsl_bookmark_hold_ds(dp, nvpair_name(pair),
197 &bmark_fs, FTAG, &shortname));
198 if (bmark_fs->ds_bookmarks == 0) {
199 bmark_fs->ds_bookmarks =
200 zap_create_norm(mos, U8_TEXTPREP_TOUPPER,
201 DMU_OTN_ZAP_METADATA, DMU_OT_NONE, 0, tx);
202 spa_feature_incr(dp->dp_spa, SPA_FEATURE_BOOKMARKS, tx);
204 dsl_dataset_zapify(bmark_fs, tx);
205 VERIFY0(zap_add(mos, bmark_fs->ds_object,
206 DS_FIELD_BOOKMARK_NAMES,
207 sizeof (bmark_fs->ds_bookmarks), 1,
208 &bmark_fs->ds_bookmarks, tx));
211 bmark_phys.zbm_guid = dsl_dataset_phys(snapds)->ds_guid;
212 bmark_phys.zbm_creation_txg =
213 dsl_dataset_phys(snapds)->ds_creation_txg;
214 bmark_phys.zbm_creation_time =
215 dsl_dataset_phys(snapds)->ds_creation_time;
217 VERIFY0(zap_add(mos, bmark_fs->ds_bookmarks,
218 shortname, sizeof (uint64_t),
219 sizeof (zfs_bookmark_phys_t) / sizeof (uint64_t),
222 spa_history_log_internal_ds(bmark_fs, "bookmark", tx,
223 "name=%s creation_txg=%llu target_snap=%llu",
225 (longlong_t)bmark_phys.zbm_creation_txg,
226 (longlong_t)snapds->ds_object);
228 dsl_dataset_rele(bmark_fs, FTAG);
229 dsl_dataset_rele(snapds, FTAG);
234 * The bookmarks must all be in the same pool.
237 dsl_bookmark_create(nvlist_t *bmarks, nvlist_t *errors)
240 dsl_bookmark_create_arg_t dbca;
242 pair = nvlist_next_nvpair(bmarks, NULL);
246 dbca.dbca_bmarks = bmarks;
247 dbca.dbca_errors = errors;
249 return (dsl_sync_task(nvpair_name(pair), dsl_bookmark_create_check,
250 dsl_bookmark_create_sync, &dbca,
251 fnvlist_num_pairs(bmarks), ZFS_SPACE_CHECK_NORMAL));
255 dsl_get_bookmarks_impl(dsl_dataset_t *ds, nvlist_t *props, nvlist_t *outnvl)
259 zap_attribute_t attr;
260 dsl_pool_t *dp = ds->ds_dir->dd_pool;
262 uint64_t bmark_zapobj = ds->ds_bookmarks;
263 if (bmark_zapobj == 0)
266 for (zap_cursor_init(&zc, dp->dp_meta_objset, bmark_zapobj);
267 zap_cursor_retrieve(&zc, &attr) == 0;
268 zap_cursor_advance(&zc)) {
269 char *bmark_name = attr.za_name;
270 zfs_bookmark_phys_t bmark_phys;
272 err = dsl_dataset_bmark_lookup(ds, bmark_name, &bmark_phys);
273 ASSERT3U(err, !=, ENOENT);
277 nvlist_t *out_props = fnvlist_alloc();
278 if (nvlist_exists(props,
279 zfs_prop_to_name(ZFS_PROP_GUID))) {
280 dsl_prop_nvlist_add_uint64(out_props,
281 ZFS_PROP_GUID, bmark_phys.zbm_guid);
283 if (nvlist_exists(props,
284 zfs_prop_to_name(ZFS_PROP_CREATETXG))) {
285 dsl_prop_nvlist_add_uint64(out_props,
286 ZFS_PROP_CREATETXG, bmark_phys.zbm_creation_txg);
288 if (nvlist_exists(props,
289 zfs_prop_to_name(ZFS_PROP_CREATION))) {
290 dsl_prop_nvlist_add_uint64(out_props,
291 ZFS_PROP_CREATION, bmark_phys.zbm_creation_time);
294 fnvlist_add_nvlist(outnvl, bmark_name, out_props);
295 fnvlist_free(out_props);
297 zap_cursor_fini(&zc);
302 * Retrieve the bookmarks that exist in the specified dataset, and the
303 * requested properties of each bookmark.
305 * The "props" nvlist specifies which properties are requested.
306 * See lzc_get_bookmarks() for the list of valid properties.
309 dsl_get_bookmarks(const char *dsname, nvlist_t *props, nvlist_t *outnvl)
315 err = dsl_pool_hold(dsname, FTAG, &dp);
318 err = dsl_dataset_hold(dp, dsname, FTAG, &ds);
320 dsl_pool_rele(dp, FTAG);
324 err = dsl_get_bookmarks_impl(ds, props, outnvl);
326 dsl_dataset_rele(ds, FTAG);
327 dsl_pool_rele(dp, FTAG);
331 typedef struct dsl_bookmark_destroy_arg {
332 nvlist_t *dbda_bmarks;
333 nvlist_t *dbda_success;
334 nvlist_t *dbda_errors;
335 } dsl_bookmark_destroy_arg_t;
338 dsl_dataset_bookmark_remove(dsl_dataset_t *ds, const char *name, dmu_tx_t *tx)
340 objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset;
341 uint64_t bmark_zapobj = ds->ds_bookmarks;
344 if (dsl_dataset_phys(ds)->ds_flags & DS_FLAG_CI_DATASET)
347 return (zap_remove_norm(mos, bmark_zapobj, name, mt, tx));
351 dsl_bookmark_destroy_check(void *arg, dmu_tx_t *tx)
353 dsl_bookmark_destroy_arg_t *dbda = arg;
354 dsl_pool_t *dp = dmu_tx_pool(tx);
357 ASSERT(nvlist_empty(dbda->dbda_success));
358 ASSERT(nvlist_empty(dbda->dbda_errors));
360 if (!spa_feature_is_enabled(dp->dp_spa, SPA_FEATURE_BOOKMARKS))
363 for (nvpair_t *pair = nvlist_next_nvpair(dbda->dbda_bmarks, NULL);
364 pair != NULL; pair = nvlist_next_nvpair(dbda->dbda_bmarks, pair)) {
365 const char *fullname = nvpair_name(pair);
367 zfs_bookmark_phys_t bm;
371 error = dsl_bookmark_hold_ds(dp, fullname, &ds,
373 if (error == ENOENT) {
374 /* ignore it; the bookmark is "already destroyed" */
378 error = dsl_dataset_bmark_lookup(ds, shortname, &bm);
379 dsl_dataset_rele(ds, FTAG);
380 if (error == ESRCH) {
382 * ignore it; the bookmark is
383 * "already destroyed"
389 if (dmu_tx_is_syncing(tx)) {
390 fnvlist_add_boolean(dbda->dbda_success,
394 fnvlist_add_int32(dbda->dbda_errors, fullname, error);
402 dsl_bookmark_destroy_sync(void *arg, dmu_tx_t *tx)
404 dsl_bookmark_destroy_arg_t *dbda = arg;
405 dsl_pool_t *dp = dmu_tx_pool(tx);
406 objset_t *mos = dp->dp_meta_objset;
408 for (nvpair_t *pair = nvlist_next_nvpair(dbda->dbda_success, NULL);
409 pair != NULL; pair = nvlist_next_nvpair(dbda->dbda_success, pair)) {
414 VERIFY0(dsl_bookmark_hold_ds(dp, nvpair_name(pair),
415 &ds, FTAG, &shortname));
416 VERIFY0(dsl_dataset_bookmark_remove(ds, shortname, tx));
419 * If all of this dataset's bookmarks have been destroyed,
420 * free the zap object and decrement the feature's use count.
422 VERIFY0(zap_count(mos, ds->ds_bookmarks,
425 dmu_buf_will_dirty(ds->ds_dbuf, tx);
426 VERIFY0(zap_destroy(mos, ds->ds_bookmarks, tx));
427 ds->ds_bookmarks = 0;
428 spa_feature_decr(dp->dp_spa, SPA_FEATURE_BOOKMARKS, tx);
429 VERIFY0(zap_remove(mos, ds->ds_object,
430 DS_FIELD_BOOKMARK_NAMES, tx));
433 spa_history_log_internal_ds(ds, "remove bookmark", tx,
434 "name=%s", shortname);
436 dsl_dataset_rele(ds, FTAG);
441 * The bookmarks must all be in the same pool.
444 dsl_bookmark_destroy(nvlist_t *bmarks, nvlist_t *errors)
447 dsl_bookmark_destroy_arg_t dbda;
448 nvpair_t *pair = nvlist_next_nvpair(bmarks, NULL);
452 dbda.dbda_bmarks = bmarks;
453 dbda.dbda_errors = errors;
454 dbda.dbda_success = fnvlist_alloc();
456 rv = dsl_sync_task(nvpair_name(pair), dsl_bookmark_destroy_check,
457 dsl_bookmark_destroy_sync, &dbda, fnvlist_num_pairs(bmarks),
458 ZFS_SPACE_CHECK_RESERVED);
459 fnvlist_free(dbda.dbda_success);
463 typedef struct dsl_bookmark_rename_arg {
464 const char *dbra_fsname;
465 const char *dbra_oldname;
466 const char *dbra_newname;
467 } dsl_bookmark_rename_arg_t;
470 dsl_bookmark_rename_check(void *arg, dmu_tx_t *tx)
472 dsl_bookmark_rename_arg_t *dbra = arg;
473 dsl_pool_t *dp = dmu_tx_pool(tx);
475 zfs_bookmark_phys_t bmark_phys;
478 if (!spa_feature_is_enabled(dp->dp_spa, SPA_FEATURE_BOOKMARKS))
479 return (SET_ERROR(ENOTSUP));
481 /* Check validity and the full length of the new bookmark name. */
482 if (zfs_component_namecheck(dbra->dbra_newname, NULL, NULL))
483 return (SET_ERROR(EINVAL));
484 if (strlen(dbra->dbra_fsname) + strlen(dbra->dbra_newname) + 1 >=
485 ZFS_MAX_DATASET_NAME_LEN)
486 return (SET_ERROR(ENAMETOOLONG));
488 error = dsl_dataset_hold(dp, dbra->dbra_fsname, FTAG, &ds);
491 if (ds->ds_is_snapshot) {
492 dsl_dataset_rele(ds, FTAG);
493 return (SET_ERROR(EINVAL));
495 error = dsl_dataset_bmark_lookup(ds, dbra->dbra_oldname, &bmark_phys);
497 dsl_dataset_rele(ds, FTAG);
501 error = dsl_dataset_bmark_lookup(ds, dbra->dbra_newname, &bmark_phys);
502 dsl_dataset_rele(ds, FTAG);
504 return (SET_ERROR(EEXIST));
511 dsl_bookmark_rename_sync(void *arg, dmu_tx_t *tx)
513 zfs_bookmark_phys_t bmark_phys;
514 dsl_bookmark_rename_arg_t *dbra = arg;
515 dsl_pool_t *dp = dmu_tx_pool(tx);
518 uint64_t bmark_zapobj;
519 uint64_t int_size, num_ints;
523 ASSERT(spa_feature_is_enabled(dp->dp_spa, SPA_FEATURE_BOOKMARKS));
524 VERIFY0(dsl_dataset_hold(dp, dbra->dbra_fsname, FTAG, &ds));
526 mos = ds->ds_dir->dd_pool->dp_meta_objset;
527 bmark_zapobj = ds->ds_bookmarks;
529 if (dsl_dataset_phys(ds)->ds_flags & DS_FLAG_CI_DATASET)
532 VERIFY0(zap_length(mos, bmark_zapobj, dbra->dbra_oldname,
533 &int_size, &num_ints));
534 ASSERT3U(int_size, ==, sizeof (uint64_t));
535 VERIFY0(zap_lookup_norm(mos, bmark_zapobj, dbra->dbra_oldname, int_size,
536 num_ints, &bmark_phys, mt, NULL, 0, NULL));
537 VERIFY0(zap_remove_norm(mos, bmark_zapobj, dbra->dbra_oldname, mt, tx));
539 VERIFY0(zap_add(mos, bmark_zapobj, dbra->dbra_newname, int_size,
540 num_ints, &bmark_phys, tx));
542 spa_history_log_internal_ds(ds, "rename bookmark", tx,
543 "#%s -> #%s creation_txg=%llu",
544 dbra->dbra_oldname, dbra->dbra_newname,
545 (longlong_t)bmark_phys.zbm_creation_txg);
547 dsl_dataset_rele(ds, FTAG);
551 * The bookmarks must all be in the same pool.
554 dsl_bookmark_rename(const char *fsname, const char *oldbmark,
555 const char *newbmark)
557 dsl_bookmark_rename_arg_t dbra;
559 dbra.dbra_fsname = fsname;
560 dbra.dbra_oldname = oldbmark;
561 dbra.dbra_newname = newbmark;
563 return (dsl_sync_task(fsname, dsl_bookmark_rename_check,
564 dsl_bookmark_rename_sync, &dbra, 1, ZFS_SPACE_CHECK_NORMAL));