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