summary refs log tree commit diff stats
path: root/nbd
diff options
context:
space:
mode:
Diffstat (limited to 'nbd')
-rw-r--r--nbd/client.c63
-rw-r--r--nbd/common.c5
-rw-r--r--nbd/nbd-internal.h15
-rw-r--r--nbd/server.c11
4 files changed, 70 insertions, 24 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;
diff --git a/nbd/common.c b/nbd/common.c
index a44718ce58..8ddb2dd2f0 100644
--- a/nbd/common.c
+++ b/nbd/common.c
@@ -50,9 +50,12 @@ ssize_t nbd_wr_syncv(QIOChannel *ioc,
                  * qio_channel_yield() that works with AIO contexts
                  * and consider using that in this branch */
                 qemu_coroutine_yield();
-            } else {
+            } else if (done) {
+                /* XXX this is needed by nbd_reply_ready.  */
                 qio_channel_wait(ioc,
                                  do_read ? G_IO_IN : G_IO_OUT);
+            } else {
+                return -EAGAIN;
             }
             continue;
         }
diff --git a/nbd/nbd-internal.h b/nbd/nbd-internal.h
index d09b4ee308..379153561d 100644
--- a/nbd/nbd-internal.h
+++ b/nbd/nbd-internal.h
@@ -33,18 +33,21 @@
 /* #define DEBUG_NBD */
 
 #ifdef DEBUG_NBD
-#define TRACE(msg, ...) do { \
-    LOG(msg, ## __VA_ARGS__); \
-} while(0)
+#define DEBUG_NBD_PRINT 1
 #else
-#define TRACE(msg, ...) \
-    do { } while (0)
+#define DEBUG_NBD_PRINT 0
 #endif
 
+#define TRACE(msg, ...) do { \
+    if (DEBUG_NBD_PRINT) { \
+        LOG(msg, ## __VA_ARGS__); \
+    } \
+} while (0)
+
 #define LOG(msg, ...) do { \
     fprintf(stderr, "%s:%s():L%d: " msg "\n", \
             __FILE__, __FUNCTION__, __LINE__, ## __VA_ARGS__); \
-} while(0)
+} while (0)
 
 /* This is all part of the "official" NBD API.
  *
diff --git a/nbd/server.c b/nbd/server.c
index b95571bdf5..2a4dd10f52 100644
--- a/nbd/server.c
+++ b/nbd/server.c
@@ -26,6 +26,7 @@ static int system_errno_to_nbd_errno(int err)
     case 0:
         return NBD_SUCCESS;
     case EPERM:
+    case EROFS:
         return NBD_EPERM;
     case EIO:
         return NBD_EIO;
@@ -482,9 +483,12 @@ static int nbd_negotiate_options(NBDClient *client)
                 return -EINVAL;
             default:
                 TRACE("Unsupported option 0x%x", clientflags);
+                if (nbd_negotiate_drop_sync(client->ioc, length) != length) {
+                    return -EIO;
+                }
                 nbd_negotiate_send_rep(client->ioc, NBD_REP_ERR_UNSUP,
                                        clientflags);
-                return -EINVAL;
+                break;
             }
         } else {
             /*
@@ -655,6 +659,9 @@ static ssize_t nbd_send_reply(QIOChannel *ioc, struct nbd_reply *reply)
 
     reply->error = system_errno_to_nbd_errno(reply->error);
 
+    TRACE("Sending response to client: { .error = %d, handle = %" PRIu64 " }",
+          reply->error, reply->handle);
+
     /* Reply
        [ 0 ..  3]    magic   (NBD_REPLY_MAGIC)
        [ 4 ..  7]    error   (0 == no error)
@@ -664,8 +671,6 @@ static ssize_t nbd_send_reply(QIOChannel *ioc, struct nbd_reply *reply)
     stl_be_p(buf + 4, reply->error);
     stq_be_p(buf + 8, reply->handle);
 
-    TRACE("Sending response to client");
-
     ret = write_sync(ioc, buf, sizeof(buf));
     if (ret < 0) {
         return ret;