From b90fb4b8f5cd01dfcf0e3b45c93977a2e3bdcc71 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 8 Sep 2011 17:24:54 +0200 Subject: nbd: support feature negotiation nbd supports writing flags in bytes 24...27 of the header, and uses that for the read-only flag. Add support for it in qemu-nbd. Signed-off-by: Paolo Bonzini Signed-off-by: Kevin Wolf --- block/nbd.c | 4 ++-- nbd.c | 32 +++++++++++++++++++++++++------- nbd.h | 9 ++++++--- qemu-nbd.c | 13 ++++++------- 4 files changed, 39 insertions(+), 19 deletions(-) diff --git a/block/nbd.c b/block/nbd.c index 70edd81bd6..76f04d863c 100644 --- a/block/nbd.c +++ b/block/nbd.c @@ -48,6 +48,7 @@ typedef struct BDRVNBDState { int sock; + uint32_t nbdflags; off_t size; size_t blocksize; char *export_name; /* An NBD server may export several devices */ @@ -111,7 +112,6 @@ static int nbd_establish_connection(BlockDriverState *bs) int ret; off_t size; size_t blocksize; - uint32_t nbdflags; if (s->host_spec[0] == '/') { sock = unix_socket_outgoing(s->host_spec); @@ -126,7 +126,7 @@ static int nbd_establish_connection(BlockDriverState *bs) } /* NBD handshake */ - ret = nbd_receive_negotiate(sock, s->export_name, &nbdflags, &size, + ret = nbd_receive_negotiate(sock, s->export_name, &s->nbdflags, &size, &blocksize); if (ret == -1) { logout("Failed to negotiate with the NBD server\n"); diff --git a/nbd.c b/nbd.c index 6d81cfbcd0..47ecb2234b 100644 --- a/nbd.c +++ b/nbd.c @@ -30,6 +30,10 @@ #include #include +#ifdef __linux__ +#include +#endif + #include "qemu_socket.h" //#define DEBUG_NBD @@ -172,7 +176,7 @@ int unix_socket_outgoing(const char *path) Request (type == 2) */ -int nbd_negotiate(int csock, off_t size) +int nbd_negotiate(int csock, off_t size, uint32_t flags) { char buf[8 + 8 + 8 + 128]; @@ -180,14 +184,16 @@ int nbd_negotiate(int csock, off_t size) [ 0 .. 7] passwd ("NBDMAGIC") [ 8 .. 15] magic (0x00420281861253) [16 .. 23] size - [24 .. 151] reserved (0) + [24 .. 27] flags + [28 .. 151] reserved (0) */ TRACE("Beginning negotiation."); memcpy(buf, "NBDMAGIC", 8); cpu_to_be64w((uint64_t*)(buf + 8), 0x00420281861253LL); cpu_to_be64w((uint64_t*)(buf + 16), size); - memset(buf + 24, 0, 128); + cpu_to_be32w((uint32_t*)(buf + 24), flags | NBD_FLAG_HAS_FLAGS); + memset(buf + 28, 0, 124); if (write_sync(csock, buf, sizeof(buf)) != sizeof(buf)) { LOG("write failed"); @@ -337,8 +343,8 @@ int nbd_receive_negotiate(int csock, const char *name, uint32_t *flags, return 0; } -#ifndef _WIN32 -int nbd_init(int fd, int csock, off_t size, size_t blocksize) +#ifdef __linux__ +int nbd_init(int fd, int csock, uint32_t flags, off_t size, size_t blocksize) { TRACE("Setting block size to %lu", (unsigned long)blocksize); @@ -358,6 +364,18 @@ int nbd_init(int fd, int csock, off_t size, size_t blocksize) return -1; } + if (flags & NBD_FLAG_READ_ONLY) { + int read_only = 1; + TRACE("Setting readonly attribute"); + + if (ioctl(fd, BLKROSET, (unsigned long) &read_only) < 0) { + int serrno = errno; + LOG("Failed setting read-only attribute"); + errno = serrno; + return -1; + } + } + TRACE("Clearing NBD socket"); if (ioctl(fd, NBD_CLEAR_SOCK) == -1) { @@ -548,7 +566,7 @@ static int nbd_send_reply(int csock, struct nbd_reply *reply) } int nbd_trip(BlockDriverState *bs, int csock, off_t size, uint64_t dev_offset, - off_t *offset, bool readonly, uint8_t *data, int data_size) + off_t *offset, uint32_t nbdflags, uint8_t *data, int data_size) { struct nbd_request request; struct nbd_reply reply; @@ -632,7 +650,7 @@ int nbd_trip(BlockDriverState *bs, int csock, off_t size, uint64_t dev_offset, return -1; } - if (readonly) { + if (nbdflags & NBD_FLAG_READ_ONLY) { TRACE("Server is read-only, return error"); reply.error = 1; } else { diff --git a/nbd.h b/nbd.h index df7b7af7c0..b9c3b391d6 100644 --- a/nbd.h +++ b/nbd.h @@ -37,6 +37,9 @@ struct nbd_reply { uint64_t handle; } QEMU_PACKED; +#define NBD_FLAG_HAS_FLAGS (1 << 0) /* Flags are there */ +#define NBD_FLAG_READ_ONLY (1 << 1) /* Device is read-only */ + enum { NBD_CMD_READ = 0, NBD_CMD_WRITE = 1, @@ -53,14 +56,14 @@ int tcp_socket_incoming_spec(const char *address_and_port); int unix_socket_outgoing(const char *path); int unix_socket_incoming(const char *path); -int nbd_negotiate(int csock, off_t size); +int nbd_negotiate(int csock, off_t size, uint32_t flags); int nbd_receive_negotiate(int csock, const char *name, uint32_t *flags, off_t *size, size_t *blocksize); -int nbd_init(int fd, int csock, off_t size, size_t blocksize); +int nbd_init(int fd, int csock, uint32_t flags, off_t size, size_t blocksize); int nbd_send_request(int csock, struct nbd_request *request); int nbd_receive_reply(int csock, struct nbd_reply *reply); int nbd_trip(BlockDriverState *bs, int csock, off_t size, uint64_t dev_offset, - off_t *offset, bool readonly, uint8_t *data, int data_size); + off_t *offset, uint32_t nbdflags, uint8_t *data, int data_size); int nbd_client(int fd); int nbd_disconnect(int fd); diff --git a/qemu-nbd.c b/qemu-nbd.c index 3a39145174..d8d3e15a84 100644 --- a/qemu-nbd.c +++ b/qemu-nbd.c @@ -185,7 +185,7 @@ int main(int argc, char **argv) BlockDriverState *bs; off_t dev_offset = 0; off_t offset = 0; - bool readonly = false; + uint32_t nbdflags = 0; bool disconnect = false; const char *bindto = "0.0.0.0"; int port = NBD_DEFAULT_PORT; @@ -230,7 +230,6 @@ int main(int argc, char **argv) int nb_fds = 0; int max_fd; int persistent = 0; - uint32_t nbdflags; while ((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) { switch (ch) { @@ -263,7 +262,7 @@ int main(int argc, char **argv) } break; case 'r': - readonly = true; + nbdflags |= NBD_FLAG_READ_ONLY; flags &= ~BDRV_O_RDWR; break; case 'P': @@ -398,13 +397,13 @@ int main(int argc, char **argv) } ret = nbd_receive_negotiate(sock, NULL, &nbdflags, - &size, &blocksize); + &size, &blocksize); if (ret == -1) { ret = 1; goto out; } - ret = nbd_init(fd, sock, size, blocksize); + ret = nbd_init(fd, sock, nbdflags, size, blocksize); if (ret == -1) { ret = 1; goto out; @@ -463,7 +462,7 @@ int main(int argc, char **argv) for (i = 1; i < nb_fds && ret; i++) { if (FD_ISSET(sharing_fds[i], &fds)) { if (nbd_trip(bs, sharing_fds[i], fd_size, dev_offset, - &offset, readonly, data, NBD_BUFFER_SIZE) != 0) { + &offset, nbdflags, data, NBD_BUFFER_SIZE) != 0) { close(sharing_fds[i]); nb_fds--; sharing_fds[i] = sharing_fds[nb_fds]; @@ -479,7 +478,7 @@ int main(int argc, char **argv) (struct sockaddr *)&addr, &addr_len); if (sharing_fds[nb_fds] != -1 && - nbd_negotiate(sharing_fds[nb_fds], fd_size) != -1) { + nbd_negotiate(sharing_fds[nb_fds], fd_size, nbdflags) != -1) { if (sharing_fds[nb_fds] > max_fd) max_fd = sharing_fds[nb_fds]; nb_fds++; -- cgit v1.2.1