Commit 7a5ca8648b6a3ecd359bc3029b2988de452494e9

Authored by bellard
1 parent e00c1e71

qemu-nbd tool (Anthony Liguori)

git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@4596 c046a42c-6fe2-441c-8c8c-71466251a162
Makefile
... ... @@ -17,7 +17,7 @@ ifdef CONFIG_STATIC
17 17 LDFLAGS += -static
18 18 endif
19 19 ifdef BUILD_DOCS
20   -DOCS=qemu-doc.html qemu-tech.html qemu.1 qemu-img.1
  20 +DOCS=qemu-doc.html qemu-tech.html qemu.1 qemu-img.1 qemu-nbd.8
21 21 else
22 22 DOCS=
23 23 endif
... ... @@ -159,6 +159,10 @@ qemu-img-%.o: %.c
159 159 %.o: %.c
160 160 $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $<
161 161  
  162 +qemu-nbd$(EXESUF): qemu-nbd.o nbd.o qemu-img-block.o \
  163 + $(QEMU_IMG_BLOCK_OBJS)
  164 + $(CC) $(LDFLAGS) -o $@ $^ -lz $(LIBS)
  165 +
162 166 # dyngen host tool
163 167 dyngen$(EXESUF): dyngen.c
164 168 $(HOST_CC) $(CFLAGS) $(CPPFLAGS) -o $@ $^
... ... @@ -191,6 +195,8 @@ install-doc: $(DOCS)
191 195 ifndef CONFIG_WIN32
192 196 mkdir -p "$(DESTDIR)$(mandir)/man1"
193 197 $(INSTALL) qemu.1 qemu-img.1 "$(DESTDIR)$(mandir)/man1"
  198 + mkdir -p "$(DESTDIR)$(mandir)/man8"
  199 + $(INSTALL) qemu-nbd.8 "$(DESTDIR)$(mandir)/man8"
194 200 endif
195 201  
196 202 install: all $(if $(BUILD_DOCS),install-doc)
... ... @@ -244,6 +250,10 @@ qemu-img.1: qemu-img.texi
244 250 $(SRC_PATH)/texi2pod.pl $< qemu-img.pod
245 251 pod2man --section=1 --center=" " --release=" " qemu-img.pod > $@
246 252  
  253 +qemu-nbd.8: qemu-nbd.texi
  254 + $(SRC_PATH)/texi2pod.pl $< qemu-nbd.pod
  255 + pod2man --section=8 --center=" " --release=" " qemu-nbd.pod > $@
  256 +
247 257 info: qemu-doc.info qemu-tech.info
248 258  
249 259 dvi: qemu-doc.dvi qemu-tech.dvi
... ... @@ -296,6 +306,7 @@ tarbin:
296 306 $(bindir)/qemu-sh4eb \
297 307 $(bindir)/qemu-cris \
298 308 $(bindir)/qemu-img \
  309 + $(bindir)/qemu-nbd \
299 310 $(datadir)/bios.bin \
300 311 $(datadir)/vgabios.bin \
301 312 $(datadir)/vgabios-cirrus.bin \
... ... @@ -309,6 +320,7 @@ tarbin:
309 320 $(docdir)/qemu-doc.html \
310 321 $(docdir)/qemu-tech.html \
311 322 $(mandir)/man1/qemu.1 $(mandir)/man1/qemu-img.1
  323 + $(mandir)/man8/qemu-nbd.8
