diff options
author | Andriy Gapon <[email protected]> | 2017-03-11 20:26:47 +0200 |
---|---|---|
committer | Brian Behlendorf <[email protected]> | 2017-07-04 15:29:52 -0700 |
commit | 8ca78ab00278332a877d7d95e057c0b4aca5f9ad (patch) | |
tree | 60f894137651ee83fba59b74625fdd8798c66c8c /module | |
parent | 018503911c3a4d01768270c69e6ec87b3034e86f (diff) |
OpenZFS 7600 - zfs rollback should pass target snapshot to kernel
Authored by: Andriy Gapon <[email protected]>
Reviewed by: Matthew Ahrens <[email protected]>
Reviewed by: Pavel Zakharov <[email protected]>
Approved by: Robert Mustacchi <[email protected]>
Reviewed-by: Brian Behlendorf <[email protected]>
Ported-by: Giuseppe Di Natale <[email protected]>
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
Diffstat (limited to 'module')
-rw-r--r-- | module/zfs/dsl_dataset.c | 17 | ||||
-rw-r--r-- | module/zfs/zfs_ioctl.c | 23 |
2 files changed, 34 insertions, 6 deletions
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; diff --git a/module/zfs/zfs_ioctl.c b/module/zfs/zfs_ioctl.c index a2f7f045f..fff1a3c06 100644 --- a/module/zfs/zfs_ioctl.c +++ b/module/zfs/zfs_ioctl.c @@ -3653,19 +3653,30 @@ zfs_ioc_destroy(zfs_cmd_t *zc) /* * fsname is name of dataset to rollback (to most recent snapshot) * - * innvl is not used. + * innvl may contain name of expected target snapshot * * outnvl: "target" -> name of most recent snapshot * } */ /* ARGSUSED */ static int -zfs_ioc_rollback(const char *fsname, nvlist_t *args, nvlist_t *outnvl) +zfs_ioc_rollback(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl) { zfsvfs_t *zfsvfs; zvol_state_t *zv; + char *target = NULL; int error; + (void) nvlist_lookup_string(innvl, "target", &target); + if (target != NULL) { + int fslen = strlen(fsname); + + if (strncmp(fsname, target, fslen) != 0) + return (SET_ERROR(EINVAL)); + if (target[fslen] != '@') + return (SET_ERROR(EINVAL)); + } + if (getzfsvfs(fsname, &zfsvfs) == 0) { dsl_dataset_t *ds; @@ -3674,16 +3685,18 @@ zfs_ioc_rollback(const char *fsname, nvlist_t *args, nvlist_t *outnvl) if (error == 0) { int resume_err; - error = dsl_dataset_rollback(fsname, zfsvfs, outnvl); + error = dsl_dataset_rollback(fsname, target, zfsvfs, + outnvl); resume_err = zfs_resume_fs(zfsvfs, ds); error = error ? error : resume_err; } deactivate_super(zfsvfs->z_sb); } else if ((zv = zvol_suspend(fsname)) != NULL) { - error = dsl_dataset_rollback(fsname, zvol_tag(zv), outnvl); + error = dsl_dataset_rollback(fsname, target, zvol_tag(zv), + outnvl); zvol_resume(zv); } else { - error = dsl_dataset_rollback(fsname, NULL, outnvl); + error = dsl_dataset_rollback(fsname, target, NULL, outnvl); } return (error); } |