/* nbd_request_simple_option: Send an option request, and parse the reply * return 1 for successful negotiation, * 0 if operation is unsupported, * -1 with errp set for any other error */ static int nbd_request_simple_option(QIOChannel *ioc, int opt, Error **errp) { NBDOptionReply reply; int error; if (nbd_send_option_request(ioc, opt, 0, NULL, errp) < 0) { return -1; } if (nbd_receive_option_reply(ioc, opt, &reply, errp) < 0) { return -1; } error = nbd_handle_reply_err(ioc, &reply, errp); if (error <= 0) { return error; } if (reply.type != NBD_REP_ACK) { error_setg(errp, "Server answered option %d (%s) with unexpected " "reply %" PRIu32 " (%s)", opt, nbd_opt_lookup(opt), reply.type, nbd_rep_lookup(reply.type)); nbd_send_opt_abort(ioc); return -1; } if (reply.length != 0) { error_setg(errp, "Option %d ('%s') response length is %" PRIu32 " (it should be zero)", opt, nbd_opt_lookup(opt), reply.length); nbd_send_opt_abort(ioc); return -1; } return 1; }
/* Send an option request. * * The request is for option @opt, with @data containing @len bytes of * additional payload for the request (@len may be -1 to treat @data as * a C string; and @data may be NULL if @len is 0). * Return 0 if successful, -1 with errp set if it is impossible to * continue. */ static int nbd_send_option_request(QIOChannel *ioc, uint32_t opt, uint32_t len, const char *data, Error **errp) { nbd_option req; QEMU_BUILD_BUG_ON(sizeof(req) != 16); if (len == -1) { req.length = len = strlen(data); } trace_nbd_send_option_request(opt, nbd_opt_lookup(opt), len); stq_be_p(&req.magic, NBD_OPTS_MAGIC); stl_be_p(&req.option, opt); stl_be_p(&req.length, len); if (nbd_write(ioc, &req, sizeof(req), errp) < 0) { error_prepend(errp, "Failed to send option request header"); return -1; } if (len && nbd_write(ioc, (char *) data, len, errp) < 0) { error_prepend(errp, "Failed to send option request data"); return -1; } return 0; }
/* Receive the header of an option reply, which should match the given * opt. Read through the length field, but NOT the length bytes of * payload. Return 0 if successful, -1 with errp set if it is * impossible to continue. */ static int nbd_receive_option_reply(QIOChannel *ioc, uint32_t opt, nbd_opt_reply *reply, Error **errp) { QEMU_BUILD_BUG_ON(sizeof(*reply) != 20); if (nbd_read(ioc, reply, sizeof(*reply), errp) < 0) { error_prepend(errp, "failed to read option reply"); nbd_send_opt_abort(ioc); return -1; } be64_to_cpus(&reply->magic); be32_to_cpus(&reply->option); be32_to_cpus(&reply->type); be32_to_cpus(&reply->length); trace_nbd_receive_option_reply(reply->option, nbd_opt_lookup(reply->option), reply->type, nbd_rep_lookup(reply->type), reply->length); if (reply->magic != NBD_REP_MAGIC) { error_setg(errp, "Unexpected option reply magic"); nbd_send_opt_abort(ioc); return -1; } if (reply->option != opt) { error_setg(errp, "Unexpected option type %x expected %x", reply->option, opt); nbd_send_opt_abort(ioc); return -1; } return 0; }
/* If reply represents success, return 1 without further action. * If reply represents an error, consume the optional payload of * the packet on ioc. Then return 0 for unsupported (so the client * can fall back to other approaches), or -1 with errp set for other * errors. */ static int nbd_handle_reply_err(QIOChannel *ioc, nbd_opt_reply *reply, Error **errp) { char *msg = NULL; int result = -1; if (!(reply->type & (1 << 31))) { return 1; } if (reply->length) { if (reply->length > NBD_MAX_BUFFER_SIZE) { error_setg(errp, "server error 0x%" PRIx32 " (%s) message is too long", reply->type, nbd_rep_lookup(reply->type)); goto cleanup; } msg = g_malloc(reply->length + 1); if (nbd_read(ioc, msg, reply->length, errp) < 0) { error_prepend(errp, "failed to read option error 0x%" PRIx32 " (%s) message", reply->type, nbd_rep_lookup(reply->type)); goto cleanup; } msg[reply->length] = '\0'; } switch (reply->type) { case NBD_REP_ERR_UNSUP: trace_nbd_reply_err_unsup(reply->option, nbd_opt_lookup(reply->option)); result = 0; goto cleanup; case NBD_REP_ERR_POLICY: error_setg(errp, "Denied by server for option %" PRIx32 " (%s)", reply->option, nbd_opt_lookup(reply->option)); break; case NBD_REP_ERR_INVALID: error_setg(errp, "Invalid data length for option %" PRIx32 " (%s)", reply->option, nbd_opt_lookup(reply->option)); break; case NBD_REP_ERR_PLATFORM: error_setg(errp, "Server lacks support for option %" PRIx32 " (%s)", reply->option, nbd_opt_lookup(reply->option)); break; case NBD_REP_ERR_TLS_REQD: error_setg(errp, "TLS negotiation required before option %" PRIx32 " (%s)", reply->option, nbd_opt_lookup(reply->option)); break; case NBD_REP_ERR_UNKNOWN: error_setg(errp, "Requested export not available"); break; case NBD_REP_ERR_SHUTDOWN: error_setg(errp, "Server shutting down before option %" PRIx32 " (%s)", reply->option, nbd_opt_lookup(reply->option)); break; case NBD_REP_ERR_BLOCK_SIZE_REQD: error_setg(errp, "Server requires INFO_BLOCK_SIZE for option %" PRIx32 " (%s)", reply->option, nbd_opt_lookup(reply->option)); break; default: error_setg(errp, "Unknown error code when asking for option %" PRIx32 " (%s)", reply->option, nbd_opt_lookup(reply->option)); break; } if (msg) { error_append_hint(errp, "server reported: %s\n", msg); } cleanup: g_free(msg); if (result < 0) { nbd_send_opt_abort(ioc); } return result; }