func: 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";
  
          goto invalid;
  
      }
  
      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";
  
          goto invalid;
  
      }
  
      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";
  
          goto invalid;
  
      }
  
      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;
  
          }
  
      }
  
  
  
      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, length, 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);
  
  }
  