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
);