OpenZFS 6111 - zfs send should ignore datasets created after the ending snapshot
Authored by: Alex Deiter <alex.deiter@gmail.com> Reviewed by: Alex Aizman alex.aizman@nexenta.com Reviewed by: Alek Pinchuk alek.pinchuk@nexenta.com Reviewed by: Roman Strashkin roman.strashkin@nexenta.com Reviewed by: Matthew Ahrens <mahrens@delphix.com> Reviewed by: Paul Dagnelie <pcd@delphix.com> Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov> Approved by: Garrett D'Amore <garrett@damore.org> Ported-by: kernelOfTruth <kerneloftruth@gmail.com> OpenZFS-issue: https://www.illumos.org/issues/6111 OpenZFS-commit: https://github.com/illumos/illumos-gate/commit/4a20c933 Closes #5110 Porting notes: There were changes from upstream due to the following commits: - zfs send -p send properties only for snapshots that are actually sent057485504e
- Produce a full snapshot list for zfs send -pe890dd85a7
- Implement zfs_ioc_recv_new() for OpenZFS 260543e52eddb1
- OpenZFS 6314 - buffer overflow in dsl_dataset_name ZFS_MAXNAMELEN was changed to the now used ZFS_MAX_DATASET_NAME_LEN sinceeca7b76001
This commit is contained in:
parent
51907a31bc
commit
6635624020
|
@ -574,13 +574,30 @@ fsavl_create(nvlist_t *fss)
|
|||
* Routines for dealing with the giant nvlist of fs-nvlists, etc.
|
||||
*/
|
||||
typedef struct send_data {
|
||||
/*
|
||||
* assigned inside every recursive call,
|
||||
* restored from *_save on return:
|
||||
*
|
||||
* guid of fromsnap snapshot in parent dataset
|
||||
* txg of fromsnap snapshot in current dataset
|
||||
* txg of tosnap snapshot in current dataset
|
||||
*/
|
||||
|
||||
uint64_t parent_fromsnap_guid;
|
||||
uint64_t fromsnap_txg;
|
||||
uint64_t tosnap_txg;
|
||||
|
||||
/* the nvlists get accumulated during depth-first traversal */
|
||||
nvlist_t *parent_snaps;
|
||||
nvlist_t *fss;
|
||||
nvlist_t *snapprops;
|
||||
|
||||
/* send-receive configuration, does not change during traversal */
|
||||
const char *fsname;
|
||||
const char *fromsnap;
|
||||
const char *tosnap;
|
||||
boolean_t recursive;
|
||||
boolean_t verbose;
|
||||
boolean_t seenfrom;
|
||||
boolean_t seento;
|
||||
|
||||
|
@ -615,6 +632,7 @@ send_iterate_snap(zfs_handle_t *zhp, void *arg)
|
|||
{
|
||||
send_data_t *sd = arg;
|
||||
uint64_t guid = zhp->zfs_dmustats.dds_guid;
|
||||
uint64_t txg = zhp->zfs_dmustats.dds_creation_txg;
|
||||
char *snapname;
|
||||
nvlist_t *nv;
|
||||
boolean_t isfromsnap, istosnap, istosnapwithnofrom;
|
||||
|
@ -625,6 +643,17 @@ send_iterate_snap(zfs_handle_t *zhp, void *arg)
|
|||
istosnap = (sd->tosnap != NULL && (strcmp(sd->tosnap, snapname) == 0));
|
||||
istosnapwithnofrom = (istosnap && sd->fromsnap == NULL);
|
||||
|
||||
if (sd->tosnap_txg != 0 && txg > sd->tosnap_txg) {
|
||||
if (sd->verbose) {
|
||||
(void) fprintf(stderr, dgettext(TEXT_DOMAIN,
|
||||
"skipping snapshot %s because it was created "
|
||||
"after the destination snapshot (%s)\n"),
|
||||
zhp->zfs_name, sd->tosnap);
|
||||
}
|
||||
zfs_close(zhp);
|
||||
return (0);
|
||||
}
|
||||
|
||||
VERIFY(0 == nvlist_add_uint64(sd->parent_snaps, snapname, guid));
|
||||
/*
|
||||
* NB: if there is no fromsnap here (it's a newly created fs in
|
||||
|
@ -731,6 +760,31 @@ send_iterate_prop(zfs_handle_t *zhp, nvlist_t *nv)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* returns snapshot creation txg
|
||||
* and returns 0 if the snapshot does not exist
|
||||
*/
|
||||
static uint64_t
|
||||
get_snap_txg(libzfs_handle_t *hdl, const char *fs, const char *snap)
|
||||
{
|
||||
char name[ZFS_MAX_DATASET_NAME_LEN];
|
||||
uint64_t txg = 0;
|
||||
|
||||
if (fs == NULL || fs[0] == '\0' || snap == NULL || snap[0] == '\0')
|
||||
return (txg);
|
||||
|
||||
(void) snprintf(name, sizeof (name), "%s@%s", fs, snap);
|
||||
if (zfs_dataset_exists(hdl, name, ZFS_TYPE_SNAPSHOT)) {
|
||||
zfs_handle_t *zhp = zfs_open(hdl, name, ZFS_TYPE_SNAPSHOT);
|
||||
if (zhp != NULL) {
|
||||
txg = zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG);
|
||||
zfs_close(zhp);
|
||||
}
|
||||
}
|
||||
|
||||
return (txg);
|
||||
}
|
||||
|
||||
/*
|
||||
* recursively generate nvlists describing datasets. See comment
|
||||
* for the data structure send_data_t above for description of contents
|
||||
|
@ -743,9 +797,48 @@ send_iterate_fs(zfs_handle_t *zhp, void *arg)
|
|||
nvlist_t *nvfs, *nv;
|
||||
int rv = 0;
|
||||
uint64_t parent_fromsnap_guid_save = sd->parent_fromsnap_guid;
|
||||
uint64_t fromsnap_txg_save = sd->fromsnap_txg;
|
||||
uint64_t tosnap_txg_save = sd->tosnap_txg;
|
||||
uint64_t txg = zhp->zfs_dmustats.dds_creation_txg;
|
||||
uint64_t guid = zhp->zfs_dmustats.dds_guid;
|
||||
uint64_t fromsnap_txg, tosnap_txg;
|
||||
char guidstring[64];
|
||||
|
||||
fromsnap_txg = get_snap_txg(zhp->zfs_hdl, zhp->zfs_name, sd->fromsnap);
|
||||
if (fromsnap_txg != 0)
|
||||
sd->fromsnap_txg = fromsnap_txg;
|
||||
|
||||
tosnap_txg = get_snap_txg(zhp->zfs_hdl, zhp->zfs_name, sd->tosnap);
|
||||
if (tosnap_txg != 0)
|
||||
sd->tosnap_txg = tosnap_txg;
|
||||
|
||||
/*
|
||||
* on the send side, if the current dataset does not have tosnap,
|
||||
* perform two additional checks:
|
||||
*
|
||||
* - skip sending the current dataset if it was created later than
|
||||
* the parent tosnap
|
||||
* - return error if the current dataset was created earlier than
|
||||
* the parent tosnap
|
||||
*/
|
||||
if (sd->tosnap != NULL && tosnap_txg == 0) {
|
||||
if (sd->tosnap_txg != 0 && txg > sd->tosnap_txg) {
|
||||
if (sd->verbose) {
|
||||
(void) fprintf(stderr, dgettext(TEXT_DOMAIN,
|
||||
"skipping dataset %s: snapshot %s does "
|
||||
"not exist\n"), zhp->zfs_name, sd->tosnap);
|
||||
}
|
||||
} else {
|
||||
(void) fprintf(stderr, dgettext(TEXT_DOMAIN,
|
||||
"cannot send %s@%s%s: snapshot %s@%s does not "
|
||||
"exist\n"), sd->fsname, sd->tosnap, sd->recursive ?
|
||||
dgettext(TEXT_DOMAIN, " recursively") : "",
|
||||
zhp->zfs_name, sd->tosnap);
|
||||
rv = -1;
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
|
||||
VERIFY(0 == nvlist_alloc(&nvfs, NV_UNIQUE_NAME, 0));
|
||||
VERIFY(0 == nvlist_add_string(nvfs, "name", zhp->zfs_name));
|
||||
VERIFY(0 == nvlist_add_uint64(nvfs, "parentfromsnap",
|
||||
|
@ -754,8 +847,10 @@ send_iterate_fs(zfs_handle_t *zhp, void *arg)
|
|||
if (zhp->zfs_dmustats.dds_origin[0]) {
|
||||
zfs_handle_t *origin = zfs_open(zhp->zfs_hdl,
|
||||
zhp->zfs_dmustats.dds_origin, ZFS_TYPE_SNAPSHOT);
|
||||
if (origin == NULL)
|
||||
return (-1);
|
||||
if (origin == NULL) {
|
||||
rv = -1;
|
||||
goto out;
|
||||
}
|
||||
VERIFY(0 == nvlist_add_uint64(nvfs, "origin",
|
||||
origin->zfs_dmustats.dds_guid));
|
||||
}
|
||||
|
@ -786,7 +881,10 @@ send_iterate_fs(zfs_handle_t *zhp, void *arg)
|
|||
if (sd->recursive)
|
||||
rv = zfs_iter_filesystems(zhp, send_iterate_fs, sd);
|
||||
|
||||
out:
|
||||
sd->parent_fromsnap_guid = parent_fromsnap_guid_save;
|
||||
sd->fromsnap_txg = fromsnap_txg_save;
|
||||
sd->tosnap_txg = tosnap_txg_save;
|
||||
|
||||
zfs_close(zhp);
|
||||
return (rv);
|
||||
|
@ -794,7 +892,8 @@ send_iterate_fs(zfs_handle_t *zhp, void *arg)
|
|||
|
||||
static int
|
||||
gather_nvlist(libzfs_handle_t *hdl, const char *fsname, const char *fromsnap,
|
||||
const char *tosnap, boolean_t recursive, nvlist_t **nvlp, avl_tree_t **avlp)
|
||||
const char *tosnap, boolean_t recursive, boolean_t verbose,
|
||||
nvlist_t **nvlp, avl_tree_t **avlp)
|
||||
{
|
||||
zfs_handle_t *zhp;
|
||||
send_data_t sd = { 0 };
|
||||
|
@ -805,9 +904,11 @@ gather_nvlist(libzfs_handle_t *hdl, const char *fsname, const char *fromsnap,
|
|||
return (EZFS_BADTYPE);
|
||||
|
||||
VERIFY(0 == nvlist_alloc(&sd.fss, NV_UNIQUE_NAME, 0));
|
||||
sd.fsname = fsname;
|
||||
sd.fromsnap = fromsnap;
|
||||
sd.tosnap = tosnap;
|
||||
sd.recursive = recursive;
|
||||
sd.verbose = verbose;
|
||||
|
||||
if ((error = send_iterate_fs(zhp, &sd)) != 0) {
|
||||
nvlist_free(sd.fss);
|
||||
|
@ -1722,7 +1823,8 @@ zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap,
|
|||
}
|
||||
|
||||
err = gather_nvlist(zhp->zfs_hdl, zhp->zfs_name,
|
||||
fromsnap, tosnap, flags->replicate, &fss, &fsavl);
|
||||
fromsnap, tosnap, flags->replicate, flags->verbose,
|
||||
&fss, &fsavl);
|
||||
if (err)
|
||||
goto err_out;
|
||||
VERIFY(0 == nvlist_add_nvlist(hdrnv, "fss", fss));
|
||||
|
@ -2337,7 +2439,7 @@ again:
|
|||
VERIFY(0 == nvlist_alloc(&deleted, NV_UNIQUE_NAME, 0));
|
||||
|
||||
if ((error = gather_nvlist(hdl, tofs, fromsnap, NULL,
|
||||
recursive, &local_nv, &local_avl)) != 0)
|
||||
recursive, B_FALSE, &local_nv, &local_avl)) != 0)
|
||||
return (error);
|
||||
|
||||
/*
|
||||
|
@ -3386,7 +3488,7 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
|
|||
*/
|
||||
*cp = '\0';
|
||||
if (gather_nvlist(hdl, destsnap, NULL, NULL, B_FALSE,
|
||||
&local_nv, &local_avl) == 0) {
|
||||
B_FALSE, &local_nv, &local_avl) == 0) {
|
||||
*cp = '@';
|
||||
fs = fsavl_find(local_avl, drrb->drr_toguid, NULL);
|
||||
fsavl_destroy(local_avl);
|
||||
|
|
Loading…
Reference in New Issue