nbd: Less allocation during NBD_OPT_LIST
Since we know that the maximum name we are willing to accept is small enough to stack-allocate, rework the iteration over NBD_OPT_LIST responses to reuse a stack buffer rather than allocating every time. Furthermore, we don't even have to allocate if we know the server's length doesn't match what we are searching for. Signed-off-by: Eric Blake <eblake@redhat.com> Message-Id: <1476469998-28592-12-git-send-email-eblake@redhat.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
parent
7d3123e177
commit
75368aab9b
107
nbd/client.c
107
nbd/client.c
|
@ -254,19 +254,28 @@ static int nbd_handle_reply_err(QIOChannel *ioc, nbd_opt_reply *reply,
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int nbd_receive_list(QIOChannel *ioc, char **name, Error **errp)
|
/* Process another portion of the NBD_OPT_LIST reply. Set *@match if
|
||||||
|
* the current reply matches @want or if the server does not support
|
||||||
|
* NBD_OPT_LIST, otherwise leave @match alone. Return 0 if iteration
|
||||||
|
* is complete, positive if more replies are expected, or negative
|
||||||
|
* with @errp set if an unrecoverable error occurred. */
|
||||||
|
static int nbd_receive_list(QIOChannel *ioc, const char *want, bool *match,
|
||||||
|
Error **errp)
|
||||||
{
|
{
|
||||||
nbd_opt_reply reply;
|
nbd_opt_reply reply;
|
||||||
uint32_t len;
|
uint32_t len;
|
||||||
uint32_t namelen;
|
uint32_t namelen;
|
||||||
|
char name[NBD_MAX_NAME_SIZE + 1];
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
*name = NULL;
|
|
||||||
if (nbd_receive_option_reply(ioc, NBD_OPT_LIST, &reply, errp) < 0) {
|
if (nbd_receive_option_reply(ioc, NBD_OPT_LIST, &reply, errp) < 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
error = nbd_handle_reply_err(ioc, &reply, errp);
|
error = nbd_handle_reply_err(ioc, &reply, errp);
|
||||||
if (error <= 0) {
|
if (error <= 0) {
|
||||||
|
/* The server did not support NBD_OPT_LIST, so set *match on
|
||||||
|
* the assumption that any name will be accepted. */
|
||||||
|
*match = true;
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
len = reply.length;
|
len = reply.length;
|
||||||
|
@ -277,7 +286,14 @@ static int nbd_receive_list(QIOChannel *ioc, char **name, Error **errp)
|
||||||
nbd_send_opt_abort(ioc);
|
nbd_send_opt_abort(ioc);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
} else if (reply.type == NBD_REP_SERVER) {
|
return 0;
|
||||||
|
} else if (reply.type != NBD_REP_SERVER) {
|
||||||
|
error_setg(errp, "Unexpected reply type %" PRIx32 " expected %x",
|
||||||
|
reply.type, NBD_REP_SERVER);
|
||||||
|
nbd_send_opt_abort(ioc);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
if (len < sizeof(namelen) || len > NBD_MAX_BUFFER_SIZE) {
|
if (len < sizeof(namelen) || len > NBD_MAX_BUFFER_SIZE) {
|
||||||
error_setg(errp, "incorrect option length %" PRIu32, len);
|
error_setg(errp, "incorrect option length %" PRIu32, len);
|
||||||
nbd_send_opt_abort(ioc);
|
nbd_send_opt_abort(ioc);
|
||||||
|
@ -295,88 +311,67 @@ static int nbd_receive_list(QIOChannel *ioc, char **name, Error **errp)
|
||||||
nbd_send_opt_abort(ioc);
|
nbd_send_opt_abort(ioc);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (namelen > NBD_MAX_NAME_SIZE) {
|
if (namelen != strlen(want)) {
|
||||||
error_setg(errp, "export name length too long %" PRIu32, namelen);
|
|
||||||
nbd_send_opt_abort(ioc);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
*name = g_new0(char, namelen + 1);
|
|
||||||
if (read_sync(ioc, *name, namelen) != namelen) {
|
|
||||||
error_setg(errp, "failed to read export name");
|
|
||||||
g_free(*name);
|
|
||||||
*name = NULL;
|
|
||||||
nbd_send_opt_abort(ioc);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
(*name)[namelen] = '\0';
|
|
||||||
len -= namelen;
|
|
||||||
if (drop_sync(ioc, len) != len) {
|
if (drop_sync(ioc, len) != len) {
|
||||||
error_setg(errp, "failed to read export description");
|
error_setg(errp, "failed to skip export name with wrong length");
|
||||||
g_free(*name);
|
|
||||||
*name = NULL;
|
|
||||||
nbd_send_opt_abort(ioc);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
error_setg(errp, "Unexpected reply type %" PRIx32 " expected %x",
|
|
||||||
reply.type, NBD_REP_SERVER);
|
|
||||||
nbd_send_opt_abort(ioc);
|
nbd_send_opt_abort(ioc);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert(namelen < sizeof(name));
|
||||||
|
if (read_sync(ioc, name, namelen) != namelen) {
|
||||||
|
error_setg(errp, "failed to read export name");
|
||||||
|
nbd_send_opt_abort(ioc);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
name[namelen] = '\0';
|
||||||
|
len -= namelen;
|
||||||
|
if (drop_sync(ioc, len) != len) {
|
||||||
|
error_setg(errp, "failed to read export description");
|
||||||
|
nbd_send_opt_abort(ioc);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (!strcmp(name, want)) {
|
||||||
|
*match = true;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Return -1 on failure, 0 if wantname is an available export. */
|
||||||
static int nbd_receive_query_exports(QIOChannel *ioc,
|
static int nbd_receive_query_exports(QIOChannel *ioc,
|
||||||
const char *wantname,
|
const char *wantname,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
bool foundExport = false;
|
bool foundExport = false;
|
||||||
|
|
||||||
TRACE("Querying export list");
|
TRACE("Querying export list for '%s'", wantname);
|
||||||
if (nbd_send_option_request(ioc, NBD_OPT_LIST, 0, NULL, errp) < 0) {
|
if (nbd_send_option_request(ioc, NBD_OPT_LIST, 0, NULL, errp) < 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
TRACE("Reading available export names");
|
TRACE("Reading available export names");
|
||||||
while (1) {
|
while (1) {
|
||||||
char *name = NULL;
|
int ret = nbd_receive_list(ioc, wantname, &foundExport, errp);
|
||||||
int ret = nbd_receive_list(ioc, &name, errp);
|
|
||||||
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
g_free(name);
|
/* Server gave unexpected reply */
|
||||||
name = NULL;
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
} else if (ret == 0) {
|
||||||
if (ret == 0) {
|
/* Done iterating. */
|
||||||
/* Server doesn't support export listing, so
|
|
||||||
* we will just assume an export with our
|
|
||||||
* wanted name exists */
|
|
||||||
foundExport = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (name == NULL) {
|
|
||||||
TRACE("End of export name list");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (g_str_equal(name, wantname)) {
|
|
||||||
foundExport = true;
|
|
||||||
TRACE("Found desired export name '%s'", name);
|
|
||||||
} else {
|
|
||||||
TRACE("Ignored export name '%s'", name);
|
|
||||||
}
|
|
||||||
g_free(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!foundExport) {
|
if (!foundExport) {
|
||||||
error_setg(errp, "No export with name '%s' available", wantname);
|
error_setg(errp, "No export with name '%s' available",
|
||||||
|
wantname);
|
||||||
nbd_send_opt_abort(ioc);
|
nbd_send_opt_abort(ioc);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
TRACE("Found desired export name '%s'", wantname);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static QIOChannel *nbd_receive_starttls(QIOChannel *ioc,
|
static QIOChannel *nbd_receive_starttls(QIOChannel *ioc,
|
||||||
QCryptoTLSCreds *tlscreds,
|
QCryptoTLSCreds *tlscreds,
|
||||||
|
|
Loading…
Reference in a new issue