aboutsummaryrefslogtreecommitdiff
path: root/lib/libzfs/libzfs_sendrecv.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libzfs/libzfs_sendrecv.c')
-rw-r--r--lib/libzfs/libzfs_sendrecv.c114
1 files changed, 108 insertions, 6 deletions
diff --git a/lib/libzfs/libzfs_sendrecv.c b/lib/libzfs/libzfs_sendrecv.c
index 448ee15ec213..95998638913a 100644
--- a/lib/libzfs/libzfs_sendrecv.c
+++ b/lib/libzfs/libzfs_sendrecv.c
@@ -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
@@ -732,6 +761,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
* of the nvlist.
@@ -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);