312 324  
313 325 # Include automatically generated dependency files
314 326 -include $(wildcard *.d audio/*.d slirp/*.d)
... ...
configure
... ... @@ -1105,6 +1105,9 @@ echo &quot;#define CONFIG_UNAME_RELEASE \&quot;$uname_release\&quot;&quot; &gt;&gt; $config_h
1105 1105 tools=
1106 1106 if test `expr "$target_list" : ".*softmmu.*"` != 0 ; then
1107 1107 tools="qemu-img\$(EXESUF) $tools"
  1108 + if [ "$linux" = "yes" ] ; then
  1109 + tools="qemu-nbd\$(EXESUF) $tools"
  1110 + fi
1108 1111 fi
1109 1112 echo "TOOLS=$tools" >> $config_mak
1110 1113  
... ...
exec-all.h
... ... @@ -126,7 +126,7 @@ static inline int tlb_set_page(CPUState *env1, target_ulong vaddr,
126 126 #define CODE_GEN_BUFFER_SIZE (6 * 1024 * 1024)
127 127 #else
128 128 /* XXX: make it dynamic on x86 */
129   -#define CODE_GEN_BUFFER_SIZE (16 * 1024 * 1024)
  129 +#define CODE_GEN_BUFFER_SIZE (64 * 1024 * 1024)
130 130 #endif
131 131  
132 132 //#define CODE_GEN_BUFFER_SIZE (128 * 1024)
... ...
nbd.c 0 → 100644
  1 +/*\
  2 + * Copyright (C) 2005 Anthony Liguori <anthony@codemonkey.ws>
  3 + *
  4 + * Network Block Device
  5 + *
  6 + * This program is free software; you can redistribute it and/or modify
  7 + * it under the terms of the GNU General Public License as published by
  8 + * the Free Software Foundation; under version 2 of the License.
  9 + *
  10 + * This program is distributed in the hope that it will be useful,
  11 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13 + * GNU General Public License for more details.
  14 + *
  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
  17 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  18 +\*/
  19 +
  20 +#include "nbd.h"
  21 +
  22 +#include <errno.h>
  23 +#include <string.h>
  24 +#include <sys/ioctl.h>
  25 +#include <ctype.h>
  26 +#include <inttypes.h>
  27 +#include <sys/socket.h>
  28 +#include <netinet/in.h>
  29 +#include <netinet/tcp.h>
  30 +#include <arpa/inet.h>
  31 +#include <netdb.h>
  32 +
  33 +extern int verbose;
  34 +
  35 +#define LOG(msg, ...) do { \
  36 + fprintf(stderr, "%s:%s():L%d: " msg "\n", \
  37 + __FILE__, __FUNCTION__, __LINE__, ## __VA_ARGS__); \
  38 +} while(0)
  39 +
  40 +#define TRACE(msg, ...) do { \
  41 + if (verbose) LOG(msg, ## __VA_ARGS__); \
  42 +} while(0)
  43 +
  44 +/* This is all part of the "official" NBD API */
  45 +
  46 +#define NBD_REQUEST_MAGIC 0x25609513
  47 +#define NBD_REPLY_MAGIC 0x67446698
  48 +
  49 +#define NBD_SET_SOCK _IO(0xab, 0)
  50 +#define NBD_SET_BLKSIZE _IO(0xab, 1)
  51 +#define NBD_SET_SIZE _IO(0xab, 2)
  52 +#define NBD_DO_IT _IO(0xab, 3)
  53 +#define NBD_CLEAR_SOCK _IO(0xab, 4)
  54 +#define NBD_CLEAR_QUE _IO(0xab, 5)
  55 +#define NBD_PRINT_DEBUG _IO(0xab, 6)
  56 +#define NBD_SET_SIZE_BLOCKS _IO(0xab, 7)
  57 +#define NBD_DISCONNECT _IO(0xab, 8)
  58 +
  59 +/* That's all folks */
  60 +
  61 +#define read_sync(fd, buffer, size) wr_sync(fd, buffer, size, true)
  62 +#define write_sync(fd, buffer, size) wr_sync(fd, buffer, size, false)
  63 +
  64 +static size_t wr_sync(int fd, void *buffer, size_t size, bool do_read)
  65 +{
  66 + size_t offset = 0;
  67 +
  68 + while (offset < size) {
  69 + ssize_t len;
  70 +
  71 + if (do_read) {
  72 + len = read(fd, buffer + offset, size - offset);
  73 + } else {
  74 + len = write(fd, buffer + offset, size - offset);
  75 + }
  76 +
  77 + /* recoverable error */
  78 + if (len == -1 && errno == EAGAIN) {
  79 + continue;
  80 + }
  81 +
  82 + /* eof */
  83 + if (len == 0) {
  84 + break;
  85 + }
  86 +
  87 + /* unrecoverable error */
  88 + if (len == -1) {
  89 + return 0;
  90 + }
  91 +
  92 + offset += len;
  93 + }
  94 +
  95 + return offset;
  96 +}
  97 +
  98 +static int tcp_socket_outgoing(const char *address, uint16_t port)
  99 +{
  100 + int s;
  101 + struct in_addr in;
  102 + struct sockaddr_in addr;
  103 + int serrno;
  104 +
  105 + s = socket(PF_INET, SOCK_STREAM, 0);
  106 + if (s == -1) {
  107 + return -1;
  108 + }
  109 +
  110 + if (inet_aton(address, &in) == 0) {
  111 + struct hostent *ent;
  112 +
  113 + ent = gethostbyname(address);
  114 + if (ent == NULL) {
  115 + goto error;
  116 + }
  117 +
  118 + memcpy(&in, ent->h_addr, sizeof(in));
  119 + }
  120 +
  121 + addr.sin_family = AF_INET;
  122 + addr.sin_port = htons(port);
  123 + memcpy(&addr.sin_addr.s_addr, &in, sizeof(in));
  124 +
  125 + if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
  126 + goto error;
  127 + }
  128 +
  129 + return s;
  130 +error:
  131 + serrno = errno;
  132 + close(s);
  133 + errno = serrno;
  134 + return -1;
  135 +}
  136 +
  137 +int tcp_socket_incoming(const char *address, uint16_t port)
  138 +{
  139 + int s;
  140 + struct in_addr in;
  141 + struct sockaddr_in addr;
  142 + int serrno;
  143 + int opt;
  144 +
  145 + s = socket(PF_INET, SOCK_STREAM, 0);
  146 + if (s == -1) {
  147 + return -1;
  148 + }
  149 +
  150 + if (inet_aton(address, &in) == 0) {
  151 + struct hostent *ent;
  152 +
  153 + ent = gethostbyname(address);
  154 + if (ent == NULL) {
  155 + goto error;
  156 + }
  157 +
  158 + memcpy(&in, ent->h_addr, sizeof(in));
  159 + }
  160 +
  161 + addr.sin_family = AF_INET;
  162 + addr.sin_port = htons(port);
  163 + memcpy(&addr.sin_addr.s_addr, &in, sizeof(in));
  164 +
  165 + opt = 1;
  166 + if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1) {
  167 + goto error;
  168 + }
  169 +
  170 + if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
  171 + goto error;
  172 + }
  173 +
  174 + if (listen(s, 128) == -1) {
  175 + goto error;
  176 + }
  177 +
  178 + return s;
  179 +error:
  180 + serrno = errno;
  181 + close(s);
  182 + errno = serrno;
  183 + return -1;
  184 +}
  185 +
  186 +/* Basic flow
  187 +
  188 + Server Client
  189 +
  190 + Negotiate
  191 + Request
  192 + Response
  193 + Request
  194 + Response
  195 + ...
  196 + ...
  197 + Request (type == 2)
  198 +*/
  199 +
  200 +int nbd_negotiate(BlockDriverState *bs, int csock, off_t size)
  201 +{
  202 + char buf[8 + 8 + 8 + 128];
  203 +
  204 + /* Negotiate
  205 + [ 0 .. 7] passwd ("NBDMAGIC")
  206 + [ 8 .. 15] magic (0x00420281861253)
  207 + [16 .. 23] size
  208 + [24 .. 151] reserved (0)
  209 + */
  210 +
  211 + TRACE("Beginning negotiation.");
  212 + memcpy(buf, "NBDMAGIC", 8);
  213 + cpu_to_be64w((uint64_t*)(buf + 8), 0x00420281861253LL);
  214 + cpu_to_be64w((uint64_t*)(buf + 16), size);
  215 + memset(buf + 24, 0, 128);
  216 +
  217 + if (write_sync(csock, buf, sizeof(buf)) != sizeof(buf)) {
  218 + LOG("write failed");
  219 + errno = EINVAL;
  220 + return -1;
  221 + }
  222 +
  223 + TRACE("Negotation succeeded.");
  224 +
  225 + return 0;
  226 +}
  227 +
  228 +int nbd_receive_negotiate(int fd, int csock)
  229 +{
  230 + char buf[8 + 8 + 8 + 128];
  231 + uint64_t magic;
  232 + off_t size;
  233 + size_t blocksize;
  234 +
  235 + TRACE("Receiving negotation.");
  236 +
  237 + if (read_sync(csock, buf, sizeof(buf)) != sizeof(buf)) {
  238 + LOG("read failed");
  239 + errno = EINVAL;
  240 + return -1;
  241 + }
  242 +
  243 + magic = be64_to_cpup((uint64_t*)(buf + 8));
  244 + size = be64_to_cpup((uint64_t*)(buf + 16));
  245 + blocksize = 1024;
  246 +
  247 + TRACE("Magic is %c%c%c%c%c%c%c%c",
  248 + isprint(buf[0]) ? buf[0] : '.',
  249 + isprint(buf[1]) ? buf[1] : '.',
  250 + isprint(buf[2]) ? buf[2] : '.',
  251 + isprint(buf[3]) ? buf[3] : '.',
  252 + isprint(buf[4]) ? buf[4] : '.',
  253 + isprint(buf[5]) ? buf[5] : '.',
  254 + isprint(buf[6]) ? buf[6] : '.',
  255 + isprint(buf[7]) ? buf[7] : '.');
  256 + TRACE("Magic is 0x%" PRIx64, magic);
  257 + TRACE("Size is %" PRIu64, size);
  258 +
  259 + if (memcmp(buf, "NBDMAGIC", 8) != 0) {
  260 + LOG("Invalid magic received");
  261 + errno = EINVAL;
  262 + return -1;
  263 + }
  264 +
  265 + TRACE("Checking magic");
  266 +
  267 + if (magic != 0x00420281861253LL) {
  268 + LOG("Bad magic received");
  269 + errno = EINVAL;
  270 + return -1;
  271 + }
  272 +
  273 + TRACE("Setting block size to %lu", (unsigned long)blocksize);
  274 +
  275 + if (ioctl(fd, NBD_SET_BLKSIZE, blocksize) == -1) {
  276 + int serrno = errno;
  277 + LOG("Failed setting NBD block size");
  278 + errno = serrno;
  279 + return -1;
  280 + }
  281 +
  282 + TRACE("Setting size to %llu block(s)",
  283 + (unsigned long long)(size / blocksize));
  284 +
  285 + if (ioctl(fd, NBD_SET_SIZE_BLOCKS, size / blocksize) == -1) {
  286 + int serrno = errno;
  287 + LOG("Failed setting size (in blocks)");
  288 + errno = serrno;
  289 + return -1;
  290 + }
  291 +
  292 + TRACE("Clearing NBD socket");
  293 +
  294 + if (ioctl(fd, NBD_CLEAR_SOCK) == -1) {
  295 + int serrno = errno;
  296 + LOG("Failed clearing NBD socket");
  297 + errno = serrno;
  298 + return -1;
  299 + }
  300 +
  301 + TRACE("Setting NBD socket");
  302 +
  303 + if (ioctl(fd, NBD_SET_SOCK, csock) == -1) {
  304 + int serrno = errno;
  305 + LOG("Failed to set NBD socket");
  306 + errno = serrno;
  307 + return -1;
  308 + }
  309 +
  310 + TRACE("Negotiation ended");
  311 +
  312 + return 0;
  313 +}
  314 +
  315 +int nbd_disconnect(int fd)
  316 +{
  317 + ioctl(fd, NBD_CLEAR_QUE);
  318 + ioctl(fd, NBD_DISCONNECT);
  319 + ioctl(fd, NBD_CLEAR_SOCK);
  320 + return 0;
  321 +}
  322 +
  323 +int nbd_client(int fd, int csock)
  324 +{
  325 + int ret;
  326 + int serrno;
  327 +
  328 + TRACE("Doing NBD loop");
  329 +
  330 + ret = ioctl(fd, NBD_DO_IT);
  331 + serrno = errno;
  332 +
  333 + TRACE("NBD loop returned %d: %s", ret, strerror(serrno));
  334 +
  335 + TRACE("Clearing NBD queue");
  336 + ioctl(fd, NBD_CLEAR_QUE);
  337 +
  338 + TRACE("Clearing NBD socket");
  339 + ioctl(fd, NBD_CLEAR_SOCK);
  340 +
  341 + errno = serrno;
  342 + return ret;
  343 +}
  344 +
  345 +int nbd_trip(BlockDriverState *bs, int csock, off_t size, uint64_t dev_offset, off_t *offset, bool readonly)
  346 +{
  347 +#ifndef _REENTRANT
  348 + static uint8_t data[1024 * 1024]; // keep this off of the stack
  349 +#else
  350 + uint8_t data[1024 * 1024];
  351 +#endif
  352 + uint8_t buf[4 + 4 + 8 + 8 + 4];
  353 + uint32_t magic;
  354 + uint32_t type;
  355 + uint64_t from;
  356 + uint32_t len;
  357 +
  358 + TRACE("Reading request.");
  359 +
  360 + if (read_sync(csock, buf, sizeof(buf)) != sizeof(buf)) {
  361 + LOG("read failed");
  362 + errno = EINVAL;
  363 + return -1;
  364 + }
  365 +
  366 + /* Request
  367 + [ 0 .. 3] magic (NBD_REQUEST_MAGIC)
  368 + [ 4 .. 7] type (0 == READ, 1 == WRITE)
  369 + [ 8 .. 15] handle
  370 + [16 .. 23] from
  371 + [24 .. 27] len
  372 + */
  373 +
  374 + magic = be32_to_cpup((uint32_t*)buf);
  375 + type = be32_to_cpup((uint32_t*)(buf + 4));
  376 + from = be64_to_cpup((uint64_t*)(buf + 16));
  377 + len = be32_to_cpup((uint32_t*)(buf + 24));
  378 +
  379 + TRACE("Got request: "
  380 + "{ magic = 0x%x, .type = %d, from = %" PRIu64" , len = %u }",
  381 + magic, type, from, len);
  382 +
  383 +
  384 + if (magic != NBD_REQUEST_MAGIC) {
  385 + LOG("invalid magic (got 0x%x)", magic);
  386 + errno = EINVAL;
  387 + return -1;
  388 + }
  389 +
  390 + if (len > sizeof(data)) {
  391 + LOG("len (%u) is larger than max len (%lu)",
  392 + len, sizeof(data));
  393 + errno = EINVAL;
  394 + return -1;
  395 + }
  396 +
  397 + if ((from + len) < from) {
  398 + LOG("integer overflow detected! "
  399 + "you're probably being attacked");
  400 + errno = EINVAL;
  401 + return -1;
  402 + }
  403 +
  404 + if ((from + len) > size) {
  405 + LOG("From: %" PRIu64 ", Len: %u, Size: %" PRIu64
  406 + ", Offset: %" PRIu64 "\n",
  407 + from, len, size, dev_offset);
  408 + LOG("requested operation past EOF--bad client?");
  409 + errno = EINVAL;
  410 + return -1;
  411 + }
  412 +
  413 + /* Reply
  414 + [ 0 .. 3] magic (NBD_REPLY_MAGIC)
  415 + [ 4 .. 7] error (0 == no error)
  416 + [ 7 .. 15] handle
  417 + */
  418 + cpu_to_be32w((uint32_t*)buf, NBD_REPLY_MAGIC);
  419 + cpu_to_be32w((uint32_t*)(buf + 4), 0);
  420 +
  421 + TRACE("Decoding type");
  422 +
  423 + switch (type) {
  424 + case 0:
  425 + TRACE("Request type is READ");
  426 +
  427 + if (bdrv_read(bs, (from + dev_offset) / 512, data, len / 512) == -1) {
  428 + LOG("reading from file failed");
  429 + errno = EINVAL;
  430 + return -1;
  431 + }
  432 + *offset += len;
  433 +
  434 + TRACE("Read %u byte(s)", len);
  435 +
  436 + TRACE("Sending OK response");
  437 +
  438 + if (write_sync(csock, buf, 16) != 16) {
  439 + LOG("writing to socket failed");
  440 + errno = EINVAL;
  441 + return -1;
  442 + }
  443 +
  444 + TRACE("Sending data to client");
  445 +
  446 + if (write_sync(csock, data, len) != len) {
  447 + LOG("writing to socket failed");
  448 + errno = EINVAL;
  449 + return -1;
  450 + }
  451 + break;
  452 + case 1:
  453 + TRACE("Request type is WRITE");
  454 +
  455 + TRACE("Reading %u byte(s)", len);
  456 +
  457 + if (read_sync(csock, data, len) != len) {
  458 + LOG("reading from socket failed");
  459 + errno = EINVAL;
  460 + return -1;
  461 + }
  462 +
  463 + if (readonly) {
  464 + TRACE("Server is read-only, return error");
  465 +
  466 + cpu_to_be32w((uint32_t*)(buf + 4), 1);
  467 + } else {
  468 + TRACE("Writing to device");
  469 +
  470 + if (bdrv_write(bs, (from + dev_offset) / 512, data, len / 512) == -1) {
  471 + LOG("writing to file failed");
  472 + errno = EINVAL;
  473 + return -1;
  474 + }
  475 +
  476 + *offset += len;
  477 + }
  478 +
  479 + TRACE("Sending response to client");
  480 +
  481 + if (write_sync(csock, buf, 16) != 16) {
  482 + LOG("writing to socket failed");
  483 + errno = EINVAL;
  484 + return -1;
  485 + }
  486 + break;
  487 + case 2:
  488 + TRACE("Request type is DISCONNECT");
  489 + errno = 0;
  490 + return 1;
  491 + default:
  492 + LOG("invalid request type (%u) received", type);
  493 + errno = EINVAL;
  494 + return -1;
  495 + }
  496 +
  497 + TRACE("Request/Reply complete");
  498 +
  499 + return 0;
  500 +}
