From 8ca78ab00278332a877d7d95e057c0b4aca5f9ad Mon Sep 17 00:00:00 2001 From: Andriy Gapon Date: Sat, 11 Mar 2017 20:26:47 +0200 Subject: OpenZFS 7600 - zfs rollback should pass target snapshot to kernel Authored by: Andriy Gapon Reviewed by: Matthew Ahrens Reviewed by: Pavel Zakharov Approved by: Robert Mustacchi Reviewed-by: Brian Behlendorf Ported-by: Giuseppe Di Natale The existing kernel-side code only provides a method to rollback to a latest snapshot, whatever it happens to be at the time when the rollback is actually done. That could be unsafe or confusing in environments where concurrent DSL changes are possible as the resulting state could correspond to a newer or older snapshot than the originally requested one. This change allows to amend that method such that the rollback is performed only when the latest snapshot has a specific name. That is, if a new snapshot is concurrently created or the target snapshot is destroyed, then no rollback is done and EXDEV error is returned. New libzfs_core function lzc_rollback_to() is provided for the new functionality. libzfs is changed to use lzc_rollback_to() to implement zfs rollback command. Perhaps we should return different errors to distinguish the case where the desired snapshot exists but it's not the latest snapshot and the case where the desired snapshot does not exist. OpenZFS-issue: https://www.illumos.org/issues/7600 OpenZFS-commit: https://github.com/openzfs/openzfs/commit/3d645eb Closes #6292 --- module/zfs/dsl_dataset.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) (limited to 'module/zfs/dsl_dataset.c') diff --git a/module/zfs/dsl_dataset.c b/module/zfs/dsl_dataset.c index 5fa04e7ba..bd03b4868 100644 --- a/module/zfs/dsl_dataset.c +++ b/module/zfs/dsl_dataset.c @@ -2210,6 +2210,7 @@ dsl_dataset_handoff_check(dsl_dataset_t *ds, void *owner, dmu_tx_t *tx) typedef struct dsl_dataset_rollback_arg { const char *ddra_fsname; + const char *ddra_tosnap; void *ddra_owner; nvlist_t *ddra_result; } dsl_dataset_rollback_arg_t; @@ -2253,6 +2254,18 @@ dsl_dataset_rollback_check(void *arg, dmu_tx_t *tx) return (SET_ERROR(EAGAIN)); } + /* + * If the expected target snapshot is specified, then check that + * the latest snapshot is it. + */ + if (ddra->ddra_tosnap != NULL) { + char namebuf[ZFS_MAX_DATASET_NAME_LEN]; + + dsl_dataset_name(ds->ds_prev, namebuf); + if (strcmp(namebuf, ddra->ddra_tosnap) != 0) + return (SET_ERROR(EXDEV)); + } + /* must not have any bookmarks after the most recent snapshot */ proprequest = fnvlist_alloc(); fnvlist_add_boolean(proprequest, zfs_prop_to_name(ZFS_PROP_CREATETXG)); @@ -2354,11 +2367,13 @@ dsl_dataset_rollback_sync(void *arg, dmu_tx_t *tx) * notes above zfs_suspend_fs() for further details. */ int -dsl_dataset_rollback(const char *fsname, void *owner, nvlist_t *result) +dsl_dataset_rollback(const char *fsname, const char *tosnap, void *owner, + nvlist_t *result) { dsl_dataset_rollback_arg_t ddra; ddra.ddra_fsname = fsname; + ddra.ddra_tosnap = tosnap; ddra.ddra_owner = owner; ddra.ddra_result = result; -- cgit v1.2.3