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.
|
* Routines for dealing with the giant nvlist of fs-nvlists, etc.
|
||||||
*/
|
*/
|
||||||
typedef struct send_data {
|
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 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 *parent_snaps;
|
||||||
nvlist_t *fss;
|
nvlist_t *fss;
|
||||||
nvlist_t *snapprops;
|
nvlist_t *snapprops;
|
||||||
|
|
||||||
|
/* send-receive configuration, does not change during traversal */
|
||||||
|
const char *fsname;
|
||||||
const char *fromsnap;
|
const char *fromsnap;
|
||||||
const char *tosnap;
|
const char *tosnap;
|
||||||
boolean_t recursive;
|
boolean_t recursive;
|
||||||
|
boolean_t verbose;
|
||||||
boolean_t seenfrom;
|
boolean_t seenfrom;
|
||||||
boolean_t seento;
|
boolean_t seento;
|
||||||
|
|
||||||
|
@ -615,6 +632,7 @@ send_iterate_snap(zfs_handle_t *zhp, void *arg)
|
||||||
{
|
{
|
||||||
send_data_t *sd = arg;
|
send_data_t *sd = arg;
|
||||||
uint64_t guid = zhp->zfs_dmustats.dds_guid;
|
uint64_t guid = zhp->zfs_dmustats.dds_guid;
|
||||||
|
uint64_t txg = zhp->zfs_dmustats.dds_creation_txg;
|
||||||
char *snapname;
|
char *snapname;
|
||||||
nvlist_t *nv;
|
nvlist_t *nv;
|
||||||
boolean_t isfromsnap, istosnap, istosnapwithnofrom;
|
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));
|
istosnap = (sd->tosnap != NULL && (strcmp(sd->tosnap, snapname) == 0));
|
||||||
istosnapwithnofrom = (istosnap && sd->fromsnap == NULL);
|
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));
|
VERIFY(0 == nvlist_add_uint64(sd->parent_snaps, snapname, guid));
|
||||||
/*
|
/*
|
||||||
* NB: if there is no fromsnap here (it's a newly created fs in
|
* 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
|
* recursively generate nvlists describing datasets. See comment
|
||||||
* for the data structure send_data_t above for description of contents
|
* 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;
|
nvlist_t *nvfs, *nv;
|
||||||
int rv = 0;
|
int rv = 0;
|
||||||
uint64_t parent_fromsnap_guid_save = sd->parent_fromsnap_guid;
|
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 guid = zhp->zfs_dmustats.dds_guid;
|
||||||
|
uint64_t fromsnap_txg, tosnap_txg;
|
||||||
char guidstring[64];
|
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_alloc(&nvfs, NV_UNIQUE_NAME, 0));
|
||||||
VERIFY(0 == nvlist_add_string(nvfs, "name", zhp->zfs_name));
|
VERIFY(0 == nvlist_add_string(nvfs, "name", zhp->zfs_name));
|
||||||
VERIFY(0 == nvlist_add_uint64(nvfs, "parentfromsnap",
|
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]) {
|
if (zhp->zfs_dmustats.dds_origin[0]) {
|
||||||
zfs_handle_t *origin = zfs_open(zhp->zfs_hdl,
|
zfs_handle_t *origin = zfs_open(zhp->zfs_hdl,
|
||||||
zhp->zfs_dmustats.dds_origin, ZFS_TYPE_SNAPSHOT);
|
zhp->zfs_dmustats.dds_origin, ZFS_TYPE_SNAPSHOT);
|
||||||
if (origin == NULL)
|
if (origin == NULL) {
|
||||||
return (-1);
|
rv = -1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
VERIFY(0 == nvlist_add_uint64(nvfs, "origin",
|
VERIFY(0 == nvlist_add_uint64(nvfs, "origin",
|
||||||
origin->zfs_dmustats.dds_guid));
|
origin->zfs_dmustats.dds_guid));
|
||||||
}
|
}
|
||||||
|
@ -786,7 +881,10 @@ send_iterate_fs(zfs_handle_t *zhp, void *arg)
|
||||||
if (sd->recursive)
|
if (sd->recursive)
|
||||||
rv = zfs_iter_filesystems(zhp, send_iterate_fs, sd);
|
rv = zfs_iter_filesystems(zhp, send_iterate_fs, sd);
|
||||||
|
|
||||||
|
out:
|
||||||
sd->parent_fromsnap_guid = parent_fromsnap_guid_save;
|
sd->parent_fromsnap_guid = parent_fromsnap_guid_save;
|
||||||
|
sd->fromsnap_txg = fromsnap_txg_save;
|
||||||
|
sd->tosnap_txg = tosnap_txg_save;
|
||||||
|
|
||||||
zfs_close(zhp);
|
zfs_close(zhp);
|
||||||
return (rv);
|
return (rv);
|
||||||
|
@ -794,7 +892,8 @@ send_iterate_fs(zfs_handle_t *zhp, void *arg)
|
||||||
|
|
||||||
static int
|
static int
|
||||||
gather_nvlist(libzfs_handle_t *hdl, const char *fsname, const char *fromsnap,
|
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;
|
zfs_handle_t *zhp;
|
||||||
send_data_t sd = { 0 };
|
send_data_t sd = { 0 };
|
||||||
|
@ -805,9 +904,11 @@ gather_nvlist(libzfs_handle_t *hdl, const char *fsname, const char *fromsnap,
|
||||||
return (EZFS_BADTYPE);
|
return (EZFS_BADTYPE);
|
||||||
|
|
||||||
VERIFY(0 == nvlist_alloc(&sd.fss, NV_UNIQUE_NAME, 0));
|
VERIFY(0 == nvlist_alloc(&sd.fss, NV_UNIQUE_NAME, 0));
|
||||||
|
sd.fsname = fsname;
|
||||||
sd.fromsnap = fromsnap;
|
sd.fromsnap = fromsnap;
|
||||||
sd.tosnap = tosnap;
|
sd.tosnap = tosnap;
|
||||||
sd.recursive = recursive;
|
sd.recursive = recursive;
|
||||||
|
sd.verbose = verbose;
|
||||||
|
|
||||||
if ((error = send_iterate_fs(zhp, &sd)) != 0) {
|
if ((error = send_iterate_fs(zhp, &sd)) != 0) {
|
||||||
nvlist_free(sd.fss);
|
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,
|
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)
|
if (err)
|
||||||
goto err_out;
|
goto err_out;
|
||||||
VERIFY(0 == nvlist_add_nvlist(hdrnv, "fss", fss));
|
VERIFY(0 == nvlist_add_nvlist(hdrnv, "fss", fss));
|
||||||
|
@ -2337,7 +2439,7 @@ again:
|
||||||
VERIFY(0 == nvlist_alloc(&deleted, NV_UNIQUE_NAME, 0));
|
VERIFY(0 == nvlist_alloc(&deleted, NV_UNIQUE_NAME, 0));
|
||||||
|
|
||||||
if ((error = gather_nvlist(hdl, tofs, fromsnap, NULL,
|
if ((error = gather_nvlist(hdl, tofs, fromsnap, NULL,
|
||||||
recursive, &local_nv, &local_avl)) != 0)
|
recursive, B_FALSE, &local_nv, &local_avl)) != 0)
|
||||||
return (error);
|
return (error);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -3386,7 +3488,7 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
|
||||||
*/
|
*/
|
||||||
*cp = '\0';
|
*cp = '\0';
|
||||||
if (gather_nvlist(hdl, destsnap, NULL, NULL, B_FALSE,
|
if (gather_nvlist(hdl, destsnap, NULL, NULL, B_FALSE,
|
||||||
&local_nv, &local_avl) == 0) {
|
B_FALSE, &local_nv, &local_avl) == 0) {
|
||||||
*cp = '@';
|
*cp = '@';
|
||||||
fs = fsavl_find(local_avl, drrb->drr_toguid, NULL);
|
fs = fsavl_find(local_avl, drrb->drr_toguid, NULL);
|
||||||
fsavl_destroy(local_avl);
|
fsavl_destroy(local_avl);
|
||||||
|
|
Loading…
Reference in New Issue