The NBD spec says that clients should not try to write/trim to
an export advertised as read-only by the server.  But we failed
to inform the block layer of what we learned from the server,
meaning that we were depending on the server sending a proper
EPERM failure for various commands.  In fact, this was noticeable
in the case of a 0-length write: we had a difference in behavior
depending on whether we were writing zero (silent success
locally, because the driver is not invoked from the block layer)
or normal writes (noisy failure, because we sent a 0-length
request across the wire and the server flagged it as invalid);
now we always fail locally without sending anything over the wire.

Signed-off-by: Eric Blake <ebl...@redhat.com>
---
 block/nbd-client.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/block/nbd-client.c b/block/nbd-client.c
index de6c153328..c35aad59b0 100644
--- a/block/nbd-client.c
+++ b/block/nbd-client.c
@@ -697,6 +697,7 @@ int nbd_client_co_pwritev(BlockDriverState *bs, uint64_t 
offset,
         .len = bytes,
     };

+    assert(!(client->info.flags & NBD_FLAG_READ_ONLY));
     if (flags & BDRV_REQ_FUA) {
         assert(client->info.flags & NBD_FLAG_SEND_FUA);
         request.flags |= NBD_CMD_FLAG_FUA;
@@ -717,6 +718,7 @@ int nbd_client_co_pwrite_zeroes(BlockDriverState *bs, 
int64_t offset,
         .len = bytes,
     };

+    assert(!(client->info.flags & NBD_FLAG_READ_ONLY));
     if (!(client->info.flags & NBD_FLAG_SEND_WRITE_ZEROES)) {
         return -ENOTSUP;
     }
@@ -756,6 +758,7 @@ int nbd_client_co_pdiscard(BlockDriverState *bs, int64_t 
offset, int bytes)
         .len = bytes,
     };

+    assert(!(client->info.flags & NBD_FLAG_READ_ONLY));
     if (!(client->info.flags & NBD_FLAG_SEND_TRIM)) {
         return 0;
     }
@@ -814,6 +817,12 @@ int nbd_client_init(BlockDriverState *bs,
         logout("Failed to negotiate with the NBD server\n");
         return ret;
     }
+    if (client->info.flags & NBD_FLAG_READ_ONLY) {
+        ret = bdrv_set_read_only(bs, true, errp);
+        if (ret < 0) {
+            return ret;
+        }
+    }
     if (client->info.flags & NBD_FLAG_SEND_FUA) {
         bs->supported_write_flags = BDRV_REQ_FUA;
         bs->supported_zero_flags |= BDRV_REQ_FUA;
-- 
2.13.6


Reply via email to