Commit 75368aab authored by Eric Blake's avatar Eric Blake Committed by Paolo Bonzini
Browse files

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: default avatarEric Blake <eblake@redhat.com>
Message-Id: <1476469998-28592-12-git-send-email-eblake@redhat.com>
Signed-off-by: default avatarPaolo Bonzini <pbonzini@redhat.com>
parent 7d3123e1
Loading
Loading
Loading
Loading
+67 −72
Original line number Diff line number Diff line
@@ -254,19 +254,28 @@ static int nbd_handle_reply_err(QIOChannel *ioc, nbd_opt_reply *reply,
    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;
    uint32_t len;
    uint32_t namelen;
    char name[NBD_MAX_NAME_SIZE + 1];
    int error;

    *name = NULL;
    if (nbd_receive_option_reply(ioc, NBD_OPT_LIST, &reply, errp) < 0) {
        return -1;
    }
    error = nbd_handle_reply_err(ioc, &reply, errp);
    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;
    }
    len = reply.length;
@@ -277,7 +286,14 @@ static int nbd_receive_list(QIOChannel *ioc, char **name, Error **errp)
            nbd_send_opt_abort(ioc);
            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) {
        error_setg(errp, "incorrect option length %" PRIu32, len);
        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);
        return -1;
    }
        if (namelen > NBD_MAX_NAME_SIZE) {
            error_setg(errp, "export name length too long %" PRIu32, namelen);
    if (namelen != strlen(want)) {
        if (drop_sync(ioc, len) != len) {
            error_setg(errp, "failed to skip export name with wrong length");
            nbd_send_opt_abort(ioc);
            return -1;
        }
        return 1;
    }

        *name = g_new0(char, namelen + 1);
        if (read_sync(ioc, *name, namelen) != namelen) {
    assert(namelen < sizeof(name));
    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';
    name[namelen] = '\0';
    len -= namelen;
    if (drop_sync(ioc, len) != len) {
        error_setg(errp, "failed to read export description");
            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);
        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,
                                     const char *wantname,
                                     Error **errp)
{
    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) {
        return -1;
    }

    TRACE("Reading available export names");
    while (1) {
        char *name = NULL;
        int ret = nbd_receive_list(ioc, &name, errp);
        int ret = nbd_receive_list(ioc, wantname, &foundExport, errp);

        if (ret < 0) {
            g_free(name);
            name = NULL;
            /* Server gave unexpected reply */
            return -1;
        }
        if (ret == 0) {
            /* 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);
    }

        } else if (ret == 0) {
            /* Done iterating. */
            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);
                return -1;
            }

            TRACE("Found desired export name '%s'", wantname);
            return 0;
        }
    }
}

static QIOChannel *nbd_receive_starttls(QIOChannel *ioc,
                                        QCryptoTLSCreds *tlscreds,