diff options
Diffstat (limited to 'module/zfs/dsl_dataset.c')
-rw-r--r-- | module/zfs/dsl_dataset.c | 48 |
1 files changed, 44 insertions, 4 deletions
diff --git a/module/zfs/dsl_dataset.c b/module/zfs/dsl_dataset.c index cae56534d..2fe6b858d 100644 --- a/module/zfs/dsl_dataset.c +++ b/module/zfs/dsl_dataset.c @@ -48,6 +48,7 @@ #include <sys/dsl_deadlist.h> #include <sys/dsl_destroy.h> #include <sys/dsl_userhold.h> +#include <sys/dsl_bookmark.h> #define SWITCH64(x, y) \ { \ @@ -404,6 +405,14 @@ dsl_dataset_hold_obj(dsl_pool_t *dp, uint64_t dsobj, void *tag, ds->ds_phys->ds_prev_snap_obj, ds, &ds->ds_prev); } + if (doi.doi_type == DMU_OTN_ZAP_METADATA) { + int zaperr = zap_lookup(mos, ds->ds_object, + DS_FIELD_BOOKMARK_NAMES, + sizeof (ds->ds_bookmarks), 1, + &ds->ds_bookmarks); + if (zaperr != ENOENT) + VERIFY0(zaperr); + } } else { if (zfs_flags & ZFS_DEBUG_SNAPNAMES) err = dsl_dataset_get_snapname(ds); @@ -1734,6 +1743,8 @@ dsl_dataset_rollback_check(void *arg, dmu_tx_t *tx) dsl_dataset_t *ds; int64_t unused_refres_delta; int error; + nvpair_t *pair; + nvlist_t *proprequest, *bookmarks; error = dsl_dataset_hold(dp, ddra->ddra_fsname, FTAG, &ds); if (error != 0) @@ -1751,6 +1762,28 @@ dsl_dataset_rollback_check(void *arg, dmu_tx_t *tx) return (SET_ERROR(EINVAL)); } + /* must not have any bookmarks after the most recent snapshot */ + proprequest = fnvlist_alloc(); + fnvlist_add_boolean(proprequest, zfs_prop_to_name(ZFS_PROP_CREATETXG)); + bookmarks = fnvlist_alloc(); + error = dsl_get_bookmarks_impl(ds, proprequest, bookmarks); + fnvlist_free(proprequest); + if (error != 0) + return (error); + for (pair = nvlist_next_nvpair(bookmarks, NULL); + pair != NULL; pair = nvlist_next_nvpair(bookmarks, pair)) { + nvlist_t *valuenv = + fnvlist_lookup_nvlist(fnvpair_value_nvlist(pair), + zfs_prop_to_name(ZFS_PROP_CREATETXG)); + uint64_t createtxg = fnvlist_lookup_uint64(valuenv, "value"); + if (createtxg > ds->ds_phys->ds_prev_snap_txg) { + fnvlist_free(bookmarks); + dsl_dataset_rele(ds, FTAG); + return (SET_ERROR(EEXIST)); + } + } + fnvlist_free(bookmarks); + error = dsl_dataset_handoff_check(ds, ddra->ddra_owner, tx); if (error != 0) { dsl_dataset_rele(ds, FTAG); @@ -2972,9 +3005,12 @@ dsl_dataset_space_wouldfree(dsl_dataset_t *firstsnap, * 'earlier' is before 'later'. Or 'earlier' could be the origin of * 'later's filesystem. Or 'earlier' could be an older snapshot in the origin's * filesystem. Or 'earlier' could be the origin's origin. + * + * If non-zero, earlier_txg is used instead of earlier's ds_creation_txg. */ boolean_t -dsl_dataset_is_before(dsl_dataset_t *later, dsl_dataset_t *earlier) +dsl_dataset_is_before(dsl_dataset_t *later, dsl_dataset_t *earlier, + uint64_t earlier_txg) { dsl_pool_t *dp = later->ds_dir->dd_pool; int error; @@ -2982,9 +3018,13 @@ dsl_dataset_is_before(dsl_dataset_t *later, dsl_dataset_t *earlier) dsl_dataset_t *origin; ASSERT(dsl_pool_config_held(dp)); + ASSERT(dsl_dataset_is_snapshot(earlier) || earlier_txg != 0); + + if (earlier_txg == 0) + earlier_txg = earlier->ds_phys->ds_creation_txg; - if (earlier->ds_phys->ds_creation_txg >= - later->ds_phys->ds_creation_txg) + if (dsl_dataset_is_snapshot(later) && + earlier_txg >= later->ds_phys->ds_creation_txg) return (B_FALSE); if (later->ds_dir == earlier->ds_dir) @@ -2998,7 +3038,7 @@ dsl_dataset_is_before(dsl_dataset_t *later, dsl_dataset_t *earlier) later->ds_dir->dd_phys->dd_origin_obj, FTAG, &origin); if (error != 0) return (B_FALSE); - ret = dsl_dataset_is_before(origin, earlier); + ret = dsl_dataset_is_before(origin, earlier, earlier_txg); dsl_dataset_rele(origin, FTAG); return (ret); } |