diff options
author | Allan Jude <allan@klarasystems.com> | 2020-12-17 18:05:27 -0600 |
---|---|---|
committer | Jason King <jason.king@joyent.com> | 2021-02-19 09:11:37 -0600 |
commit | c5286370b84c690a18e8100a5237a1000d7e29c6 (patch) | |
tree | 7bd38e16d9fc556156b5ebaab7471e1d469ae1f5 | |
parent | 3f423849d2afa35883f6feac478a08bdc8a1e68e (diff) | |
download | illumos-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.h | 1 | ||||
-rw-r--r-- | usr/src/lib/libzfs/common/libzfs_sendrecv.c | 46 | ||||
-rw-r--r-- | usr/src/lib/libzfs/common/libzfs_util.c | 16 |
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); } |