Skip to content

Commit

Permalink
8520 lzc_rollback_to should support rolling back to origin
Browse files Browse the repository at this point in the history
7198 libzfs should gracefully handle EINVAL from lzc_rollback
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Approved by: Dan McDonald <danmcd@joyent.com>
  • Loading branch information
avg-I authored and Prakash Surya committed Feb 8, 2018
1 parent f4c1745 commit 95643f7
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 19 deletions.
32 changes: 23 additions & 9 deletions usr/src/lib/libzfs/common/libzfs_dataset.c
Original file line number Diff line number Diff line change
Expand Up @@ -4023,17 +4023,31 @@ zfs_rollback(zfs_handle_t *zhp, zfs_handle_t *snap, boolean_t force)
* a new snapshot is created before this request is processed.
*/
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,
if (err != 0) {
char errbuf[1024];

(void) snprintf(errbuf, sizeof (errbuf),
dgettext(TEXT_DOMAIN, "cannot rollback '%s'"),
zhp->zfs_name);
switch (err) {
case EEXIST:
zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
"there is a snapshot or bookmark more recent "
"than '%s'"), snap->zfs_name);
(void) zfs_error(zhp->zfs_hdl, EZFS_EXISTS, errbuf);
break;
case ESRCH:
zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
"'%s' is not found among snapshots of '%s'"),
snap->zfs_name, zhp->zfs_name);
(void) zfs_error(zhp->zfs_hdl, EZFS_NOENT, errbuf);
break;
case EINVAL:
(void) zfs_error(zhp->zfs_hdl, EZFS_BADTYPE, errbuf);
break;
default:
(void) zfs_standard_error(zhp->zfs_hdl, err, errbuf);
}
return (err);
}

Expand Down
49 changes: 43 additions & 6 deletions usr/src/uts/common/fs/zfs/dsl_dataset.c
Original file line number Diff line number Diff line change
Expand Up @@ -2539,7 +2539,7 @@ dsl_dataset_rollback_check(void *arg, dmu_tx_t *tx)
/* must have a most recent snapshot */
if (dsl_dataset_phys(ds)->ds_prev_snap_txg < TXG_INITIAL) {
dsl_dataset_rele(ds, FTAG);
return (SET_ERROR(EINVAL));
return (SET_ERROR(ESRCH));
}

/*
Expand All @@ -2559,11 +2559,46 @@ dsl_dataset_rollback_check(void *arg, dmu_tx_t *tx)
* the latest snapshot is it.
*/
if (ddra->ddra_tosnap != NULL) {
char namebuf[ZFS_MAX_DATASET_NAME_LEN];
dsl_dataset_t *snapds;

/* Check if the target snapshot exists at all. */
error = dsl_dataset_hold(dp, ddra->ddra_tosnap, FTAG, &snapds);
if (error != 0) {
/*
* ESRCH is used to signal that the target snapshot does
* not exist, while ENOENT is used to report that
* the rolled back dataset does not exist.
* ESRCH is also used to cover other cases where the
* target snapshot is not related to the dataset being
* rolled back such as being in a different pool.
*/
if (error == ENOENT || error == EXDEV)
error = SET_ERROR(ESRCH);
dsl_dataset_rele(ds, FTAG);
return (error);
}
ASSERT(snapds->ds_is_snapshot);

dsl_dataset_name(ds->ds_prev, namebuf);
if (strcmp(namebuf, ddra->ddra_tosnap) != 0)
return (SET_ERROR(EXDEV));
/* Check if the snapshot is the latest snapshot indeed. */
if (snapds != ds->ds_prev) {
/*
* Distinguish between the case where the only problem
* is intervening snapshots (EEXIST) vs the snapshot
* not being a valid target for rollback (ESRCH).
*/
if (snapds->ds_dir == ds->ds_dir ||
(dsl_dir_is_clone(ds->ds_dir) &&
dsl_dir_phys(ds->ds_dir)->dd_origin_obj ==
snapds->ds_object)) {
error = SET_ERROR(EEXIST);
} else {
error = SET_ERROR(ESRCH);
}
dsl_dataset_rele(snapds, FTAG);
dsl_dataset_rele(ds, FTAG);
return (error);
}
dsl_dataset_rele(snapds, FTAG);
}

/* must not have any bookmarks after the most recent snapshot */
Expand All @@ -2572,8 +2607,10 @@ dsl_dataset_rollback_check(void *arg, dmu_tx_t *tx)
nvlist_t *bookmarks = fnvlist_alloc();
error = dsl_get_bookmarks_impl(ds, proprequest, bookmarks);
fnvlist_free(proprequest);
if (error != 0)
if (error != 0) {
dsl_dataset_rele(ds, FTAG);
return (error);
}
for (nvpair_t *pair = nvlist_next_nvpair(bookmarks, NULL);
pair != NULL; pair = nvlist_next_nvpair(bookmarks, pair)) {
nvlist_t *valuenv =
Expand Down
11 changes: 7 additions & 4 deletions usr/src/uts/common/fs/zfs/zfs_ioctl.c
Original file line number Diff line number Diff line change
Expand Up @@ -3701,11 +3701,14 @@ zfs_ioc_rollback(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl)

(void) nvlist_lookup_string(innvl, "target", &target);
if (target != NULL) {
int fslen = strlen(fsname);
const char *cp = strchr(target, '@');

if (strncmp(fsname, target, fslen) != 0)
return (SET_ERROR(EINVAL));
if (target[fslen] != '@')
/*
* The snap name must contain an @, and the part after it must
* contain only valid characters.
*/
if (cp == NULL ||
zfs_component_namecheck(cp + 1, NULL, NULL) != 0)
return (SET_ERROR(EINVAL));
}

Expand Down

0 comments on commit 95643f7

Please sign in to comment.