summaryrefslogtreecommitdiffstats
path: root/nbd/client.c
diff options
context:
space:
mode:
Diffstat (limited to 'nbd/client.c')
-rw-r--r--nbd/client.c63
1 files changed, 49 insertions, 14 deletions
diff --git a/nbd/client.c b/nbd/client.c
index d9b7a9b07e..6777e589d1 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)) {
@@ -628,16 +663,16 @@ ssize_t nbd_send_request(QIOChannel *ioc, struct nbd_request *request)
uint8_t buf[NBD_REQUEST_SIZE];
ssize_t ret;
+ TRACE("Sending request to server: "
+ "{ .from = %" PRIu64", .len = %u, .handle = %" PRIu64", .type=%i}",
+ request->from, request->len, request->handle, request->type);
+
cpu_to_be32w((uint32_t*)buf, NBD_REQUEST_MAGIC);
cpu_to_be32w((uint32_t*)(buf + 4), request->type);
cpu_to_be64w((uint64_t*)(buf + 8), request->handle);
cpu_to_be64w((uint64_t*)(buf + 16), request->from);
cpu_to_be32w((uint32_t*)(buf + 24), request->len);
- TRACE("Sending request to server: "
- "{ .from = %" PRIu64", .len = %u, .handle = %" PRIu64", .type=%i}",
- request->from, request->len, request->handle, request->type);
-
ret = write_sync(ioc, buf, sizeof(buf));
if (ret < 0) {
return ret;