Actually we have 2 major options without intruduction of the new concept: - follow VMDK approach (original approach in v4 patchset) - chain backing stores in XML parsing code in additional block driver
This is very rough but working conceptual code with a new approach. The patch should be applied on top of patch 1 of the original patchset. The idea is to parse XML file in new "XML" block driver and open real image as a backing store inside XML parsing code. The first image will be read-write, other underlying ones will be readnly. Padding will be passed through options dictionary. I have not addressed any other things from the review. Can you pls comment this? Signed-off-by: Denis V. Lunev <d...@openvz.org> CC: Jeff Cody <jc...@redhat.com> CC: Kevin Wolf <kw...@redhat.com> CC: Stefan Hajnoczi <stefa...@redhat.com> CC: Roman Kagan <rka...@parallels.com> --- block/parallels.c | 232 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 232 insertions(+) diff --git a/block/parallels.c b/block/parallels.c index 4f9cd8d..bc0d683 100644 --- a/block/parallels.c +++ b/block/parallels.c @@ -27,6 +27,12 @@ #include "block/block_int.h" #include "qemu/module.h" +#if defined(CONFIG_LIBXML2) +#include <libxml/parser.h> +#include <libxml/tree.h> +#endif +#include <stdarg.h> + /**************************************************************/ #define HEADER_MAGIC "WithoutFreeSpace" @@ -211,3 +217,229 @@ static void bdrv_parallels_init(void) } block_init(bdrv_parallels_init); + + +#if defined(CONFIG_LIBXML2) + +typedef struct BDRVPrlXmlState { + xmlDoc *xml; + BlockDriverState *bi; +} BDRVPrlXmlState; + + +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 prl_probe_xml(const uint8_t *data, int buf_size, const char *fn) +{ + int score = 0; + char *buf = g_malloc(buf_size + 1); + + memcpy(buf, data, buf_size); + buf[buf_size] = 0; + if (strstr(buf, "<?xml version=\"1.0\"?>") && + strstr(buf, "<Parallels_disk_image>")) { + score = 100; + } + g_free(buf); + return score; +} + +static int prl_open_xml(BlockDriverState *bs, QDict *opts, int fl, Error **errp) +{ + int64_t size; + int ret; + xmlDoc *doc = NULL; + xmlNode *root, *image; + char *xml = NULL; + const char *data; + char image_path[PATH_MAX]; + Error *local_err = NULL; + BDRVPrlXmlState *s = bs->opaque; + BlockDriverState *backing_hd = NULL; + + size = bdrv_getlength(bs->file); + if (size < 0) { + ret = (int)size; + goto fail; + } + /* XML file size should be reasonable */ + if (size > 65536) { + ret = -EFBIG; + goto fail; + } + + xml = g_malloc(size + 1); + + ret = bdrv_pread(bs->file, 0, xml, size); + if (ret != size) { + g_free(xml); + goto fail; + } + xml[size] = 0; + + ret = -EINVAL; + doc = xmlReadMemory(xml, size, NULL, NULL, + XML_PARSE_NOERROR | XML_PARSE_NOWARNING); + g_free(xml); + if (doc == NULL) { + goto fail; + } + root = xmlDocGetRootElement(doc); + if (root == NULL) { + goto fail; + } + + data = xml_get_text(root, "Disk_Parameters", "Disk_size", NULL); + if (data == NULL) { + goto fail; + } else { + char *endptr; + bs->total_sectors = strtoull(data, &endptr, 0); + if (*endptr != '\0') { + 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); + + bs->open_flags &= ~BDRV_O_NO_BACKING; + + fl &= ~(BDRV_O_RDWR | BDRV_O_COPY_ON_READ | BDRV_O_SNAPSHOT | + BDRV_O_TEMPORARY); + backing_hd = bdrv_new(); + ret = bdrv_open(&backing_hd, image_path, NULL, NULL, + fl, NULL, &local_err); + if (ret < 0) { + bdrv_unref(backing_hd); + backing_hd = NULL; + bs->open_flags |= BDRV_O_NO_BACKING; + error_setg(errp, "Could not open backing file: %s", + error_get_pretty(local_err)); + error_free(local_err); + goto done; + } + bdrv_set_backing_hd(bs, backing_hd); + + s->xml = doc; + doc = NULL; + +done: + if (doc != NULL) { + xmlFreeDoc(doc); + } + return ret; + +fail: + error_setg(errp, "Failed to parse Parallels disk descriptor XML"); + goto done; +} + +static coroutine_fn int +prl_co_read(BlockDriverState *bs, int64_t sect, uint8_t *buf, int n) +{ + return bdrv_read(bs->backing_hd, sect, buf, n); +} + +static void prl_close_xml(BlockDriverState *bs) +{ + BDRVPrlXmlState *s = bs->opaque; + xmlFreeDoc(s->xml); +} + +static BlockDriver bdrv_prl_xml = { + .format_name = "prl", + .instance_size = sizeof(BDRVPrlXmlState), + .bdrv_probe = prl_probe_xml, + .bdrv_open = prl_open_xml, + .bdrv_read = prl_co_read, + .bdrv_close = prl_close_xml, + .supports_backing = true, +}; + +static void bdrv_prl_init_xml(void) +{ + bdrv_register(&bdrv_prl_xml); +} + +block_init(bdrv_prl_init_xml); + +#endif -- 1.9.1