From: Laurent Vivier <Laurent.Vivier@bull.net>
To: qemu-devel@nongnu.org
Cc: Alexander Graf <alex@csgraf.de>
Subject: Re: [Qemu-devel] [PATCH][RFC] To mount qemu disk image on the host
Date: Fri, 25 Jan 2008 14:51:18 +0100 [thread overview]
Message-ID: <1201269078.4114.51.camel@frecb07144> (raw)
In-Reply-To: <D5246B52-7316-4B2F-A450-B438472EA729@csgraf.de>
[-- Attachment #1: Type: text/plain, Size: 1528 bytes --]
Le vendredi 25 janvier 2008 à 14:37 +0100, Alexander Graf a écrit :
> On Jan 25, 2008, at 1:58 PM, Laurent Vivier wrote:
>
> > Le vendredi 25 janvier 2008 à 12:48 +0000, Johannes Schindelin a
> > écrit :
> >> Hi,
> >>
> >> On Fri, 25 Jan 2008, Laurent Vivier wrote:
> >>
> >>> this patch allows to mount qemu disk images on the host.
> >>
> >> This patch has an awful lot of "#ifdef __linux__" in it. But I
> >> imagine
> >> that you could use it on a non-linux host, too, for example with yet
> >> another qemu instance running Linux... Or coLinux, if it supports
> >> network
> >> block devices somehow.
> >>
> >> I certainly saw nothing Linux-specific in the _code_...
> >
> > Yes, but as I can't test this on something else than linux, I prefer
> > to
> > disable this part to not break the existing and let competent people
> > (like you) doing the work (IMHO, I've introduced enough bugs into
> > Qemu...).
>
> Please make it a seperate define and ifdef then. Something like
>
> #ifdef __LINUX__
> #define NBD_SERVER
> #endif
>
> #ifdef NBD_SERVER
> ...
>
> This way it's a lot easier to distinguish between platform and feature
> specific code and if someone finds out it works for windows, it's only
> one line to change.
I agree, new patch attached.
Laurent
--
----------------- Laurent.Vivier@bull.net ------------------
"La perfection est atteinte non quand il ne reste rien à
ajouter mais quand il ne reste rien à enlever." Saint Exupéry
[-- Attachment #2: nbd-server.patch --]
[-- Type: text/x-vhdl, Size: 10013 bytes --]
---
qemu-img.c | 352 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 352 insertions(+)
Index: qemu/qemu-img.c
===================================================================
--- qemu.orig/qemu-img.c 2008-01-25 13:09:10.000000000 +0100
+++ qemu/qemu-img.c 2008-01-25 14:52:01.000000000 +0100
@@ -25,6 +25,16 @@
#include "block_int.h"
#include <assert.h>
+#ifdef __linux__
+#define NBD_SERVER
+#endif
+
+#ifdef NBD_SERVER
+#include <arpa/inet.h>
+#include <netinet/tcp.h>
+#include <sys/wait.h>
+#endif /* NBD_SERVER */
+
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
@@ -92,6 +102,9 @@ static void help(void)
" commit [-f fmt] filename\n"
" convert [-c] [-e] [-6] [-f fmt] filename [filename2 [...]] [-O output_fmt] output_filename\n"
" info [-f fmt] filename\n"
+#ifdef NBD_SERVER
+ " server [-d] [-f fmt] port filename\n"
+#endif
"\n"
"Command parameters:\n"
" 'filename' is a disk image filename\n"
@@ -105,6 +118,9 @@ static void help(void)
" '-c' indicates that target image must be compressed (qcow format only)\n"
" '-e' indicates that the target image must be encrypted (qcow format only)\n"
" '-6' indicates that the target image must use compatibility level 6 (vmdk format only)\n"
+#ifdef NBD_SERVER
+ " '-d' daemonize (server only)\n"
+#endif
);
printf("\nSupported format:");
bdrv_iterate_format(format_print, NULL);
@@ -602,6 +618,338 @@ static int img_convert(int argc, char **
return 0;
}
+#ifdef NBD_SERVER
+
+//#define DEBUG_SERVER
+
+#ifdef DEBUG_SERVER
+#define DPRINTF(fmt, args...) \
+do { printf("img-server: " fmt , ##args); } while (0)
+#else
+#define DPRINTF(fmt, args...) do {} while(0)
+#endif
+
+# if __BYTE_ORDER == __BIG_ENDIAN
+# define htonll(x) (x)
+# define ntohll(x) (x)
+#else
+# define htonll(x) __bswap_64(x)
+# define ntohll(x) __bswap_64(x)
+#endif
+
+#define BUFSIZE (1024*1024)
+
+#define INIT_PASSWD "NBDMAGIC"
+
+#define NBD_REQUEST_MAGIC 0x25609513
+#define NBD_REPLY_MAGIC 0x67446698
+
+enum {
+ NBD_CMD_READ = 0,
+ NBD_CMD_WRITE = 1,
+ NBD_CMD_DISC = 2
+};
+
+struct nbd_request {
+ uint32_t magic;
+ uint32_t type;
+ char handle[8];
+ uint64_t from;
+ uint32_t len;
+} __attribute__ ((packed));
+
+struct nbd_reply {
+ uint32_t magic;
+ uint32_t error;
+ char handle[8];
+} __attribute__ ((packed));
+
+static void sigchld_handler(int s)
+{
+ int status;
+ pid_t pid;
+
+ pid = waitpid(-1, &status, WNOHANG);
+ if (WIFEXITED(status)) {
+ DPRINTF("child %d exited\n", pid);
+ }
+}
+
+static int nbd_receive(int fd, char *buf, size_t len)
+{
+ ssize_t rd;
+
+ while (len > 0) {
+ rd = read(fd, buf, len);
+ if (rd < 0)
+ return -errno;
+ len -= rd;
+ buf += rd;
+ }
+ return 0;
+}
+
+static int nbd_send(int fd, char *buf, size_t len)
+{
+ ssize_t written;
+
+ while (len > 0) {
+ written = write(fd, buf, len);
+ if (written < 0)
+ return -errno;
+ len -= written;
+ buf += written;
+ }
+ return 0;
+}
+
+static int negotiate(int sock, uint64_t size)
+{
+ char zeros[128];
+ uint64_t magic = htonll(0x00420281861253ULL);
+ int ret;
+
+ DPRINTF("negotiate(%d,%ld)\n", sock, size);
+
+ memset(zeros, 0, sizeof(zeros));
+
+ ret = nbd_send(sock, INIT_PASSWD, 8);
+ if (ret < 0)
+ return -1;
+ ret = nbd_send(sock, (char*)&magic, sizeof(magic));
+ if (ret < 0)
+ return -1;
+ size = htonll(size);
+ ret = nbd_send(sock, (char*)&size, sizeof(size));
+ if (ret < 0)
+ return -1;
+ ret = nbd_send(sock, zeros, sizeof(zeros));
+ if (ret < 0)
+ return -1;
+
+ return 0;
+}
+
+static void client_loop(BlockDriverState *drv, int net)
+{
+ struct nbd_request request;
+ struct nbd_reply reply;
+ uint64_t total_sectors;
+ char buf[BUFSIZE];
+ int ret;
+
+ DPRINTF("client_loop(%p,%d)\n", drv, net);
+ int size = 1;
+ ret = setsockopt(net, IPPROTO_TCP, TCP_NODELAY, &size, sizeof(int));
+ if (ret < 0) {
+ DPRINTF("setsockopt failed %d\n", errno);
+ return;
+ }
+
+ bdrv_get_geometry(drv, &total_sectors);
+ if (negotiate(net, total_sectors * 512) < 0) {
+ DPRINTF("negotiate failed %d\n", errno);
+ return;
+ }
+
+ while(1) {
+ uint32_t len;
+ uint64_t from;
+
+ ret = nbd_receive(net, (char*)&request, sizeof(request));
+ if (ret < 0) {
+ DPRINTF("read failed %d (%d)\n", ret, errno);
+ break;
+ }
+
+ DPRINTF("request magic %x type %d from %lx len %x\n",
+ ntohl(request.magic), ntohl(request.type),
+ ntohll(request.from), ntohl(request.len));
+
+ if (request.magic != htonl(NBD_REQUEST_MAGIC)) {
+ DPRINTF("Bad Magic\n");
+ return;
+ }
+
+ if (request.type == htonl(NBD_CMD_DISC)) {
+ /* disconnect */
+ DPRINTF("Command Disconnect\n");
+ break;
+ }
+
+ len = ntohl(request.len);
+ if (len > BUFSIZE - sizeof(struct nbd_reply)) {
+ DPRINTF("len too big %d\n", len);
+ break;
+ }
+
+ /* prepare reply */
+
+ reply.magic = htonl(NBD_REPLY_MAGIC);
+ reply.error = 0;
+ memcpy(reply.handle, request.handle, sizeof(reply.handle));
+
+ /* do I/O */
+
+ from = ntohll(request.from);
+
+ switch(ntohl(request.type)) {
+ case NBD_CMD_READ:
+ reply.error = bdrv_read(drv, from >> 9,
+ buf + sizeof(struct nbd_reply), len >> 9);
+ if (reply.error != 0) {
+ DPRINTF("bdrv_read error %d\n", reply.error);
+ }
+ memcpy(buf, &reply, sizeof(struct nbd_reply));
+ ret = nbd_send(net, buf, len + sizeof(struct nbd_reply));
+ if (ret < 0) {
+ DPRINTF("NBD_CMD_READ: cannot sent result\n");
+ return;
+ }
+ break;
+ case NBD_CMD_WRITE:
+ ret = nbd_receive(net, buf, len);
+ if (ret < 0) {
+ DPRINTF("NBD_CMD_WRITE: cannot receive block %d != %d\n", ret, len);
+ return;
+ }
+ reply.error = bdrv_write(drv, from >> 9, buf, len >> 9);
+ if (reply.error != 0) {
+ DPRINTF("bdrv_write error %d\n", reply.error);
+ }
+ ret = nbd_send(net, (char*)&reply, sizeof(reply));
+ if (ret < 0) {
+ DPRINTF("NBD_CMD_WRITE: cannot sent result %d != %d\n", ret, len);
+ return;
+ }
+ break;
+ }
+ }
+}
+
+void server_loop(BlockDriverState *drv, int port)
+{
+ struct sockaddr_in addrin;
+ int addrinlen = sizeof(addrin);
+ int sock;
+ int ret;
+ int yes;
+ fd_set read_fds;
+ struct sigaction sa;
+
+ sa.sa_handler = sigchld_handler;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = SA_RESTART;
+ if(sigaction(SIGCHLD, &sa, NULL) == -1)
+ error("Failed to catch SIGCHLD\n");
+
+ sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (sock < 0)
+ error("error while creating socket");
+
+ yes = 1;
+
+ ret = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
+ if (ret == -1)
+ error("setsockopt SO_REUSEADDR");
+
+ ret = setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &yes, sizeof(int));
+ if (ret == -1)
+ error("setsockopt SO_KEEPALIVE");
+
+ addrin.sin_family = AF_INET;
+ addrin.sin_port = htons(port);
+ addrin.sin_addr.s_addr = 0;
+
+ ret = bind(sock, (struct sockaddr *) &addrin, addrinlen);
+ if (ret < 0)
+ error("error bind");
+
+ ret = listen(sock, 1);
+ if (ret < 0)
+ error("error listen");
+
+ FD_ZERO(&read_fds);
+ FD_SET(sock, &read_fds);
+
+ /* server main loop */
+
+ while(1) {
+ DPRINTF("server select\n");
+ ret = select(sock + 1, &read_fds, NULL, NULL, NULL);
+ if (ret <= 0)
+ continue;
+
+ if (FD_ISSET(sock, &read_fds)) {
+ int net;
+ pid_t pid;
+
+ /* accept connection */
+
+ net = accept(sock, &addrin, &addrinlen);
+ if (net < 0)
+ continue;
+
+ pid = fork();
+ if (pid == 0) {
+ client_loop(drv, net);
+ return;
+ }
+ close(net);
+ }
+ }
+}
+
+static int img_server(int argc, char **argv)
+{
+ int c;
+ const char *fmt;
+ BlockDriverState *drv;
+ int port;
+ char *filename;
+ int daemonize = 0;
+
+ fmt = NULL;
+ for(;;) {
+ c = getopt(argc, argv, "f:hd");
+ if (c == -1)
+ break;
+ switch(c) {
+ case 'h':
+ help();
+ break;
+ case 'f':
+ fmt = optarg;
+ break;
+ case 'd':
+ daemonize = 1;
+ break;
+ }
+ }
+
+ if (argc - optind < 2)
+ help();
+
+ port = strtol(argv[optind], NULL, 0);
+ filename = argv[optind + 1];
+
+ drv = bdrv_new_open(filename, fmt);
+ if (!drv)
+ error("Could not open '%s'", filename);
+
+#ifndef DEBUG_SERVER
+ if (daemonize)
+ daemon(0, 0);
+#endif
+
+ server_loop(drv, port);
+
+ bdrv_delete(drv);
+
+ return 0;
+}
+#endif /* NBD_SERVER */
+
#ifdef _WIN32
static int64_t get_allocated_file_size(const char *filename)
{
@@ -746,6 +1094,10 @@ int main(int argc, char **argv)
img_convert(argc, argv);
} else if (!strcmp(cmd, "info")) {
img_info(argc, argv);
+#ifdef NBD_SERVER
+ } else if (!strcmp(cmd, "server")) {
+ img_server(argc, argv);
+#endif /* NBD_SERVER */
} else {
help();
}
next prev parent reply other threads:[~2008-01-25 13:50 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2008-01-25 12:30 [Qemu-devel] [PATCH][RFC] To mount qemu disk image on the host Laurent Vivier
2008-01-25 12:48 ` Johannes Schindelin
2008-01-25 12:58 ` Laurent Vivier
2008-01-25 13:37 ` Alexander Graf
2008-01-25 13:51 ` Laurent Vivier [this message]
2008-01-25 15:11 ` Anthony Liguori
2008-01-25 15:18 ` Anthony Liguori
2008-01-25 15:49 ` Laurent Vivier
2008-01-25 19:52 ` Andre Przywara
2008-01-25 20:27 ` Anthony Liguori
2008-01-25 20:55 ` Daniel P. Berrange
2008-01-25 21:17 ` Laurent Vivier
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1201269078.4114.51.camel@frecb07144 \
--to=laurent.vivier@bull.net \
--cc=alex@csgraf.de \
--cc=qemu-devel@nongnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).