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
);
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
,
253 fnvlist_num_pairs(bmarks
), ZFS_SPACE_CHECK_NORMAL
));
257 dsl_get_bookmarks_impl(dsl_dataset_t
*ds
, nvlist_t
*props
, nvlist_t
*outnvl
)
261 zap_attribute_t attr
;
262 dsl_pool_t
*dp
= ds
->ds_dir
->dd_pool
;
264 uint64_t bmark_zapobj
= ds
->ds_bookmarks
;
265 if (bmark_zapobj
== 0)
268 for (zap_cursor_init(&zc
, dp
->dp_meta_objset
, bmark_zapobj
);
269 zap_cursor_retrieve(&zc
, &attr
) == 0;
270 zap_cursor_advance(&zc
)) {
272 char *bmark_name
= attr
.za_name
;
273 zfs_bookmark_phys_t bmark_phys
;
275 err
= dsl_dataset_bmark_lookup(ds
, bmark_name
, &bmark_phys
);
276 ASSERT3U(err
, !=, ENOENT
);
280 out_props
= fnvlist_alloc();
281 if (nvlist_exists(props
,
282 zfs_prop_to_name(ZFS_PROP_GUID
))) {
283 dsl_prop_nvlist_add_uint64(out_props
,
284 ZFS_PROP_GUID
, bmark_phys
.zbm_guid
);
286 if (nvlist_exists(props
,
287 zfs_prop_to_name(ZFS_PROP_CREATETXG
))) {
288 dsl_prop_nvlist_add_uint64(out_props
,
289 ZFS_PROP_CREATETXG
, bmark_phys
.zbm_creation_txg
);
291 if (nvlist_exists(props
,
292 zfs_prop_to_name(ZFS_PROP_CREATION
))) {
293 dsl_prop_nvlist_add_uint64(out_props
,
294 ZFS_PROP_CREATION
, bmark_phys
.zbm_creation_time
);
297 fnvlist_add_nvlist(outnvl
, bmark_name
, out_props
);
298 fnvlist_free(out_props
);
300 zap_cursor_fini(&zc
);
305 * Retrieve the bookmarks that exist in the specified dataset, and the
306 * requested properties of each bookmark.
308 * The "props" nvlist specifies which properties are requested.
309 * See lzc_get_bookmarks() for the list of valid properties.
312 dsl_get_bookmarks(const char *dsname
, nvlist_t
*props
, nvlist_t
*outnvl
)
318 err
= dsl_pool_hold(dsname
, FTAG
, &dp
);
321 err
= dsl_dataset_hold(dp
, dsname
, FTAG
, &ds
);
323 dsl_pool_rele(dp
, FTAG
);
327 err
= dsl_get_bookmarks_impl(ds
, props
, outnvl
);
329 dsl_dataset_rele(ds
, FTAG
);
330 dsl_pool_rele(dp
, FTAG
);
334 typedef struct dsl_bookmark_destroy_arg
{
335 nvlist_t
*dbda_bmarks
;
336 nvlist_t
*dbda_success
;
337 nvlist_t
*dbda_errors
;
338 } dsl_bookmark_destroy_arg_t
;
341 dsl_dataset_bookmark_remove(dsl_dataset_t
*ds
, const char *name
, dmu_tx_t
*tx
)
343 objset_t
*mos
= ds
->ds_dir
->dd_pool
->dp_meta_objset
;
344 uint64_t bmark_zapobj
= ds
->ds_bookmarks
;
347 if (dsl_dataset_phys(ds
)->ds_flags
& DS_FLAG_CI_DATASET
)
350 return (zap_remove_norm(mos
, bmark_zapobj
, name
, mt
, tx
));
354 dsl_bookmark_destroy_check(void *arg
, dmu_tx_t
*tx
)
356 dsl_bookmark_destroy_arg_t
*dbda
= arg
;
357 dsl_pool_t
*dp
= dmu_tx_pool(tx
);
361 ASSERT(nvlist_empty(dbda
->dbda_success
));
362 ASSERT(nvlist_empty(dbda
->dbda_errors
));
364 if (!spa_feature_is_enabled(dp
->dp_spa
, SPA_FEATURE_BOOKMARKS
))
367 for (pair
= nvlist_next_nvpair(dbda
->dbda_bmarks
, NULL
);
368 pair
!= NULL
; pair
= nvlist_next_nvpair(dbda
->dbda_bmarks
, pair
)) {
369 const char *fullname
= nvpair_name(pair
);
371 zfs_bookmark_phys_t bm
;
375 error
= dsl_bookmark_hold_ds(dp
, fullname
, &ds
,
377 if (error
== ENOENT
) {
378 /* ignore it; the bookmark is "already destroyed" */
382 error
= dsl_dataset_bmark_lookup(ds
, shortname
, &bm
);
383 dsl_dataset_rele(ds
, FTAG
);
384 if (error
== ESRCH
) {
386 * ignore it; the bookmark is
387 * "already destroyed"
393 if (dmu_tx_is_syncing(tx
)) {
394 fnvlist_add_boolean(dbda
->dbda_success
,
398 fnvlist_add_int32(dbda
->dbda_errors
, fullname
, error
);
406 dsl_bookmark_destroy_sync(void *arg
, dmu_tx_t
*tx
)
408 dsl_bookmark_destroy_arg_t
*dbda
= arg
;
409 dsl_pool_t
*dp
= dmu_tx_pool(tx
);
410 objset_t
*mos
= dp
->dp_meta_objset
;
413 for (pair
= nvlist_next_nvpair(dbda
->dbda_success
, NULL
);
414 pair
!= NULL
; pair
= nvlist_next_nvpair(dbda
->dbda_success
, pair
)) {
419 VERIFY0(dsl_bookmark_hold_ds(dp
, nvpair_name(pair
),
420 &ds
, FTAG
, &shortname
));
421 VERIFY0(dsl_dataset_bookmark_remove(ds
, shortname
, tx
));
424 * If all of this dataset's bookmarks have been destroyed,
425 * free the zap object and decrement the feature's use count.
427 VERIFY0(zap_count(mos
, ds
->ds_bookmarks
,
430 dmu_buf_will_dirty(ds
->ds_dbuf
, tx
);
431 VERIFY0(zap_destroy(mos
, ds
->ds_bookmarks
, tx
));
432 ds
->ds_bookmarks
= 0;
433 spa_feature_decr(dp
->dp_spa
, SPA_FEATURE_BOOKMARKS
, tx
);
434 VERIFY0(zap_remove(mos
, ds
->ds_object
,
435 DS_FIELD_BOOKMARK_NAMES
, tx
));
438 spa_history_log_internal_ds(ds
, "remove bookmark", tx
,
439 "name=%s", shortname
);
441 dsl_dataset_rele(ds
, FTAG
);
446 * The bookmarks must all be in the same pool.
449 dsl_bookmark_destroy(nvlist_t
*bmarks
, nvlist_t
*errors
)
452 dsl_bookmark_destroy_arg_t dbda
;
453 nvpair_t
*pair
= nvlist_next_nvpair(bmarks
, NULL
);
457 dbda
.dbda_bmarks
= bmarks
;
458 dbda
.dbda_errors
= errors
;
459 dbda
.dbda_success
= fnvlist_alloc();
461 rv
= dsl_sync_task(nvpair_name(pair
), dsl_bookmark_destroy_check
,
462 dsl_bookmark_destroy_sync
, &dbda
, fnvlist_num_pairs(bmarks
),
463 ZFS_SPACE_CHECK_RESERVED
);
464 fnvlist_free(dbda
.dbda_success
);