Skip to content

Commit

Permalink
6729 incremental replication stream of a fs tree with lots of snapsho…
Browse files Browse the repository at this point in the history
…ts 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>
  • Loading branch information
allanjude authored and Jason King committed Feb 19, 2021
1 parent 3f42384 commit c528637
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 3 deletions.
1 change: 1 addition & 0 deletions usr/src/lib/libzfs/common/libzfs_impl.h
Expand Up @@ -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 {
Expand Down
46 changes: 43 additions & 3 deletions usr/src/lib/libzfs/common/libzfs_sendrecv.c
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down
16 changes: 16 additions & 0 deletions usr/src/lib/libzfs/common/libzfs_util.c
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
}
Expand Down

0 comments on commit c528637

Please sign in to comment.