Commit 75818250ba0f61d9e3008b11d8c63b7b46d80ba2
1 parent
3b05a8e9
Allow QEMU to connect directly to an NBD server, by Laurent Vivier.
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@4838 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
9 changed files
with
412 additions
and
76 deletions
Makefile
| ... | ... | @@ -42,7 +42,7 @@ recurse-all: $(SUBDIR_RULES) |
| 42 | 42 | BLOCK_OBJS=cutils.o qemu-malloc.o |
| 43 | 43 | BLOCK_OBJS+=block-cow.o block-qcow.o aes.o block-vmdk.o block-cloop.o |
| 44 | 44 | BLOCK_OBJS+=block-dmg.o block-bochs.o block-vpc.o block-vvfat.o |
| 45 | -BLOCK_OBJS+=block-qcow2.o block-parallels.o | |
| 45 | +BLOCK_OBJS+=block-qcow2.o block-parallels.o block-nbd.o | |
| 46 | 46 | |
| 47 | 47 | ###################################################################### |
| 48 | 48 | # libqemu_common.a: Target independent part of system emulation. The |
| ... | ... | @@ -50,7 +50,7 @@ BLOCK_OBJS+=block-qcow2.o block-parallels.o |
| 50 | 50 | # system emulation, i.e. a single QEMU executable should support all |
| 51 | 51 | # CPUs and machines. |
| 52 | 52 | |
| 53 | -OBJS=$(BLOCK_OBJS) | |
| 53 | +OBJS=nbd.o $(BLOCK_OBJS) | |
| 54 | 54 | OBJS+=readline.o console.o |
| 55 | 55 | OBJS+=block.o |
| 56 | 56 | |
| ... | ... | @@ -159,7 +159,7 @@ libqemu_user.a: $(USER_OBJS) |
| 159 | 159 | rm -f $@ |
| 160 | 160 | $(AR) rcs $@ $(USER_OBJS) |
| 161 | 161 | |
| 162 | -QEMU_IMG_BLOCK_OBJS = $(BLOCK_OBJS) | |
| 162 | +QEMU_IMG_BLOCK_OBJS = nbd.o $(BLOCK_OBJS) | |
| 163 | 163 | ifdef CONFIG_WIN32 |
| 164 | 164 | QEMU_IMG_BLOCK_OBJS += qemu-img-block-raw-win32.o |
| 165 | 165 | else |
| ... | ... | @@ -180,7 +180,7 @@ qemu-img-%.o: %.c |
| 180 | 180 | qemu-nbd-%.o: %.c |
| 181 | 181 | $(CC) $(CFLAGS) $(CPPFLAGS) -DQEMU_NBD -c -o $@ $< |
| 182 | 182 | |
| 183 | -qemu-nbd$(EXESUF): qemu-nbd.o nbd.o qemu-img-block.o \ | |
| 183 | +qemu-nbd$(EXESUF): qemu-nbd.o qemu-nbd-nbd.o qemu-img-block.o \ | |
| 184 | 184 | osdep.o qemu-nbd-block-raw-posix.o $(BLOCK_OBJS) |
| 185 | 185 | $(CC) $(LDFLAGS) -o $@ $^ -lz $(LIBS) |
| 186 | 186 | ... | ... |
block-nbd.c
0 → 100644
| 1 | +/* | |
| 2 | + * QEMU Block driver for NBD | |
| 3 | + * | |
| 4 | + * Copyright (C) 2008 Bull S.A.S. | |
| 5 | + * Author: Laurent Vivier <Laurent.Vivier@bull;net> | |
| 6 | + * | |
| 7 | + * Some parts: | |
| 8 | + * Copyright (C) 2007 Anthony Liguori <anthony@codemonkey.ws> | |
| 9 | + * | |
| 10 | + * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 11 | + * of this software and associated documentation files (the "Software"), to deal | |
| 12 | + * in the Software without restriction, including without limitation the rights | |
| 13 | + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 14 | + * copies of the Software, and to permit persons to whom the Software is | |
| 15 | + * furnished to do so, subject to the following conditions: | |
| 16 | + * | |
| 17 | + * The above copyright notice and this permission notice shall be included in | |
| 18 | + * all copies or substantial portions of the Software. | |
| 19 | + * | |
| 20 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 21 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 22 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
| 23 | + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 24 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 25 | + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 26 | + * THE SOFTWARE. | |
| 27 | + */ | |
| 28 | + | |
| 29 | +#include "qemu-common.h" | |
| 30 | +#include "nbd.h" | |
| 31 | + | |
| 32 | +#include <sys/types.h> | |
| 33 | +#include <unistd.h> | |
| 34 | +#include <sys/socket.h> | |
| 35 | +#include <sys/un.h> | |
| 36 | +#include <netinet/in.h> | |
| 37 | +#include <arpa/inet.h> | |
| 38 | +#include <pthread.h> | |
| 39 | + | |
| 40 | +typedef struct BDRVNBDState { | |
| 41 | + int sock; | |
| 42 | + off_t size; | |
| 43 | + size_t blocksize; | |
| 44 | +} BDRVNBDState; | |
| 45 | + | |
| 46 | +static int nbd_open(BlockDriverState *bs, const char* filename, int flags) | |
| 47 | +{ | |
| 48 | + BDRVNBDState *s = bs->opaque; | |
| 49 | + const char *host; | |
| 50 | + const char *unixpath; | |
| 51 | + int sock; | |
| 52 | + off_t size; | |
| 53 | + size_t blocksize; | |
| 54 | + int ret; | |
| 55 | + | |
| 56 | + if ((flags & BDRV_O_CREAT)) | |
| 57 | + return -EINVAL; | |
| 58 | + | |
| 59 | + if (!strstart(filename, "nbd:", &host)) | |
| 60 | + return -EINVAL; | |
| 61 | + | |
| 62 | + if (strstart(host, "unix:", &unixpath)) { | |
| 63 | + | |
| 64 | + if (unixpath[0] != '/') | |
| 65 | + return -EINVAL; | |
| 66 | + | |
| 67 | + sock = unix_socket_outgoing(unixpath); | |
| 68 | + | |
| 69 | + } else { | |
| 70 | + uint16_t port; | |
| 71 | + char *p, *r; | |
| 72 | + char hostname[128]; | |
| 73 | + | |
| 74 | + pstrcpy(hostname, 128, host); | |
| 75 | + | |
| 76 | + p = strchr(hostname, ':'); | |
| 77 | + if (p == NULL) | |
| 78 | + return -EINVAL; | |
| 79 | + | |
| 80 | + *p = '\0'; | |
| 81 | + p++; | |
| 82 | + | |
| 83 | + port = strtol(p, &r, 0); | |
| 84 | + if (r == p) | |
| 85 | + return -EINVAL; | |
| 86 | + sock = tcp_socket_outgoing(hostname, port); | |
| 87 | + } | |
| 88 | + | |
| 89 | + if (sock == -1) | |
| 90 | + return -errno; | |
| 91 | + | |
| 92 | + ret = nbd_receive_negotiate(sock, &size, &blocksize); | |
| 93 | + if (ret == -1) | |
| 94 | + return -errno; | |
| 95 | + | |
| 96 | + s->sock = sock; | |
| 97 | + s->size = size; | |
| 98 | + s->blocksize = blocksize; | |
| 99 | + | |
| 100 | + return 0; | |
| 101 | +} | |
| 102 | + | |
| 103 | +static int nbd_read(BlockDriverState *bs, int64_t sector_num, | |
| 104 | + uint8_t *buf, int nb_sectors) | |
| 105 | +{ | |
| 106 | + BDRVNBDState *s = bs->opaque; | |
| 107 | + struct nbd_request request; | |
| 108 | + struct nbd_reply reply; | |
| 109 | + | |
| 110 | + request.type = NBD_CMD_READ; | |
| 111 | + request.handle = (uint64_t)(intptr_t)bs; | |
| 112 | + request.from = sector_num * 512;; | |
| 113 | + request.len = nb_sectors * 512; | |
| 114 | + | |
| 115 | + if (nbd_send_request(s->sock, &request) == -1) | |
| 116 | + return -errno; | |
| 117 | + | |
| 118 | + if (nbd_receive_reply(s->sock, &reply) == -1) | |
| 119 | + return -errno; | |
| 120 | + | |
| 121 | + if (reply.error !=0) | |
| 122 | + return -reply.error; | |
| 123 | + | |
| 124 | + if (reply.handle != request.handle) | |
| 125 | + return -EIO; | |
| 126 | + | |
| 127 | + if (nbd_wr_sync(s->sock, buf, request.len, 1) != request.len) | |
| 128 | + return -EIO; | |
| 129 | + | |
| 130 | + return 0; | |
| 131 | +} | |
| 132 | + | |
| 133 | +static int nbd_write(BlockDriverState *bs, int64_t sector_num, | |
| 134 | + const uint8_t *buf, int nb_sectors) | |
| 135 | +{ | |
| 136 | + BDRVNBDState *s = bs->opaque; | |
| 137 | + struct nbd_request request; | |
| 138 | + struct nbd_reply reply; | |
| 139 | + | |
| 140 | + request.type = NBD_CMD_WRITE; | |
| 141 | + request.handle = (uint64_t)(intptr_t)bs; | |
| 142 | + request.from = sector_num * 512;; | |
| 143 | + request.len = nb_sectors * 512; | |
| 144 | + | |
| 145 | + if (nbd_send_request(s->sock, &request) == -1) | |
| 146 | + return -errno; | |
| 147 | + | |
| 148 | + if (nbd_wr_sync(s->sock, (uint8_t*)buf, request.len, 0) != request.len) | |
| 149 | + return -EIO; | |
| 150 | + | |
| 151 | + if (nbd_receive_reply(s->sock, &reply) == -1) | |
| 152 | + return -errno; | |
| 153 | + | |
| 154 | + if (reply.error !=0) | |
| 155 | + return -reply.error; | |
| 156 | + | |
| 157 | + if (reply.handle != request.handle) | |
| 158 | + return -EIO; | |
| 159 | + | |
| 160 | + return 0; | |
| 161 | +} | |
| 162 | + | |
| 163 | +static void nbd_close(BlockDriverState *bs) | |
| 164 | +{ | |
| 165 | + BDRVNBDState *s = bs->opaque; | |
| 166 | + struct nbd_request request; | |
| 167 | + | |
| 168 | + request.type = NBD_CMD_DISC; | |
| 169 | + request.handle = (uint64_t)(intptr_t)bs; | |
| 170 | + request.from = 0; | |
| 171 | + request.len = 0; | |
| 172 | + nbd_send_request(s->sock, &request); | |
| 173 | + | |
| 174 | + close(s->sock); | |
| 175 | +} | |
| 176 | + | |
| 177 | +static int64_t nbd_getlength(BlockDriverState *bs) | |
| 178 | +{ | |
| 179 | + BDRVNBDState *s = bs->opaque; | |
| 180 | + | |
| 181 | + return s->size; | |
| 182 | +} | |
| 183 | + | |
| 184 | +BlockDriver bdrv_nbd = { | |
| 185 | + "nbd", | |
| 186 | + sizeof(BDRVNBDState), | |
| 187 | + NULL, /* no probe for protocols */ | |
| 188 | + nbd_open, | |
| 189 | + nbd_read, | |
| 190 | + nbd_write, | |
| 191 | + nbd_close, | |
| 192 | + .bdrv_getlength = nbd_getlength, | |
| 193 | + .protocol_name = "nbd", | |
| 194 | +}; | ... | ... |
block.c
| ... | ... | @@ -1332,6 +1332,7 @@ void bdrv_init(void) |
| 1332 | 1332 | bdrv_register(&bdrv_vvfat); |
| 1333 | 1333 | bdrv_register(&bdrv_qcow2); |
| 1334 | 1334 | bdrv_register(&bdrv_parallels); |
| 1335 | + bdrv_register(&bdrv_nbd); | |
| 1335 | 1336 | } |
| 1336 | 1337 | |
| 1337 | 1338 | void *qemu_aio_get(BlockDriverState *bs, BlockDriverCompletionFunc *cb, | ... | ... |
block.h
nbd.c
| 1 | -/*\ | |
| 1 | +/* | |
| 2 | 2 | * Copyright (C) 2005 Anthony Liguori <anthony@codemonkey.ws> |
| 3 | 3 | * |
| 4 | 4 | * Network Block Device |
| ... | ... | @@ -15,7 +15,7 @@ |
| 15 | 15 | * You should have received a copy of the GNU General Public License |
| 16 | 16 | * along with this program; if not, write to the Free Software |
| 17 | 17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| 18 | -\*/ | |
| 18 | + */ | |
| 19 | 19 | |
| 20 | 20 | #include "nbd.h" |
| 21 | 21 | |
| ... | ... | @@ -31,17 +31,21 @@ |
| 31 | 31 | #include <arpa/inet.h> |
| 32 | 32 | #include <netdb.h> |
| 33 | 33 | |
| 34 | +#if defined(QEMU_NBD) | |
| 34 | 35 | extern int verbose; |
| 36 | +#else | |
| 37 | +static int verbose = 0; | |
| 38 | +#endif | |
| 39 | + | |
| 40 | +#define TRACE(msg, ...) do { \ | |
| 41 | + if (verbose) LOG(msg, ## __VA_ARGS__); \ | |
| 42 | +} while(0) | |
| 35 | 43 | |
| 36 | 44 | #define LOG(msg, ...) do { \ |
| 37 | 45 | fprintf(stderr, "%s:%s():L%d: " msg "\n", \ |
| 38 | 46 | __FILE__, __FUNCTION__, __LINE__, ## __VA_ARGS__); \ |
| 39 | 47 | } while(0) |
| 40 | 48 | |
| 41 | -#define TRACE(msg, ...) do { \ | |
| 42 | - if (verbose) LOG(msg, ## __VA_ARGS__); \ | |
| 43 | -} while(0) | |
| 44 | - | |
| 45 | 49 | /* This is all part of the "official" NBD API */ |
| 46 | 50 | |
| 47 | 51 | #define NBD_REQUEST_MAGIC 0x25609513 |
| ... | ... | @@ -59,10 +63,10 @@ extern int verbose; |
| 59 | 63 | |
| 60 | 64 | /* That's all folks */ |
| 61 | 65 | |
| 62 | -#define read_sync(fd, buffer, size) wr_sync(fd, buffer, size, true) | |
| 63 | -#define write_sync(fd, buffer, size) wr_sync(fd, buffer, size, false) | |
| 66 | +#define read_sync(fd, buffer, size) nbd_wr_sync(fd, buffer, size, true) | |
| 67 | +#define write_sync(fd, buffer, size) nbd_wr_sync(fd, buffer, size, false) | |
| 64 | 68 | |
| 65 | -static size_t wr_sync(int fd, void *buffer, size_t size, bool do_read) | |
| 69 | +size_t nbd_wr_sync(int fd, void *buffer, size_t size, bool do_read) | |
| 66 | 70 | { |
| 67 | 71 | size_t offset = 0; |
| 68 | 72 | |
| ... | ... | @@ -76,7 +80,7 @@ static size_t wr_sync(int fd, void *buffer, size_t size, bool do_read) |
| 76 | 80 | } |
| 77 | 81 | |
| 78 | 82 | /* recoverable error */ |
| 79 | - if (len == -1 && errno == EAGAIN) { | |
| 83 | + if (len == -1 && (errno == EAGAIN || errno == EINTR)) { | |
| 80 | 84 | continue; |
| 81 | 85 | } |
| 82 | 86 | |
| ... | ... | @@ -96,7 +100,7 @@ static size_t wr_sync(int fd, void *buffer, size_t size, bool do_read) |
| 96 | 100 | return offset; |
| 97 | 101 | } |
| 98 | 102 | |
| 99 | -static int tcp_socket_outgoing(const char *address, uint16_t port) | |
| 103 | +int tcp_socket_outgoing(const char *address, uint16_t port) | |
| 100 | 104 | { |
| 101 | 105 | int s; |
| 102 | 106 | struct in_addr in; |
| ... | ... | @@ -404,16 +408,31 @@ int nbd_client(int fd, int csock) |
| 404 | 408 | return ret; |
| 405 | 409 | } |
| 406 | 410 | |
| 407 | -int nbd_trip(BlockDriverState *bs, int csock, off_t size, uint64_t dev_offset, | |
| 408 | - off_t *offset, bool readonly, uint8_t *data, int data_size) | |
| 411 | +int nbd_send_request(int csock, struct nbd_request *request) | |
| 409 | 412 | { |
| 410 | 413 | uint8_t buf[4 + 4 + 8 + 8 + 4]; |
| 411 | - uint32_t magic; | |
| 412 | - uint32_t type; | |
| 413 | - uint64_t from; | |
| 414 | - uint32_t len; | |
| 415 | 414 | |
| 416 | - TRACE("Reading request."); | |
| 415 | + cpu_to_be32w((uint32_t*)buf, NBD_REQUEST_MAGIC); | |
| 416 | + cpu_to_be32w((uint32_t*)(buf + 4), request->type); | |
| 417 | + cpu_to_be64w((uint64_t*)(buf + 8), request->handle); | |
| 418 | + cpu_to_be64w((uint64_t*)(buf + 16), request->from); | |
| 419 | + cpu_to_be32w((uint32_t*)(buf + 24), request->len); | |
| 420 | + | |
| 421 | + TRACE("Sending request to client"); | |
| 422 | + | |
| 423 | + if (write_sync(csock, buf, sizeof(buf)) != sizeof(buf)) { | |
| 424 | + LOG("writing to socket failed"); | |
| 425 | + errno = EINVAL; | |
| 426 | + return -1; | |
| 427 | + } | |
| 428 | + return 0; | |
| 429 | +} | |
| 430 | + | |
| 431 | + | |
| 432 | +static int nbd_receive_request(int csock, struct nbd_request *request) | |
| 433 | +{ | |
| 434 | + uint8_t buf[4 + 4 + 8 + 8 + 4]; | |
| 435 | + uint32_t magic; | |
| 417 | 436 | |
| 418 | 437 | if (read_sync(csock, buf, sizeof(buf)) != sizeof(buf)) { |
| 419 | 438 | LOG("read failed"); |
| ... | ... | @@ -422,97 +441,158 @@ int nbd_trip(BlockDriverState *bs, int csock, off_t size, uint64_t dev_offset, |
| 422 | 441 | } |
| 423 | 442 | |
| 424 | 443 | /* Request |
| 425 | - [ 0 .. 3] magic (NBD_REQUEST_MAGIC) | |
| 426 | - [ 4 .. 7] type (0 == READ, 1 == WRITE) | |
| 427 | - [ 8 .. 15] handle | |
| 428 | - [16 .. 23] from | |
| 429 | - [24 .. 27] len | |
| 444 | + [ 0 .. 3] magic (NBD_REQUEST_MAGIC) | |
| 445 | + [ 4 .. 7] type (0 == READ, 1 == WRITE) | |
| 446 | + [ 8 .. 15] handle | |
| 447 | + [16 .. 23] from | |
| 448 | + [24 .. 27] len | |
| 430 | 449 | */ |
| 431 | 450 | |
| 432 | 451 | magic = be32_to_cpup((uint32_t*)buf); |
| 433 | - type = be32_to_cpup((uint32_t*)(buf + 4)); | |
| 434 | - from = be64_to_cpup((uint64_t*)(buf + 16)); | |
| 435 | - len = be32_to_cpup((uint32_t*)(buf + 24)); | |
| 452 | + request->type = be32_to_cpup((uint32_t*)(buf + 4)); | |
| 453 | + request->handle = be64_to_cpup((uint64_t*)(buf + 8)); | |
| 454 | + request->from = be64_to_cpup((uint64_t*)(buf + 16)); | |
| 455 | + request->len = be32_to_cpup((uint32_t*)(buf + 24)); | |
| 436 | 456 | |
| 437 | 457 | TRACE("Got request: " |
| 438 | 458 | "{ magic = 0x%x, .type = %d, from = %" PRIu64" , len = %u }", |
| 439 | - magic, type, from, len); | |
| 440 | - | |
| 459 | + magic, request->type, request->from, request->len); | |
| 441 | 460 | |
| 442 | 461 | if (magic != NBD_REQUEST_MAGIC) { |
| 443 | 462 | LOG("invalid magic (got 0x%x)", magic); |
| 444 | 463 | errno = EINVAL; |
| 445 | 464 | return -1; |
| 446 | 465 | } |
| 466 | +} | |
| 467 | + | |
| 468 | +int nbd_receive_reply(int csock, struct nbd_reply *reply) | |
| 469 | +{ | |
| 470 | + uint8_t buf[4 + 4 + 8]; | |
| 471 | + uint32_t magic; | |
| 472 | + | |
| 473 | + memset(buf, 0xAA, sizeof(buf)); | |
| 474 | + | |
| 475 | + if (read_sync(csock, buf, sizeof(buf)) != sizeof(buf)) { | |
| 476 | + LOG("read failed"); | |
| 477 | + errno = EINVAL; | |
| 478 | + return -1; | |
| 479 | + } | |
| 480 | + | |
| 481 | + /* Reply | |
| 482 | + [ 0 .. 3] magic (NBD_REPLY_MAGIC) | |
| 483 | + [ 4 .. 7] error (0 == no error) | |
| 484 | + [ 7 .. 15] handle | |
| 485 | + */ | |
| 486 | + | |
| 487 | + magic = be32_to_cpup((uint32_t*)buf); | |
| 488 | + reply->error = be32_to_cpup((uint32_t*)(buf + 4)); | |
| 489 | + reply->handle = be64_to_cpup((uint64_t*)(buf + 8)); | |
| 490 | + | |
| 491 | + TRACE("Got reply: " | |
| 492 | + "{ magic = 0x%x, .error = %d, handle = %" PRIu64" }", | |
| 493 | + magic, reply->error, reply->handle); | |
| 494 | + | |
| 495 | + if (magic != NBD_REPLY_MAGIC) { | |
| 496 | + LOG("invalid magic (got 0x%x)", magic); | |
| 497 | + errno = EINVAL; | |
| 498 | + return -1; | |
| 499 | + } | |
| 500 | + return 0; | |
| 501 | +} | |
| 502 | + | |
| 503 | +static int nbd_send_reply(int csock, struct nbd_reply *reply) | |
| 504 | +{ | |
| 505 | + uint8_t buf[4 + 4 + 8]; | |
| 506 | + | |
| 507 | + /* Reply | |
| 508 | + [ 0 .. 3] magic (NBD_REPLY_MAGIC) | |
| 509 | + [ 4 .. 7] error (0 == no error) | |
| 510 | + [ 7 .. 15] handle | |
| 511 | + */ | |
| 512 | + cpu_to_be32w((uint32_t*)buf, NBD_REPLY_MAGIC); | |
| 513 | + cpu_to_be32w((uint32_t*)(buf + 4), reply->error); | |
| 514 | + cpu_to_be64w((uint64_t*)(buf + 8), reply->handle); | |
| 515 | + | |
| 516 | + TRACE("Sending response to client"); | |
| 517 | + | |
| 518 | + if (write_sync(csock, buf, sizeof(buf)) != sizeof(buf)) { | |
| 519 | + LOG("writing to socket failed"); | |
| 520 | + errno = EINVAL; | |
| 521 | + return -1; | |
| 522 | + } | |
| 523 | + return 0; | |
| 524 | +} | |
| 447 | 525 | |
| 448 | - if (len > data_size) { | |
| 526 | +int nbd_trip(BlockDriverState *bs, int csock, off_t size, uint64_t dev_offset, | |
| 527 | + off_t *offset, bool readonly, uint8_t *data, int data_size) | |
| 528 | +{ | |
| 529 | + struct nbd_request request; | |
| 530 | + struct nbd_reply reply; | |
| 531 | + | |
| 532 | + TRACE("Reading request."); | |
| 533 | + | |
| 534 | + if (nbd_receive_request(csock, &request) == -1) | |
| 535 | + return -1; | |
| 536 | + | |
| 537 | + if (request.len > data_size) { | |
| 449 | 538 | LOG("len (%u) is larger than max len (%u)", |
| 450 | - len, data_size); | |
| 539 | + request.len, data_size); | |
| 451 | 540 | errno = EINVAL; |
| 452 | 541 | return -1; |
| 453 | 542 | } |
| 454 | 543 | |
| 455 | - if ((from + len) < from) { | |
| 544 | + if ((request.from + request.len) < request.from) { | |
| 456 | 545 | LOG("integer overflow detected! " |
| 457 | 546 | "you're probably being attacked"); |
| 458 | 547 | errno = EINVAL; |
| 459 | 548 | return -1; |
| 460 | 549 | } |
| 461 | 550 | |
| 462 | - if ((from + len) > size) { | |
| 551 | + if ((request.from + request.len) > size) { | |
| 463 | 552 | LOG("From: %" PRIu64 ", Len: %u, Size: %" PRIu64 |
| 464 | 553 | ", Offset: %" PRIu64 "\n", |
| 465 | - from, len, size, dev_offset); | |
| 554 | + request.from, request.len, size, dev_offset); | |
| 466 | 555 | LOG("requested operation past EOF--bad client?"); |
| 467 | 556 | errno = EINVAL; |
| 468 | 557 | return -1; |
| 469 | 558 | } |
| 470 | 559 | |
| 471 | - /* Reply | |
| 472 | - [ 0 .. 3] magic (NBD_REPLY_MAGIC) | |
| 473 | - [ 4 .. 7] error (0 == no error) | |
| 474 | - [ 7 .. 15] handle | |
| 475 | - */ | |
| 476 | - cpu_to_be32w((uint32_t*)buf, NBD_REPLY_MAGIC); | |
| 477 | - cpu_to_be32w((uint32_t*)(buf + 4), 0); | |
| 478 | - | |
| 479 | 560 | TRACE("Decoding type"); |
| 480 | 561 | |
| 481 | - switch (type) { | |
| 482 | - case 0: | |
| 562 | + reply.handle = request.handle; | |
| 563 | + reply.error = 0; | |
| 564 | + | |
| 565 | + switch (request.type) { | |
| 566 | + case NBD_CMD_READ: | |
| 483 | 567 | TRACE("Request type is READ"); |
| 484 | 568 | |
| 485 | - if (bdrv_read(bs, (from + dev_offset) / 512, data, len / 512) == -1) { | |
| 569 | + if (bdrv_read(bs, (request.from + dev_offset) / 512, data, | |
| 570 | + request.len / 512) == -1) { | |
| 486 | 571 | LOG("reading from file failed"); |
| 487 | 572 | errno = EINVAL; |
| 488 | 573 | return -1; |
| 489 | 574 | } |
| 490 | - *offset += len; | |
| 491 | - | |
| 492 | - TRACE("Read %u byte(s)", len); | |
| 575 | + *offset += request.len; | |
| 493 | 576 | |
| 494 | - TRACE("Sending OK response"); | |
| 577 | + TRACE("Read %u byte(s)", request.len); | |
| 495 | 578 | |
| 496 | - if (write_sync(csock, buf, 16) != 16) { | |
| 497 | - LOG("writing to socket failed"); | |
| 498 | - errno = EINVAL; | |
| 579 | + if (nbd_send_reply(csock, &reply) == -1) | |
| 499 | 580 | return -1; |
| 500 | - } | |
| 501 | 581 | |
| 502 | 582 | TRACE("Sending data to client"); |
| 503 | 583 | |
| 504 | - if (write_sync(csock, data, len) != len) { | |
| 584 | + if (write_sync(csock, data, request.len) != request.len) { | |
| 505 | 585 | LOG("writing to socket failed"); |
| 506 | 586 | errno = EINVAL; |
| 507 | 587 | return -1; |
| 508 | 588 | } |
| 509 | 589 | break; |
| 510 | - case 1: | |
| 590 | + case NBD_CMD_WRITE: | |
| 511 | 591 | TRACE("Request type is WRITE"); |
| 512 | 592 | |
| 513 | - TRACE("Reading %u byte(s)", len); | |
| 593 | + TRACE("Reading %u byte(s)", request.len); | |
| 514 | 594 | |
| 515 | - if (read_sync(csock, data, len) != len) { | |
| 595 | + if (read_sync(csock, data, request.len) != request.len) { | |
| 516 | 596 | LOG("reading from socket failed"); |
| 517 | 597 | errno = EINVAL; |
| 518 | 598 | return -1; |
| ... | ... | @@ -520,34 +600,29 @@ int nbd_trip(BlockDriverState *bs, int csock, off_t size, uint64_t dev_offset, |
| 520 | 600 | |
| 521 | 601 | if (readonly) { |
| 522 | 602 | TRACE("Server is read-only, return error"); |
| 523 | - | |
| 524 | - cpu_to_be32w((uint32_t*)(buf + 4), 1); | |
| 603 | + reply.error = 1; | |
| 525 | 604 | } else { |
| 526 | 605 | TRACE("Writing to device"); |
| 527 | 606 | |
| 528 | - if (bdrv_write(bs, (from + dev_offset) / 512, data, len / 512) == -1) { | |
| 607 | + if (bdrv_write(bs, (request.from + dev_offset) / 512, | |
| 608 | + data, request.len / 512) == -1) { | |
| 529 | 609 | LOG("writing to file failed"); |
| 530 | 610 | errno = EINVAL; |
| 531 | 611 | return -1; |
| 532 | 612 | } |
| 533 | 613 | |
| 534 | - *offset += len; | |
| 614 | + *offset += request.len; | |
| 535 | 615 | } |
| 536 | 616 | |
| 537 | - TRACE("Sending response to client"); | |
| 538 | - | |
| 539 | - if (write_sync(csock, buf, 16) != 16) { | |
| 540 | - LOG("writing to socket failed"); | |
| 541 | - errno = EINVAL; | |
| 617 | + if (nbd_send_reply(csock, &reply) == -1) | |
| 542 | 618 | return -1; |
| 543 | - } | |
| 544 | 619 | break; |
| 545 | - case 2: | |
| 620 | + case NBD_CMD_DISC: | |
| 546 | 621 | TRACE("Request type is DISCONNECT"); |
| 547 | 622 | errno = 0; |
| 548 | 623 | return 1; |
| 549 | 624 | default: |
| 550 | - LOG("invalid request type (%u) received", type); | |
| 625 | + LOG("invalid request type (%u) received", request.type); | |
| 551 | 626 | errno = EINVAL; |
| 552 | 627 | return -1; |
| 553 | 628 | } | ... | ... |
nbd.h
| 1 | -/*\ | |
| 1 | +/* | |
| 2 | 2 | * Copyright (C) 2005 Anthony Liguori <anthony@codemonkey.ws> |
| 3 | 3 | * |
| 4 | 4 | * Network Block Device |
| ... | ... | @@ -15,7 +15,7 @@ |
| 15 | 15 | * You should have received a copy of the GNU General Public License |
| 16 | 16 | * along with this program; if not, write to the Free Software |
| 17 | 17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| 18 | -\*/ | |
| 18 | + */ | |
| 19 | 19 | |
| 20 | 20 | #ifndef NBD_H |
| 21 | 21 | #define NBD_H |
| ... | ... | @@ -26,6 +26,26 @@ |
| 26 | 26 | #include <qemu-common.h> |
| 27 | 27 | #include "block_int.h" |
| 28 | 28 | |
| 29 | +struct nbd_request { | |
| 30 | + uint32_t type; | |
| 31 | + uint64_t handle; | |
| 32 | + uint64_t from; | |
| 33 | + uint32_t len; | |
| 34 | +}; | |
| 35 | + | |
| 36 | +struct nbd_reply { | |
| 37 | + uint32_t error; | |
| 38 | + uint64_t handle; | |
| 39 | +}; | |
| 40 | + | |
| 41 | +enum { | |
| 42 | + NBD_CMD_READ = 0, | |
| 43 | + NBD_CMD_WRITE = 1, | |
| 44 | + NBD_CMD_DISC = 2 | |
| 45 | +}; | |
| 46 | + | |
| 47 | +size_t nbd_wr_sync(int fd, void *buffer, size_t size, bool do_read); | |
| 48 | +int tcp_socket_outgoing(const char *address, uint16_t port); | |
| 29 | 49 | int tcp_socket_incoming(const char *address, uint16_t port); |
| 30 | 50 | int unix_socket_outgoing(const char *path); |
| 31 | 51 | int unix_socket_incoming(const char *path); |
| ... | ... | @@ -33,6 +53,8 @@ int unix_socket_incoming(const char *path); |
| 33 | 53 | int nbd_negotiate(BlockDriverState *bs, int csock, off_t size); |
| 34 | 54 | int nbd_receive_negotiate(int csock, off_t *size, size_t *blocksize); |
| 35 | 55 | int nbd_init(int fd, int csock, off_t size, size_t blocksize); |
| 56 | +int nbd_send_request(int csock, struct nbd_request *request); | |
| 57 | +int nbd_receive_reply(int csock, struct nbd_reply *reply); | |
| 36 | 58 | int nbd_trip(BlockDriverState *bs, int csock, off_t size, uint64_t dev_offset, |
| 37 | 59 | off_t *offset, bool readonly, uint8_t *data, int data_size); |
| 38 | 60 | int nbd_client(int fd, int csock); | ... | ... |
qemu-doc.texi
| ... | ... | @@ -1321,6 +1321,7 @@ snapshots. |
| 1321 | 1321 | * qemu_nbd_invocation:: qemu-nbd Invocation |
| 1322 | 1322 | * host_drives:: Using host drives |
| 1323 | 1323 | * disk_images_fat_images:: Virtual FAT disk images |
| 1324 | +* disk_images_nbd:: NBD access | |
| 1324 | 1325 | @end menu |
| 1325 | 1326 | |
| 1326 | 1327 | @node disk_images_quickstart |
| ... | ... | @@ -1503,6 +1504,40 @@ What you should @emph{never} do: |
| 1503 | 1504 | @item write to the FAT directory on the host system while accessing it with the guest system. |
| 1504 | 1505 | @end itemize |
| 1505 | 1506 | |
| 1507 | +@node disk_images_nbd | |
| 1508 | +@subsection NBD access | |
| 1509 | + | |
| 1510 | +QEMU can access directly to block device exported using the Network Block Device | |
| 1511 | +protocol. | |
| 1512 | + | |
| 1513 | +@example | |
| 1514 | +qemu linux.img -hdb nbd:my_nbd_server.mydomain.org:1024 | |
| 1515 | +@end example | |
| 1516 | + | |
| 1517 | +If the NBD server is located on the same host, you can use an unix socket instead | |
| 1518 | +of an inet socket: | |
| 1519 | + | |
| 1520 | +@example | |
| 1521 | +qemu linux.img -hdb nbd:unix:/tmp/my_socket | |
| 1522 | +@end example | |
| 1523 | + | |
| 1524 | +In this case, the block device must be exported using qemu-nbd: | |
| 1525 | + | |
| 1526 | +@example | |
| 1527 | +qemu-nbd --socket=/tmp/my_socket my_disk.qcow2 | |
| 1528 | +@end example | |
| 1529 | + | |
| 1530 | +The use of qemu-nbd allows to share a disk between several guests: | |
| 1531 | +@example | |
| 1532 | +qemu-nbd --socket=/tmp/my_socket --share=2 my_disk.qcow2 | |
| 1533 | +@end example | |
| 1534 | + | |
| 1535 | +and then you can use it with two guests: | |
| 1536 | +@example | |
| 1537 | +qemu linux1.img -hdb nbd:unix:/tmp/my_socket | |
| 1538 | +qemu linux2.img -hdb nbd:unix:/tmp/my_socket | |
| 1539 | +@end example | |
| 1540 | + | |
| 1506 | 1541 | @node pcsys_network |
| 1507 | 1542 | @section Network emulation |
| 1508 | 1543 | ... | ... |
qemu-nbd.c
| ... | ... | @@ -56,6 +56,7 @@ static void usage(const char *name) |
| 56 | 56 | " -c, --connect=DEV connect FILE to the local NBD device DEV\n" |
| 57 | 57 | " -d, --disconnect disconnect the specified device\n" |
| 58 | 58 | " -e, --shared=NUM device can be shared by NUM clients (default '1')\n" |
| 59 | +" -t, --persistent don't exit on the last connection\n" | |
| 59 | 60 | " -v, --verbose display extra debugging information\n" |
| 60 | 61 | " -h, --help display this help and exit\n" |
| 61 | 62 | " -V, --version output version information and exit\n" |
| ... | ... | @@ -189,7 +190,7 @@ int main(int argc, char **argv) |
| 189 | 190 | char *device = NULL; |
| 190 | 191 | char *socket = NULL; |
| 191 | 192 | char sockpath[128]; |
| 192 | - const char *sopt = "hVbo:p:rsnP:c:dvk:e:"; | |
| 193 | + const char *sopt = "hVbo:p:rsnP:c:dvk:e:t"; | |
| 193 | 194 | struct option lopt[] = { |
| 194 | 195 | { "help", 0, 0, 'h' }, |
| 195 | 196 | { "version", 0, 0, 'V' }, |
| ... | ... | @@ -204,6 +205,7 @@ int main(int argc, char **argv) |
| 204 | 205 | { "snapshot", 0, 0, 's' }, |
| 205 | 206 | { "nocache", 0, 0, 'n' }, |
| 206 | 207 | { "shared", 1, 0, 'e' }, |
| 208 | + { "persistent", 0, 0, 't' }, | |
| 207 | 209 | { "verbose", 0, 0, 'v' }, |
| 208 | 210 | { NULL, 0, 0, 0 } |
| 209 | 211 | }; |
| ... | ... | @@ -222,6 +224,7 @@ int main(int argc, char **argv) |
| 222 | 224 | int i; |
| 223 | 225 | int nb_fds = 0; |
| 224 | 226 | int max_fd; |
| 227 | + int persistent = 0; | |
| 225 | 228 | |
| 226 | 229 | while ((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) { |
| 227 | 230 | switch (ch) { |
| ... | ... | @@ -283,6 +286,9 @@ int main(int argc, char **argv) |
| 283 | 286 | errx(EINVAL, "Shared device number must be greater than 0\n"); |
| 284 | 287 | } |
| 285 | 288 | break; |
| 289 | + case 't': | |
| 290 | + persistent = 1; | |
| 291 | + break; | |
| 286 | 292 | case 'v': |
| 287 | 293 | verbose = 1; |
| 288 | 294 | break; |
| ... | ... | @@ -459,7 +465,7 @@ int main(int argc, char **argv) |
| 459 | 465 | } |
| 460 | 466 | } |
| 461 | 467 | } |
| 462 | - } while (nb_fds > 1); | |
| 468 | + } while (persistent || nb_fds > 1); | |
| 463 | 469 | qemu_free(data); |
| 464 | 470 | |
| 465 | 471 | close(sharing_fds[0]); | ... | ... |
qemu-nbd.texi
| ... | ... | @@ -36,6 +36,8 @@ Export Qemu disk image using NBD protocol. |
| 36 | 36 | disconnect the specified device |
| 37 | 37 | @item -e, --shared=NUM |
| 38 | 38 | device can be shared by NUM clients (default '1') |
| 39 | +@item -t, --persistent | |
| 40 | + don't exit on the last connection | |
| 39 | 41 | @item -v, --verbose |
| 40 | 42 | display extra debugging information |
| 41 | 43 | @item -h, --help | ... | ... |