static int nbd_negotiate_handle_info(NBDClient *client, uint32_t length,

                                     uint32_t opt, uint16_t myflags,

                                     Error **errp)

{

    int rc;

    char name[NBD_MAX_NAME_SIZE + 1];

    NBDExport *exp;

    uint16_t requests;

    uint16_t request;

    uint32_t namelen;

    bool sendname = false;

    bool blocksize = false;

    uint32_t sizes[3];

    char buf[sizeof(uint64_t) + sizeof(uint16_t)];

    const char *msg;



    /* Client sends:

        4 bytes: L, name length (can be 0)

        L bytes: export name

        2 bytes: N, number of requests (can be 0)

        N * 2 bytes: N requests

    */

    if (length < sizeof(namelen) + sizeof(requests)) {

        msg = "overall request too short";



    if (nbd_read(client->ioc, &namelen, sizeof(namelen), errp) < 0) {

        return -EIO;


    be32_to_cpus(&namelen);

    length -= sizeof(namelen);

    if (namelen > length - sizeof(requests) || (length - namelen) % 2) {

        msg = "name length is incorrect";







    if (nbd_read(client->ioc, name, namelen, errp) < 0) {

        return -EIO;


    name[namelen] = '\0';

    length -= namelen;

    trace_nbd_negotiate_handle_export_name_request(name);



    if (nbd_read(client->ioc, &requests, sizeof(requests), errp) < 0) {

        return -EIO;


    be16_to_cpus(&requests);

    length -= sizeof(requests);

    trace_nbd_negotiate_handle_info_requests(requests);

    if (requests != length / sizeof(request)) {

        msg = "incorrect number of  requests for overall length";



    while (requests--) {

        if (nbd_read(client->ioc, &request, sizeof(request), errp) < 0) {

            return -EIO;


        be16_to_cpus(&request);

        length -= sizeof(request);

        trace_nbd_negotiate_handle_info_request(request,

                                                nbd_info_lookup(request));

        /* We care about NBD_INFO_NAME and NBD_INFO_BLOCK_SIZE;

         * everything else is either a request we don't know or

         * something we send regardless of request */

        switch (request) {

        case NBD_INFO_NAME:

            sendname = true;

            break;

        case NBD_INFO_BLOCK_SIZE:

            blocksize = true;

            break;



    assert(length == 0);



    exp = nbd_export_find(name);

    if (!exp) {

        return nbd_negotiate_send_rep_err(client->ioc, NBD_REP_ERR_UNKNOWN,

                                          opt, errp, "export '%s' not present",

                                          name);




    /* Don't bother sending NBD_INFO_NAME unless client requested it */

    if (sendname) {

        rc = nbd_negotiate_send_info(client, opt, NBD_INFO_NAME, namelen, name,

                                     errp);

        if (rc < 0) {

            return rc;





    /* Send NBD_INFO_DESCRIPTION only if available, regardless of

     * client request */

    if (exp->description) {

        size_t len = strlen(exp->description);



        rc = nbd_negotiate_send_info(client, opt, NBD_INFO_DESCRIPTION,

                                     len, exp->description, errp);

        if (rc < 0) {

            return rc;





    /* Send NBD_INFO_BLOCK_SIZE always, but tweak the minimum size

     * according to whether the client requested it, and according to

     * whether this is OPT_INFO or OPT_GO. */

    /* minimum - 1 for back-compat, or 512 if client is new enough.

     * TODO: consult blk_bs(blk)->bl.request_alignment? */

    sizes[0] = (opt == NBD_OPT_INFO || blocksize) ? BDRV_SECTOR_SIZE : 1;

    /* preferred - Hard-code to 4096 for now.

     * TODO: is blk_bs(blk)->bl.opt_transfer appropriate? */

    sizes[1] = 4096;

    /* maximum - At most 32M, but smaller as appropriate. */

    sizes[2] = MIN(blk_get_max_transfer(exp->blk), NBD_MAX_BUFFER_SIZE);

    trace_nbd_negotiate_handle_info_block_size(sizes[0], sizes[1], sizes[2]);

    cpu_to_be32s(&sizes[0]);

    cpu_to_be32s(&sizes[1]);

    cpu_to_be32s(&sizes[2]);

    rc = nbd_negotiate_send_info(client, opt, NBD_INFO_BLOCK_SIZE,

                                 sizeof(sizes), sizes, errp);

    if (rc < 0) {

        return rc;




    /* Send NBD_INFO_EXPORT always */

    trace_nbd_negotiate_new_style_size_flags(exp->size,

                                             exp->nbdflags | myflags);

    stq_be_p(buf, exp->size);

    stw_be_p(buf + 8, exp->nbdflags | myflags);

    rc = nbd_negotiate_send_info(client, opt, NBD_INFO_EXPORT,

                                 sizeof(buf), buf, errp);

    if (rc < 0) {

        return rc;




    /* If the client is just asking for NBD_OPT_INFO, but forgot to

     * request block sizes, return an error.

     * TODO: consult blk_bs(blk)->request_align, and only error if it

     * is not 1? */

    if (opt == NBD_OPT_INFO && !blocksize) {

        return nbd_negotiate_send_rep_err(client->ioc,

                                          NBD_REP_ERR_BLOCK_SIZE_REQD, opt,

                                          errp,

                                          "request NBD_INFO_BLOCK_SIZE to "

                                          "use this export");




    /* Final reply */

    rc = nbd_negotiate_send_rep(client->ioc, NBD_REP_ACK, opt, errp);

    if (rc < 0) {

        return rc;




    if (opt == NBD_OPT_GO) {

        client->exp = exp;

        QTAILQ_INSERT_TAIL(&client->exp->clients, client, next);

        nbd_export_get(client->exp);

        rc = 1;


    return rc;



 invalid:

    if (nbd_drop(client->ioc, length, errp) < 0) {

        return -EIO;


    return nbd_negotiate_send_rep_err(client->ioc, NBD_REP_ERR_INVALID, opt,

                                      errp, "%s", msg);

static int nbd_negotiate_handle_info(NBDClient *client, uint32_t length,

                                     uint32_t opt, uint16_t myflags,

                                     Error **errp)

{

    int rc;

    char name[NBD_MAX_NAME_SIZE + 1];

    NBDExport *exp;

    uint16_t requests;

    uint16_t request;

    uint32_t namelen;

    bool sendname = false;

    bool blocksize = false;

    uint32_t sizes[3];

    char buf[sizeof(uint64_t) + sizeof(uint16_t)];

    const char *msg;



    











    if (length < sizeof(namelen) + sizeof(requests)) {

        msg = "overall request too short";



    if (nbd_read(client->ioc, &namelen, sizeof(namelen), errp) < 0) {

        return -EIO;


    be32_to_cpus(&namelen);

    length -= sizeof(namelen);

    if (namelen > length - sizeof(requests) || (length - namelen) % 2) {

        msg = "name length is incorrect";







    if (nbd_read(client->ioc, name, namelen, errp) < 0) {

        return -EIO;


    name[namelen] = '\0';

    length -= namelen;

    trace_nbd_negotiate_handle_export_name_request(name);



    if (nbd_read(client->ioc, &requests, sizeof(requests), errp) < 0) {

        return -EIO;


    be16_to_cpus(&requests);

    length -= sizeof(requests);

    trace_nbd_negotiate_handle_info_requests(requests);

    if (requests != length / sizeof(request)) {

        msg = "incorrect number of  requests for overall length";



    while (requests--) {

        if (nbd_read(client->ioc, &request, sizeof(request), errp) < 0) {

            return -EIO;


        be16_to_cpus(&request);

        length -= sizeof(request);

        trace_nbd_negotiate_handle_info_request(request,

                                                nbd_info_lookup(request));

        





        switch (request) {

        case NBD_INFO_NAME:

            sendname = true;

            break;

        case NBD_INFO_BLOCK_SIZE:

            blocksize = true;

            break;



    assert(length == 0);



    exp = nbd_export_find(name);

    if (!exp) {

        return nbd_negotiate_send_rep_err(client->ioc, NBD_REP_ERR_UNKNOWN,

                                          opt, errp, "export '%s' not present",

                                          name);




    

    if (sendname) {

        rc = nbd_negotiate_send_info(client, opt, NBD_INFO_NAME, namelen, name,

                                     errp);

        if (rc < 0) {

            return rc;





    



    if (exp->description) {

        size_t len = strlen(exp->description);



        rc = nbd_negotiate_send_info(client, opt, NBD_INFO_DESCRIPTION,

                                     len, exp->description, errp);

        if (rc < 0) {

            return rc;





    





    



    sizes[0] = (opt == NBD_OPT_INFO || blocksize) ? BDRV_SECTOR_SIZE : 1;

    



    sizes[1] = 4096;

    

    sizes[2] = MIN(blk_get_max_transfer(exp->blk), NBD_MAX_BUFFER_SIZE);

    trace_nbd_negotiate_handle_info_block_size(sizes[0], sizes[1], sizes[2]);

    cpu_to_be32s(&sizes[0]);

    cpu_to_be32s(&sizes[1]);

    cpu_to_be32s(&sizes[2]);

    rc = nbd_negotiate_send_info(client, opt, NBD_INFO_BLOCK_SIZE,

                                 sizeof(sizes), sizes, errp);

    if (rc < 0) {

        return rc;




    

    trace_nbd_negotiate_new_style_size_flags(exp->size,

                                             exp->nbdflags | myflags);

    stq_be_p(buf, exp->size);

    stw_be_p(buf + 8, exp->nbdflags | myflags);

    rc = nbd_negotiate_send_info(client, opt, NBD_INFO_EXPORT,

                                 sizeof(buf), buf, errp);

    if (rc < 0) {

        return rc;




    







    if (opt == NBD_OPT_INFO && !blocksize) {

        return nbd_negotiate_send_rep_err(client->ioc,

                                          NBD_REP_ERR_BLOCK_SIZE_REQD, opt,

                                          errp,

                                          "request NBD_INFO_BLOCK_SIZE to "

                                          "use this export");




    

    rc = nbd_negotiate_send_rep(client->ioc, NBD_REP_ACK, opt, errp);

    if (rc < 0) {

        return rc;




    if (opt == NBD_OPT_GO) {

        client->exp = exp;

        QTAILQ_INSERT_TAIL(&client->exp->clients, client, next);

        nbd_export_get(client->exp);

        rc = 1;


    return rc;



 invalid:

    if (nbd_drop(client->ioc, length, errp) < 0) {

        return -EIO;


    return nbd_negotiate_send_rep_err(client->ioc, NBD_REP_ERR_INVALID, opt,

                                      errp, "%s", msg);

static int FUNC_0(NBDClient *VAR_0, uint32_t VAR_1,

                                     uint32_t VAR_2, uint16_t VAR_3,

                                     Error **VAR_4)

{

    int VAR_5;

    char VAR_6[NBD_MAX_NAME_SIZE + 1];

    NBDExport *exp;

    uint16_t requests;

    uint16_t request;

    uint32_t namelen;

    bool sendname = false;

    bool blocksize = false;

    uint32_t sizes[3];

    char VAR_7[sizeof(uint64_t) + sizeof(uint16_t)];

    const char *VAR_8;



    











    if (VAR_1 < sizeof(namelen) + sizeof(requests)) {

        VAR_8 = "overall request too short";



    if (nbd_read(VAR_0->ioc, &namelen, sizeof(namelen), VAR_4) < 0) {

        return -EIO;


    be32_to_cpus(&namelen);

    VAR_1 -= sizeof(namelen);

    if (namelen > VAR_1 - sizeof(requests) || (VAR_1 - namelen) % 2) {

        VAR_8 = "VAR_6 VAR_1 is incorrect";







    if (nbd_read(VAR_0->ioc, VAR_6, namelen, VAR_4) < 0) {

        return -EIO;


    VAR_6[namelen] = '\0';

    VAR_1 -= namelen;

    trace_nbd_negotiate_handle_export_name_request(VAR_6);



    if (nbd_read(VAR_0->ioc, &requests, sizeof(requests), VAR_4) < 0) {

        return -EIO;


    be16_to_cpus(&requests);

    VAR_1 -= sizeof(requests);

    trace_nbd_negotiate_handle_info_requests(requests);

    if (requests != VAR_1 / sizeof(request)) {

        VAR_8 = "incorrect number of  requests for overall VAR_1";



    while (requests--) {

        if (nbd_read(VAR_0->ioc, &request, sizeof(request), VAR_4) < 0) {

            return -EIO;


        be16_to_cpus(&request);

        VAR_1 -= sizeof(request);

        trace_nbd_negotiate_handle_info_request(request,

                                                nbd_info_lookup(request));

        





        switch (request) {

        case NBD_INFO_NAME:

            sendname = true;

            break;

        case NBD_INFO_BLOCK_SIZE:

            blocksize = true;

            break;



    assert(VAR_1 == 0);



    exp = nbd_export_find(VAR_6);

    if (!exp) {

        return nbd_negotiate_send_rep_err(VAR_0->ioc, NBD_REP_ERR_UNKNOWN,

                                          VAR_2, VAR_4, "export '%s' not present",

                                          VAR_6);




    

    if (sendname) {

        VAR_5 = nbd_negotiate_send_info(VAR_0, VAR_2, NBD_INFO_NAME, namelen, VAR_6,

                                     VAR_4);

        if (VAR_5 < 0) {

            return VAR_5;





    



    if (exp->description) {

        size_t len = strlen(exp->description);



        VAR_5 = nbd_negotiate_send_info(VAR_0, VAR_2, NBD_INFO_DESCRIPTION,

                                     len, exp->description, VAR_4);

        if (VAR_5 < 0) {

            return VAR_5;





    





    



    sizes[0] = (VAR_2 == NBD_OPT_INFO || blocksize) ? BDRV_SECTOR_SIZE : 1;

    



    sizes[1] = 4096;

    

    sizes[2] = MIN(blk_get_max_transfer(exp->blk), NBD_MAX_BUFFER_SIZE);

    trace_nbd_negotiate_handle_info_block_size(sizes[0], sizes[1], sizes[2]);

    cpu_to_be32s(&sizes[0]);

    cpu_to_be32s(&sizes[1]);

    cpu_to_be32s(&sizes[2]);

    VAR_5 = nbd_negotiate_send_info(VAR_0, VAR_2, NBD_INFO_BLOCK_SIZE,

                                 sizeof(sizes), sizes, VAR_4);

    if (VAR_5 < 0) {

        return VAR_5;




    

    trace_nbd_negotiate_new_style_size_flags(exp->size,

                                             exp->nbdflags | VAR_3);

    stq_be_p(VAR_7, exp->size);

    stw_be_p(VAR_7 + 8, exp->nbdflags | VAR_3);

    VAR_5 = nbd_negotiate_send_info(VAR_0, VAR_2, NBD_INFO_EXPORT,

                                 sizeof(VAR_7), VAR_7, VAR_4);

    if (VAR_5 < 0) {

        return VAR_5;




    







    if (VAR_2 == NBD_OPT_INFO && !blocksize) {

        return nbd_negotiate_send_rep_err(VAR_0->ioc,

                                          NBD_REP_ERR_BLOCK_SIZE_REQD, VAR_2,

                                          VAR_4,

                                          "request NBD_INFO_BLOCK_SIZE to "

                                          "use this export");




    

    VAR_5 = nbd_negotiate_send_rep(VAR_0->ioc, NBD_REP_ACK, VAR_2, VAR_4);

    if (VAR_5 < 0) {

        return VAR_5;




    if (VAR_2 == NBD_OPT_GO) {

        VAR_0->exp = exp;

        QTAILQ_INSERT_TAIL(&VAR_0->exp->clients, VAR_0, next);

        nbd_export_get(VAR_0->exp);

        VAR_5 = 1;


    return VAR_5;



 invalid:

    if (nbd_drop(VAR_0->ioc, VAR_1, VAR_4) < 0) {

        return -EIO;


    return nbd_negotiate_send_rep_err(VAR_0->ioc, NBD_REP_ERR_INVALID, VAR_2,

                                      VAR_4, "%s", VAR_8);

static int FUNC_0(NBDClient *VAR_0, uint32_t VAR_1,
uint32_t VAR_2, uint16_t VAR_3,
Error **VAR_4)
{ int VAR_5; char VAR_6[NBD_MAX_NAME_SIZE + 1]; NBDExport *exp; uint16_t requests; uint16_t request; uint32_t namelen; bool sendname = false; bool blocksize = false; uint32_t sizes[3]; char VAR_7[sizeof(uint64_t) + sizeof(uint16_t)]; const char *VAR_8; if (VAR_1 < sizeof(namelen) + sizeof(requests)) { VAR_8 = "overall request too short"; if (nbd_read(VAR_0->ioc, &namelen, sizeof(namelen), VAR_4) < 0) { return -EIO; be32_to_cpus(&namelen); VAR_1 -= sizeof(namelen); if (namelen > VAR_1 - sizeof(requests) || (VAR_1 - namelen) % 2) { VAR_8 = "VAR_6 VAR_1 is incorrect"; if (nbd_read(VAR_0->ioc, VAR_6, namelen, VAR_4) < 0) { return -EIO; VAR_6[namelen] = '\0'; VAR_1 -= namelen; trace_nbd_negotiate_handle_export_name_request(VAR_6); if (nbd_read(VAR_0->ioc, &requests, sizeof(requests), VAR_4) < 0) { return -EIO; be16_to_cpus(&requests); VAR_1 -= sizeof(requests); trace_nbd_negotiate_handle_info_requests(requests); if (requests != VAR_1 / sizeof(request)) { VAR_8 = "incorrect number of  requests for overall VAR_1"; while (requests--) { if (nbd_read(VAR_0->ioc, &request, sizeof(request), VAR_4) < 0) { return -EIO; be16_to_cpus(&request); VAR_1 -= sizeof(request); trace_nbd_negotiate_handle_info_request(request,
nbd_info_lookup(request)); switch (request) { case NBD_INFO_NAME:
sendname = true; break; case NBD_INFO_BLOCK_SIZE:
blocksize = true; break; assert(VAR_1 == 0); exp = nbd_export_find(VAR_6); if (!exp) { return nbd_negotiate_send_rep_err(VAR_0->ioc, NBD_REP_ERR_UNKNOWN,
VAR_2, VAR_4, "export '%s' not present",
VAR_6); if (sendname) { VAR_5 = nbd_negotiate_send_info(VAR_0, VAR_2, NBD_INFO_NAME, namelen, VAR_6,
VAR_4); if (VAR_5 < 0) { return VAR_5; if (exp->description) { size_t len = strlen(exp->description); VAR_5 = nbd_negotiate_send_info(VAR_0, VAR_2, NBD_INFO_DESCRIPTION,
len, exp->description, VAR_4); if (VAR_5 < 0) { return VAR_5; sizes[0] = (VAR_2 == NBD_OPT_INFO || blocksize) ? BDRV_SECTOR_SIZE : 1; sizes[1] = 4096; sizes[2] = MIN(blk_get_max_transfer(exp->blk), NBD_MAX_BUFFER_SIZE); trace_nbd_negotiate_handle_info_block_size(sizes[0], sizes[1], sizes[2]); cpu_to_be32s(&sizes[0]); cpu_to_be32s(&sizes[1]); cpu_to_be32s(&sizes[2]); VAR_5 = nbd_negotiate_send_info(VAR_0, VAR_2, NBD_INFO_BLOCK_SIZE,
sizeof(sizes), sizes, VAR_4); if (VAR_5 < 0) { return VAR_5; trace_nbd_negotiate_new_style_size_flags(exp->size,
exp->nbdflags | VAR_3); stq_be_p(VAR_7, exp->size); stw_be_p(VAR_7 + 8, exp->nbdflags | VAR_3); VAR_5 = nbd_negotiate_send_info(VAR_0, VAR_2, NBD_INFO_EXPORT,
sizeof(VAR_7), VAR_7, VAR_4); if (VAR_5 < 0) { return VAR_5; if (VAR_2 == NBD_OPT_INFO && !blocksize) { return nbd_negotiate_send_rep_err(VAR_0->ioc,
NBD_REP_ERR_BLOCK_SIZE_REQD, VAR_2,
VAR_4,
"request NBD_INFO_BLOCK_SIZE to "
"use this export"); VAR_5 = nbd_negotiate_send_rep(VAR_0->ioc, NBD_REP_ACK, VAR_2, VAR_4); if (VAR_5 < 0) { return VAR_5; if (VAR_2 == NBD_OPT_GO) { VAR_0->exp = exp; QTAILQ_INSERT_TAIL(&VAR_0->exp->clients, VAR_0, next); nbd_export_get(VAR_0->exp); VAR_5 = 1; return VAR_5; invalid:
if (nbd_drop(VAR_0->ioc, VAR_1, VAR_4) < 0) { return -EIO; return nbd_negotiate_send_rep_err(VAR_0->ioc, NBD_REP_ERR_INVALID, VAR_2,
VAR_4, "%s", VAR_8);