summaryrefslogtreecommitdiff
path: root/usr/src/lib/libzfs/common/libzfs_sendrecv.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/lib/libzfs/common/libzfs_sendrecv.c')
-rw-r--r--usr/src/lib/libzfs/common/libzfs_sendrecv.c369
1 files changed, 148 insertions, 221 deletions
diff --git a/usr/src/lib/libzfs/common/libzfs_sendrecv.c b/usr/src/lib/libzfs/common/libzfs_sendrecv.c
index 598709ff89..3093ab974d 100644
--- a/usr/src/lib/libzfs/common/libzfs_sendrecv.c
+++ b/usr/src/lib/libzfs/common/libzfs_sendrecv.c
@@ -21,7 +21,6 @@
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2011 by Delphix. All rights reserved.
*/
#include <assert.h>
@@ -772,6 +771,88 @@ gather_nvlist(libzfs_handle_t *hdl, const char *fsname, const char *fromsnap,
}
/*
+ * Routines for dealing with the sorted snapshot functionality
+ */
+typedef struct zfs_node {
+ zfs_handle_t *zn_handle;
+ avl_node_t zn_avlnode;
+} zfs_node_t;
+
+static int
+zfs_sort_snaps(zfs_handle_t *zhp, void *data)
+{
+ avl_tree_t *avl = data;
+ zfs_node_t *node;
+ zfs_node_t search;
+
+ search.zn_handle = zhp;
+ node = avl_find(avl, &search, NULL);
+ if (node) {
+ /*
+ * If this snapshot was renamed while we were creating the
+ * AVL tree, it's possible that we already inserted it under
+ * its old name. Remove the old handle before adding the new
+ * one.
+ */
+ zfs_close(node->zn_handle);
+ avl_remove(avl, node);
+ free(node);
+ }
+
+ node = zfs_alloc(zhp->zfs_hdl, sizeof (zfs_node_t));
+ node->zn_handle = zhp;
+ avl_add(avl, node);
+
+ return (0);
+}
+
+static int
+zfs_snapshot_compare(const void *larg, const void *rarg)
+{
+ zfs_handle_t *l = ((zfs_node_t *)larg)->zn_handle;
+ zfs_handle_t *r = ((zfs_node_t *)rarg)->zn_handle;
+ uint64_t lcreate, rcreate;
+
+ /*
+ * Sort them according to creation time. We use the hidden
+ * CREATETXG property to get an absolute ordering of snapshots.
+ */
+ lcreate = zfs_prop_get_int(l, ZFS_PROP_CREATETXG);
+ rcreate = zfs_prop_get_int(r, ZFS_PROP_CREATETXG);
+
+ if (lcreate < rcreate)
+ return (-1);
+ else if (lcreate > rcreate)
+ return (+1);
+ else
+ return (0);
+}
+
+int
+zfs_iter_snapshots_sorted(zfs_handle_t *zhp, zfs_iter_f callback, void *data)
+{
+ int ret = 0;
+ zfs_node_t *node;
+ avl_tree_t avl;
+ void *cookie = NULL;
+
+ avl_create(&avl, zfs_snapshot_compare,
+ sizeof (zfs_node_t), offsetof(zfs_node_t, zn_avlnode));
+
+ ret = zfs_iter_snapshots(zhp, zfs_sort_snaps, &avl);
+
+ for (node = avl_first(&avl); node != NULL; node = AVL_NEXT(&avl, node))
+ ret |= callback(node->zn_handle, data);
+
+ while ((node = avl_destroy_nodes(&avl, &cookie)) != NULL)
+ free(node);
+
+ avl_destroy(&avl);
+
+ return (ret);
+}
+
+/*
* Routines specific to "zfs send"
*/
typedef struct send_dump_data {
@@ -781,7 +862,7 @@ typedef struct send_dump_data {
char prevsnap[ZFS_MAXNAMELEN];
uint64_t prevsnap_obj;
boolean_t seenfrom, seento, replicate, doall, fromorigin;
- boolean_t verbose, noop, parsable;
+ boolean_t verbose;
int outfd;
boolean_t err;
nvlist_t *fss;
@@ -791,69 +872,8 @@ typedef struct send_dump_data {
nvlist_t *debugnv;
char holdtag[ZFS_MAXNAMELEN];
int cleanup_fd;
- uint64_t size;
} send_dump_data_t;
-static int
-estimate_ioctl(zfs_handle_t *zhp, uint64_t fromsnap_obj,
- boolean_t fromorigin, uint64_t *sizep)
-{
- zfs_cmd_t zc = { 0 };
- libzfs_handle_t *hdl = zhp->zfs_hdl;
-
- assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT);
- assert(fromsnap_obj == 0 || !fromorigin);
-
- (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
- zc.zc_obj = fromorigin;
- zc.zc_sendobj = zfs_prop_get_int(zhp, ZFS_PROP_OBJSETID);
- zc.zc_fromobj = fromsnap_obj;
- zc.zc_guid = 1; /* estimate flag */
-
- if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_SEND, &zc) != 0) {
- char errbuf[1024];
- (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
- "warning: cannot estimate space for '%s'"), zhp->zfs_name);
-
- switch (errno) {
- case EXDEV:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "not an earlier snapshot from the same fs"));
- return (zfs_error(hdl, EZFS_CROSSTARGET, errbuf));
-
- case ENOENT:
- if (zfs_dataset_exists(hdl, zc.zc_name,
- ZFS_TYPE_SNAPSHOT)) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "incremental source (@%s) does not exist"),
- zc.zc_value);
- }
- return (zfs_error(hdl, EZFS_NOENT, errbuf));
-
- case EDQUOT:
- case EFBIG:
- case EIO:
- case ENOLINK:
- case ENOSPC:
- case ENOSTR:
- case ENXIO:
- case EPIPE:
- case ERANGE:
- case EFAULT:
- case EROFS:
- zfs_error_aux(hdl, strerror(errno));
- return (zfs_error(hdl, EZFS_BADBACKUP, errbuf));
-
- default:
- return (zfs_standard_error(hdl, errno, errbuf));
- }
- }
-
- *sizep = zc.zc_objset_type;
-
- return (0);
-}
-
/*
* Dumps a backup of the given snapshot (incremental from fromsnap if it's not
* NULL) to the file descriptor specified by outfd.
@@ -881,7 +901,7 @@ dump_ioctl(zfs_handle_t *zhp, const char *fromsnap, uint64_t fromsnap_obj,
"fromsnap", fromsnap));
}
- if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_SEND, &zc) != 0) {
+ if (ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_SEND, &zc) != 0) {
char errbuf[1024];
(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
"warning: cannot send '%s'"), zhp->zfs_name);
@@ -894,6 +914,7 @@ dump_ioctl(zfs_handle_t *zhp, const char *fromsnap, uint64_t fromsnap_obj,
nvlist_free(thisdbg);
switch (errno) {
+
case EXDEV:
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"not an earlier snapshot from the same fs"));
@@ -943,9 +964,6 @@ hold_for_send(zfs_handle_t *zhp, send_dump_data_t *sdd)
assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT);
- if (sdd->noop)
- return (0);
-
/*
* zfs_send() only opens a cleanup_fd for sends that need it,
* e.g. replication and doall.
@@ -979,7 +997,7 @@ dump_snapshot(zfs_handle_t *zhp, void *arg)
send_dump_data_t *sdd = arg;
char *thissnap;
int err;
- boolean_t isfromsnap, istosnap, fromorigin;
+ boolean_t isfromsnap, istosnap;
boolean_t exclude = B_FALSE;
thissnap = strchr(zhp->zfs_name, '@') + 1;
@@ -1056,47 +1074,15 @@ dump_snapshot(zfs_handle_t *zhp, void *arg)
return (err);
}
- fromorigin = sdd->prevsnap[0] == '\0' &&
- (sdd->fromorigin || sdd->replicate);
-
+ /* send it */
if (sdd->verbose) {
- uint64_t size;
- err = estimate_ioctl(zhp, sdd->prevsnap_obj,
- fromorigin, &size);
-
- if (sdd->parsable) {
- if (sdd->prevsnap[0] != '\0') {
- (void) fprintf(stderr, "incremental\t%s\t%s",
- sdd->prevsnap, zhp->zfs_name);
- } else {
- (void) fprintf(stderr, "full\t%s",
- zhp->zfs_name);
- }
- } else {
- (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
- "send from @%s to %s"),
- sdd->prevsnap, zhp->zfs_name);
- }
- if (err == 0) {
- if (sdd->parsable) {
- (void) fprintf(stderr, "\t%llu\n",
- (longlong_t)size);
- } else {
- char buf[16];
- zfs_nicenum(size, buf, sizeof (buf));
- (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
- " estimated size is %s\n"), buf);
- }
- sdd->size += size;
- } else {
- (void) fprintf(stderr, "\n");
- }
+ (void) fprintf(stderr, "sending from @%s to %s\n",
+ sdd->prevsnap, zhp->zfs_name);
}
- if (!sdd->noop) {
- err = dump_ioctl(zhp, sdd->prevsnap, sdd->prevsnap_obj,
- fromorigin, sdd->outfd, sdd->debugnv);
- }
+ err = dump_ioctl(zhp, sdd->prevsnap, sdd->prevsnap_obj,
+ sdd->prevsnap[0] == '\0' && (sdd->fromorigin || sdd->replicate),
+ sdd->outfd, sdd->debugnv);
(void) strcpy(sdd->prevsnap, thissnap);
sdd->prevsnap_obj = zfs_prop_get_int(zhp, ZFS_PROP_OBJSETID);
@@ -1115,8 +1101,8 @@ dump_filesystem(zfs_handle_t *zhp, void *arg)
(void) snprintf(zc.zc_name, sizeof (zc.zc_name), "%s@%s",
zhp->zfs_name, sdd->tosnap);
if (ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0) {
- (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
- "WARNING: could not send %s@%s: does not exist\n"),
+ (void) fprintf(stderr, "WARNING: "
+ "could not send %s@%s: does not exist\n",
zhp->zfs_name, sdd->tosnap);
sdd->err = B_TRUE;
return (0);
@@ -1145,24 +1131,23 @@ dump_filesystem(zfs_handle_t *zhp, void *arg)
rv = zfs_iter_snapshots_sorted(zhp, dump_snapshot, arg);
if (!sdd->seenfrom) {
- (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
+ (void) fprintf(stderr,
"WARNING: could not send %s@%s:\n"
- "incremental source (%s@%s) does not exist\n"),
+ "incremental source (%s@%s) does not exist\n",
zhp->zfs_name, sdd->tosnap,
zhp->zfs_name, sdd->fromsnap);
sdd->err = B_TRUE;
} else if (!sdd->seento) {
if (sdd->fromsnap) {
- (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
+ (void) fprintf(stderr,
"WARNING: could not send %s@%s:\n"
"incremental source (%s@%s) "
- "is not earlier than it\n"),
+ "is not earlier than it\n",
zhp->zfs_name, sdd->tosnap,
zhp->zfs_name, sdd->fromsnap);
} else {
- (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
- "WARNING: "
- "could not send %s@%s: does not exist\n"),
+ (void) fprintf(stderr, "WARNING: "
+ "could not send %s@%s: does not exist\n",
zhp->zfs_name, sdd->tosnap);
}
sdd->err = B_TRUE;
@@ -1208,12 +1193,11 @@ again:
needagain = progress = B_FALSE;
for (fspair = nvlist_next_nvpair(sdd->fss, NULL); fspair;
fspair = nvlist_next_nvpair(sdd->fss, fspair)) {
- nvlist_t *fslist, *parent_nv;
+ nvlist_t *fslist;
char *fsname;
zfs_handle_t *zhp;
int err;
uint64_t origin_guid = 0;
- uint64_t parent_guid = 0;
VERIFY(nvpair_value_nvlist(fspair, &fslist) == 0);
if (nvlist_lookup_boolean(fslist, "sent") == 0)
@@ -1221,23 +1205,13 @@ again:
VERIFY(nvlist_lookup_string(fslist, "name", &fsname) == 0);
(void) nvlist_lookup_uint64(fslist, "origin", &origin_guid);
- (void) nvlist_lookup_uint64(fslist, "parentfromsnap",
- &parent_guid);
-
- if (parent_guid != 0) {
- parent_nv = fsavl_find(sdd->fsavl, parent_guid, NULL);
- if (!nvlist_exists(parent_nv, "sent")) {
- /* parent has not been sent; skip this one */
- needagain = B_TRUE;
- continue;
- }
- }
if (origin_guid != 0) {
nvlist_t *origin_nv = fsavl_find(sdd->fsavl,
origin_guid, NULL);
if (origin_nv != NULL &&
- !nvlist_exists(origin_nv, "sent")) {
+ nvlist_lookup_boolean(origin_nv,
+ "sent") == ENOENT) {
/*
* origin has not been sent yet;
* skip this clone.
@@ -1261,16 +1235,6 @@ again:
assert(progress);
goto again;
}
-
- /* clean out the sent flags in case we reuse this fss */
- for (fspair = nvlist_next_nvpair(sdd->fss, NULL); fspair;
- fspair = nvlist_next_nvpair(sdd->fss, fspair)) {
- nvlist_t *fslist;
-
- VERIFY(nvpair_value_nvlist(fspair, &fslist) == 0);
- (void) nvlist_remove_all(fslist, "sent");
- }
-
return (0);
}
@@ -1297,7 +1261,7 @@ zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap,
{
char errbuf[1024];
send_dump_data_t sdd = { 0 };
- int err = 0;
+ int err;
nvlist_t *fss = NULL;
avl_tree_t *fsavl = NULL;
static uint64_t holdseq;
@@ -1325,12 +1289,12 @@ zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap,
}
}
- if (!flags.noop && zfs_spa_version(zhp, &spa_version) == 0 &&
+ if (zfs_spa_version(zhp, &spa_version) == 0 &&
spa_version >= SPA_VERSION_USERREFS &&
(flags.doall || flags.replicate))
holdsnaps = B_TRUE;
- if (flags.dedup && !flags.noop) {
+ if (flags.dedup) {
featureflags |= (DMU_BACKUP_FEATURE_DEDUP |
DMU_BACKUP_FEATURE_DEDUPPROPS);
if (err = pipe(pipefd)) {
@@ -1388,48 +1352,43 @@ zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap,
}
}
- if (!flags.noop) {
- /* write first begin record */
- drr.drr_type = DRR_BEGIN;
- drr.drr_u.drr_begin.drr_magic = DMU_BACKUP_MAGIC;
- DMU_SET_STREAM_HDRTYPE(drr.drr_u.drr_begin.
- drr_versioninfo, DMU_COMPOUNDSTREAM);
- DMU_SET_FEATUREFLAGS(drr.drr_u.drr_begin.
- drr_versioninfo, featureflags);
- (void) snprintf(drr.drr_u.drr_begin.drr_toname,
- sizeof (drr.drr_u.drr_begin.drr_toname),
- "%s@%s", zhp->zfs_name, tosnap);
- drr.drr_payloadlen = buflen;
- err = cksum_and_write(&drr, sizeof (drr), &zc, outfd);
-
- /* write header nvlist */
- if (err != -1 && packbuf != NULL) {
- err = cksum_and_write(packbuf, buflen, &zc,
- outfd);
- }
- free(packbuf);
+ /* write first begin record */
+ drr.drr_type = DRR_BEGIN;
+ drr.drr_u.drr_begin.drr_magic = DMU_BACKUP_MAGIC;
+ DMU_SET_STREAM_HDRTYPE(drr.drr_u.drr_begin.drr_versioninfo,
+ DMU_COMPOUNDSTREAM);
+ DMU_SET_FEATUREFLAGS(drr.drr_u.drr_begin.drr_versioninfo,
+ featureflags);
+ (void) snprintf(drr.drr_u.drr_begin.drr_toname,
+ sizeof (drr.drr_u.drr_begin.drr_toname),
+ "%s@%s", zhp->zfs_name, tosnap);
+ drr.drr_payloadlen = buflen;
+ err = cksum_and_write(&drr, sizeof (drr), &zc, outfd);
+
+ /* write header nvlist */
+ if (err != -1 && packbuf != NULL) {
+ err = cksum_and_write(packbuf, buflen, &zc, outfd);
+ }
+ free(packbuf);
+ if (err == -1) {
+ fsavl_destroy(fsavl);
+ nvlist_free(fss);
+ err = errno;
+ goto stderr_out;
+ }
+
+ /* write end record */
+ if (err != -1) {
+ bzero(&drr, sizeof (drr));
+ drr.drr_type = DRR_END;
+ drr.drr_u.drr_end.drr_checksum = zc;
+ err = write(outfd, &drr, sizeof (drr));
if (err == -1) {
fsavl_destroy(fsavl);
nvlist_free(fss);
err = errno;
goto stderr_out;
}
-
- /* write end record */
- if (err != -1) {
- bzero(&drr, sizeof (drr));
- drr.drr_type = DRR_END;
- drr.drr_u.drr_end.drr_checksum = zc;
- err = write(outfd, &drr, sizeof (drr));
- if (err == -1) {
- fsavl_destroy(fsavl);
- nvlist_free(fss);
- err = errno;
- goto stderr_out;
- }
- }
- if (err != -1)
- err = 0;
}
}
@@ -1446,8 +1405,6 @@ zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap,
sdd.fss = fss;
sdd.fsavl = fsavl;
sdd.verbose = flags.verbose;
- sdd.parsable = flags.parsable;
- sdd.noop = flags.noop;
sdd.filter_cb = filter_func;
sdd.filter_cb_arg = cb_arg;
if (debugnvp)
@@ -1464,26 +1421,6 @@ zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap,
} else {
sdd.cleanup_fd = -1;
}
- if (flags.verbose) {
- /*
- * Do a verbose no-op dry run to get all the verbose output
- * before generating any data. Then do a non-verbose real
- * run to generate the streams.
- */
- sdd.noop = B_TRUE;
- err = dump_filesystems(zhp, &sdd);
- sdd.noop = flags.noop;
- sdd.verbose = B_FALSE;
- if (flags.parsable) {
- (void) fprintf(stderr, "size\t%llu\n",
- (longlong_t)sdd.size);
- } else {
- char buf[16];
- zfs_nicenum(sdd.size, buf, sizeof (buf));
- (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
- "total estimated size is %s\n"), buf);
- }
- }
err = dump_filesystems(zhp, &sdd);
fsavl_destroy(fsavl);
nvlist_free(fss);
@@ -1498,7 +1435,7 @@ zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap,
sdd.cleanup_fd = -1;
}
- if (!flags.noop && (flags.replicate || flags.doall || flags.props)) {
+ if (flags.replicate || flags.doall || flags.props) {
/*
* write final end record. NB: want to do this even if
* there was some error, because it might not be totally
@@ -1778,8 +1715,7 @@ guid_to_name(libzfs_handle_t *hdl, const char *parent, uint64_t guid,
}
/*
- * Return +1 if guid1 is before guid2, 0 if they are the same, and -1 if
- * guid1 is after guid2.
+ * Return true if dataset guid1 is created before guid2.
*/
static int
created_before(libzfs_handle_t *hdl, avl_tree_t *avl,
@@ -1789,8 +1725,7 @@ created_before(libzfs_handle_t *hdl, avl_tree_t *avl,
char *fsname, *snapname;
char buf[ZFS_MAXNAMELEN];
int rv;
- zfs_handle_t *guid1hdl, *guid2hdl;
- uint64_t create1, create2;
+ zfs_node_t zn1, zn2;
if (guid2 == 0)
return (0);
@@ -1800,31 +1735,23 @@ created_before(libzfs_handle_t *hdl, avl_tree_t *avl,
nvfs = fsavl_find(avl, guid1, &snapname);
VERIFY(0 == nvlist_lookup_string(nvfs, "name", &fsname));
(void) snprintf(buf, sizeof (buf), "%s@%s", fsname, snapname);
- guid1hdl = zfs_open(hdl, buf, ZFS_TYPE_SNAPSHOT);
- if (guid1hdl == NULL)
+ zn1.zn_handle = zfs_open(hdl, buf, ZFS_TYPE_SNAPSHOT);
+ if (zn1.zn_handle == NULL)
return (-1);
nvfs = fsavl_find(avl, guid2, &snapname);
VERIFY(0 == nvlist_lookup_string(nvfs, "name", &fsname));
(void) snprintf(buf, sizeof (buf), "%s@%s", fsname, snapname);
- guid2hdl = zfs_open(hdl, buf, ZFS_TYPE_SNAPSHOT);
- if (guid2hdl == NULL) {
- zfs_close(guid1hdl);
+ zn2.zn_handle = zfs_open(hdl, buf, ZFS_TYPE_SNAPSHOT);
+ if (zn2.zn_handle == NULL) {
+ zfs_close(zn2.zn_handle);
return (-1);
}
- create1 = zfs_prop_get_int(guid1hdl, ZFS_PROP_CREATETXG);
- create2 = zfs_prop_get_int(guid2hdl, ZFS_PROP_CREATETXG);
-
- if (create1 < create2)
- rv = -1;
- else if (create1 > create2)
- rv = +1;
- else
- rv = 0;
+ rv = (zfs_snapshot_compare(&zn1, &zn2) == -1);
- zfs_close(guid1hdl);
- zfs_close(guid2hdl);
+ zfs_close(zn1.zn_handle);
+ zfs_close(zn2.zn_handle);
return (rv);
}