diff options
-rw-r--r-- | nbd/client.c | 55 |
1 files changed, 45 insertions, 10 deletions
diff --git a/nbd/client.c b/nbd/client.c index 1593cd6..6777e58 100644 --- a/nbd/client.c +++ b/nbd/client.c @@ -73,16 +73,46 @@ static QTAILQ_HEAD(, NBDExport) exports = QTAILQ_HEAD_INITIALIZER(exports); */ -static int nbd_handle_reply_err(uint32_t opt, uint32_t type, Error **errp) +/* If type represents success, return 1 without further action. + * If type represents an error reply, consume the rest 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, uint32_t opt, uint32_t type, + Error **errp) { + uint32_t len; + char *msg = NULL; + int result = -1; + if (!(type & (1 << 31))) { - return 0; + return 1; + } + + if (read_sync(ioc, &len, sizeof(len)) != sizeof(len)) { + error_setg(errp, "failed to read option length"); + return -1; + } + len = be32_to_cpu(len); + if (len) { + if (len > NBD_MAX_BUFFER_SIZE) { + error_setg(errp, "server's error message is too long"); + goto cleanup; + } + msg = g_malloc(len + 1); + if (read_sync(ioc, msg, len) != len) { + error_setg(errp, "failed to read option error message"); + goto cleanup; + } + msg[len] = '\0'; } switch (type) { case NBD_REP_ERR_UNSUP: - error_setg(errp, "Unsupported option type %x", opt); - break; + TRACE("server doesn't understand request %d, attempting fallback", + opt); + result = 0; + goto cleanup; case NBD_REP_ERR_POLICY: error_setg(errp, "Denied by server for option %x", opt); @@ -101,7 +131,13 @@ static int nbd_handle_reply_err(uint32_t opt, uint32_t type, Error **errp) break; } - return -1; + if (msg) { + error_append_hint(errp, "%s\n", msg); + } + + cleanup: + g_free(msg); + return result; } static int nbd_receive_list(QIOChannel *ioc, char **name, Error **errp) @@ -111,6 +147,7 @@ static int nbd_receive_list(QIOChannel *ioc, char **name, Error **errp) uint32_t type; uint32_t len; uint32_t namelen; + int error; *name = NULL; if (read_sync(ioc, &magic, sizeof(magic)) != sizeof(magic)) { @@ -138,11 +175,9 @@ static int nbd_receive_list(QIOChannel *ioc, char **name, Error **errp) return -1; } type = be32_to_cpu(type); - if (type == NBD_REP_ERR_UNSUP) { - return 0; - } - if (nbd_handle_reply_err(opt, type, errp) < 0) { - return -1; + error = nbd_handle_reply_err(ioc, opt, type, errp); + if (error <= 0) { + return error; } if (read_sync(ioc, &len, sizeof(len)) != sizeof(len)) { |