qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: "Denis V. Lunev" <den@openvz.org>
Cc: Kevin Wolf <kwolf@redhat.com>, "Denis V. Lunev" <den@openvz.org>,
	Jeff Cody <jcody@redhat.com>,
	qemu-devel@nongnu.org, Stefan Hajnoczi <stefanha@redhat.com>
Subject: [Qemu-devel] [PATCH 02/16] block/parallels: allow to specify DiskDescriptor.xml instead of image file
Date: Mon, 15 Dec 2014 11:27:47 +0300	[thread overview]
Message-ID: <1418632081-20667-3-git-send-email-den@openvz.org> (raw)
In-Reply-To: <1418632081-20667-1-git-send-email-den@openvz.org>

Typically Parallels disk bundle consists of several images which are
glued by XML disk descriptor. Also XML hides inside several important
parameters which are not available in the image header.

This patch allows to specify this XML file as a filename for an image
to open. It is allowed only to open Compressed images with the only
snapshot inside. No additional options are parsed at the moment.

The code itself is dumb enough for a while. If XML file is specified,
the file is parsed and the image is reopened as bs->file to keep the
rest of the driver untouched. This would be changed later with more
features added.

Signed-off-by: Denis V. Lunev <den@openvz.org>
Acked-by: Roman Kagan <rkagan@parallels.com>
CC: Jeff Cody <jcody@redhat.com>
CC: Kevin Wolf <kwolf@redhat.com>
CC: Stefan Hajnoczi <stefanha@redhat.com>
---
 block/parallels.c | 209 ++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 205 insertions(+), 4 deletions(-)

diff --git a/block/parallels.c b/block/parallels.c
index 4f9cd8d..c22b91b 100644
--- a/block/parallels.c
+++ b/block/parallels.c
@@ -27,6 +27,12 @@
 #include "block/block_int.h"
 #include "qemu/module.h"
 
+#if CONFIG_LIBXML2
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+#endif
+#include <stdarg.h>
+
 /**************************************************************/
 
 #define HEADER_MAGIC "WithoutFreeSpace"
@@ -59,6 +65,24 @@ typedef struct BDRVParallelsState {
     unsigned int off_multiplier;
 } BDRVParallelsState;
 
+
+static int parallels_probe_xml(const uint8_t *__buf, int buf_size)
+{
+    int score = 0;
+#if CONFIG_LIBXML2
+    char *buf = g_malloc(buf_size + 1);
+
+    memcpy(buf, __buf, buf_size);
+    buf[buf_size] = 0;
+    if (strstr(buf, "<?xml version=\"1.0\"?>") &&
+        strstr(buf, "<Parallels_disk_image>")) {
+        score = 100;
+    }
+    g_free(buf);
+#endif
+    return score;
+}
+
 static int parallels_probe(const uint8_t *buf, int buf_size, const char *filename)
 {
     const struct parallels_header *ph = (const void *)buf;
@@ -71,11 +95,10 @@ static int parallels_probe(const uint8_t *buf, int buf_size, const char *filenam
         (le32_to_cpu(ph->version) == HEADER_VERSION))
         return 100;
 
-    return 0;
+    return parallels_probe_xml(buf, buf_size);
 }
 
-static int parallels_open(BlockDriverState *bs, QDict *options, int flags,
-                          Error **errp)
+static int parallels_open_image(BlockDriverState *bs, Error **errp)
 {
     BDRVParallelsState *s = bs->opaque;
     int i;
@@ -139,13 +162,191 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags,
     return 0;
 
 fail_format:
-    error_setg(errp, "Image not in Parallels format");
+    error_setg(errp, "Image is not in Parallels format");
     ret = -EINVAL;
 fail:
     g_free(s->catalog_bitmap);
     return ret;
 }
 