... ...
nbd.h 0 → 100644
  1 +/*\
  2 + * Copyright (C) 2005 Anthony Liguori <anthony@codemonkey.ws>
  3 + *
  4 + * Network Block Device
  5 + *
  6 + * This program is free software; you can redistribute it and/or modify
  7 + * it under the terms of the GNU General Public License as published by
  8 + * the Free Software Foundation; under version 2 of the License.
  9 + *
  10 + * This program is distributed in the hope that it will be useful,
  11 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13 + * GNU General Public License for more details.
  14 + *
  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
  17 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  18 +\*/
  19 +
  20 +#ifndef NBD_H
  21 +#define NBD_H
  22 +
  23 +#include <sys/types.h>
  24 +#include <stdbool.h>
  25 +
  26 +#include <qemu-common.h>
  27 +#include "block_int.h"
  28 +
  29 +int tcp_socket_incoming(const char *address, uint16_t port);
  30 +
  31 +int nbd_negotiate(BlockDriverState *bs, int csock, off_t size);
  32 +int nbd_receive_negotiate(int fd, int csock);
  33 +int nbd_trip(BlockDriverState *bs, int csock, off_t size, uint64_t dev_offset, off_t *offset, bool readonly);
  34 +int nbd_client(int fd, int csock);
  35 +int nbd_disconnect(int fd);
  36 +
  37 +#endif
