summaryrefslogtreecommitdiffstats
path: root/module/zfs/dsl_dataset.c
diff options
context:
space:
mode:
Diffstat (limited to 'module/zfs/dsl_dataset.c')
-rw-r--r--module/zfs/dsl_dataset.c48
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);
}