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, 2014 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
)
37 char buf
[ZFS_MAX_DATASET_NAME_LEN
];
40 if (strlen(fullname
) >= ZFS_MAX_DATASET_NAME_LEN
)
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
,
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
)
352 return (zap_remove_norm(mos
, bmark_zapobj
, name
, mt
, tx
));
356 dsl_bookmark_destroy_check(void *arg
, dmu_tx_t
*tx
)
358 dsl_bookmark_destroy_arg_t
*dbda
= arg
;
359 dsl_pool_t
*dp
= dmu_tx_pool(tx
);
363 if (!spa_feature_is_enabled(dp
->dp_spa
, SPA_FEATURE_BOOKMARKS
))
366 for (pair
= nvlist_next_nvpair(dbda
->dbda_bmarks
, NULL
);
367 pair
!= NULL
; pair
= nvlist_next_nvpair(dbda
->dbda_bmarks
, pair
)) {
368 const char *fullname
= nvpair_name(pair
);
370 zfs_bookmark_phys_t bm
;
374 error
= dsl_bookmark_hold_ds(dp
, fullname
, &ds
,
376 if (error
== ENOENT
) {
377 /* ignore it; the bookmark is "already destroyed" */
381 error
= dsl_dataset_bmark_lookup(ds
, shortname
, &bm
);
382 dsl_dataset_rele(ds
, FTAG
);
383 if (error
== ESRCH
) {
385 * ignore it; the bookmark is
386 * "already destroyed"
392 fnvlist_add_boolean(dbda
->dbda_success
, fullname
);
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
;
409 for (pair
= nvlist_next_nvpair(dbda
->dbda_success
, NULL
);
410 pair
!= NULL
; pair
= nvlist_next_nvpair(dbda
->dbda_success
, pair
)) {
415 VERIFY0(dsl_bookmark_hold_ds(dp
, nvpair_name(pair
),
416 &ds
, FTAG
, &shortname
));
417 VERIFY0(dsl_dataset_bookmark_remove(ds
, shortname
, tx
));
420 * If all of this dataset's bookmarks have been destroyed,
421 * free the zap object and decrement the feature's use count.
423 VERIFY0(zap_count(mos
, ds
->ds_bookmarks
,
426 dmu_buf_will_dirty(ds
->ds_dbuf
, tx
);
427 VERIFY0(zap_destroy(mos
, ds
->ds_bookmarks
, tx
));
428 ds
->ds_bookmarks
= 0;
429 spa_feature_decr(dp
->dp_spa
, SPA_FEATURE_BOOKMARKS
, tx
);
430 VERIFY0(zap_remove(mos
, ds
->ds_object
,
431 DS_FIELD_BOOKMARK_NAMES
, tx
));
434 spa_history_log_internal_ds(ds
, "remove bookmark", tx
,
435 "name=%s", shortname
);
437 dsl_dataset_rele(ds
, FTAG
);
442 * The bookmarks must all be in the same pool.
445 dsl_bookmark_destroy(nvlist_t
*bmarks
, nvlist_t
*errors
)
448 dsl_bookmark_destroy_arg_t dbda
;
449 nvpair_t
*pair
= nvlist_next_nvpair(bmarks
, NULL
);
453 dbda
.dbda_bmarks
= bmarks
;
454 dbda
.dbda_errors
= errors
;
455 dbda
.dbda_success
= fnvlist_alloc();
457 rv
= dsl_sync_task(nvpair_name(pair
), dsl_bookmark_destroy_check
,
458 dsl_bookmark_destroy_sync
, &dbda
, fnvlist_num_pairs(bmarks
),
459 ZFS_SPACE_CHECK_RESERVED
);
460 fnvlist_free(dbda
.dbda_success
);