From patchwork Mon Aug 30 16:32:30 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kevin Wolf X-Patchwork-Id: 63079 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [199.232.76.165]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 49F80B70FB for ; Tue, 31 Aug 2010 02:44:00 +1000 (EST) Received: from localhost ([127.0.0.1]:50381 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1Oq7Sf-0004SC-N3 for incoming@patchwork.ozlabs.org; Mon, 30 Aug 2010 12:43:57 -0400 Received: from [140.186.70.92] (port=42492 helo=eggs.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1Oq7Ja-0001zh-1a for qemu-devel@nongnu.org; Mon, 30 Aug 2010 12:34:38 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.69) (envelope-from ) id 1Oq7IH-0005C3-Ek for qemu-devel@nongnu.org; Mon, 30 Aug 2010 12:33:17 -0400 Received: from mx1.redhat.com ([209.132.183.28]:56583) by eggs.gnu.org with esmtp (Exim 4.69) (envelope-from ) id 1Oq7IH-0005Bw-3i for qemu-devel@nongnu.org; Mon, 30 Aug 2010 12:33:13 -0400 Received: from int-mx08.intmail.prod.int.phx2.redhat.com (int-mx08.intmail.prod.int.phx2.redhat.com [10.5.11.21]) by mx1.redhat.com (8.13.8/8.13.8) with ESMTP id o7UGWUU6010921 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK); Mon, 30 Aug 2010 12:32:30 -0400 Received: from dhcp-5-188.str.redhat.com (dhcp-5-175.str.redhat.com [10.32.5.175]) by int-mx08.intmail.prod.int.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id o7UGWKth029322; Mon, 30 Aug 2010 12:32:29 -0400 From: Kevin Wolf To: anthony@codemonkey.ws Date: Mon, 30 Aug 2010 18:32:30 +0200 Message-Id: <1283185953-30639-8-git-send-email-kwolf@redhat.com> In-Reply-To: <1283185953-30639-1-git-send-email-kwolf@redhat.com> References: <1283185953-30639-1-git-send-email-kwolf@redhat.com> X-Scanned-By: MIMEDefang 2.67 on 10.5.11.21 X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. Cc: kwolf@redhat.com, qemu-devel@nongnu.org Subject: [Qemu-devel] [PATCH 07/10] nbd: Introduce NBD named exports. X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org From: Laurent Vivier This patch allows to connect Qemu using NBD protocol to an nbd-server using named exports. For instance, if on the host "isoserver", in /etc/nbd-server/config, you have: [generic] [debian-500-ppc-netinst] exportname = /ISO/debian-500-powerpc-netinst.iso [Fedora-10-ppc-netinst] exportname = /ISO/Fedora-10-ppc-netinst.iso You can connect to it, using: qemu -cdrom nbd:isoserver:exportname=debian-500-ppc-netinst qemu -cdrom nbd:isoserver:exportname=Fedora-10-ppc-netinst NOTE: you need at least nbd-server 2.9.18 Signed-off-by: Laurent Vivier Signed-off-by: Kevin Wolf --- block/nbd.c | 68 +++++++++++++++++++++++--------- nbd.c | 118 +++++++++++++++++++++++++++++++++++++++++++++++++++------ nbd.h | 5 ++- qemu-doc.texi | 7 +++ qemu-nbd.c | 4 +- 5 files changed, 169 insertions(+), 33 deletions(-) diff --git a/block/nbd.c b/block/nbd.c index a1ec123..5e9d6cb 100644 --- a/block/nbd.c +++ b/block/nbd.c @@ -33,6 +33,8 @@ #include #include +#define EN_OPTSTR ":exportname=" + typedef struct BDRVNBDState { int sock; off_t size; @@ -42,55 +44,83 @@ typedef struct BDRVNBDState { static int nbd_open(BlockDriverState *bs, const char* filename, int flags) { BDRVNBDState *s = bs->opaque; + uint32_t nbdflags; + + char *file; + char *name; const char *host; const char *unixpath; int sock; off_t size; size_t blocksize; int ret; + int err = -EINVAL; + + file = qemu_strdup(filename); - if (!strstart(filename, "nbd:", &host)) - return -EINVAL; + name = strstr(file, EN_OPTSTR); + if (name) { + if (name[strlen(EN_OPTSTR)] == 0) { + goto out; + } + name[0] = 0; + name += strlen(EN_OPTSTR); + } + + if (!strstart(file, "nbd:", &host)) { + goto out; + } if (strstart(host, "unix:", &unixpath)) { - if (unixpath[0] != '/') - return -EINVAL; + if (unixpath[0] != '/') { + goto out; + } sock = unix_socket_outgoing(unixpath); } else { - uint16_t port; + uint16_t port = NBD_DEFAULT_PORT; char *p, *r; char hostname[128]; pstrcpy(hostname, 128, host); p = strchr(hostname, ':'); - if (p == NULL) - return -EINVAL; + if (p != NULL) { + *p = '\0'; + p++; + + port = strtol(p, &r, 0); + if (r == p) { + goto out; + } + } else if (name == NULL) { + goto out; + } - *p = '\0'; - p++; - - port = strtol(p, &r, 0); - if (r == p) - return -EINVAL; sock = tcp_socket_outgoing(hostname, port); } - if (sock == -1) - return -errno; + if (sock == -1) { + err = -errno; + goto out; + } - ret = nbd_receive_negotiate(sock, &size, &blocksize); - if (ret == -1) - return -errno; + ret = nbd_receive_negotiate(sock, name, &nbdflags, &size, &blocksize); + if (ret == -1) { + err = -errno; + goto out; + } s->sock = sock; s->size = size; s->blocksize = blocksize; + err = 0; - return 0; +out: + qemu_free(file); + return err; } static int nbd_read(BlockDriverState *bs, int64_t sector_num, diff --git a/nbd.c b/nbd.c index a9f295f..30dec8d 100644 --- a/nbd.c +++ b/nbd.c @@ -62,6 +62,8 @@ #define NBD_SET_SIZE_BLOCKS _IO(0xab, 7) #define NBD_DISCONNECT _IO(0xab, 8) +#define NBD_OPT_EXPORT_NAME (1 << 0) + /* That's all folks */ #define read_sync(fd, buffer, size) nbd_wr_sync(fd, buffer, size, true) @@ -296,22 +298,27 @@ int nbd_negotiate(int csock, off_t size) return 0; } -int nbd_receive_negotiate(int csock, off_t *size, size_t *blocksize) +int nbd_receive_negotiate(int csock, const char *name, uint32_t *flags, + off_t *size, size_t *blocksize) { - char buf[8 + 8 + 8 + 128]; - uint64_t magic; + char buf[256]; + uint64_t magic, s; + uint16_t tmp; TRACE("Receiving negotation."); - if (read_sync(csock, buf, sizeof(buf)) != sizeof(buf)) { + if (read_sync(csock, buf, 8) != 8) { LOG("read failed"); errno = EINVAL; return -1; } - magic = be64_to_cpup((uint64_t*)(buf + 8)); - *size = be64_to_cpup((uint64_t*)(buf + 16)); - *blocksize = 1024; + buf[8] = '\0'; + if (strlen(buf) == 0) { + LOG("server connection closed"); + errno = EINVAL; + return -1; + } TRACE("Magic is %c%c%c%c%c%c%c%c", qemu_isprint(buf[0]) ? buf[0] : '.', @@ -322,8 +329,6 @@ int nbd_receive_negotiate(int csock, off_t *size, size_t *blocksize) qemu_isprint(buf[5]) ? buf[5] : '.', qemu_isprint(buf[6]) ? buf[6] : '.', qemu_isprint(buf[7]) ? buf[7] : '.'); - TRACE("Magic is 0x%" PRIx64, magic); - TRACE("Size is %" PRIu64, *size); if (memcmp(buf, "NBDMAGIC", 8) != 0) { LOG("Invalid magic received"); @@ -331,10 +336,99 @@ int nbd_receive_negotiate(int csock, off_t *size, size_t *blocksize) return -1; } - TRACE("Checking magic"); + if (read_sync(csock, &magic, sizeof(magic)) != sizeof(magic)) { + LOG("read failed"); + errno = EINVAL; + return -1; + } + magic = be64_to_cpu(magic); + TRACE("Magic is 0x%" PRIx64, magic); + + if (name) { + uint32_t reserved = 0; + uint32_t opt; + uint32_t namesize; + + TRACE("Checking magic (opts_magic)"); + if (magic != 0x49484156454F5054LL) { + LOG("Bad magic received"); + errno = EINVAL; + return -1; + } + if (read_sync(csock, &tmp, sizeof(tmp)) != sizeof(tmp)) { + LOG("flags read failed"); + errno = EINVAL; + return -1; + } + *flags = be16_to_cpu(tmp) << 16; + /* reserved for future use */ + if (write_sync(csock, &reserved, sizeof(reserved)) != + sizeof(reserved)) { + LOG("write failed (reserved)"); + errno = EINVAL; + return -1; + } + /* write the export name */ + magic = cpu_to_be64(magic); + if (write_sync(csock, &magic, sizeof(magic)) != sizeof(magic)) { + LOG("write failed (magic)"); + errno = EINVAL; + return -1; + } + opt = cpu_to_be32(NBD_OPT_EXPORT_NAME); + if (write_sync(csock, &opt, sizeof(opt)) != sizeof(opt)) { + LOG("write failed (opt)"); + errno = EINVAL; + return -1; + } + namesize = cpu_to_be32(strlen(name)); + if (write_sync(csock, &namesize, sizeof(namesize)) != + sizeof(namesize)) { + LOG("write failed (namesize)"); + errno = EINVAL; + return -1; + } + if (write_sync(csock, (char*)name, strlen(name)) != strlen(name)) { + LOG("write failed (name)"); + errno = EINVAL; + return -1; + } + } else { + TRACE("Checking magic (cli_magic)"); + + if (magic != 0x00420281861253LL) { + LOG("Bad magic received"); + errno = EINVAL; + return -1; + } + } + + if (read_sync(csock, &s, sizeof(s)) != sizeof(s)) { + LOG("read failed"); + errno = EINVAL; + return -1; + } + *size = be64_to_cpu(s); + *blocksize = 1024; + TRACE("Size is %" PRIu64, *size); - if (magic != 0x00420281861253LL) { - LOG("Bad magic received"); + if (!name) { + if (read_sync(csock, flags, sizeof(*flags)) != sizeof(*flags)) { + LOG("read failed (flags)"); + errno = EINVAL; + return -1; + } + *flags = be32_to_cpup(flags); + } else { + if (read_sync(csock, &tmp, sizeof(tmp)) != sizeof(tmp)) { + LOG("read failed (tmp)"); + errno = EINVAL; + return -1; + } + *flags |= be32_to_cpu(tmp); + } + if (read_sync(csock, &buf, 124) != 124) { + LOG("read failed (buf)"); errno = EINVAL; return -1; } diff --git a/nbd.h b/nbd.h index 5a1fbdf..8ff65a1 100644 --- a/nbd.h +++ b/nbd.h @@ -42,6 +42,8 @@ enum { NBD_CMD_DISC = 2 }; +#define NBD_DEFAULT_PORT 10809 + size_t nbd_wr_sync(int fd, void *buffer, size_t size, bool do_read); int tcp_socket_outgoing(const char *address, uint16_t port); int tcp_socket_incoming(const char *address, uint16_t port); @@ -49,7 +51,8 @@ int unix_socket_outgoing(const char *path); int unix_socket_incoming(const char *path); int nbd_negotiate(int csock, off_t size); -int nbd_receive_negotiate(int csock, off_t *size, size_t *blocksize); +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_send_request(int csock, struct nbd_request *request); int nbd_receive_reply(int csock, struct nbd_reply *reply); diff --git a/qemu-doc.texi b/qemu-doc.texi index 55a966f..d7d760f 100644 --- a/qemu-doc.texi +++ b/qemu-doc.texi @@ -620,6 +620,13 @@ qemu linux1.img -hdb nbd:unix:/tmp/my_socket qemu linux2.img -hdb nbd:unix:/tmp/my_socket @end example +If the nbd-server uses named exports (since NBD 2.9.18), you must use the +"exportname" option: +@example +qemu -cdrom nbd:localhost:exportname=debian-500-ppc-netinst +qemu -cdrom nbd:localhost:exportname=openSUSE-11.1-ppc-netinst +@end example + @node pcsys_network @section Network emulation diff --git a/qemu-nbd.c b/qemu-nbd.c index 4e607cf..41e5c5d 100644 --- a/qemu-nbd.c +++ b/qemu-nbd.c @@ -230,6 +230,7 @@ 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) { @@ -398,7 +399,8 @@ int main(int argc, char **argv) goto out; } - ret = nbd_receive_negotiate(sock, &size, &blocksize); + ret = nbd_receive_negotiate(sock, NULL, &nbdflags, + &size, &blocksize); if (ret == -1) { ret = 1; goto out;