diff options
Diffstat (limited to 'module/zfs')
-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); } |