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,7 +42,7 @@ recurse-all: $(SUBDIR_RULES) | ||
42 | BLOCK_OBJS=cutils.o qemu-malloc.o | 42 | BLOCK_OBJS=cutils.o qemu-malloc.o |
43 | BLOCK_OBJS+=block-cow.o block-qcow.o aes.o block-vmdk.o block-cloop.o | 43 | BLOCK_OBJS+=block-cow.o block-qcow.o aes.o block-vmdk.o block-cloop.o |
44 | BLOCK_OBJS+=block-dmg.o block-bochs.o block-vpc.o block-vvfat.o | 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 | # libqemu_common.a: Target independent part of system emulation. The | 48 | # libqemu_common.a: Target independent part of system emulation. The |
@@ -50,7 +50,7 @@ BLOCK_OBJS+=block-qcow2.o block-parallels.o | @@ -50,7 +50,7 @@ BLOCK_OBJS+=block-qcow2.o block-parallels.o | ||
50 | # system emulation, i.e. a single QEMU executable should support all | 50 | # system emulation, i.e. a single QEMU executable should support all |
51 | # CPUs and machines. | 51 | # CPUs and machines. |
52 | 52 | ||
53 | -OBJS=$(BLOCK_OBJS) | 53 | +OBJS=nbd.o $(BLOCK_OBJS) |
54 | OBJS+=readline.o console.o | 54 | OBJS+=readline.o console.o |
55 | OBJS+=block.o | 55 | OBJS+=block.o |
56 | 56 | ||
@@ -159,7 +159,7 @@ libqemu_user.a: $(USER_OBJS) | @@ -159,7 +159,7 @@ libqemu_user.a: $(USER_OBJS) | ||
159 | rm -f $@ | 159 | rm -f $@ |
160 | $(AR) rcs $@ $(USER_OBJS) | 160 | $(AR) rcs $@ $(USER_OBJS) |
161 | 161 | ||
162 | -QEMU_IMG_BLOCK_OBJS = $(BLOCK_OBJS) | 162 | +QEMU_IMG_BLOCK_OBJS = nbd.o $(BLOCK_OBJS) |
163 | ifdef CONFIG_WIN32 | 163 | ifdef CONFIG_WIN32 |
164 | QEMU_IMG_BLOCK_OBJS += qemu-img-block-raw-win32.o | 164 | QEMU_IMG_BLOCK_OBJS += qemu-img-block-raw-win32.o |
165 | else | 165 | else |
@@ -180,7 +180,7 @@ qemu-img-%.o: %.c | @@ -180,7 +180,7 @@ qemu-img-%.o: %.c | ||
180 | qemu-nbd-%.o: %.c | 180 | qemu-nbd-%.o: %.c |
181 | $(CC) $(CFLAGS) $(CPPFLAGS) -DQEMU_NBD -c -o $@ $< | 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 | osdep.o qemu-nbd-block-raw-posix.o $(BLOCK_OBJS) | 184 | osdep.o qemu-nbd-block-raw-posix.o $(BLOCK_OBJS) |
185 | $(CC) $(LDFLAGS) -o $@ $^ -lz $(LIBS) | 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,6 +1332,7 @@ void bdrv_init(void) | ||
1332 | bdrv_register(&bdrv_vvfat); | 1332 | bdrv_register(&bdrv_vvfat); |
1333 | bdrv_register(&bdrv_qcow2); | 1333 | bdrv_register(&bdrv_qcow2); |
1334 | bdrv_register(&bdrv_parallels); | 1334 | bdrv_register(&bdrv_parallels); |
1335 | + bdrv_register(&bdrv_nbd); | ||
1335 | } | 1336 | } |
1336 | 1337 | ||
1337 | void *qemu_aio_get(BlockDriverState *bs, BlockDriverCompletionFunc *cb, | 1338 | void *qemu_aio_get(BlockDriverState *bs, BlockDriverCompletionFunc *cb, |
block.h
@@ -16,6 +16,7 @@ extern BlockDriver bdrv_vpc; | @@ -16,6 +16,7 @@ extern BlockDriver bdrv_vpc; | ||
16 | extern BlockDriver bdrv_vvfat; | 16 | extern BlockDriver bdrv_vvfat; |
17 | extern BlockDriver bdrv_qcow2; | 17 | extern BlockDriver bdrv_qcow2; |
18 | extern BlockDriver bdrv_parallels; | 18 | extern BlockDriver bdrv_parallels; |
19 | +extern BlockDriver bdrv_nbd; | ||
19 | 20 | ||
20 | typedef struct BlockDriverInfo { | 21 | typedef struct BlockDriverInfo { |
21 | /* in bytes, 0 if irrelevant */ | 22 | /* in bytes, 0 if irrelevant */ |
nbd.c
1 | -/*\ | 1 | +/* |
2 | * Copyright (C) 2005 Anthony Liguori <anthony@codemonkey.ws> | 2 | * Copyright (C) 2005 Anthony Liguori <anthony@codemonkey.ws> |
3 | * | 3 | * |
4 | * Network Block Device | 4 | * Network Block Device |
@@ -15,7 +15,7 @@ | @@ -15,7 +15,7 @@ | ||
15 | * You should have received a copy of the GNU General Public License | 15 | * You should have received a copy of the GNU General Public License |
16 | * along with this program; if not, write to the Free Software | 16 | * along with this program; if not, write to the Free Software |
17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | 17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
18 | -\*/ | 18 | + */ |
19 | 19 | ||
20 | #include "nbd.h" | 20 | #include "nbd.h" |
21 | 21 | ||
@@ -31,17 +31,21 @@ | @@ -31,17 +31,21 @@ | ||
31 | #include <arpa/inet.h> | 31 | #include <arpa/inet.h> |
32 | #include <netdb.h> | 32 | #include <netdb.h> |
33 | 33 | ||
34 | +#if defined(QEMU_NBD) | ||
34 | extern int verbose; | 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 | #define LOG(msg, ...) do { \ | 44 | #define LOG(msg, ...) do { \ |
37 | fprintf(stderr, "%s:%s():L%d: " msg "\n", \ | 45 | fprintf(stderr, "%s:%s():L%d: " msg "\n", \ |
38 | __FILE__, __FUNCTION__, __LINE__, ## __VA_ARGS__); \ | 46 | __FILE__, __FUNCTION__, __LINE__, ## __VA_ARGS__); \ |
39 | } while(0) | 47 | } while(0) |
40 | 48 | ||
41 | -#define TRACE(msg, ...) do { \ | ||
42 | - if (verbose) LOG(msg, ## __VA_ARGS__); \ | ||
43 | -} while(0) | ||
44 | - | ||
45 | /* This is all part of the "official" NBD API */ | 49 | /* This is all part of the "official" NBD API */ |
46 | 50 | ||
47 | #define NBD_REQUEST_MAGIC 0x25609513 | 51 | #define NBD_REQUEST_MAGIC 0x25609513 |
@@ -59,10 +63,10 @@ extern int verbose; | @@ -59,10 +63,10 @@ extern int verbose; | ||
59 | 63 | ||
60 | /* That's all folks */ | 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 | size_t offset = 0; | 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,7 +80,7 @@ static size_t wr_sync(int fd, void *buffer, size_t size, bool do_read) | ||
76 | } | 80 | } |
77 | 81 | ||
78 | /* recoverable error */ | 82 | /* recoverable error */ |
79 | - if (len == -1 && errno == EAGAIN) { | 83 | + if (len == -1 && (errno == EAGAIN || errno == EINTR)) { |
80 | continue; | 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,7 +100,7 @@ static size_t wr_sync(int fd, void *buffer, size_t size, bool do_read) | ||
96 | return offset; | 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 | int s; | 105 | int s; |
102 | struct in_addr in; | 106 | struct in_addr in; |
@@ -404,16 +408,31 @@ int nbd_client(int fd, int csock) | @@ -404,16 +408,31 @@ int nbd_client(int fd, int csock) | ||
404 | return ret; | 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 | uint8_t buf[4 + 4 + 8 + 8 + 4]; | 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 | if (read_sync(csock, buf, sizeof(buf)) != sizeof(buf)) { | 437 | if (read_sync(csock, buf, sizeof(buf)) != sizeof(buf)) { |
419 | LOG("read failed"); | 438 | LOG("read failed"); |
@@ -422,97 +441,158 @@ int nbd_trip(BlockDriverState *bs, int csock, off_t size, uint64_t dev_offset, | @@ -422,97 +441,158 @@ int nbd_trip(BlockDriverState *bs, int csock, off_t size, uint64_t dev_offset, | ||
422 | } | 441 | } |
423 | 442 | ||
424 | /* Request | 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 | magic = be32_to_cpup((uint32_t*)buf); | 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 | TRACE("Got request: " | 457 | TRACE("Got request: " |
438 | "{ magic = 0x%x, .type = %d, from = %" PRIu64" , len = %u }", | 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 | if (magic != NBD_REQUEST_MAGIC) { | 461 | if (magic != NBD_REQUEST_MAGIC) { |
443 | LOG("invalid magic (got 0x%x)", magic); | 462 | LOG("invalid magic (got 0x%x)", magic); |
444 | errno = EINVAL; | 463 | errno = EINVAL; |
445 | return -1; | 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 | LOG("len (%u) is larger than max len (%u)", | 538 | LOG("len (%u) is larger than max len (%u)", |
450 | - len, data_size); | 539 | + request.len, data_size); |
451 | errno = EINVAL; | 540 | errno = EINVAL; |
452 | return -1; | 541 | return -1; |
453 | } | 542 | } |
454 | 543 | ||
455 | - if ((from + len) < from) { | 544 | + if ((request.from + request.len) < request.from) { |
456 | LOG("integer overflow detected! " | 545 | LOG("integer overflow detected! " |
457 | "you're probably being attacked"); | 546 | "you're probably being attacked"); |
458 | errno = EINVAL; | 547 | errno = EINVAL; |
459 | return -1; | 548 | return -1; |
460 | } | 549 | } |
461 | 550 | ||
462 | - if ((from + len) > size) { | 551 | + if ((request.from + request.len) > size) { |
463 | LOG("From: %" PRIu64 ", Len: %u, Size: %" PRIu64 | 552 | LOG("From: %" PRIu64 ", Len: %u, Size: %" PRIu64 |
464 | ", Offset: %" PRIu64 "\n", | 553 | ", Offset: %" PRIu64 "\n", |
465 | - from, len, size, dev_offset); | 554 | + request.from, request.len, size, dev_offset); |
466 | LOG("requested operation past EOF--bad client?"); | 555 | LOG("requested operation past EOF--bad client?"); |
467 | errno = EINVAL; | 556 | errno = EINVAL; |
468 | return -1; | 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 | TRACE("Decoding type"); | 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 | TRACE("Request type is READ"); | 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 | LOG("reading from file failed"); | 571 | LOG("reading from file failed"); |
487 | errno = EINVAL; | 572 | errno = EINVAL; |
488 | return -1; | 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 | return -1; | 580 | return -1; |
500 | - } | ||
501 | 581 | ||
502 | TRACE("Sending data to client"); | 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 | LOG("writing to socket failed"); | 585 | LOG("writing to socket failed"); |
506 | errno = EINVAL; | 586 | errno = EINVAL; |
507 | return -1; | 587 | return -1; |
508 | } | 588 | } |
509 | break; | 589 | break; |
510 | - case 1: | 590 | + case NBD_CMD_WRITE: |
511 | TRACE("Request type is WRITE"); | 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 | LOG("reading from socket failed"); | 596 | LOG("reading from socket failed"); |
517 | errno = EINVAL; | 597 | errno = EINVAL; |
518 | return -1; | 598 | return -1; |
@@ -520,34 +600,29 @@ int nbd_trip(BlockDriverState *bs, int csock, off_t size, uint64_t dev_offset, | @@ -520,34 +600,29 @@ int nbd_trip(BlockDriverState *bs, int csock, off_t size, uint64_t dev_offset, | ||
520 | 600 | ||
521 | if (readonly) { | 601 | if (readonly) { |
522 | TRACE("Server is read-only, return error"); | 602 | TRACE("Server is read-only, return error"); |
523 | - | ||
524 | - cpu_to_be32w((uint32_t*)(buf + 4), 1); | 603 | + reply.error = 1; |
525 | } else { | 604 | } else { |
526 | TRACE("Writing to device"); | 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 | LOG("writing to file failed"); | 609 | LOG("writing to file failed"); |
530 | errno = EINVAL; | 610 | errno = EINVAL; |
531 | return -1; | 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 | return -1; | 618 | return -1; |
543 | - } | ||
544 | break; | 619 | break; |
545 | - case 2: | 620 | + case NBD_CMD_DISC: |
546 | TRACE("Request type is DISCONNECT"); | 621 | TRACE("Request type is DISCONNECT"); |
547 | errno = 0; | 622 | errno = 0; |
548 | return 1; | 623 | return 1; |
549 | default: | 624 | default: |
550 | - LOG("invalid request type (%u) received", type); | 625 | + LOG("invalid request type (%u) received", request.type); |
551 | errno = EINVAL; | 626 | errno = EINVAL; |
552 | return -1; | 627 | return -1; |
553 | } | 628 | } |
nbd.h
1 | -/*\ | 1 | +/* |
2 | * Copyright (C) 2005 Anthony Liguori <anthony@codemonkey.ws> | 2 | * Copyright (C) 2005 Anthony Liguori <anthony@codemonkey.ws> |
3 | * | 3 | * |
4 | * Network Block Device | 4 | * Network Block Device |
@@ -15,7 +15,7 @@ | @@ -15,7 +15,7 @@ | ||
15 | * You should have received a copy of the GNU General Public License | 15 | * You should have received a copy of the GNU General Public License |
16 | * along with this program; if not, write to the Free Software | 16 | * along with this program; if not, write to the Free Software |
17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | 17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
18 | -\*/ | 18 | + */ |
19 | 19 | ||
20 | #ifndef NBD_H | 20 | #ifndef NBD_H |
21 | #define NBD_H | 21 | #define NBD_H |
@@ -26,6 +26,26 @@ | @@ -26,6 +26,26 @@ | ||
26 | #include <qemu-common.h> | 26 | #include <qemu-common.h> |
27 | #include "block_int.h" | 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 | int tcp_socket_incoming(const char *address, uint16_t port); | 49 | int tcp_socket_incoming(const char *address, uint16_t port); |
30 | int unix_socket_outgoing(const char *path); | 50 | int unix_socket_outgoing(const char *path); |
31 | int unix_socket_incoming(const char *path); | 51 | int unix_socket_incoming(const char *path); |
@@ -33,6 +53,8 @@ int unix_socket_incoming(const char *path); | @@ -33,6 +53,8 @@ int unix_socket_incoming(const char *path); | ||
33 | int nbd_negotiate(BlockDriverState *bs, int csock, off_t size); | 53 | int nbd_negotiate(BlockDriverState *bs, int csock, off_t size); |
34 | int nbd_receive_negotiate(int csock, off_t *size, size_t *blocksize); | 54 | int nbd_receive_negotiate(int csock, off_t *size, size_t *blocksize); |
35 | int nbd_init(int fd, int csock, off_t size, size_t blocksize); | 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 | int nbd_trip(BlockDriverState *bs, int csock, off_t size, uint64_t dev_offset, | 58 | int nbd_trip(BlockDriverState *bs, int csock, off_t size, uint64_t dev_offset, |
37 | off_t *offset, bool readonly, uint8_t *data, int data_size); | 59 | off_t *offset, bool readonly, uint8_t *data, int data_size); |
38 | int nbd_client(int fd, int csock); | 60 | int nbd_client(int fd, int csock); |
qemu-doc.texi
@@ -1321,6 +1321,7 @@ snapshots. | @@ -1321,6 +1321,7 @@ snapshots. | ||
1321 | * qemu_nbd_invocation:: qemu-nbd Invocation | 1321 | * qemu_nbd_invocation:: qemu-nbd Invocation |
1322 | * host_drives:: Using host drives | 1322 | * host_drives:: Using host drives |
1323 | * disk_images_fat_images:: Virtual FAT disk images | 1323 | * disk_images_fat_images:: Virtual FAT disk images |
1324 | +* disk_images_nbd:: NBD access | ||
1324 | @end menu | 1325 | @end menu |
1325 | 1326 | ||
1326 | @node disk_images_quickstart | 1327 | @node disk_images_quickstart |
@@ -1503,6 +1504,40 @@ What you should @emph{never} do: | @@ -1503,6 +1504,40 @@ What you should @emph{never} do: | ||
1503 | @item write to the FAT directory on the host system while accessing it with the guest system. | 1504 | @item write to the FAT directory on the host system while accessing it with the guest system. |
1504 | @end itemize | 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 | @node pcsys_network | 1541 | @node pcsys_network |
1507 | @section Network emulation | 1542 | @section Network emulation |
1508 | 1543 |
qemu-nbd.c
@@ -56,6 +56,7 @@ static void usage(const char *name) | @@ -56,6 +56,7 @@ static void usage(const char *name) | ||
56 | " -c, --connect=DEV connect FILE to the local NBD device DEV\n" | 56 | " -c, --connect=DEV connect FILE to the local NBD device DEV\n" |
57 | " -d, --disconnect disconnect the specified device\n" | 57 | " -d, --disconnect disconnect the specified device\n" |
58 | " -e, --shared=NUM device can be shared by NUM clients (default '1')\n" | 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 | " -v, --verbose display extra debugging information\n" | 60 | " -v, --verbose display extra debugging information\n" |
60 | " -h, --help display this help and exit\n" | 61 | " -h, --help display this help and exit\n" |
61 | " -V, --version output version information and exit\n" | 62 | " -V, --version output version information and exit\n" |
@@ -189,7 +190,7 @@ int main(int argc, char **argv) | @@ -189,7 +190,7 @@ int main(int argc, char **argv) | ||
189 | char *device = NULL; | 190 | char *device = NULL; |
190 | char *socket = NULL; | 191 | char *socket = NULL; |
191 | char sockpath[128]; | 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 | struct option lopt[] = { | 194 | struct option lopt[] = { |
194 | { "help", 0, 0, 'h' }, | 195 | { "help", 0, 0, 'h' }, |
195 | { "version", 0, 0, 'V' }, | 196 | { "version", 0, 0, 'V' }, |
@@ -204,6 +205,7 @@ int main(int argc, char **argv) | @@ -204,6 +205,7 @@ int main(int argc, char **argv) | ||
204 | { "snapshot", 0, 0, 's' }, | 205 | { "snapshot", 0, 0, 's' }, |
205 | { "nocache", 0, 0, 'n' }, | 206 | { "nocache", 0, 0, 'n' }, |
206 | { "shared", 1, 0, 'e' }, | 207 | { "shared", 1, 0, 'e' }, |
208 | + { "persistent", 0, 0, 't' }, | ||
207 | { "verbose", 0, 0, 'v' }, | 209 | { "verbose", 0, 0, 'v' }, |
208 | { NULL, 0, 0, 0 } | 210 | { NULL, 0, 0, 0 } |
209 | }; | 211 | }; |
@@ -222,6 +224,7 @@ int main(int argc, char **argv) | @@ -222,6 +224,7 @@ int main(int argc, char **argv) | ||
222 | int i; | 224 | int i; |
223 | int nb_fds = 0; | 225 | int nb_fds = 0; |
224 | int max_fd; | 226 | int max_fd; |
227 | + int persistent = 0; | ||
225 | 228 | ||
226 | while ((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) { | 229 | while ((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) { |
227 | switch (ch) { | 230 | switch (ch) { |
@@ -283,6 +286,9 @@ int main(int argc, char **argv) | @@ -283,6 +286,9 @@ int main(int argc, char **argv) | ||
283 | errx(EINVAL, "Shared device number must be greater than 0\n"); | 286 | errx(EINVAL, "Shared device number must be greater than 0\n"); |
284 | } | 287 | } |
285 | break; | 288 | break; |
289 | + case 't': | ||
290 | + persistent = 1; | ||
291 | + break; | ||
286 | case 'v': | 292 | case 'v': |
287 | verbose = 1; | 293 | verbose = 1; |
288 | break; | 294 | break; |
@@ -459,7 +465,7 @@ int main(int argc, char **argv) | @@ -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 | qemu_free(data); | 469 | qemu_free(data); |
464 | 470 | ||
465 | close(sharing_fds[0]); | 471 | close(sharing_fds[0]); |
qemu-nbd.texi
@@ -36,6 +36,8 @@ Export Qemu disk image using NBD protocol. | @@ -36,6 +36,8 @@ Export Qemu disk image using NBD protocol. | ||
36 | disconnect the specified device | 36 | disconnect the specified device |
37 | @item -e, --shared=NUM | 37 | @item -e, --shared=NUM |
38 | device can be shared by NUM clients (default '1') | 38 | device can be shared by NUM clients (default '1') |
39 | +@item -t, --persistent | ||
40 | + don't exit on the last connection | ||
39 | @item -v, --verbose | 41 | @item -v, --verbose |
40 | display extra debugging information | 42 | display extra debugging information |
41 | @item -h, --help | 43 | @item -h, --help |