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 | ... | ... |