... ...
qemu-nbd.c 0 → 100644
  1 +/*\
  2 + * Copyright (C) 2005 Anthony Liguori <anthony@codemonkey.ws>
  3 + *
  4 + * Network Block Device
  5 + *
  6 + * This program is free software; you can redistribute it and/or modify
  7 + * it under the terms of the GNU General Public License as published by
  8 + * the Free Software Foundation; under version 2 of the License.
  9 + *
  10 + * This program is distributed in the hope that it will be useful,
  11 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13 + * GNU General Public License for more details.
  14 + *
  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
  17 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  18 + */
  19 +
  20 +#include <qemu-common.h>
  21 +#include "block_int.h"
  22 +#include "nbd.h"
  23 +
  24 +#include <malloc.h>
  25 +#include <stdarg.h>
  26 +#include <stdio.h>
  27 +#include <getopt.h>
  28 +#include <err.h>
  29 +#include <sys/socket.h>
  30 +#include <netinet/in.h>
  31 +#include <netinet/tcp.h>
  32 +#include <arpa/inet.h>
  33 +
  34 +int verbose;
  35 +
  36 +static void usage(const char *name)
  37 +{
  38 + printf(
  39 +"Usage: %s [OPTIONS] FILE\n"
  40 +"QEMU Disk Network Block Device Server\n"
  41 +"\n"
  42 +" -p, --port=PORT port to listen on (default `1024')\n"
  43 +" -o, --offset=OFFSET offset into the image\n"
  44 +" -b, --bind=IFACE interface to bind to (default `0.0.0.0')\n"
  45 +" -r, --read-only export read-only\n"
  46 +" -P, --partition=NUM only expose partition NUM\n"
  47 +" -v, --verbose display extra debugging information\n"
  48 +" -h, --help display this help and exit\n"
  49 +" -V, --version output version information and exit\n"
  50 +"\n"
  51 +"Report bugs to <anthony@codemonkey.ws>\n"
  52 + , name);
  53 +}
  54 +
  55 +static void version(const char *name)
  56 +{
  57 + printf(
  58 +"qemu-nbd version 0.0.1\n"
  59 +"Written by Anthony Liguori.\n"
  60 +"\n"
  61 +"Copyright (C) 2006 Anthony Liguori <anthony@codemonkey.ws>.\n"
  62 +"This is free software; see the source for copying conditions. There is NO\n"
  63 +"warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
  64 + );
  65 +}
  66 +
  67 +struct partition_record
  68 +{
  69 + uint8_t bootable;
  70 + uint8_t start_head;
  71 + uint32_t start_cylinder;
  72 + uint8_t start_sector;
  73 + uint8_t system;
  74 + uint8_t end_head;
  75 + uint8_t end_cylinder;
  76 + uint8_t end_sector;
  77 + uint32_t start_sector_abs;
  78 + uint32_t nb_sectors_abs;
  79 +};
  80 +
  81 +static void read_partition(uint8_t *p, struct partition_record *r)
  82 +{
  83 + r->bootable = p[0];
  84 + r->start_head = p[1];
  85 + r->start_cylinder = p[3] | ((p[2] << 2) & 0x0300);
  86 + r->start_sector = p[2] & 0x3f;
  87 + r->system = p[4];
  88 + r->end_head = p[5];
  89 + r->end_cylinder = p[7] | ((p[6] << 2) & 0x300);
  90 + r->end_sector = p[6] & 0x3f;
  91 + r->start_sector_abs = p[8] | p[9] << 8 | p[10] << 16 | p[11] << 24;
  92 + r->nb_sectors_abs = p[12] | p[13] << 8 | p[14] << 16 | p[15] << 24;
  93 +}
  94 +
  95 +static int find_partition(BlockDriverState *bs, int partition,
  96 + off_t *offset, off_t *size)
  97 +{
  98 + struct partition_record mbr[4];
  99 + uint8_t data[512];
  100 + int i;
  101 + int ext_partnum = 4;
  102 +
  103 + if (bdrv_read(bs, 0, data, 1))
  104 + errx(EINVAL, "error while reading");
  105 +
  106 + if (data[510] != 0x55 || data[511] != 0xaa) {
  107 + errno = -EINVAL;
  108 + return -1;
  109 + }
  110 +
  111 + for (i = 0; i < 4; i++) {
  112 + read_partition(&data[446 + 16 * i], &mbr[i]);
  113 +
  114 + if (!mbr[i].nb_sectors_abs)
  115 + continue;
  116 +
  117 + if (mbr[i].system == 0xF || mbr[i].system == 0x5) {
  118 + struct partition_record ext[4];
  119 + uint8_t data1[512];
  120 + int j;
  121 +
  122 + if (bdrv_read(bs, mbr[i].start_sector_abs, data1, 1))
  123 + errx(EINVAL, "error while reading");
  124 +
  125 + for (j = 0; j < 4; j++) {
  126 + read_partition(&data1[446 + 16 * j], &ext[j]);
  127 + if (!ext[j].nb_sectors_abs)
  128 + continue;
  129 +
  130 + if ((ext_partnum + j + 1) == partition) {
  131 + *offset = (uint64_t)ext[j].start_sector_abs << 9;
  132 + *size = (uint64_t)ext[j].nb_sectors_abs << 9;
  133 + return 0;
  134 + }
  135 + }
  136 + ext_partnum += 4;
  137 + } else if ((i + 1) == partition) {
  138 + *offset = (uint64_t)mbr[i].start_sector_abs << 9;
  139 + *size = (uint64_t)mbr[i].nb_sectors_abs << 9;
  140 + return 0;
  141 + }
  142 + }
  143 +
  144 + errno = -ENOENT;
  145 + return -1;
  146 +}
  147 +
  148 +int main(int argc, char **argv)
  149 +{
  150 + BlockDriverState *bs;
  151 + off_t dev_offset = 0;
  152 + off_t offset = 0;
  153 + bool readonly = false;
  154 + const char *bindto = "0.0.0.0";
  155 + int port = 1024;
  156 + int sock, csock;
  157 + struct sockaddr_in addr;
  158 + socklen_t addr_len = sizeof(addr);
  159 + off_t fd_size;
  160 + const char *sopt = "hVbo:p:rsP:v";
  161 + struct option lopt[] = {
  162 + { "help", 0, 0, 'h' },
  163 + { "version", 0, 0, 'V' },
  164 + { "bind", 1, 0, 'b' },
  165 + { "port", 1, 0, 'p' },
  166 + { "offset", 1, 0, 'o' },
  167 + { "read-only", 0, 0, 'r' },
  168 + { "partition", 1, 0, 'P' },
  169 + { "snapshot", 0, 0, 's' },
  170 + { "verbose", 0, 0, 'v' },
  171 + };
  172 + int ch;
  173 + int opt_ind = 0;
  174 + int li;
  175 + char *end;
  176 + bool snapshot = false;
  177 + int partition = -1;
  178 +
  179 + while ((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) {
  180 + switch (ch) {
  181 + case 's':
  182 + snapshot = true;
  183 + break;
  184 + case 'b':
  185 + bindto = optarg;
  186 + break;
  187 + case 'p':
  188 + li = strtol(optarg, &end, 0);
  189 + if (*end) {
  190 + errx(EINVAL, "Invalid port `%s'", optarg);
  191 + }
  192 + if (li < 1 || li > 65535) {
  193 + errx(EINVAL, "Port out of range `%s'", optarg);
  194 + }
  195 + port = (uint16_t)li;
  196 + break;
  197 + case 'o':
  198 + dev_offset = strtoll (optarg, &end, 0);
  199 + if (*end) {
  200 + errx(EINVAL, "Invalid offset `%s'", optarg);
  201 + }
  202 + if (dev_offset < 0) {
  203 + errx(EINVAL, "Offset must be positive `%s'", optarg);
  204 + }
  205 + break;
  206 + case 'r':
  207 + readonly = true;
  208 + break;
  209 + case 'P':
  210 + partition = strtol(optarg, &end, 0);
  211 + if (*end)
  212 + errx(EINVAL, "Invalid partition `%s'", optarg);
  213 + if (partition < 1 || partition > 8)
  214 + errx(EINVAL, "Invalid partition %d", partition);
  215 + break;
  216 + case 'v':
  217 + verbose = 1;
  218 + break;
  219 + case 'V':
  220 + version(argv[0]);
  221 + exit(0);
  222 + break;
  223 + case 'h':
  224 + usage(argv[0]);
  225 + exit(0);
  226 + break;
  227 + case '?':
  228 + errx(EINVAL, "Try `%s --help' for more information.",
  229 + argv[0]);
  230 + }
  231 + }
  232 +
  233 + if ((argc - optind) != 1) {
  234 + errx(EINVAL, "Invalid number of argument.\n"
  235 + "Try `%s --help' for more information.",
  236 + argv[0]);
  237 + }
  238 +
  239 + bdrv_init();
  240 +
  241 + bs = bdrv_new("hda");
  242 + if (bs == NULL)
  243 + return 1;
  244 +
  245 + if (bdrv_open(bs, argv[optind], snapshot) == -1)
  246 + return 1;
  247 +
  248 + fd_size = bs->total_sectors * 512;
  249 +
  250 + if (partition != -1 &&
  251 + find_partition(bs, partition, &dev_offset, &fd_size))
  252 + errx(errno, "Could not find partition %d", partition);
  253 +
  254 + sock = tcp_socket_incoming(bindto, port);
  255 + if (sock == -1)
  256 + return 1;
  257 +
  258 + csock = accept(sock,
  259 + (struct sockaddr *)&addr,
  260 + &addr_len);
  261 + if (csock == -1)
  262 + return 1;
  263 +
  264 + /* new fd_size is calculated by find_partition */
  265 + if (nbd_negotiate(bs, csock, fd_size) == -1)
  266 + return 1;
  267 +
  268 + while (nbd_trip(bs, csock, fd_size, dev_offset, &offset, readonly) == 0);
  269 +
  270 + close(csock);
  271 + close(sock);
  272 + bdrv_close(bs);
  273 +
  274 + return 0;
  275 +}
... ...
qemu-nbd.texi 0 → 100644
  1 +@example
  2 +@c man begin SYNOPSIS
  3 +usage: qemu-nbd [OPTION]... @var{filename}
  4 +@c man end
  5 +@end example
  6 +
  7 +@c man begin DESCRIPTION
  8 +
  9 +Export Qemu disk image using NBD protocol.
  10 +
  11 +@c man end
  12 +
  13 +@c man begin OPTIONS
  14 +@table @var
  15 +@item filename
  16 + is a disk image filename
  17 +@item -p, --port=PORT
  18 + port to listen on (default `1024')
  19 +@item -o, --offset=OFFSET
  20 + offset into the image
  21 +@item -b, --bind=IFACE
  22 + interface to bind to (default `0.0.0.0')
  23 +@item -r, --read-only
  24 + export read-only
  25 +@item -P, --partition=NUM
  26 + only expose partition NUM
  27 +@item -v, --verbose
  28 + display extra debugging information
  29 +@item -h, --help
  30 + display this help and exit
  31 +@item -V, --version
  32 + output version information and exit
  33 +@end table
  34 +
  35 +@c man end
  36 +
  37 +@ignore
  38 +
  39 +@setfilename qemu-nbd
  40 +@settitle QEMU Disk Network Block Device Server
  41 +
  42 +@c man begin AUTHOR
  43 +Copyright (C) 2006 Anthony Liguori <anthony@codemonkey.ws>.
  44 +This is free software; see the source for copying conditions. There is NO
  45 +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  46 +@c man end
  47 +
  48 +@c man begin SEEALSO
  49 +qemu-img(1)
  50 +@c man end
  51 +
  52 +@end ignore
... ...