Don't assert on nvlists larger than SPA_MAXBLOCKSIZE
Originally we asserted that all reads are less than SPA_MAXBLOCKSIZE However, nvlists are not ZFS records, and are not limited to SPA_MAXBLOCKSIZE. Add a new environment variable, ZFS_SENDRECV_MAX_NVLIST, to allow the user to specify the maximum size of the nvlist that can be sent or received. Default value: 4 * SPA_MAXBLOCKSIZE (64 MB) Modify libzfs send routines to return a useful error if the send stream will generate an nvlist that is beyond the maximum size. Modify libzfs recv routines to add an explicit error message if the nvlist is too large, rather than abort()ing. Move the change the assert() to only trigger on data records 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> Signed-off-by: Allan Jude <allan@klarasystems.com> Closes #9616
This commit is contained in:
parent
87688b686b
commit
7a6c12fd6a
|
@ -71,6 +71,7 @@ struct libzfs_handle {
|
||||||
char libzfs_chassis_id[256];
|
char libzfs_chassis_id[256];
|
||||||
boolean_t libzfs_prop_debug;
|
boolean_t libzfs_prop_debug;
|
||||||
regex_t libzfs_urire;
|
regex_t libzfs_urire;
|
||||||
|
uint64_t libzfs_max_nvlist;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct zfs_handle {
|
struct zfs_handle {
|
||||||
|
|
|
@ -610,6 +610,18 @@ send_iterate_fs(zfs_handle_t *zhp, void *arg)
|
||||||
fnvlist_free(sd->snapprops);
|
fnvlist_free(sd->snapprops);
|
||||||
fnvlist_free(sd->snapholds);
|
fnvlist_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 */
|
/* add this fs to nvlist */
|
||||||
(void) snprintf(guidstring, sizeof (guidstring),
|
(void) snprintf(guidstring, sizeof (guidstring),
|
||||||
"0x%llx", (longlong_t)guid);
|
"0x%llx", (longlong_t)guid);
|
||||||
|
@ -2015,6 +2027,21 @@ send_prelim_records(zfs_handle_t *zhp, const char *from, int fd,
|
||||||
return (zfs_error(zhp->zfs_hdl, EZFS_BADBACKUP,
|
return (zfs_error(zhp->zfs_hdl, EZFS_BADBACKUP,
|
||||||
errbuf));
|
errbuf));
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
* 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);
|
||||||
|
return (zfs_error(zhp->zfs_hdl, EZFS_NOSPC,
|
||||||
|
errbuf));
|
||||||
|
}
|
||||||
fnvlist_add_nvlist(hdrnv, "fss", fss);
|
fnvlist_add_nvlist(hdrnv, "fss", fss);
|
||||||
VERIFY0(nvlist_pack(hdrnv, &packbuf, &buflen, NV_ENCODE_XDR,
|
VERIFY0(nvlist_pack(hdrnv, &packbuf, &buflen, NV_ENCODE_XDR,
|
||||||
0));
|
0));
|
||||||
|
@ -2578,8 +2605,6 @@ recv_read(libzfs_handle_t *hdl, int fd, void *buf, int ilen,
|
||||||
int rv;
|
int rv;
|
||||||
int len = ilen;
|
int len = ilen;
|
||||||
|
|
||||||
assert(ilen <= SPA_MAXBLOCKSIZE);
|
|
||||||
|
|
||||||
do {
|
do {
|
||||||
rv = read(fd, cp, len);
|
rv = read(fd, cp, len);
|
||||||
cp += rv;
|
cp += rv;
|
||||||
|
@ -2613,6 +2638,11 @@ recv_read_nvlist(libzfs_handle_t *hdl, int fd, int len, nvlist_t **nvp,
|
||||||
if (buf == NULL)
|
if (buf == NULL)
|
||||||
return (ENOMEM);
|
return (ENOMEM);
|
||||||
|
|
||||||
|
if (len > hdl->libzfs_max_nvlist) {
|
||||||
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "nvlist too large"));
|
||||||
|
return (ENOMEM);
|
||||||
|
}
|
||||||
|
|
||||||
err = recv_read(hdl, fd, buf, len, byteswap, zc);
|
err = recv_read(hdl, fd, buf, len, byteswap, zc);
|
||||||
if (err != 0) {
|
if (err != 0) {
|
||||||
free(buf);
|
free(buf);
|
||||||
|
@ -3783,6 +3813,7 @@ recv_skip(libzfs_handle_t *hdl, int fd, boolean_t byteswap)
|
||||||
}
|
}
|
||||||
payload_size =
|
payload_size =
|
||||||
DRR_WRITE_PAYLOAD_SIZE(&drr->drr_u.drr_write);
|
DRR_WRITE_PAYLOAD_SIZE(&drr->drr_u.drr_write);
|
||||||
|
assert(payload_size <= SPA_MAXBLOCKSIZE);
|
||||||
(void) recv_read(hdl, fd, buf,
|
(void) recv_read(hdl, fd, buf,
|
||||||
payload_size, B_FALSE, NULL);
|
payload_size, B_FALSE, NULL);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -1008,6 +1008,7 @@ libzfs_init(void)
|
||||||
{
|
{
|
||||||
libzfs_handle_t *hdl;
|
libzfs_handle_t *hdl;
|
||||||
int error;
|
int error;
|
||||||
|
char *env;
|
||||||
|
|
||||||
error = libzfs_load_module();
|
error = libzfs_load_module();
|
||||||
if (error) {
|
if (error) {
|
||||||
|
@ -1055,6 +1056,15 @@ libzfs_init(void)
|
||||||
if (getenv("ZFS_PROP_DEBUG") != NULL) {
|
if (getenv("ZFS_PROP_DEBUG") != NULL) {
|
||||||
hdl->libzfs_prop_debug = B_TRUE;
|
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;
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
hdl->libzfs_max_nvlist = (SPA_MAXBLOCKSIZE * 4);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* For testing, remove some settable properties and features
|
* For testing, remove some settable properties and features
|
||||||
|
|
Loading…
Reference in New Issue