MFV r322229: 7600 zfs rollback should pass target snapshot to kernel
illumos/illumos-gate@77b171372e
77b171372e
https://www.illumos.org/issues/7600
At present, the kernel side code seems to blindly rollback to whatever happens
to be the latest snapshot at the time when the rollback task is processed.
The expected target's name should be passed to the kernel driver and the sync
task should validate that the target exists and that it is the latest snapshot
indeed.
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed by: Pavel Zakharov <pavel.zakharov@delphix.com>
Approved by: Robert Mustacchi <rm@joyent.com>
Author: Andriy Gapon <avg@FreeBSD.org>
MFC after: 3 weeks
This commit is contained in:
commit
9c48e95dd9
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=322230
@ -3957,14 +3957,19 @@ zfs_rollback(zfs_handle_t *zhp, zfs_handle_t *snap, boolean_t force)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We rely on zfs_iter_children() to verify that there are no
|
* Pass both the filesystem and the wanted snapshot names,
|
||||||
* newer snapshots for the given dataset. Therefore, we can
|
* we would get an error back if the snapshot is destroyed or
|
||||||
* simply pass the name on to the ioctl() call. There is still
|
* a new snapshot is created before this request is processed.
|
||||||
* an unlikely race condition where the user has taken a
|
|
||||||
* snapshot since we verified that this was the most recent.
|
|
||||||
*/
|
*/
|
||||||
err = lzc_rollback(zhp->zfs_name, NULL, 0);
|
err = lzc_rollback_to(zhp->zfs_name, snap->zfs_name);
|
||||||
if (err != 0) {
|
if (err == EXDEV) {
|
||||||
|
zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
|
||||||
|
"'%s' is not the latest snapshot"), snap->zfs_name);
|
||||||
|
(void) zfs_error_fmt(zhp->zfs_hdl, EZFS_BUSY,
|
||||||
|
dgettext(TEXT_DOMAIN, "cannot rollback '%s'"),
|
||||||
|
zhp->zfs_name);
|
||||||
|
return (err);
|
||||||
|
} else if (err != 0) {
|
||||||
(void) zfs_standard_error_fmt(zhp->zfs_hdl, errno,
|
(void) zfs_standard_error_fmt(zhp->zfs_hdl, errno,
|
||||||
dgettext(TEXT_DOMAIN, "cannot rollback '%s'"),
|
dgettext(TEXT_DOMAIN, "cannot rollback '%s'"),
|
||||||
zhp->zfs_name);
|
zhp->zfs_name);
|
||||||
|
@ -765,6 +765,9 @@ lzc_receive_with_header(const char *snapname, nvlist_t *props,
|
|||||||
* Roll back this filesystem or volume to its most recent snapshot.
|
* Roll back this filesystem or volume to its most recent snapshot.
|
||||||
* If snapnamebuf is not NULL, it will be filled in with the name
|
* If snapnamebuf is not NULL, it will be filled in with the name
|
||||||
* of the most recent snapshot.
|
* of the most recent snapshot.
|
||||||
|
* Note that the latest snapshot may change if a new one is concurrently
|
||||||
|
* created or the current one is destroyed. lzc_rollback_to can be used
|
||||||
|
* to roll back to a specific latest snapshot.
|
||||||
*
|
*
|
||||||
* Return 0 on success or an errno on failure.
|
* Return 0 on success or an errno on failure.
|
||||||
*/
|
*/
|
||||||
@ -787,6 +790,27 @@ lzc_rollback(const char *fsname, char *snapnamebuf, int snapnamelen)
|
|||||||
return (err);
|
return (err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Roll back this filesystem or volume to the specified snapshot,
|
||||||
|
* if possible.
|
||||||
|
*
|
||||||
|
* Return 0 on success or an errno on failure.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
lzc_rollback_to(const char *fsname, const char *snapname)
|
||||||
|
{
|
||||||
|
nvlist_t *args;
|
||||||
|
nvlist_t *result;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
args = fnvlist_alloc();
|
||||||
|
fnvlist_add_string(args, "target", snapname);
|
||||||
|
err = lzc_ioctl(ZFS_IOC_ROLLBACK, fsname, args, &result);
|
||||||
|
nvlist_free(args);
|
||||||
|
nvlist_free(result);
|
||||||
|
return (err);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Creates bookmarks.
|
* Creates bookmarks.
|
||||||
*
|
*
|
||||||
|
@ -84,6 +84,7 @@ int lzc_receive_with_header(const char *, nvlist_t *, const char *, boolean_t,
|
|||||||
boolean_t lzc_exists(const char *);
|
boolean_t lzc_exists(const char *);
|
||||||
|
|
||||||
int lzc_rollback(const char *, char *, int);
|
int lzc_rollback(const char *, char *, int);
|
||||||
|
int lzc_rollback_to(const char *, const char *);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
@ -2259,6 +2259,7 @@ dsl_dataset_handoff_check(dsl_dataset_t *ds, void *owner, dmu_tx_t *tx)
|
|||||||
|
|
||||||
typedef struct dsl_dataset_rollback_arg {
|
typedef struct dsl_dataset_rollback_arg {
|
||||||
const char *ddra_fsname;
|
const char *ddra_fsname;
|
||||||
|
const char *ddra_tosnap;
|
||||||
void *ddra_owner;
|
void *ddra_owner;
|
||||||
nvlist_t *ddra_result;
|
nvlist_t *ddra_result;
|
||||||
} dsl_dataset_rollback_arg_t;
|
} dsl_dataset_rollback_arg_t;
|
||||||
@ -2300,6 +2301,18 @@ dsl_dataset_rollback_check(void *arg, dmu_tx_t *tx)
|
|||||||
return (SET_ERROR(EAGAIN));
|
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 */
|
/* must not have any bookmarks after the most recent snapshot */
|
||||||
nvlist_t *proprequest = fnvlist_alloc();
|
nvlist_t *proprequest = fnvlist_alloc();
|
||||||
fnvlist_add_boolean(proprequest, zfs_prop_to_name(ZFS_PROP_CREATETXG));
|
fnvlist_add_boolean(proprequest, zfs_prop_to_name(ZFS_PROP_CREATETXG));
|
||||||
@ -2401,11 +2414,13 @@ dsl_dataset_rollback_sync(void *arg, dmu_tx_t *tx)
|
|||||||
* notes above zfs_suspend_fs() for further details.
|
* notes above zfs_suspend_fs() for further details.
|
||||||
*/
|
*/
|
||||||
int
|
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;
|
dsl_dataset_rollback_arg_t ddra;
|
||||||
|
|
||||||
ddra.ddra_fsname = fsname;
|
ddra.ddra_fsname = fsname;
|
||||||
|
ddra.ddra_tosnap = tosnap;
|
||||||
ddra.ddra_owner = owner;
|
ddra.ddra_owner = owner;
|
||||||
ddra.ddra_result = result;
|
ddra.ddra_result = result;
|
||||||
|
|
||||||
|
@ -334,7 +334,8 @@ void dsl_dataset_set_refreservation_sync_impl(dsl_dataset_t *ds,
|
|||||||
void dsl_dataset_zapify(dsl_dataset_t *ds, dmu_tx_t *tx);
|
void dsl_dataset_zapify(dsl_dataset_t *ds, dmu_tx_t *tx);
|
||||||
boolean_t dsl_dataset_is_zapified(dsl_dataset_t *ds);
|
boolean_t dsl_dataset_is_zapified(dsl_dataset_t *ds);
|
||||||
boolean_t dsl_dataset_has_resume_receive_state(dsl_dataset_t *ds);
|
boolean_t dsl_dataset_has_resume_receive_state(dsl_dataset_t *ds);
|
||||||
int dsl_dataset_rollback(const char *fsname, void *owner, nvlist_t *result);
|
int dsl_dataset_rollback(const char *fsname, const char *tosnap, void *owner,
|
||||||
|
nvlist_t *result);
|
||||||
|
|
||||||
void dsl_dataset_deactivate_feature(uint64_t dsobj,
|
void dsl_dataset_deactivate_feature(uint64_t dsobj,
|
||||||
spa_feature_t f, dmu_tx_t *tx);
|
spa_feature_t f, dmu_tx_t *tx);
|
||||||
|
@ -3780,18 +3780,29 @@ zfs_ioc_destroy(zfs_cmd_t *zc)
|
|||||||
/*
|
/*
|
||||||
* fsname is name of dataset to rollback (to most recent snapshot)
|
* 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
|
* outnvl: "target" -> name of most recent snapshot
|
||||||
* }
|
* }
|
||||||
*/
|
*/
|
||||||
/* ARGSUSED */
|
/* ARGSUSED */
|
||||||
static int
|
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;
|
zfsvfs_t *zfsvfs;
|
||||||
|
char *target = NULL;
|
||||||
int error;
|
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) {
|
if (getzfsvfs(fsname, &zfsvfs) == 0) {
|
||||||
dsl_dataset_t *ds;
|
dsl_dataset_t *ds;
|
||||||
|
|
||||||
@ -3800,7 +3811,8 @@ zfs_ioc_rollback(const char *fsname, nvlist_t *args, nvlist_t *outnvl)
|
|||||||
if (error == 0) {
|
if (error == 0) {
|
||||||
int resume_err;
|
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);
|
resume_err = zfs_resume_fs(zfsvfs, ds);
|
||||||
error = error ? error : resume_err;
|
error = error ? error : resume_err;
|
||||||
}
|
}
|
||||||
@ -3810,7 +3822,7 @@ zfs_ioc_rollback(const char *fsname, nvlist_t *args, nvlist_t *outnvl)
|
|||||||
vfs_unbusy(zfsvfs->z_vfs);
|
vfs_unbusy(zfsvfs->z_vfs);
|
||||||
#endif
|
#endif
|
||||||
} else {
|
} else {
|
||||||
error = dsl_dataset_rollback(fsname, NULL, outnvl);
|
error = dsl_dataset_rollback(fsname, target, NULL, outnvl);
|
||||||
}
|
}
|
||||||
return (error);
|
return (error);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user