summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAllan Jude <allan@klarasystems.com>2020-12-17 18:05:27 -0600
committerJason King <jason.king@joyent.com>2021-02-19 09:11:37 -0600
commitc5286370b84c690a18e8100a5237a1000d7e29c6 (patch)
tree7bd38e16d9fc556156b5ebaab7471e1d469ae1f5
parent3f423849d2afa35883f6feac478a08bdc8a1e68e (diff)
downloadillumos-gate-c5286370b84c690a18e8100a5237a1000d7e29c6.tar.gz
6729 incremental replication stream of a fs tree with lots of snapshots trips assert in zfs recv
Portions contributed by: Jason King <jason.king@joyent.com> Reviewed by: Paul Dagnelie <pcd@delphix.com> Reviewed by: Kjeld Schouten <kjeld@schouten-lebbing.nl> Reviewed by: Brian Behlendorf <behlendorf1@llnl.gov> Reviewed by: Matthew Ahrens <mahrens@delphix.com> Reviewed by: Andy Fiddaman <andy@omnios.org> Reviewed by: Toomas Soome <tsoome@me.com> Approved by: Gordon Ross <gordon.w.ross@gmail.com>
-rw-r--r--usr/src/lib/libzfs/common/libzfs_impl.h1
-rw-r--r--usr/src/lib/libzfs/common/libzfs_sendrecv.c46
-rw-r--r--usr/src/lib/libzfs/common/libzfs_util.c16
3 files changed, 60 insertions, 3 deletions
diff --git a/usr/src/lib/libzfs/common/libzfs_impl.h b/usr/src/lib/libzfs/common/libzfs_impl.h
index 85bcc47e97..9facac8a93 100644
--- a/usr/src/lib/libzfs/common/libzfs_impl.h
+++ b/usr/src/lib/libzfs/common/libzfs_impl.h
@@ -93,6 +93,7 @@ struct libzfs_handle {
boolean_t libzfs_prop_debug;
di_devlink_handle_t libzfs_devlink;
regex_t libzfs_urire;
+ uint64_t libzfs_max_nvlist;
};
struct zfs_handle {
diff --git a/usr/src/lib/libzfs/common/libzfs_sendrecv.c b/usr/src/lib/libzfs/common/libzfs_sendrecv.c
index 2bfb49875d..7bdea7dc55 100644
--- a/usr/src/lib/libzfs/common/libzfs_sendrecv.c
+++ b/usr/src/lib/libzfs/common/libzfs_sendrecv.c
@@ -929,6 +929,18 @@ send_iterate_fs(zfs_handle_t *zhp, void *arg)
nvlist_free(sd->snapprops);
nvlist_free(sd->snapholds);
+ /* Do not allow the size of the properties list to exceed the limit */
+ if ((fnvlist_size(nvfs) + fnvlist_size(sd->fss)) >
+ zhp->zfs_hdl->libzfs_max_nvlist) {
+ (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
+ "warning: cannot send %s@%s: the size of the list of "
+ "snapshots and properties is too large to be received "
+ "successfully.\n"
+ "Select a smaller number of snapshots to send.\n"),
+ zhp->zfs_name, sd->tosnap);
+ rv = EZFS_NOSPC;
+ goto out;
+ }
/* add this fs to nvlist */
(void) snprintf(guidstring, sizeof (guidstring),
"0x%llx", (longlong_t)guid);
@@ -1926,8 +1938,31 @@ zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap,
flags->verbose, flags->backup,
flags->holds, flags->props, &fss,
&fsavl);
- if (err)
+ if (err) {
+ nvlist_free(hdrnv);
+ goto err_out;
+ }
+
+ /*
+ * Do not allow the size of the properties list to
+ * exceed the limit
+ */
+ if ((fnvlist_size(fss) + fnvlist_size(hdrnv)) >
+ zhp->zfs_hdl->libzfs_max_nvlist) {
+ (void) snprintf(errbuf, sizeof (errbuf),
+ dgettext(TEXT_DOMAIN,
+ "warning: cannot send '%s': "
+ "the size of the list of snapshots and "
+ "properties is too large to be received "
+ "successfully.\n"
+ "Select a smaller number of snapshots to "
+ "send.\n"),
+ zhp->zfs_name);
+ nvlist_free(hdrnv);
+ err = zfs_error(zhp->zfs_hdl, EZFS_NOSPC,
+ errbuf);
goto err_out;
+ }
VERIFY(0 == nvlist_add_nvlist(hdrnv, "fss", fss));
err = nvlist_pack(hdrnv, &packbuf, &buflen,
NV_ENCODE_XDR, 0);
@@ -2198,8 +2233,6 @@ recv_read(libzfs_handle_t *hdl, int fd, void *buf, int ilen,
int rv;
int len = ilen;
- assert(ilen <= SPA_MAXBLOCKSIZE);
-
do {
rv = read(fd, cp, len);
cp += rv;
@@ -2233,6 +2266,12 @@ recv_read_nvlist(libzfs_handle_t *hdl, int fd, int len, nvlist_t **nvp,
if (buf == NULL)
return (ENOMEM);
+ if (len > hdl->libzfs_max_nvlist) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "nvlist too large"));
+ free(buf);
+ return (ENOMEM);
+ }
+
err = recv_read(hdl, fd, buf, len, byteswap, zc);
if (err != 0) {
free(buf);
@@ -3325,6 +3364,7 @@ recv_skip(libzfs_handle_t *hdl, int fd, boolean_t byteswap)
}
uint64_t payload_size =
DRR_WRITE_PAYLOAD_SIZE(&drr->drr_u.drr_write);
+ assert(payload_size <= SPA_MAXBLOCKSIZE);
(void) recv_read(hdl, fd, buf,
payload_size, B_FALSE, NULL);
break;
diff --git a/usr/src/lib/libzfs/common/libzfs_util.c b/usr/src/lib/libzfs/common/libzfs_util.c
index 64bfa6981c..40101befaf 100644
--- a/usr/src/lib/libzfs/common/libzfs_util.c
+++ b/usr/src/lib/libzfs/common/libzfs_util.c
@@ -675,6 +675,8 @@ libzfs_handle_t *
libzfs_init(void)
{
libzfs_handle_t *hdl;
+ int error;
+ char *env;
if ((hdl = calloc(1, sizeof (libzfs_handle_t))) == NULL) {
return (NULL);
@@ -717,6 +719,20 @@ libzfs_init(void)
if (getenv("ZFS_PROP_DEBUG") != NULL) {
hdl->libzfs_prop_debug = B_TRUE;
}
+ if ((env = getenv("ZFS_SENDRECV_MAX_NVLIST")) != NULL) {
+ if ((error = zfs_nicestrtonum(hdl, env,
+ &hdl->libzfs_max_nvlist))) {
+ errno = error;
+ (void) close(hdl->libzfs_fd);
+ (void) fclose(hdl->libzfs_mnttab);
+ (void) fclose(hdl->libzfs_sharetab);
+ regfree(&hdl->libzfs_urire);
+ free(hdl);
+ return (NULL);
+ }
+ } else {
+ hdl->libzfs_max_nvlist = (SPA_MAXBLOCKSIZE * 4);
+ }
return (hdl);
}