From patchwork Mon Dec 15 08:28:00 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Denis V. Lunev" X-Patchwork-Id: 421016 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [IPv6:2001:4830:134:3::11]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id B18981400D2 for ; Mon, 15 Dec 2014 20:12:01 +1100 (AEDT) Received: from localhost ([::1]:38538 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Y0Rh9-0001XI-Q5 for incoming@patchwork.ozlabs.org; Mon, 15 Dec 2014 04:11:59 -0500 Received: from eggs.gnu.org ([2001:4830:134:3::10]:47840) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Y0Rfy-0007vl-VG for qemu-devel@nongnu.org; Mon, 15 Dec 2014 04:10:51 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1Y0Rft-0005c0-6G for qemu-devel@nongnu.org; Mon, 15 Dec 2014 04:10:46 -0500 Received: from mailhub.sw.ru ([195.214.232.25]:27462 helo=relay.sw.ru) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Y0Rfs-0004kT-QH for qemu-devel@nongnu.org; Mon, 15 Dec 2014 04:10:41 -0500 Received: from hades.sw.ru ([10.30.8.132]) by relay.sw.ru (8.13.4/8.13.4) with ESMTP id sBF8RAUR012492; Mon, 15 Dec 2014 12:27:37 +0400 (MSK) From: "Denis V. Lunev" To: Date: Mon, 15 Dec 2014 11:28:00 +0300 Message-Id: <1418632081-20667-16-git-send-email-den@openvz.org> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1418632081-20667-1-git-send-email-den@openvz.org> References: <1418632081-20667-1-git-send-email-den@openvz.org> X-detected-operating-system: by eggs.gnu.org: OpenBSD 3.x X-Received-From: 195.214.232.25 Cc: Kevin Wolf , "Denis V. Lunev" , Jeff Cody , qemu-devel@nongnu.org, Stefan Hajnoczi Subject: [Qemu-devel] [PATCH 15/16] block/parallels: support read-only parallels snapshots X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org This patch reads snapshot information from the XML descriptor file. The lastest snapshot (read-write one) could be located using the following rules: GUID could be used as a source to locate leaf in the snapshot tree: - TopGUID tag is specified in the XML file - {704718E1-2314-44c8-9087-D78ED36B0F4E} snapshot is present. Used for backups by Parallels - {5FBAABE3-6958-40ff-92A7-860E329AAB41} snapshot Without one of the snapshots above disk image is invalid. Snapshots are read from top to bottom placing them into linear array, which is already properly handled at the moment. Signed-off-by: Denis V. Lunev CC: Jeff Cody CC: Kevin Wolf CC: Stefan Hajnoczi --- block/parallels.c | 102 +++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 77 insertions(+), 25 deletions(-) diff --git a/block/parallels.c b/block/parallels.c index d1b52f3..93a8498 100644 --- a/block/parallels.c +++ b/block/parallels.c @@ -42,6 +42,10 @@ #define HEADER_VERSION 2 #define HEADER_SIZE 64 +#define CURRENT_SNAP_ID "{5FBAABE3-6958-40ff-92A7-860E329AAB41}" +#define BACKUP_SNAP_ID "{704718E1-2314-44c8-9087-D78ED36B0F4E}" + + // always little-endian struct parallels_header { char magic[16]; // "WithoutFreeSpace" @@ -239,13 +243,34 @@ static const char *xml_get_text(xmlNode *node, ...) return NULL; } +static xmlNode *xml_find_snapshot(xmlNode *snaps, const char *guid) +{ + if (guid == NULL) { + return NULL; + } + + for (snaps = snaps->children; snaps != NULL; snaps = snaps->next) { + const char *id; + if (snaps->type != XML_ELEMENT_NODE) { + continue; + } + id = xml_get_text(snaps, "GUID", NULL); + + if (!xmlStrcasecmp((const xmlChar *)guid, (const xmlChar *)id)) { + return snaps; + } + } + return NULL; +} + static int parallels_open_xml(BlockDriverState *bs, int flags, Error **errp) { - int size, ret; + int ret, size, i; xmlDoc *doc = NULL; - xmlNode *root, *image; + xmlNode *root, *storage, *snaps; char *xml = NULL, *endptr; const char *data; + const char *leaf_snap; char image_path[PATH_MAX]; Error *local_err = NULL; BDRVParallelsState *s = bs->opaque; @@ -300,14 +325,47 @@ static int parallels_open_xml(BlockDriverState *bs, int flags, Error **errp) } } - 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; + snaps = xml_seek(root, "Snapshots", NULL); + + leaf_snap = xml_get_text(snaps, "TopGUID", NULL); + if (leaf_snap != NULL && xml_find_snapshot(snaps, leaf_snap) == NULL) { + goto fail; + } else if (xml_find_snapshot(snaps, BACKUP_SNAP_ID) != NULL) { + leaf_snap = BACKUP_SNAP_ID; + } else if (xml_find_snapshot(snaps, CURRENT_SNAP_ID) != NULL) { + leaf_snap = CURRENT_SNAP_ID; + } else { + goto fail; + } + + data = leaf_snap; + for (s->snaps_count = 0; data != NULL; s->snaps_count++) { + xmlNode *node = xml_find_snapshot(snaps, data); + if (node == NULL) { + break; + } + data = xml_get_text(node, "ParentGUID", NULL); + } + if (s->snaps_count == 0) { + goto fail; + } + s->snaps = g_malloc0(sizeof(ParallelsSnapshot) * s->snaps_count); + + storage = xml_seek(root, "StorageData", "Storage", NULL); + if (storage == NULL) { + goto fail; + } + + data = leaf_snap; + for (i = 0; i < s->snaps_count; i++) { + int64_t total_sectors; + xmlNode *image = xml_find_snapshot(storage, data); + xmlNode *snap = xml_find_snapshot(snaps, data); + + if (image == NULL || snap == NULL) { + goto fail; } - size++; data = xml_get_text(image, "Type", NULL); if (data != NULL && strcmp(data, "Compressed")) { error_setg(errp, "Only compressed Parallels images are supported"); @@ -318,32 +376,26 @@ static int parallels_open_xml(BlockDriverState *bs, int flags, Error **errp) 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; - } - s->snaps_count = size; - s->snaps = g_malloc0(sizeof(ParallelsSnapshot) * s->snaps_count); + path_combine(image_path, sizeof(image_path), bs->file->filename, data); - path_combine(image_path, sizeof(image_path), bs->file->filename, data); + ret = bdrv_open(&s->snaps[i].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); + goto done; + } - ret = bdrv_open(&s->snaps[0].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 { - int64_t total_sectors; ret = parallels_open_image(s->snaps, &total_sectors, errp); if (bs->total_sectors != total_sectors) { error_setg(errp, "Invalid image: wrong total sectors"); ret = -EINVAL; goto done; } + + data = xml_get_text(snap, "ParentGUID", NULL); } done: