Skip to content

Commit

Permalink
7600 zfs rollback should pass target snapshot to kernel
Browse files Browse the repository at this point in the history
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed by: Pavel Zakharov <pavel.zakharov@delphix.com>
Approved by: Robert Mustacchi <rm@joyent.com>
  • Loading branch information
avg-I authored and Prakash Surya committed Jun 29, 2017
1 parent 42418f9 commit 77b1713
Show file tree
Hide file tree
Showing 7 changed files with 72 additions and 13 deletions.
19 changes: 12 additions & 7 deletions usr/src/lib/libzfs/common/libzfs_dataset.c
Original file line number Diff line number Diff line change
Expand Up @@ -3996,14 +3996,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
* newer snapshots for the given dataset. Therefore, we can
* simply pass the name on to the ioctl() call. There is still
* an unlikely race condition where the user has taken a
* snapshot since we verified that this was the most recent.
* Pass both the filesystem and the wanted snapshot names,
* we would get an error back if the snapshot is destroyed or
* a new snapshot is created before this request is processed.
*/
err = lzc_rollback(zhp->zfs_name, NULL, 0);
if (err != 0) {
err = lzc_rollback_to(zhp->zfs_name, snap->zfs_name);
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,
dgettext(TEXT_DOMAIN, "cannot rollback '%s'"),
zhp->zfs_name);
Expand Down
24 changes: 24 additions & 0 deletions usr/src/lib/libzfs_core/common/libzfs_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -736,6 +736,9 @@ lzc_receive_with_header(const char *snapname, nvlist_t *props,
* Roll back this filesystem or volume to its most recent snapshot.
* If snapnamebuf is not NULL, it will be filled in with the name
* 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.
*/
Expand All @@ -758,6 +761,27 @@ lzc_rollback(const char *fsname, char *snapnamebuf, int snapnamelen)
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.
*
Expand Down
1 change: 1 addition & 0 deletions usr/src/lib/libzfs_core/common/libzfs_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ int lzc_receive_with_header(const char *, nvlist_t *, const char *, boolean_t,
boolean_t lzc_exists(const char *);

int lzc_rollback(const char *, char *, int);
int lzc_rollback_to(const char *, const char *);

int lzc_channel_program(const char *, const char *, uint64_t, uint64_t,
nvlist_t *, nvlist_t **);
Expand Down
1 change: 1 addition & 0 deletions usr/src/lib/libzfs_core/common/mapfile-vers
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ SYMBOL_VERSION ILLUMOS_0.1 {
lzc_receive_with_header;
lzc_release;
lzc_rollback;
lzc_rollback_to;
lzc_send;
lzc_send_resume;
lzc_send_space;
Expand Down
17 changes: 16 additions & 1 deletion usr/src/uts/common/fs/zfs/dsl_dataset.c
Original file line number Diff line number Diff line change
Expand Up @@ -2454,6 +2454,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;
Expand Down Expand Up @@ -2495,6 +2496,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 */
nvlist_t *proprequest = fnvlist_alloc();
fnvlist_add_boolean(proprequest, zfs_prop_to_name(ZFS_PROP_CREATETXG));
Expand Down Expand Up @@ -2596,11 +2609,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;

Expand Down
3 changes: 2 additions & 1 deletion usr/src/uts/common/fs/zfs/sys/dsl_dataset.h
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,8 @@ void dsl_dataset_set_refreservation_sync_impl(dsl_dataset_t *ds,
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_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,
spa_feature_t f, dmu_tx_t *tx);
Expand Down
20 changes: 16 additions & 4 deletions usr/src/uts/common/fs/zfs/zfs_ioctl.c
Original file line number Diff line number Diff line change
Expand Up @@ -3678,18 +3678,29 @@ 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;
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;

Expand All @@ -3698,13 +3709,14 @@ 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;
}
VFS_RELE(zfsvfs->z_vfs);
} else {
error = dsl_dataset_rollback(fsname, NULL, outnvl);
error = dsl_dataset_rollback(fsname, target, NULL, outnvl);
}
return (error);
}
Expand Down

0 comments on commit 77b1713

Please sign in to comment.