+#if CONFIG_LIBXML2
+static xmlNodePtr xml_find(xmlNode *node, const char *elem)
+{
+    xmlNode *child;
+
+    for (child = node->xmlChildrenNode; child != NULL; child = child->next) {
+        if (!xmlStrcmp(child->name, (const xmlChar *)elem) &&
+                child->type == XML_ELEMENT_NODE) {
+            return child;
+        }
+    }
+    return NULL;
+}
+
+static xmlNodePtr xml_seek_va(xmlNode *root, va_list args)
+{
+    const char *elem;
+
+    while ((elem = va_arg(args, const char *)) != NULL) {
+        root = xml_find(root, elem);
+        if (root == NULL) {
+            return NULL;
+        }
+    }
+    return root;
+}
+
+static xmlNodePtr xml_seek(xmlNode *root, ...)
+{
+    va_list args;
+    va_start(args, root);
+    root = xml_seek_va(root, args);
+    va_end(args);
+    return root;
+}
+
+static const char *xml_get_text(xmlNode *node, ...)
+{
+    xmlNode *child;
+    va_list args;
+
+    va_start(args, node);
+    node = xml_seek_va(node, args);
+    va_end(args);
+
+    if (node == NULL) {
+        return NULL;
+    }
+
+    for (child = node->xmlChildrenNode; child; child = child->next) {
+        if (child->type == XML_TEXT_NODE) {
+            return (const char *)child->content;
+        }
+    }
+    return NULL;
+}
+
+static int parallels_open_xml(BlockDriverState *bs, int flags, Error **errp)
+{
+    int size, ret;
+    xmlDoc *doc = NULL;
+    xmlNode *root, *image;
+    char *xml = NULL;
+    const char *data;
+    char image_path[PATH_MAX];
+    Error *local_err = NULL;
+
+    ret = size = bdrv_getlength(bs->file);
+    if (ret < 0) {
+        goto fail;
+    }
+    /* XML file size should be reasonable */
+    ret = -EFBIG;
+    if (size > 65536) {
+        goto fail;
+    }
+
+    xml = g_malloc(size + 1);
+
+    ret = bdrv_pread(bs->file, 0, xml, size);
+    if (ret != size) {
+        goto fail;
+    }
+    xml[size] = 0;
+
+    ret = -EINVAL;
+    doc = xmlReadMemory(xml, size, NULL, NULL,
+                        XML_PARSE_NOERROR | XML_PARSE_NOWARNING);
+    if (doc == NULL) {
+        goto fail;
+    }
+    root = xmlDocGetRootElement(doc);
+    if (root == NULL) {
+        goto fail;
+    }
+    image = xml_seek(root, "StorageData", "Storage", "Image", NULL);
+    data = ""; /* make gcc happy */
+    for (size = 0; image != NULL; image = image->next) {
+        if (image->type != XML_ELEMENT_NODE) {
+            continue;
+        }
+
+        size++;
+        data = xml_get_text(image, "Type", NULL);
+        if (data != NULL && strcmp(data, "Compressed")) {
+            error_setg(errp, "Only compressed Parallels images are supported");
+            goto done;
+        }
+
+        data = xml_get_text(image, "File", NULL);
+        if (data == NULL) {
+            goto fail;
+        }
+    }
+    /* Images with more than 1 snapshots are not supported at the moment */
+    if (size != 1) {
+        error_setg(errp, "Parallels images with snapshots are not supported");
+        goto done;
+    }
+
+    path_combine(image_path, sizeof(image_path), bs->file->filename, data);
+    /* the caller (top layer bdrv_open) will close file for us if bs->file
+       is changed. */
+    bs->file = NULL;
+
+    ret = bdrv_open(&bs->file, image_path, NULL, NULL, flags | BDRV_O_PROTOCOL,
+                    NULL, &local_err);
+    if (ret < 0) {
+        error_setg_errno(errp, -ret, "Could not open '%s': %s",
+                         image_path, error_get_pretty(local_err));
+        error_free(local_err);
+    } else {
+        ret = parallels_open_image(bs, errp);
+    }
+
+done:
+    if (doc != NULL) {
+        xmlFreeDoc(doc);
+    }
+    if (xml != NULL) {
+        g_free(xml);
+    }
+    return ret;
+
+fail:
+    error_setg(errp, "Failed to parse Parallels disk descriptor XML");
+    goto done;
+}
+#endif
+
+static int parallels_open(BlockDriverState *bs, QDict *options, int flags,
+                          Error **errp)
+{
+    uint8_t buf[MAX(1024, HEADER_SIZE)];
+    int size;
+    const struct parallels_header *ph;
+
+    size = bdrv_pread(bs->file, 0, buf, sizeof(buf));
+    if (size < 0) {
+        return size;
+    }
+
+    ph = (const struct parallels_header *)buf;
+    if ((!memcmp(ph->magic, HEADER_MAGIC, 16) ||
+         !memcmp(ph->magic, HEADER_MAGIC2, 16)) &&
+        le32_to_cpu(ph->version) == HEADER_VERSION) {
+        return parallels_open_image(bs, errp);
+    } else if (parallels_probe_xml(buf, (unsigned)size)) {
+#if CONFIG_LIBXML2
+        return parallels_open_xml(bs, flags, errp);
+#endif
+    }
+
+    error_setg(errp, "Image is not in Parallels format");
+    return -EINVAL;
+}
+
+
 static int64_t seek_to_sector(BlockDriverState *bs, int64_t sector_num)
 {
     BDRVParallelsState *s = bs->opaque;
-- 
1.9.1

  parent reply	other threads:[~2014-12-15  9:10 UTC|newest]

Thread overview: 33+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-12-15  8:27 [Qemu-devel] [PATCH v4 0/16] parallels format support improvements Denis V. Lunev
2014-12-15  8:27 ` [Qemu-devel] [PATCH 01/16] configure: add dependency from libxml2 Denis V. Lunev
2014-12-15  8:27 ` Denis V. Lunev [this message]
2014-12-15 10:45   ` [Qemu-devel] [PATCH 02/16] block/parallels: allow to specify DiskDescriptor.xml instead of image file Kevin Wolf
2014-12-15 11:51     ` Denis V. Lunev
2014-12-15  8:27 ` [Qemu-devel] [PATCH 03/16] iotests, parallels: quote TEST_IMG in 076 test to be path-safe Denis V. Lunev
2014-12-15  8:27 ` [Qemu-devel] [PATCH 04/16] iotests: simple parallels XML disk descriptor file test added Denis V. Lunev
2014-12-15 10:49   ` Kevin Wolf
2014-12-15  8:27 ` [Qemu-devel] [PATCH 05/16] block/parallels: support padded Parallels images Denis V. Lunev
2014-12-15 11:05   ` Kevin Wolf
2014-12-15 11:33     ` Denis V. Lunev
2014-12-15  8:27 ` [Qemu-devel] [PATCH 06/16] iotests: padded parallels image test Denis V. Lunev
2014-12-15  8:27 ` [Qemu-devel] [PATCH 07/16] parallels: change copyright information in the image header Denis V. Lunev
2014-12-15 11:06   ` Kevin Wolf
2014-12-15 11:52     ` Denis V. Lunev
2014-12-16 16:29     ` Denis V. Lunev
2014-12-15  8:27 ` [Qemu-devel] [PATCH 08/16] block/parallels: switch to bdrv_read Denis V. Lunev
2014-12-15  8:27 ` [Qemu-devel] [PATCH 09/16] block/parallels: read up to cluster end in one go Denis V. Lunev
2014-12-15  8:27 ` [Qemu-devel] [PATCH 10/16] block/parallels: add get_block_status Denis V. Lunev
2014-12-15 11:52   ` Denis V. Lunev
2014-12-15 12:18     ` Kevin Wolf
2014-12-15  8:27 ` [Qemu-devel] [PATCH 11/16] block/parallels: add support for backing files Denis V. Lunev
2014-12-15 12:30   ` Kevin Wolf
2014-12-15 13:08     ` Roman Kagan
2014-12-15  8:27 ` [Qemu-devel] [PATCH 12/16] iotests: testcase for backing in parallels format Denis V. Lunev
2014-12-15  8:27 ` [Qemu-devel] [PATCH 13/16] block/parallels: read disk size from XML if DiskDescriptor.xml is passed Denis V. Lunev
2014-12-15 12:38   ` Kevin Wolf
2014-12-15  8:27 ` [Qemu-devel] [PATCH 14/16] block/parallels: introduce ParallelsSnapshot data structure Denis V. Lunev
2014-12-15 12:45   ` Kevin Wolf
2014-12-15 13:32     ` Denis V. Lunev
2014-12-17 16:15     ` [Qemu-devel] [RFC PATCH 1/1] block/parallels: new concept for DiskDescriptor.xml Denis V. Lunev
2014-12-15  8:28 ` [Qemu-devel] [PATCH 15/16] block/parallels: support read-only parallels snapshots Denis V. Lunev
2014-12-15  8:28 ` [Qemu-devel] [PATCH 16/16] iotests: testcase parallels image with snapshots Denis V. Lunev

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=1418632081-20667-3-git-send-email-den@openvz.org \
    --to=den@openvz.org \
    --cc=jcody@redhat.com \
    --cc=kwolf@redhat.com \
    --cc=qemu-devel@nongnu.org \
    --cc=stefanha@redhat.com \
    /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).