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.
16 * Copyright (c) 2013 by Delphix. All rights reserved.
19 #include <sys/zfs_context.h>
20 #include <sys/dsl_dataset.h>
21 #include <sys/dsl_dir.h>
22 #include <sys/dsl_prop.h>
23 #include <sys/dsl_synctask.h>
24 #include <sys/dmu_impl.h>
25 #include <sys/dmu_tx.h>
28 #include <sys/zfeature.h>
30 #include <sys/dsl_bookmark.h>
31 #include <zfs_namecheck.h>
34 dsl_bookmark_hold_ds(dsl_pool_t
*dp
, const char *fullname
,
35 dsl_dataset_t
**dsp
, void *tag
, char **shortnamep
)
40 if (strlen(fullname
) >= MAXNAMELEN
)
41 return (SET_ERROR(ENAMETOOLONG
));
42 hashp
= strchr(fullname
, '#');
44 return (SET_ERROR(EINVAL
));
46 *shortnamep
= hashp
+ 1;
47 if (zfs_component_namecheck(*shortnamep
, NULL
, NULL
))
48 return (SET_ERROR(EINVAL
));
49 (void) strlcpy(buf
, fullname
, hashp
- fullname
+ 1);
50 return (dsl_dataset_hold(dp
, buf
, tag
, dsp
));
54 * Returns ESRCH if bookmark is not found.
57 dsl_dataset_bmark_lookup(dsl_dataset_t
*ds
, const char *shortname
,
58 zfs_bookmark_phys_t
*bmark_phys
)
60 objset_t
*mos
= ds
->ds_dir
->dd_pool
->dp_meta_objset
;
61 uint64_t bmark_zapobj
= ds
->ds_bookmarks
;
65 if (bmark_zapobj
== 0)
66 return (SET_ERROR(ESRCH
));
68 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
);
154 if (!spa_feature_is_enabled(dp
->dp_spa
, SPA_FEATURE_BOOKMARKS
))
155 return (SET_ERROR(ENOTSUP
));
157 for (pair
= nvlist_next_nvpair(dbca
->dbca_bmarks
, NULL
);
158 pair
!= NULL
; pair
= nvlist_next_nvpair(dbca
->dbca_bmarks
, pair
)) {
159 dsl_dataset_t
*snapds
;
162 /* note: validity of nvlist checked by ioctl layer */
163 error
= dsl_dataset_hold(dp
, fnvpair_value_string(pair
),
166 error
= dsl_bookmark_create_check_impl(snapds
,
167 nvpair_name(pair
), tx
);
168 dsl_dataset_rele(snapds
, FTAG
);
171 fnvlist_add_int32(dbca
->dbca_errors
,
172 nvpair_name(pair
), error
);
181 dsl_bookmark_create_sync(void *arg
, dmu_tx_t
*tx
)
183 dsl_bookmark_create_arg_t
*dbca
= arg
;
184 dsl_pool_t
*dp
= dmu_tx_pool(tx
);
185 objset_t
*mos
= dp
->dp_meta_objset
;
188 ASSERT(spa_feature_is_enabled(dp
->dp_spa
, SPA_FEATURE_BOOKMARKS
));
190 for (pair
= nvlist_next_nvpair(dbca
->dbca_bmarks
, NULL
);
191 pair
!= NULL
; pair
= nvlist_next_nvpair(dbca
->dbca_bmarks
, pair
)) {
192 dsl_dataset_t
*snapds
, *bmark_fs
;
193 zfs_bookmark_phys_t bmark_phys
;
196 VERIFY0(dsl_dataset_hold(dp
, fnvpair_value_string(pair
),
198 VERIFY0(dsl_bookmark_hold_ds(dp
, nvpair_name(pair
),
199 &bmark_fs
, FTAG
, &shortname
));
200 if (bmark_fs
->ds_bookmarks
== 0) {
201 bmark_fs
->ds_bookmarks
=
202 zap_create_norm(mos
, U8_TEXTPREP_TOUPPER
,
203 DMU_OTN_ZAP_METADATA
, DMU_OT_NONE
, 0, tx
);
204 spa_feature_incr(dp
->dp_spa
, SPA_FEATURE_BOOKMARKS
, tx
);
206 dsl_dataset_zapify(bmark_fs
, tx
);
207 VERIFY0(zap_add(mos
, bmark_fs
->ds_object
,
208 DS_FIELD_BOOKMARK_NAMES
,
209 sizeof (bmark_fs
->ds_bookmarks
), 1,
210 &bmark_fs
->ds_bookmarks
, tx
));
213 bmark_phys
.zbm_guid
= dsl_dataset_phys(snapds
)->ds_guid
;
214 bmark_phys
.zbm_creation_txg
=
215 dsl_dataset_phys(snapds
)->ds_creation_txg
;
216 bmark_phys
.zbm_creation_time
=
217 dsl_dataset_phys(snapds
)->ds_creation_time
;
219 VERIFY0(zap_add(mos
, bmark_fs
->ds_bookmarks
,
220 shortname
, sizeof (uint64_t),
221 sizeof (zfs_bookmark_phys_t
) / sizeof (uint64_t),
224 spa_history_log_internal_ds(bmark_fs
, "bookmark", tx
,
225 "name=%s creation_txg=%llu target_snap=%llu",
227 (longlong_t
)bmark_phys
.zbm_creation_txg
,
228 (longlong_t
)snapds
->ds_object
);
230 dsl_dataset_rele(bmark_fs
, FTAG
);
231 dsl_dataset_rele(snapds
, FTAG
);
236 * The bookmarks must all be in the same pool.
239 dsl_bookmark_create(nvlist_t
*bmarks
, nvlist_t
*errors
)
242 dsl_bookmark_create_arg_t dbca
;
244 pair
= nvlist_next_nvpair(bmarks
, NULL
);
248 dbca
.dbca_bmarks
= bmarks
;
249 dbca
.dbca_errors
= errors
;
251 return (dsl_sync_task(nvpair_name(pair
), dsl_bookmark_create_check
,
252 dsl_bookmark_create_sync
, &dbca
, fnvlist_num_pairs(bmarks
)));
256 dsl_get_bookmarks_impl(dsl_dataset_t
*ds
, nvlist_t
*props
, nvlist_t
*outnvl
)
260 zap_attribute_t attr
;
261 dsl_pool_t
*dp
= ds
->ds_dir
->dd_pool
;
263 uint64_t bmark_zapobj
= ds
->ds_bookmarks
;
264 if (bmark_zapobj
== 0)
267 for (zap_cursor_init(&zc
, dp
->dp_meta_objset
, bmark_zapobj
);
268 zap_cursor_retrieve(&zc
, &attr
) == 0;
269 zap_cursor_advance(&zc
)) {
271 char *bmark_name
= attr
.za_name
;
272 zfs_bookmark_phys_t bmark_phys
;
274 err
= dsl_dataset_bmark_lookup(ds
, bmark_name
, &bmark_phys
);
275 ASSERT3U(err
, !=, ENOENT
);
279 out_props
= fnvlist_alloc();
280 if (nvlist_exists(props
,
281 zfs_prop_to_name(ZFS_PROP_GUID
))) {
282 dsl_prop_nvlist_add_uint64(out_props
,
283 ZFS_PROP_GUID
, bmark_phys
.zbm_guid
);
285 if (nvlist_exists(props
,
286 zfs_prop_to_name(ZFS_PROP_CREATETXG
))) {
287 dsl_prop_nvlist_add_uint64(out_props
,
288 ZFS_PROP_CREATETXG
, bmark_phys
.zbm_creation_txg
);
290 if (nvlist_exists(props
,
291 zfs_prop_to_name(ZFS_PROP_CREATION
))) {
292 dsl_prop_nvlist_add_uint64(out_props
,
293 ZFS_PROP_CREATION
, bmark_phys
.zbm_creation_time
);
296 fnvlist_add_nvlist(outnvl
, bmark_name
, out_props
);
297 fnvlist_free(out_props
);
299 zap_cursor_fini(&zc
);
304 * Retrieve the bookmarks that exist in the specified dataset, and the
305 * requested properties of each bookmark.
307 * The "props" nvlist specifies which properties are requested.
308 * See lzc_get_bookmarks() for the list of valid properties.
311 dsl_get_bookmarks(const char *dsname
, nvlist_t
*props
, nvlist_t
*outnvl
)
317 err
= dsl_pool_hold(dsname
, FTAG
, &dp
);
320 err
= dsl_dataset_hold(dp
, dsname
, FTAG
, &ds
);
322 dsl_pool_rele(dp
, FTAG
);
326 err
= dsl_get_bookmarks_impl(ds
, props
, outnvl
);
328 dsl_dataset_rele(ds
, FTAG
);
329 dsl_pool_rele(dp
, FTAG
);
333 typedef struct dsl_bookmark_destroy_arg
{
334 nvlist_t
*dbda_bmarks
;
335 nvlist_t
*dbda_success
;
336 nvlist_t
*dbda_errors
;
337 } dsl_bookmark_destroy_arg_t
;
340 dsl_dataset_bookmark_remove(dsl_dataset_t
*ds
, const char *name
, dmu_tx_t
*tx
)
342 objset_t
*mos
= ds
->ds_dir
->dd_pool
->dp_meta_objset
;
343 uint64_t bmark_zapobj
= ds
->ds_bookmarks
;
346 if (dsl_dataset_phys(ds
)->ds_flags
& DS_FLAG_CI_DATASET
)
351 return (zap_remove_norm(mos
, bmark_zapobj
, name
, mt
, tx
));
355 dsl_bookmark_destroy_check(void *arg
, dmu_tx_t
*tx
)
357 dsl_bookmark_destroy_arg_t
*dbda
= arg
;
358 dsl_pool_t
*dp
= dmu_tx_pool(tx
);
362 if (!spa_feature_is_enabled(dp
->dp_spa
, SPA_FEATURE_BOOKMARKS
))
365 for (pair
= nvlist_next_nvpair(dbda
->dbda_bmarks
, NULL
);
366 pair
!= NULL
; pair
= nvlist_next_nvpair(dbda
->dbda_bmarks
, pair
)) {
367 const char *fullname
= nvpair_name(pair
);
369 zfs_bookmark_phys_t bm
;
373 error
= dsl_bookmark_hold_ds(dp
, fullname
, &ds
,
375 if (error
== ENOENT
) {
376 /* ignore it; the bookmark is "already destroyed" */
380 error
= dsl_dataset_bmark_lookup(ds
, shortname
, &bm
);
381 dsl_dataset_rele(ds
, FTAG
);
382 if (error
== ESRCH
) {
384 * ignore it; the bookmark is
385 * "already destroyed"
391 fnvlist_add_boolean(dbda
->dbda_success
, fullname
);
393 fnvlist_add_int32(dbda
->dbda_errors
, fullname
, error
);
401 dsl_bookmark_destroy_sync(void *arg
, dmu_tx_t
*tx
)
403 dsl_bookmark_destroy_arg_t
*dbda
= arg
;
404 dsl_pool_t
*dp
= dmu_tx_pool(tx
);
405 objset_t
*mos
= dp
->dp_meta_objset
;
408 for (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 fnvlist_free(dbda
.dbda_success
);