[libvirt] [PATCHv2 2/3] snapshot: split snapshot conf code into own file

Eric Blake eblake at redhat.com
Thu Aug 23 20:54:15 UTC 2012


This has several benefits:
1. Future snapshot-related code has a definite place to go (and I
_will_ be adding some)
2. Snapshot errors now use the VIR_FROM_DOMAIN_SNAPSHOT error
classification, which has been underutilized (previously only in
libvirt.c)

* src/conf/domain_conf.h, domain_conf.c: Split...
* src/conf/snapshot_conf.h, snapshot_conf.c: ...into new files.
* src/Makefile.am (DOMAIN_CONF_SOURCES): Build new files.
* po/POTFILES.in: Mark new file for translation.
* src/vbox/vbox_tmpl.c: Update caller.
* src/esx/esx_driver.c: Likewise.
* src/qemu/qemu_command.c: Likewise.
* src/qemu/qemu_domain.h: Likewise.
---
 po/POTFILES.in           |   1 +
 src/Makefile.am          |   3 +-
 src/conf/domain_conf.c   | 921 +-------------------------------------------
 src/conf/domain_conf.h   | 139 +------
 src/conf/snapshot_conf.c | 970 +++++++++++++++++++++++++++++++++++++++++++++++
 src/conf/snapshot_conf.h | 157 ++++++++
 src/esx/esx_driver.c     |   1 +
 src/qemu/qemu_command.c  |   1 +
 src/qemu/qemu_domain.h   |   3 +-
 src/vbox/vbox_tmpl.c     |   1 +
 10 files changed, 1143 insertions(+), 1054 deletions(-)
 create mode 100644 src/conf/snapshot_conf.c
 create mode 100644 src/conf/snapshot_conf.h

diff --git a/po/POTFILES.in b/po/POTFILES.in
index 6c30116..7a91eb4 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -19,6 +19,7 @@ src/conf/node_device_conf.c
 src/conf/nwfilter_conf.c
 src/conf/nwfilter_params.c
 src/conf/secret_conf.c
+src/conf/snapshot_conf.c
 src/conf/storage_conf.c
 src/conf/storage_encryption_conf.c
 src/conf/virconsole.c
diff --git a/src/Makefile.am b/src/Makefile.am
index 2074571..995e032 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -153,7 +153,8 @@ DOMAIN_CONF_SOURCES =						\
 		conf/capabilities.c conf/capabilities.h		\
 		conf/domain_conf.c conf/domain_conf.h		\
 		conf/domain_audit.c conf/domain_audit.h		\
-		conf/domain_nwfilter.c conf/domain_nwfilter.h
+		conf/domain_nwfilter.c conf/domain_nwfilter.h	\
+		conf/snapshot_conf.c conf/snapshot_conf.h

 DOMAIN_EVENT_SOURCES =						\
 		conf/domain_event.c conf/domain_event.h
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index e5711d4..e3d04a7 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -35,6 +35,7 @@
 #include "virterror_internal.h"
 #include "datatypes.h"
 #include "domain_conf.h"
+#include "snapshot_conf.h"
 #include "memory.h"
 #include "verify.h"
 #include "xml.h"
@@ -226,12 +227,6 @@ VIR_ENUM_IMPL(virDomainDiskCopyOnRead, VIR_DOMAIN_DISK_COPY_ON_READ_LAST,
               "on",
               "off")

-VIR_ENUM_IMPL(virDomainDiskSnapshot, VIR_DOMAIN_DISK_SNAPSHOT_LAST,
-              "default",
-              "no",
-              "internal",
-              "external")
-
 VIR_ENUM_IMPL(virDomainController, VIR_DOMAIN_CONTROLLER_TYPE_LAST,
               "ide",
               "fdc",
@@ -528,18 +523,6 @@ VIR_ENUM_IMPL(virDomainState, VIR_DOMAIN_LAST,
               "crashed",
               "pmsuspended")

-/* virDomainSnapshotState is really virDomainState plus one extra state */
-VIR_ENUM_IMPL(virDomainSnapshotState, VIR_DOMAIN_SNAPSHOT_STATE_LAST,
-              "nostate",
-              "running",
-              "blocked",
-              "paused",
-              "shutdown",
-              "shutoff",
-              "crashed",
-              "pmsuspended",
-              "disk-snapshot")
-
 #define VIR_DOMAIN_NOSTATE_LAST (VIR_DOMAIN_NOSTATE_UNKNOWN + 1)
 VIR_ENUM_IMPL(virDomainNostateReason, VIR_DOMAIN_NOSTATE_LAST,
               "unknown")
@@ -661,15 +644,6 @@ VIR_ENUM_IMPL(virDomainNumatuneMemPlacementMode,
 #define VIR_DOMAIN_XML_WRITE_FLAGS  VIR_DOMAIN_XML_SECURE
 #define VIR_DOMAIN_XML_READ_FLAGS   VIR_DOMAIN_XML_INACTIVE

-struct _virDomainSnapshotObjList {
-    /* name string -> virDomainSnapshotObj  mapping
-     * for O(1), lockless lookup-by-name */
-    virHashTable *objs;
-
-    virDomainSnapshotObj metaroot; /* Special parent of all root snapshots */
-};
-
-
 static virClassPtr virDomainObjClass;
 static void virDomainObjDispose(void *obj);

@@ -14136,856 +14110,6 @@ cleanup:
     return -1;
 }

-/* Snapshot Def functions */
-static void
-virDomainSnapshotDiskDefClear(virDomainSnapshotDiskDefPtr disk)
-{
-    VIR_FREE(disk->name);
-    VIR_FREE(disk->file);
-    VIR_FREE(disk->driverType);
-}
-
-void virDomainSnapshotDefFree(virDomainSnapshotDefPtr def)
-{
-    int i;
-
-    if (!def)
-        return;
-
-    VIR_FREE(def->name);
-    VIR_FREE(def->description);
-    VIR_FREE(def->parent);
-    for (i = 0; i < def->ndisks; i++)
-        virDomainSnapshotDiskDefClear(&def->disks[i]);
-    VIR_FREE(def->disks);
-    virDomainDefFree(def->dom);
-    VIR_FREE(def);
-}
-
-static int
-virDomainSnapshotDiskDefParseXML(xmlNodePtr node,
-                                 virDomainSnapshotDiskDefPtr def)
-{
-    int ret = -1;
-    char *snapshot = NULL;
-    xmlNodePtr cur;
-
-    def->name = virXMLPropString(node, "name");
-    if (!def->name) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                       _("missing name from disk snapshot element"));
-        goto cleanup;
-    }
-
-    snapshot = virXMLPropString(node, "snapshot");
-    if (snapshot) {
-        def->snapshot = virDomainDiskSnapshotTypeFromString(snapshot);
-        if (def->snapshot <= 0) {
-            virReportError(VIR_ERR_INTERNAL_ERROR,
-                           _("unknown disk snapshot setting '%s'"),
-                           snapshot);
-            goto cleanup;
-        }
-    }
-
-    cur = node->children;
-    while (cur) {
-        if (cur->type == XML_ELEMENT_NODE) {
-            if (!def->file &&
-                xmlStrEqual(cur->name, BAD_CAST "source")) {
-                def->file = virXMLPropString(cur, "file");
-            } else if (!def->driverType &&
-                       xmlStrEqual(cur->name, BAD_CAST "driver")) {
-                def->driverType = virXMLPropString(cur, "type");
-            }
-        }
-        cur = cur->next;
-    }
-
-    if (!def->snapshot && (def->file || def->driverType))
-        def->snapshot = VIR_DOMAIN_DISK_SNAPSHOT_EXTERNAL;
-
-    ret = 0;
-cleanup:
-    VIR_FREE(snapshot);
-    if (ret < 0)
-        virDomainSnapshotDiskDefClear(def);
-    return ret;
-}
-
-/* flags is bitwise-or of virDomainSnapshotParseFlags.
- * If flags does not include VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE, then
- * caps and expectedVirtTypes are ignored.
- */
-virDomainSnapshotDefPtr
-virDomainSnapshotDefParseString(const char *xmlStr,
-                                virCapsPtr caps,
-                                unsigned int expectedVirtTypes,
-                                unsigned int flags)
-{
-    xmlXPathContextPtr ctxt = NULL;
-    xmlDocPtr xml = NULL;
-    virDomainSnapshotDefPtr def = NULL;
-    virDomainSnapshotDefPtr ret = NULL;
-    xmlNodePtr *nodes = NULL;
-    int i;
-    char *creation = NULL, *state = NULL;
-    struct timeval tv;
-    int active;
-    char *tmp;
-    int keepBlanksDefault = xmlKeepBlanksDefault(0);
-
-    xml = virXMLParseCtxt(NULL, xmlStr, _("(domain_snapshot)"), &ctxt);
-    if (!xml) {
-        xmlKeepBlanksDefault(keepBlanksDefault);
-        return NULL;
-    }
-    xmlKeepBlanksDefault(keepBlanksDefault);
-
-    if (VIR_ALLOC(def) < 0) {
-        virReportOOMError();
-        goto cleanup;
-    }
-
-    if (!xmlStrEqual(ctxt->node->name, BAD_CAST "domainsnapshot")) {
-        virReportError(VIR_ERR_XML_ERROR, "%s", _("domainsnapshot"));
-        goto cleanup;
-    }
-
-    gettimeofday(&tv, NULL);
-
-    def->name = virXPathString("string(./name)", ctxt);
-    if (def->name == NULL) {
-        if (flags & VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE) {
-            virReportError(VIR_ERR_XML_ERROR, "%s",
-                           _("a redefined snapshot must have a name"));
-            goto cleanup;
-        } else {
-            ignore_value(virAsprintf(&def->name, "%lld",
-                                     (long long)tv.tv_sec));
-        }
-    }
-
-    if (def->name == NULL) {
-        virReportOOMError();
-        goto cleanup;
-    }
-
-    def->description = virXPathString("string(./description)", ctxt);
-
-    if (flags & VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE) {
-        if (virXPathLongLong("string(./creationTime)", ctxt,
-                             &def->creationTime) < 0) {
-            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                           _("missing creationTime from existing snapshot"));
-            goto cleanup;
-        }
-
-        def->parent = virXPathString("string(./parent/name)", ctxt);
-
-        state = virXPathString("string(./state)", ctxt);
-        if (state == NULL) {
-            /* there was no state in an existing snapshot; this
-             * should never happen
-             */
-            virReportError(VIR_ERR_XML_ERROR, "%s",
-                           _("missing state from existing snapshot"));
-            goto cleanup;
-        }
-        def->state = virDomainSnapshotStateTypeFromString(state);
-        if (def->state < 0) {
-            virReportError(VIR_ERR_XML_ERROR,
-                           _("Invalid state '%s' in domain snapshot XML"),
-                           state);
-            goto cleanup;
-        }
-
-        /* Older snapshots were created with just <domain>/<uuid>, and
-         * lack domain/@type.  In that case, leave dom NULL, and
-         * clients will have to decide between best effort
-         * initialization or outright failure.  */
-        if ((tmp = virXPathString("string(./domain/@type)", ctxt))) {
-            xmlNodePtr domainNode = virXPathNode("./domain", ctxt);
-
-            VIR_FREE(tmp);
-            if (!domainNode) {
-                virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                               _("missing domain in snapshot"));
-                goto cleanup;
-            }
-            def->dom = virDomainDefParseNode(caps, xml, domainNode,
-                                             expectedVirtTypes,
-                                             (VIR_DOMAIN_XML_INACTIVE |
-                                              VIR_DOMAIN_XML_SECURE));
-            if (!def->dom)
-                goto cleanup;
-        } else {
-            VIR_WARN("parsing older snapshot that lacks domain");
-        }
-    } else {
-        def->creationTime = tv.tv_sec;
-    }
-
-    if ((i = virXPathNodeSet("./disks/*", ctxt, &nodes)) < 0)
-        goto cleanup;
-    if (flags & VIR_DOMAIN_SNAPSHOT_PARSE_DISKS ||
-        (flags & VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE &&
-         def->state == VIR_DOMAIN_DISK_SNAPSHOT)) {
-        def->ndisks = i;
-        if (def->ndisks && VIR_ALLOC_N(def->disks, def->ndisks) < 0) {
-            virReportOOMError();
-            goto cleanup;
-        }
-        for (i = 0; i < def->ndisks; i++) {
-            if (virDomainSnapshotDiskDefParseXML(nodes[i], &def->disks[i]) < 0)
-                goto cleanup;
-        }
-        VIR_FREE(nodes);
-    } else if (i) {
-        virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
-                       _("unable to handle disk requests in snapshot"));
-        goto cleanup;
-    }
-
-    if (flags & VIR_DOMAIN_SNAPSHOT_PARSE_INTERNAL) {
-        if (virXPathInt("string(./active)", ctxt, &active) < 0) {
-            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                           _("Could not find 'active' element"));
-            goto cleanup;
-        }
-        def->current = active != 0;
-    }
-
-    ret = def;
-
-cleanup:
-    VIR_FREE(creation);
-    VIR_FREE(state);
-    VIR_FREE(nodes);
-    xmlXPathFreeContext(ctxt);
-    if (ret == NULL)
-        virDomainSnapshotDefFree(def);
-    xmlFreeDoc(xml);
-
-    return ret;
-}
-
-static int
-disksorter(const void *a, const void *b)
-{
-    const virDomainSnapshotDiskDef *diska = a;
-    const virDomainSnapshotDiskDef *diskb = b;
-
-    /* Integer overflow shouldn't be a problem here.  */
-    return diska->index - diskb->index;
-}
-
-/* Align def->disks to def->domain.  Sort the list of def->disks,
- * filling in any missing disks or snapshot state defaults given by
- * the domain, with a fallback to a passed in default.  Convert paths
- * to disk targets for uniformity.  Issue an error and return -1 if
- * any def->disks[n]->name appears more than once or does not map to
- * dom->disks.  If require_match, also require that existing
- * def->disks snapshot states do not override explicit def->dom
- * settings.  */
-int
-virDomainSnapshotAlignDisks(virDomainSnapshotDefPtr def,
-                            int default_snapshot,
-                            bool require_match)
-{
-    int ret = -1;
-    virBitmapPtr map = NULL;
-    int i;
-    int ndisks;
-    bool inuse;
-
-    if (!def->dom) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                       _("missing domain in snapshot"));
-        goto cleanup;
-    }
-
-    if (def->ndisks > def->dom->ndisks) {
-        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
-                       _("too many disk snapshot requests for domain"));
-        goto cleanup;
-    }
-
-    /* Unlikely to have a guest without disks but technically possible.  */
-    if (!def->dom->ndisks) {
-        ret = 0;
-        goto cleanup;
-    }
-
-    if (!(map = virBitmapAlloc(def->dom->ndisks))) {
-        virReportOOMError();
-        goto cleanup;
-    }
-
-    /* Double check requested disks.  */
-    for (i = 0; i < def->ndisks; i++) {
-        virDomainSnapshotDiskDefPtr disk = &def->disks[i];
-        int idx = virDomainDiskIndexByName(def->dom, disk->name, false);
-        int disk_snapshot;
-
-        if (idx < 0) {
-            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
-                           _("no disk named '%s'"), disk->name);
-            goto cleanup;
-        }
-        disk_snapshot = def->dom->disks[idx]->snapshot;
-
-        if (virBitmapGetBit(map, idx, &inuse) < 0 || inuse) {
-            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
-                           _("disk '%s' specified twice"),
-                           disk->name);
-            goto cleanup;
-        }
-        ignore_value(virBitmapSetBit(map, idx));
-        disk->index = idx;
-        if (!disk_snapshot)
-            disk_snapshot = default_snapshot;
-        if (!disk->snapshot) {
-            disk->snapshot = disk_snapshot;
-        } else if (disk_snapshot && require_match &&
-                   disk->snapshot != disk_snapshot) {
-            const char *tmp = virDomainDiskSnapshotTypeToString(disk_snapshot);
-            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
-                           _("disk '%s' must use snapshot mode '%s'"),
-                           disk->name, tmp);
-            goto cleanup;
-        }
-        if (disk->file &&
-            disk->snapshot != VIR_DOMAIN_DISK_SNAPSHOT_EXTERNAL) {
-            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
-                           _("file '%s' for disk '%s' requires "
-                             "use of external snapshot mode"),
-                           disk->file, disk->name);
-            goto cleanup;
-        }
-        if (STRNEQ(disk->name, def->dom->disks[idx]->dst)) {
-            VIR_FREE(disk->name);
-            if (!(disk->name = strdup(def->dom->disks[idx]->dst))) {
-                virReportOOMError();
-                goto cleanup;
-            }
-        }
-    }
-
-    /* Provide defaults for all remaining disks.  */
-    ndisks = def->ndisks;
-    if (VIR_EXPAND_N(def->disks, def->ndisks,
-                     def->dom->ndisks - def->ndisks) < 0) {
-        virReportOOMError();
-        goto cleanup;
-    }
-
-    for (i = 0; i < def->dom->ndisks; i++) {
-        virDomainSnapshotDiskDefPtr disk;
-
-        ignore_value(virBitmapGetBit(map, i, &inuse));
-        if (inuse)
-            continue;
-        disk = &def->disks[ndisks++];
-        if (!(disk->name = strdup(def->dom->disks[i]->dst))) {
-            virReportOOMError();
-            goto cleanup;
-        }
-        disk->index = i;
-        disk->snapshot = def->dom->disks[i]->snapshot;
-        if (!disk->snapshot)
-            disk->snapshot = default_snapshot;
-    }
-
-    qsort(&def->disks[0], def->ndisks, sizeof(def->disks[0]), disksorter);
-
-    /* Generate any default external file names, but only if the
-     * backing file is a regular file.  */
-    for (i = 0; i < def->ndisks; i++) {
-        virDomainSnapshotDiskDefPtr disk = &def->disks[i];
-
-        if (disk->snapshot == VIR_DOMAIN_DISK_SNAPSHOT_EXTERNAL &&
-            !disk->file) {
-            const char *original = def->dom->disks[i]->src;
-            const char *tmp;
-            struct stat sb;
-
-            if (!original) {
-                virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
-                               _("cannot generate external snapshot name "
-                                 "for disk '%s' without source"),
-                               disk->name);
-                goto cleanup;
-            }
-            if (stat(original, &sb) < 0 || !S_ISREG(sb.st_mode)) {
-                virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
-                               _("source for disk '%s' is not a regular "
-                                 "file; refusing to generate external "
-                                 "snapshot name"),
-                               disk->name);
-                goto cleanup;
-            }
-
-            tmp = strrchr(original, '.');
-            if (!tmp || strchr(tmp, '/')) {
-                ignore_value(virAsprintf(&disk->file, "%s.%s",
-                                         original, def->name));
-            } else {
-                if ((tmp - original) > INT_MAX) {
-                    virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                                   _("integer overflow"));
-                    goto cleanup;
-                }
-                ignore_value(virAsprintf(&disk->file, "%.*s.%s",
-                                         (int) (tmp - original), original,
-                                         def->name));
-            }
-            if (!disk->file) {
-                virReportOOMError();
-                goto cleanup;
-            }
-        }
-    }
-
-    ret = 0;
-
-cleanup:
-    virBitmapFree(map);
-    return ret;
-}
-
-char *virDomainSnapshotDefFormat(const char *domain_uuid,
-                                 virDomainSnapshotDefPtr def,
-                                 unsigned int flags,
-                                 int internal)
-{
-    virBuffer buf = VIR_BUFFER_INITIALIZER;
-    int i;
-
-    virCheckFlags(VIR_DOMAIN_XML_SECURE |
-                  VIR_DOMAIN_XML_UPDATE_CPU, NULL);
-
-    flags |= VIR_DOMAIN_XML_INACTIVE;
-
-    virBufferAddLit(&buf, "<domainsnapshot>\n");
-    virBufferEscapeString(&buf, "  <name>%s</name>\n", def->name);
-    if (def->description)
-        virBufferEscapeString(&buf, "  <description>%s</description>\n",
-                              def->description);
-    virBufferAsprintf(&buf, "  <state>%s</state>\n",
-                      virDomainSnapshotStateTypeToString(def->state));
-    if (def->parent) {
-        virBufferAddLit(&buf, "  <parent>\n");
-        virBufferEscapeString(&buf, "    <name>%s</name>\n", def->parent);
-        virBufferAddLit(&buf, "  </parent>\n");
-    }
-    virBufferAsprintf(&buf, "  <creationTime>%lld</creationTime>\n",
-                      def->creationTime);
-    /* For now, only output <disks> on disk-snapshot */
-    if (def->state == VIR_DOMAIN_DISK_SNAPSHOT) {
-        virBufferAddLit(&buf, "  <disks>\n");
-        for (i = 0; i < def->ndisks; i++) {
-            virDomainSnapshotDiskDefPtr disk = &def->disks[i];
-
-            if (!disk->name)
-                continue;
-
-            virBufferEscapeString(&buf, "    <disk name='%s'", disk->name);
-            if (disk->snapshot)
-                virBufferAsprintf(&buf, " snapshot='%s'",
-                                  virDomainDiskSnapshotTypeToString(disk->snapshot));
-            if (disk->file || disk->driverType) {
-                virBufferAddLit(&buf, ">\n");
-                if (disk->driverType)
-                    virBufferEscapeString(&buf, "      <driver type='%s'/>\n",
-                                          disk->driverType);
-                if (disk->file)
-                    virBufferEscapeString(&buf, "      <source file='%s'/>\n",
-                                          disk->file);
-                virBufferAddLit(&buf, "    </disk>\n");
-            } else {
-                virBufferAddLit(&buf, "/>\n");
-            }
-        }
-        virBufferAddLit(&buf, "  </disks>\n");
-    }
-    if (def->dom) {
-        virBufferAdjustIndent(&buf, 2);
-        if (virDomainDefFormatInternal(def->dom, flags, &buf) < 0) {
-            virBufferFreeAndReset(&buf);
-            return NULL;
-        }
-        virBufferAdjustIndent(&buf, -2);
-    } else if (domain_uuid) {
-        virBufferAddLit(&buf, "  <domain>\n");
-        virBufferAsprintf(&buf, "    <uuid>%s</uuid>\n", domain_uuid);
-        virBufferAddLit(&buf, "  </domain>\n");
-    }
-    if (internal)
-        virBufferAsprintf(&buf, "  <active>%d</active>\n", def->current);
-    virBufferAddLit(&buf, "</domainsnapshot>\n");
-
-    if (virBufferError(&buf)) {
-        virBufferFreeAndReset(&buf);
-        virReportOOMError();
-        return NULL;
-    }
-
-    return virBufferContentAndReset(&buf);
-}
-
-/* Snapshot Obj functions */
-static virDomainSnapshotObjPtr virDomainSnapshotObjNew(void)
-{
-    virDomainSnapshotObjPtr snapshot;
-
-    if (VIR_ALLOC(snapshot) < 0) {
-        virReportOOMError();
-        return NULL;
-    }
-
-    VIR_DEBUG("obj=%p", snapshot);
-
-    return snapshot;
-}
-
-static void virDomainSnapshotObjFree(virDomainSnapshotObjPtr snapshot)
-{
-    if (!snapshot)
-        return;
-
-    VIR_DEBUG("obj=%p", snapshot);
-
-    virDomainSnapshotDefFree(snapshot->def);
-    VIR_FREE(snapshot);
-}
-
-virDomainSnapshotObjPtr virDomainSnapshotAssignDef(virDomainSnapshotObjListPtr snapshots,
-                                                   const virDomainSnapshotDefPtr def)
-{
-    virDomainSnapshotObjPtr snap;
-
-    if (virHashLookup(snapshots->objs, def->name) != NULL) {
-        virReportError(VIR_ERR_INTERNAL_ERROR,
-                       _("unexpected domain snapshot %s already exists"),
-                       def->name);
-        return NULL;
-    }
-
-    if (!(snap = virDomainSnapshotObjNew()))
-        return NULL;
-    snap->def = def;
-
-    if (virHashAddEntry(snapshots->objs, snap->def->name, snap) < 0) {
-        VIR_FREE(snap);
-        return NULL;
-    }
-
-    return snap;
-}
-
-/* Snapshot Obj List functions */
-static void
-virDomainSnapshotObjListDataFree(void *payload,
-                                 const void *name ATTRIBUTE_UNUSED)
-{
-    virDomainSnapshotObjPtr obj = payload;
-
-    virDomainSnapshotObjFree(obj);
-}
-
-virDomainSnapshotObjListPtr
-virDomainSnapshotObjListNew(void)
-{
-    virDomainSnapshotObjListPtr snapshots;
-    if (VIR_ALLOC(snapshots) < 0) {
-        virReportOOMError();
-        return NULL;
-    }
-    snapshots->objs = virHashCreate(50, virDomainSnapshotObjListDataFree);
-    if (!snapshots->objs) {
-        VIR_FREE(snapshots);
-        return NULL;
-    }
-    return snapshots;
-}
-
-void
-virDomainSnapshotObjListFree(virDomainSnapshotObjListPtr snapshots)
-{
-    if (!snapshots)
-        return;
-    virHashFree(snapshots->objs);
-    VIR_FREE(snapshots);
-}
-
-struct virDomainSnapshotNameData {
-    char **const names;
-    int maxnames;
-    unsigned int flags;
-    int count;
-    bool error;
-};
-
-static void virDomainSnapshotObjListCopyNames(void *payload,
-                                              const void *name ATTRIBUTE_UNUSED,
-                                              void *opaque)
-{
-    virDomainSnapshotObjPtr obj = payload;
-    struct virDomainSnapshotNameData *data = opaque;
-
-    if (data->error)
-        return;
-    /* Caller already sanitized flags.  Filtering on DESCENDANTS was
-     * done by choice of iteration in the caller.  */
-    if ((data->flags & VIR_DOMAIN_SNAPSHOT_LIST_LEAVES) && obj->nchildren)
-        return;
-    if ((data->flags & VIR_DOMAIN_SNAPSHOT_LIST_NO_LEAVES) && !obj->nchildren)
-        return;
-
-    if (data->names && data->count < data->maxnames &&
-        !(data->names[data->count] = strdup(obj->def->name))) {
-        data->error = true;
-        virReportOOMError();
-        return;
-    }
-    data->count++;
-}
-
-int
-virDomainSnapshotObjListGetNames(virDomainSnapshotObjListPtr snapshots,
-                                 virDomainSnapshotObjPtr from,
-                                 char **const names, int maxnames,
-                                 unsigned int flags)
-{
-    struct virDomainSnapshotNameData data = { names, maxnames, flags, 0,
-                                              false };
-    int i;
-
-    if (!from) {
-        /* LIST_ROOTS and LIST_DESCENDANTS have the same bit value,
-         * but opposite semantics.  Toggle here to get the correct
-         * traversal on the metaroot.  */
-        flags ^= VIR_DOMAIN_SNAPSHOT_LIST_ROOTS;
-        from = &snapshots->metaroot;
-    }
-
-    /* We handle LIST_ROOT/LIST_DESCENDANTS directly, mask that bit
-     * out to determine when we must use the filter callback.  */
-    data.flags &= ~VIR_DOMAIN_SNAPSHOT_LIST_DESCENDANTS;
-
-    /* If this common code is being used, we assume that all snapshots
-     * have metadata, and thus can handle METADATA up front as an
-     * all-or-none filter.  XXX This might not always be true, if we
-     * add the ability to track qcow2 internal snapshots without the
-     * use of metadata.  */
-    if ((data.flags & VIR_DOMAIN_SNAPSHOT_FILTERS_METADATA) ==
-        VIR_DOMAIN_SNAPSHOT_LIST_NO_METADATA)
-        return 0;
-    data.flags &= ~VIR_DOMAIN_SNAPSHOT_FILTERS_METADATA;
-
-    /* For ease of coding the visitor, it is easier to zero the LEAVES
-     * group if both bits are set.  */
-    if ((data.flags & VIR_DOMAIN_SNAPSHOT_FILTERS_LEAVES) ==
-        VIR_DOMAIN_SNAPSHOT_FILTERS_LEAVES)
-        data.flags &= ~VIR_DOMAIN_SNAPSHOT_FILTERS_LEAVES;
-
-    if (flags & VIR_DOMAIN_SNAPSHOT_LIST_DESCENDANTS) {
-        if (from->def)
-            virDomainSnapshotForEachDescendant(from,
-                                               virDomainSnapshotObjListCopyNames,
-                                               &data);
-        else if (names || data.flags)
-            virHashForEach(snapshots->objs, virDomainSnapshotObjListCopyNames,
-                           &data);
-        else
-            data.count = virHashSize(snapshots->objs);
-    } else if (names || data.flags) {
-        virDomainSnapshotForEachChild(from,
-                                      virDomainSnapshotObjListCopyNames, &data);
-    } else {
-        data.count = from->nchildren;
-    }
-
-    if (data.error) {
-        for (i = 0; i < data.count; i++)
-            VIR_FREE(names[i]);
-        return -1;
-    }
-
-    return data.count;
-}
-
-int
-virDomainSnapshotObjListNum(virDomainSnapshotObjListPtr snapshots,
-                            virDomainSnapshotObjPtr from,
-                            unsigned int flags)
-{
-    return virDomainSnapshotObjListGetNames(snapshots, from, NULL, 0, flags);
-}
-
-virDomainSnapshotObjPtr
-virDomainSnapshotFindByName(const virDomainSnapshotObjListPtr snapshots,
-                            const char *name)
-{
-    return name ? virHashLookup(snapshots->objs, name) : &snapshots->metaroot;
-}
-
-void virDomainSnapshotObjListRemove(virDomainSnapshotObjListPtr snapshots,
-                                    virDomainSnapshotObjPtr snapshot)
-{
-    virHashRemoveEntry(snapshots->objs, snapshot->def->name);
-}
-
-int
-virDomainSnapshotForEach(virDomainSnapshotObjListPtr snapshots,
-                         virHashIterator iter,
-                         void *data)
-{
-    return virHashForEach(snapshots->objs, iter, data);
-}
-
-/* Run iter(data) on all direct children of snapshot, while ignoring all
- * other entries in snapshots.  Return the number of children
- * visited.  No particular ordering is guaranteed.  */
-int
-virDomainSnapshotForEachChild(virDomainSnapshotObjPtr snapshot,
-                              virHashIterator iter,
-                              void *data)
-{
-    virDomainSnapshotObjPtr child = snapshot->first_child;
-
-    while (child) {
-        virDomainSnapshotObjPtr next = child->sibling;
-        (iter)(child, child->def->name, data);
-        child = next;
-    }
-
-    return snapshot->nchildren;
-}
-
-struct snapshot_act_on_descendant {
-    int number;
-    virHashIterator iter;
-    void *data;
-};
-
-static void
-virDomainSnapshotActOnDescendant(void *payload,
-                                 const void *name,
-                                 void *data)
-{
-    virDomainSnapshotObjPtr obj = payload;
-    struct snapshot_act_on_descendant *curr = data;
-
-    curr->number += 1 + virDomainSnapshotForEachDescendant(obj,
-                                                           curr->iter,
-                                                           curr->data);
-    (curr->iter)(payload, name, curr->data);
-}
-
-/* Run iter(data) on all descendants of snapshot, while ignoring all
- * other entries in snapshots.  Return the number of descendants
- * visited.  No particular ordering is guaranteed.  */
-int
-virDomainSnapshotForEachDescendant(virDomainSnapshotObjPtr snapshot,
-                                   virHashIterator iter,
-                                   void *data)
-{
-    struct snapshot_act_on_descendant act;
-
-    act.number = 0;
-    act.iter = iter;
-    act.data = data;
-    virDomainSnapshotForEachChild(snapshot,
-                                  virDomainSnapshotActOnDescendant, &act);
-
-    return act.number;
-}
-
-/* Struct and callback function used as a hash table callback; each call
- * inspects the pre-existing snapshot->def->parent field, and adjusts
- * the snapshot->parent field as well as the parent's child fields to
- * wire up the hierarchical relations for the given snapshot.  The error
- * indicator gets set if a parent is missing or a requested parent would
- * cause a circular parent chain.  */
-struct snapshot_set_relation {
-    virDomainSnapshotObjListPtr snapshots;
-    int err;
-};
-static void
-virDomainSnapshotSetRelations(void *payload,
-                              const void *name ATTRIBUTE_UNUSED,
-                              void *data)
-{
-    virDomainSnapshotObjPtr obj = payload;
-    struct snapshot_set_relation *curr = data;
-    virDomainSnapshotObjPtr tmp;
-
-    obj->parent = virDomainSnapshotFindByName(curr->snapshots,
-                                              obj->def->parent);
-    if (!obj->parent) {
-        curr->err = -1;
-        obj->parent = &curr->snapshots->metaroot;
-        VIR_WARN("snapshot %s lacks parent", obj->def->name);
-    } else {
-        tmp = obj->parent;
-        while (tmp && tmp->def) {
-            if (tmp == obj) {
-                curr->err = -1;
-                obj->parent = &curr->snapshots->metaroot;
-                VIR_WARN("snapshot %s in circular chain", obj->def->name);
-                break;
-            }
-            tmp = tmp->parent;
-        }
-    }
-    obj->parent->nchildren++;
-    obj->sibling = obj->parent->first_child;
-    obj->parent->first_child = obj;
-}
-
-/* Populate parent link and child count of all snapshots, with all
- * relations starting as 0/NULL.  Return 0 on success, -1 if a parent
- * is missing or if a circular relationship was requested.  */
-int
-virDomainSnapshotUpdateRelations(virDomainSnapshotObjListPtr snapshots)
-{
-    struct snapshot_set_relation act = { snapshots, 0 };
-
-    virHashForEach(snapshots->objs, virDomainSnapshotSetRelations, &act);
-    return act.err;
-}
-
-/* Prepare to reparent or delete snapshot, by removing it from its
- * current listed parent.  Note that when bulk removing all children
- * of a parent, it is faster to just 0 the count rather than calling
- * this function on each child.  */
-void
-virDomainSnapshotDropParent(virDomainSnapshotObjPtr snapshot)
-{
-    virDomainSnapshotObjPtr prev = NULL;
-    virDomainSnapshotObjPtr curr = NULL;
-
-    snapshot->parent->nchildren--;
-    curr = snapshot->parent->first_child;
-    while (curr != snapshot) {
-        if (!curr) {
-            VIR_WARN("inconsistent snapshot relations");
-            return;
-        }
-        prev = curr;
-        curr = curr->sibling;
-    }
-    if (prev)
-        prev->sibling = snapshot->sibling;
-    else
-        snapshot->parent->first_child = snapshot->sibling;
-    snapshot->parent = NULL;
-    snapshot->sibling = NULL;
-}
-
-
 int virDomainChrDefForeach(virDomainDefPtr def,
                            bool abortOnError,
                            virDomainChrDefIterator iter,
@@ -15836,49 +14960,6 @@ cleanup:
     return ret;
 }

-int
-virDomainListSnapshots(virDomainSnapshotObjListPtr snapshots,
-                       virDomainSnapshotObjPtr from,
-                       virDomainPtr dom,
-                       virDomainSnapshotPtr **snaps,
-                       unsigned int flags)
-{
-    int count = virDomainSnapshotObjListNum(snapshots, from, flags);
-    virDomainSnapshotPtr *list;
-    char **names;
-    int ret = -1;
-    int i;
-
-    if (!snaps)
-        return count;
-    if (VIR_ALLOC_N(names, count) < 0 ||
-        VIR_ALLOC_N(list, count + 1) < 0) {
-        virReportOOMError();
-        goto cleanup;
-    }
-
-    if (virDomainSnapshotObjListGetNames(snapshots, from, names, count,
-                                         flags) < 0)
-        goto cleanup;
-    for (i = 0; i < count; i++)
-        if ((list[i] = virGetDomainSnapshot(dom, names[i])) == NULL)
-            goto cleanup;
-
-    ret = count;
-    *snaps = list;
-
-cleanup:
-    for (i = 0; i < count; i++)
-        VIR_FREE(names[i]);
-    VIR_FREE(names);
-    if (ret < 0 && list) {
-        for (i = 0; i < count; i++)
-            virObjectUnref(list[i]);
-        VIR_FREE(list);
-    }
-    return ret;
-}
-
 virSecurityLabelDefPtr
 virDomainDefGetSecurityLabelDef(virDomainDefPtr def, const char *model)
 {
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index 6033641..ef4feaa 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -101,6 +101,12 @@ typedef virDomainChrDef *virDomainChrDefPtr;
 typedef struct _virDomainMemballoonDef virDomainMemballoonDef;
 typedef virDomainMemballoonDef *virDomainMemballoonDefPtr;

+typedef struct _virDomainSnapshotObj virDomainSnapshotObj;
+typedef virDomainSnapshotObj *virDomainSnapshotObjPtr;
+
+typedef struct _virDomainSnapshotObjList virDomainSnapshotObjList;
+typedef virDomainSnapshotObjList *virDomainSnapshotObjListPtr;
+
 /* Flags for the 'type' field in virDomainDeviceDef */
 typedef enum {
     VIR_DOMAIN_DEVICE_NONE = 0,
@@ -501,21 +507,6 @@ enum virDomainDiskCopyOnRead {
     VIR_DOMAIN_DISK_COPY_ON_READ_LAST
 };

-enum virDomainDiskSnapshot {
-    VIR_DOMAIN_DISK_SNAPSHOT_DEFAULT = 0,
-    VIR_DOMAIN_DISK_SNAPSHOT_NO,
-    VIR_DOMAIN_DISK_SNAPSHOT_INTERNAL,
-    VIR_DOMAIN_DISK_SNAPSHOT_EXTERNAL,
-
-    VIR_DOMAIN_DISK_SNAPSHOT_LAST
-};
-
-enum virDomainSnapshotState {
-    /* Inherit the VIR_DOMAIN_* states from virDomainState.  */
-    VIR_DOMAIN_DISK_SNAPSHOT = VIR_DOMAIN_LAST,
-    VIR_DOMAIN_SNAPSHOT_STATE_LAST
-};
-
 enum virDomainStartupPolicy {
     VIR_DOMAIN_STARTUP_POLICY_DEFAULT = 0,
     VIR_DOMAIN_STARTUP_POLICY_MANDATORY,
@@ -587,7 +578,7 @@ struct _virDomainDiskDef {
     int ioeventfd;
     int event_idx;
     int copy_on_read;
-    int snapshot; /* enum virDomainDiskSnapshot */
+    int snapshot; /* enum virDomainDiskSnapshot, snapshot_conf.h */
     int startupPolicy; /* enum virDomainStartupPolicy */
     unsigned int readonly : 1;
     unsigned int shared : 1;
@@ -1713,102 +1704,6 @@ enum virDomainTaintFlags {
     VIR_DOMAIN_TAINT_LAST
 };

-/* Items related to snapshot state */
-
-/* Stores disk-snapshot information */
-typedef struct _virDomainSnapshotDiskDef virDomainSnapshotDiskDef;
-typedef virDomainSnapshotDiskDef *virDomainSnapshotDiskDefPtr;
-struct _virDomainSnapshotDiskDef {
-    char *name; /* name matching the <target dev='...' of the domain */
-    int index; /* index within snapshot->dom->disks that matches name */
-    int snapshot; /* enum virDomainDiskSnapshot */
-    char *file; /* new source file when snapshot is external */
-    char *driverType; /* file format type of new file */
-};
-
-/* Stores the complete snapshot metadata */
-typedef struct _virDomainSnapshotDef virDomainSnapshotDef;
-typedef virDomainSnapshotDef *virDomainSnapshotDefPtr;
-struct _virDomainSnapshotDef {
-    /* Public XML.  */
-    char *name;
-    char *description;
-    char *parent;
-    long long creationTime; /* in seconds */
-    int state; /* enum virDomainSnapshotState */
-
-    size_t ndisks; /* should not exceed dom->ndisks */
-    virDomainSnapshotDiskDef *disks;
-
-    virDomainDefPtr dom;
-
-    /* Internal use.  */
-    bool current; /* At most one snapshot in the list should have this set */
-};
-
-typedef struct _virDomainSnapshotObj virDomainSnapshotObj;
-typedef virDomainSnapshotObj *virDomainSnapshotObjPtr;
-struct _virDomainSnapshotObj {
-    virDomainSnapshotDefPtr def; /* non-NULL except for metaroot */
-
-    virDomainSnapshotObjPtr parent; /* non-NULL except for metaroot, before
-                                       virDomainSnapshotUpdateRelations, or
-                                       after virDomainSnapshotDropParent */
-    virDomainSnapshotObjPtr sibling; /* NULL if last child of parent */
-    size_t nchildren;
-    virDomainSnapshotObjPtr first_child; /* NULL if no children */
-};
-
-typedef struct _virDomainSnapshotObjList virDomainSnapshotObjList;
-typedef virDomainSnapshotObjList *virDomainSnapshotObjListPtr;
-
-virDomainSnapshotObjListPtr virDomainSnapshotObjListNew(void);
-void virDomainSnapshotObjListFree(virDomainSnapshotObjListPtr snapshots);
-
-typedef enum {
-    VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE = 1 << 0,
-    VIR_DOMAIN_SNAPSHOT_PARSE_DISKS    = 1 << 1,
-    VIR_DOMAIN_SNAPSHOT_PARSE_INTERNAL = 1 << 2,
-} virDomainSnapshotParseFlags;
-
-virDomainSnapshotDefPtr virDomainSnapshotDefParseString(const char *xmlStr,
-                                                        virCapsPtr caps,
-                                                        unsigned int expectedVirtTypes,
-                                                        unsigned int flags);
-void virDomainSnapshotDefFree(virDomainSnapshotDefPtr def);
-char *virDomainSnapshotDefFormat(const char *domain_uuid,
-                                 virDomainSnapshotDefPtr def,
-                                 unsigned int flags,
-                                 int internal);
-int virDomainSnapshotAlignDisks(virDomainSnapshotDefPtr snapshot,
-                                int default_snapshot,
-                                bool require_match);
-virDomainSnapshotObjPtr virDomainSnapshotAssignDef(virDomainSnapshotObjListPtr snapshots,
-                                                   const virDomainSnapshotDefPtr def);
-
-int virDomainSnapshotObjListGetNames(virDomainSnapshotObjListPtr snapshots,
-                                     virDomainSnapshotObjPtr from,
-                                     char **const names, int maxnames,
-                                     unsigned int flags);
-int virDomainSnapshotObjListNum(virDomainSnapshotObjListPtr snapshots,
-                                virDomainSnapshotObjPtr from,
-                                unsigned int flags);
-virDomainSnapshotObjPtr virDomainSnapshotFindByName(const virDomainSnapshotObjListPtr snapshots,
-                                                    const char *name);
-void virDomainSnapshotObjListRemove(virDomainSnapshotObjListPtr snapshots,
-                                    virDomainSnapshotObjPtr snapshot);
-int virDomainSnapshotForEach(virDomainSnapshotObjListPtr snapshots,
-                             virHashIterator iter,
-                             void *data);
-int virDomainSnapshotForEachChild(virDomainSnapshotObjPtr snapshot,
-                                  virHashIterator iter,
-                                  void *data);
-int virDomainSnapshotForEachDescendant(virDomainSnapshotObjPtr snapshot,
-                                       virHashIterator iter,
-                                       void *data);
-int virDomainSnapshotUpdateRelations(virDomainSnapshotObjListPtr snapshots);
-void virDomainSnapshotDropParent(virDomainSnapshotObjPtr snapshot);
-
 /* Guest VM runtime state */
 typedef struct _virDomainStateReason virDomainStateReason;
 struct _virDomainStateReason {
@@ -2202,7 +2097,6 @@ VIR_ENUM_DECL(virDomainDiskErrorPolicy)
 VIR_ENUM_DECL(virDomainDiskProtocol)
 VIR_ENUM_DECL(virDomainDiskIo)
 VIR_ENUM_DECL(virDomainDiskSecretType)
-VIR_ENUM_DECL(virDomainDiskSnapshot)
 VIR_ENUM_DECL(virDomainDiskTray)
 VIR_ENUM_DECL(virDomainIoEventFd)
 VIR_ENUM_DECL(virDomainVirtioEventIdx)
@@ -2253,7 +2147,6 @@ VIR_ENUM_DECL(virDomainGraphicsSpiceClipboardCopypaste)
 VIR_ENUM_DECL(virDomainGraphicsSpiceMouseMode)
 VIR_ENUM_DECL(virDomainNumatuneMemMode)
 VIR_ENUM_DECL(virDomainNumatuneMemPlacementMode)
-VIR_ENUM_DECL(virDomainSnapshotState)
 /* from libvirt.h */
 VIR_ENUM_DECL(virDomainState)
 VIR_ENUM_DECL(virDomainNostateReason)
@@ -2316,25 +2209,7 @@ virDomainNetDefPtr virDomainNetFind(virDomainDefPtr def,
                  VIR_CONNECT_LIST_DOMAINS_FILTERS_AUTOSTART   | \
                  VIR_CONNECT_LIST_DOMAINS_FILTERS_SNAPSHOT)

-# define VIR_DOMAIN_SNAPSHOT_FILTERS_METADATA           \
-               (VIR_DOMAIN_SNAPSHOT_LIST_METADATA     | \
-                VIR_DOMAIN_SNAPSHOT_LIST_NO_METADATA)
-
-# define VIR_DOMAIN_SNAPSHOT_FILTERS_LEAVES             \
-               (VIR_DOMAIN_SNAPSHOT_LIST_LEAVES       | \
-                VIR_DOMAIN_SNAPSHOT_LIST_NO_LEAVES)
-
-# define VIR_DOMAIN_SNAPSHOT_FILTERS_ALL                \
-               (VIR_DOMAIN_SNAPSHOT_FILTERS_METADATA  | \
-                VIR_DOMAIN_SNAPSHOT_FILTERS_LEAVES)
-
 int virDomainList(virConnectPtr conn, virHashTablePtr domobjs,
                   virDomainPtr **domains, unsigned int flags);

-int virDomainListSnapshots(virDomainSnapshotObjListPtr snapshots,
-                           virDomainSnapshotObjPtr from,
-                           virDomainPtr dom,
-                           virDomainSnapshotPtr **snaps,
-                           unsigned int flags);
-
 #endif /* __DOMAIN_CONF_H */
diff --git a/src/conf/snapshot_conf.c b/src/conf/snapshot_conf.c
new file mode 100644
index 0000000..894a74c
--- /dev/null
+++ b/src/conf/snapshot_conf.c
@@ -0,0 +1,970 @@
+/*
+ * snapshot_conf.c: domain snapshot XML processing
+ *
+ * Copyright (C) 2006-2012 Red Hat, Inc.
+ * Copyright (C) 2006-2008 Daniel P. Berrange
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Author: Daniel P. Berrange <berrange at redhat.com>
+ */
+
+#include <config.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/time.h>
+
+#include "internal.h"
+#include "virterror_internal.h"
+#include "datatypes.h"
+#include "domain_conf.h"
+#include "snapshot_conf.h"
+#include "memory.h"
+#include "xml.h"
+#include "uuid.h"
+#include "util.h"
+#include "buf.h"
+#include "logging.h"
+#include "nwfilter_conf.h"
+#include "storage_file.h"
+#include "virfile.h"
+#include "bitmap.h"
+#include "count-one-bits.h"
+#include "secret_conf.h"
+#include "netdev_vport_profile_conf.h"
+#include "netdev_bandwidth_conf.h"
+
+#define VIR_FROM_THIS VIR_FROM_DOMAIN_SNAPSHOT
+
+VIR_ENUM_IMPL(virDomainDiskSnapshot, VIR_DOMAIN_DISK_SNAPSHOT_LAST,
+              "default",
+              "no",
+              "internal",
+              "external")
+
+/* virDomainSnapshotState is really virDomainState plus one extra state */
+VIR_ENUM_IMPL(virDomainSnapshotState, VIR_DOMAIN_SNAPSHOT_STATE_LAST,
+              "nostate",
+              "running",
+              "blocked",
+              "paused",
+              "shutdown",
+              "shutoff",
+              "crashed",
+              "pmsuspended",
+              "disk-snapshot")
+
+struct _virDomainSnapshotObjList {
+    /* name string -> virDomainSnapshotObj  mapping
+     * for O(1), lockless lookup-by-name */
+    virHashTable *objs;
+
+    virDomainSnapshotObj metaroot; /* Special parent of all root snapshots */
+};
+
+/* Snapshot Def functions */
+static void
+virDomainSnapshotDiskDefClear(virDomainSnapshotDiskDefPtr disk)
+{
+    VIR_FREE(disk->name);
+    VIR_FREE(disk->file);
+    VIR_FREE(disk->driverType);
+}
+
+void virDomainSnapshotDefFree(virDomainSnapshotDefPtr def)
+{
+    int i;
+
+    if (!def)
+        return;
+
+    VIR_FREE(def->name);
+    VIR_FREE(def->description);
+    VIR_FREE(def->parent);
+    for (i = 0; i < def->ndisks; i++)
+        virDomainSnapshotDiskDefClear(&def->disks[i]);
+    VIR_FREE(def->disks);
+    virDomainDefFree(def->dom);
+    VIR_FREE(def);
+}
+
+static int
+virDomainSnapshotDiskDefParseXML(xmlNodePtr node,
+                                 virDomainSnapshotDiskDefPtr def)
+{
+    int ret = -1;
+    char *snapshot = NULL;
+    xmlNodePtr cur;
+
+    def->name = virXMLPropString(node, "name");
+    if (!def->name) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("missing name from disk snapshot element"));
+        goto cleanup;
+    }
+
+    snapshot = virXMLPropString(node, "snapshot");
+    if (snapshot) {
+        def->snapshot = virDomainDiskSnapshotTypeFromString(snapshot);
+        if (def->snapshot <= 0) {
+            virReportError(VIR_ERR_INTERNAL_ERROR,
+                           _("unknown disk snapshot setting '%s'"),
+                           snapshot);
+            goto cleanup;
+        }
+    }
+
+    cur = node->children;
+    while (cur) {
+        if (cur->type == XML_ELEMENT_NODE) {
+            if (!def->file &&
+                xmlStrEqual(cur->name, BAD_CAST "source")) {
+                def->file = virXMLPropString(cur, "file");
+            } else if (!def->driverType &&
+                       xmlStrEqual(cur->name, BAD_CAST "driver")) {
+                def->driverType = virXMLPropString(cur, "type");
+            }
+        }
+        cur = cur->next;
+    }
+
+    if (!def->snapshot && (def->file || def->driverType))
+        def->snapshot = VIR_DOMAIN_DISK_SNAPSHOT_EXTERNAL;
+
+    ret = 0;
+cleanup:
+    VIR_FREE(snapshot);
+    if (ret < 0)
+        virDomainSnapshotDiskDefClear(def);
+    return ret;
+}
+
+/* flags is bitwise-or of virDomainSnapshotParseFlags.
+ * If flags does not include VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE, then
+ * caps and expectedVirtTypes are ignored.
+ */
+virDomainSnapshotDefPtr
+virDomainSnapshotDefParseString(const char *xmlStr,
+                                virCapsPtr caps,
+                                unsigned int expectedVirtTypes,
+                                unsigned int flags)
+{
+    xmlXPathContextPtr ctxt = NULL;
+    xmlDocPtr xml = NULL;
+    virDomainSnapshotDefPtr def = NULL;
+    virDomainSnapshotDefPtr ret = NULL;
+    xmlNodePtr *nodes = NULL;
+    int i;
+    char *creation = NULL, *state = NULL;
+    struct timeval tv;
+    int active;
+    char *tmp;
+    int keepBlanksDefault = xmlKeepBlanksDefault(0);
+
+    xml = virXMLParseCtxt(NULL, xmlStr, _("(domain_snapshot)"), &ctxt);
+    if (!xml) {
+        xmlKeepBlanksDefault(keepBlanksDefault);
+        return NULL;
+    }
+    xmlKeepBlanksDefault(keepBlanksDefault);
+
+    if (VIR_ALLOC(def) < 0) {
+        virReportOOMError();
+        goto cleanup;
+    }
+
+    if (!xmlStrEqual(ctxt->node->name, BAD_CAST "domainsnapshot")) {
+        virReportError(VIR_ERR_XML_ERROR, "%s", _("domainsnapshot"));
+        goto cleanup;
+    }
+
+    gettimeofday(&tv, NULL);
+
+    def->name = virXPathString("string(./name)", ctxt);
+    if (def->name == NULL) {
+        if (flags & VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE) {
+            virReportError(VIR_ERR_XML_ERROR, "%s",
+                           _("a redefined snapshot must have a name"));
+            goto cleanup;
+        } else {
+            ignore_value(virAsprintf(&def->name, "%lld",
+                                     (long long)tv.tv_sec));
+        }
+    }
+
+    if (def->name == NULL) {
+        virReportOOMError();
+        goto cleanup;
+    }
+
+    def->description = virXPathString("string(./description)", ctxt);
+
+    if (flags & VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE) {
+        if (virXPathLongLong("string(./creationTime)", ctxt,
+                             &def->creationTime) < 0) {
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                           _("missing creationTime from existing snapshot"));
+            goto cleanup;
+        }
+
+        def->parent = virXPathString("string(./parent/name)", ctxt);
+
+        state = virXPathString("string(./state)", ctxt);
+        if (state == NULL) {
+            /* there was no state in an existing snapshot; this
+             * should never happen
+             */
+            virReportError(VIR_ERR_XML_ERROR, "%s",
+                           _("missing state from existing snapshot"));
+            goto cleanup;
+        }
+        def->state = virDomainSnapshotStateTypeFromString(state);
+        if (def->state < 0) {
+            virReportError(VIR_ERR_XML_ERROR,
+                           _("Invalid state '%s' in domain snapshot XML"),
+                           state);
+            goto cleanup;
+        }
+
+        /* Older snapshots were created with just <domain>/<uuid>, and
+         * lack domain/@type.  In that case, leave dom NULL, and
+         * clients will have to decide between best effort
+         * initialization or outright failure.  */
+        if ((tmp = virXPathString("string(./domain/@type)", ctxt))) {
+            xmlNodePtr domainNode = virXPathNode("./domain", ctxt);
+
+            VIR_FREE(tmp);
+            if (!domainNode) {
+                virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                               _("missing domain in snapshot"));
+                goto cleanup;
+            }
+            def->dom = virDomainDefParseNode(caps, xml, domainNode,
+                                             expectedVirtTypes,
+                                             (VIR_DOMAIN_XML_INACTIVE |
+                                              VIR_DOMAIN_XML_SECURE));
+            if (!def->dom)
+                goto cleanup;
+        } else {
+            VIR_WARN("parsing older snapshot that lacks domain");
+        }
+    } else {
+        def->creationTime = tv.tv_sec;
+    }
+
+    if ((i = virXPathNodeSet("./disks/*", ctxt, &nodes)) < 0)
+        goto cleanup;
+    if (flags & VIR_DOMAIN_SNAPSHOT_PARSE_DISKS ||
+        (flags & VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE &&
+         def->state == VIR_DOMAIN_DISK_SNAPSHOT)) {
+        def->ndisks = i;
+        if (def->ndisks && VIR_ALLOC_N(def->disks, def->ndisks) < 0) {
+            virReportOOMError();
+            goto cleanup;
+        }
+        for (i = 0; i < def->ndisks; i++) {
+            if (virDomainSnapshotDiskDefParseXML(nodes[i], &def->disks[i]) < 0)
+                goto cleanup;
+        }
+        VIR_FREE(nodes);
+    } else if (i) {
+        virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
+                       _("unable to handle disk requests in snapshot"));
+        goto cleanup;
+    }
+
+    if (flags & VIR_DOMAIN_SNAPSHOT_PARSE_INTERNAL) {
+        if (virXPathInt("string(./active)", ctxt, &active) < 0) {
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                           _("Could not find 'active' element"));
+            goto cleanup;
+        }
+        def->current = active != 0;
+    }
+
+    ret = def;
+
+cleanup:
+    VIR_FREE(creation);
+    VIR_FREE(state);
+    VIR_FREE(nodes);
+    xmlXPathFreeContext(ctxt);
+    if (ret == NULL)
+        virDomainSnapshotDefFree(def);
+    xmlFreeDoc(xml);
+
+    return ret;
+}
+
+static int
+disksorter(const void *a, const void *b)
+{
+    const virDomainSnapshotDiskDef *diska = a;
+    const virDomainSnapshotDiskDef *diskb = b;
+
+    /* Integer overflow shouldn't be a problem here.  */
+    return diska->index - diskb->index;
+}
+
+/* Align def->disks to def->domain.  Sort the list of def->disks,
+ * filling in any missing disks or snapshot state defaults given by
+ * the domain, with a fallback to a passed in default.  Convert paths
+ * to disk targets for uniformity.  Issue an error and return -1 if
+ * any def->disks[n]->name appears more than once or does not map to
+ * dom->disks.  If require_match, also require that existing
+ * def->disks snapshot states do not override explicit def->dom
+ * settings.  */
+int
+virDomainSnapshotAlignDisks(virDomainSnapshotDefPtr def,
+                            int default_snapshot,
+                            bool require_match)
+{
+    int ret = -1;
+    virBitmapPtr map = NULL;
+    int i;
+    int ndisks;
+    bool inuse;
+
+    if (!def->dom) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("missing domain in snapshot"));
+        goto cleanup;
+    }
+
+    if (def->ndisks > def->dom->ndisks) {
+        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+                       _("too many disk snapshot requests for domain"));
+        goto cleanup;
+    }
+
+    /* Unlikely to have a guest without disks but technically possible.  */
+    if (!def->dom->ndisks) {
+        ret = 0;
+        goto cleanup;
+    }
+
+    if (!(map = virBitmapAlloc(def->dom->ndisks))) {
+        virReportOOMError();
+        goto cleanup;
+    }
+
+    /* Double check requested disks.  */
+    for (i = 0; i < def->ndisks; i++) {
+        virDomainSnapshotDiskDefPtr disk = &def->disks[i];
+        int idx = virDomainDiskIndexByName(def->dom, disk->name, false);
+        int disk_snapshot;
+
+        if (idx < 0) {
+            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                           _("no disk named '%s'"), disk->name);
+            goto cleanup;
+        }
+        disk_snapshot = def->dom->disks[idx]->snapshot;
+
+        if (virBitmapGetBit(map, idx, &inuse) < 0 || inuse) {
+            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                           _("disk '%s' specified twice"),
+                           disk->name);
+            goto cleanup;
+        }
+        ignore_value(virBitmapSetBit(map, idx));
+        disk->index = idx;
+        if (!disk_snapshot)
+            disk_snapshot = default_snapshot;
+        if (!disk->snapshot) {
+            disk->snapshot = disk_snapshot;
+        } else if (disk_snapshot && require_match &&
+                   disk->snapshot != disk_snapshot) {
+            const char *tmp = virDomainDiskSnapshotTypeToString(disk_snapshot);
+            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                           _("disk '%s' must use snapshot mode '%s'"),
+                           disk->name, tmp);
+            goto cleanup;
+        }
+        if (disk->file &&
+            disk->snapshot != VIR_DOMAIN_DISK_SNAPSHOT_EXTERNAL) {
+            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                           _("file '%s' for disk '%s' requires "
+                             "use of external snapshot mode"),
+                           disk->file, disk->name);
+            goto cleanup;
+        }
+        if (STRNEQ(disk->name, def->dom->disks[idx]->dst)) {
+            VIR_FREE(disk->name);
+            if (!(disk->name = strdup(def->dom->disks[idx]->dst))) {
+                virReportOOMError();
+                goto cleanup;
+            }
+        }
+    }
+
+    /* Provide defaults for all remaining disks.  */
+    ndisks = def->ndisks;
+    if (VIR_EXPAND_N(def->disks, def->ndisks,
+                     def->dom->ndisks - def->ndisks) < 0) {
+        virReportOOMError();
+        goto cleanup;
+    }
+
+    for (i = 0; i < def->dom->ndisks; i++) {
+        virDomainSnapshotDiskDefPtr disk;
+
+        ignore_value(virBitmapGetBit(map, i, &inuse));
+        if (inuse)
+            continue;
+        disk = &def->disks[ndisks++];
+        if (!(disk->name = strdup(def->dom->disks[i]->dst))) {
+            virReportOOMError();
+            goto cleanup;
+        }
+        disk->index = i;
+        disk->snapshot = def->dom->disks[i]->snapshot;
+        if (!disk->snapshot)
+            disk->snapshot = default_snapshot;
+    }
+
+    qsort(&def->disks[0], def->ndisks, sizeof(def->disks[0]), disksorter);
+
+    /* Generate any default external file names, but only if the
+     * backing file is a regular file.  */
+    for (i = 0; i < def->ndisks; i++) {
+        virDomainSnapshotDiskDefPtr disk = &def->disks[i];
+
+        if (disk->snapshot == VIR_DOMAIN_DISK_SNAPSHOT_EXTERNAL &&
+            !disk->file) {
+            const char *original = def->dom->disks[i]->src;
+            const char *tmp;
+            struct stat sb;
+
+            if (!original) {
+                virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                               _("cannot generate external snapshot name "
+                                 "for disk '%s' without source"),
+                               disk->name);
+                goto cleanup;
+            }
+            if (stat(original, &sb) < 0 || !S_ISREG(sb.st_mode)) {
+                virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                               _("source for disk '%s' is not a regular "
+                                 "file; refusing to generate external "
+                                 "snapshot name"),
+                               disk->name);
+                goto cleanup;
+            }
+
+            tmp = strrchr(original, '.');
+            if (!tmp || strchr(tmp, '/')) {
+                ignore_value(virAsprintf(&disk->file, "%s.%s",
+                                         original, def->name));
+            } else {
+                if ((tmp - original) > INT_MAX) {
+                    virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                                   _("integer overflow"));
+                    goto cleanup;
+                }
+                ignore_value(virAsprintf(&disk->file, "%.*s.%s",
+                                         (int) (tmp - original), original,
+                                         def->name));
+            }
+            if (!disk->file) {
+                virReportOOMError();
+                goto cleanup;
+            }
+        }
+    }
+
+    ret = 0;
+
+cleanup:
+    virBitmapFree(map);
+    return ret;
+}
+
+char *virDomainSnapshotDefFormat(const char *domain_uuid,
+                                 virDomainSnapshotDefPtr def,
+                                 unsigned int flags,
+                                 int internal)
+{
+    virBuffer buf = VIR_BUFFER_INITIALIZER;
+    int i;
+
+    virCheckFlags(VIR_DOMAIN_XML_SECURE |
+                  VIR_DOMAIN_XML_UPDATE_CPU, NULL);
+
+    flags |= VIR_DOMAIN_XML_INACTIVE;
+
+    virBufferAddLit(&buf, "<domainsnapshot>\n");
+    virBufferEscapeString(&buf, "  <name>%s</name>\n", def->name);
+    if (def->description)
+        virBufferEscapeString(&buf, "  <description>%s</description>\n",
+                              def->description);
+    virBufferAsprintf(&buf, "  <state>%s</state>\n",
+                      virDomainSnapshotStateTypeToString(def->state));
+    if (def->parent) {
+        virBufferAddLit(&buf, "  <parent>\n");
+        virBufferEscapeString(&buf, "    <name>%s</name>\n", def->parent);
+        virBufferAddLit(&buf, "  </parent>\n");
+    }
+    virBufferAsprintf(&buf, "  <creationTime>%lld</creationTime>\n",
+                      def->creationTime);
+    /* For now, only output <disks> on disk-snapshot */
+    if (def->state == VIR_DOMAIN_DISK_SNAPSHOT) {
+        virBufferAddLit(&buf, "  <disks>\n");
+        for (i = 0; i < def->ndisks; i++) {
+            virDomainSnapshotDiskDefPtr disk = &def->disks[i];
+
+            if (!disk->name)
+                continue;
+
+            virBufferEscapeString(&buf, "    <disk name='%s'", disk->name);
+            if (disk->snapshot)
+                virBufferAsprintf(&buf, " snapshot='%s'",
+                                  virDomainDiskSnapshotTypeToString(disk->snapshot));
+            if (disk->file || disk->driverType) {
+                virBufferAddLit(&buf, ">\n");
+                if (disk->driverType)
+                    virBufferEscapeString(&buf, "      <driver type='%s'/>\n",
+                                          disk->driverType);
+                if (disk->file)
+                    virBufferEscapeString(&buf, "      <source file='%s'/>\n",
+                                          disk->file);
+                virBufferAddLit(&buf, "    </disk>\n");
+            } else {
+                virBufferAddLit(&buf, "/>\n");
+            }
+        }
+        virBufferAddLit(&buf, "  </disks>\n");
+    }
+    if (def->dom) {
+        virBufferAdjustIndent(&buf, 2);
+        if (virDomainDefFormatInternal(def->dom, flags, &buf) < 0) {
+            virBufferFreeAndReset(&buf);
+            return NULL;
+        }
+        virBufferAdjustIndent(&buf, -2);
+    } else if (domain_uuid) {
+        virBufferAddLit(&buf, "  <domain>\n");
+        virBufferAsprintf(&buf, "    <uuid>%s</uuid>\n", domain_uuid);
+        virBufferAddLit(&buf, "  </domain>\n");
+    }
+    if (internal)
+        virBufferAsprintf(&buf, "  <active>%d</active>\n", def->current);
+    virBufferAddLit(&buf, "</domainsnapshot>\n");
+
+    if (virBufferError(&buf)) {
+        virBufferFreeAndReset(&buf);
+        virReportOOMError();
+        return NULL;
+    }
+
+    return virBufferContentAndReset(&buf);
+}
+
+/* Snapshot Obj functions */
+static virDomainSnapshotObjPtr virDomainSnapshotObjNew(void)
+{
+    virDomainSnapshotObjPtr snapshot;
+
+    if (VIR_ALLOC(snapshot) < 0) {
+        virReportOOMError();
+        return NULL;
+    }
+
+    VIR_DEBUG("obj=%p", snapshot);
+
+    return snapshot;
+}
+
+static void virDomainSnapshotObjFree(virDomainSnapshotObjPtr snapshot)
+{
+    if (!snapshot)
+        return;
+
+    VIR_DEBUG("obj=%p", snapshot);
+
+    virDomainSnapshotDefFree(snapshot->def);
+    VIR_FREE(snapshot);
+}
+
+virDomainSnapshotObjPtr virDomainSnapshotAssignDef(virDomainSnapshotObjListPtr snapshots,
+                                                   const virDomainSnapshotDefPtr def)
+{
+    virDomainSnapshotObjPtr snap;
+
+    if (virHashLookup(snapshots->objs, def->name) != NULL) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("unexpected domain snapshot %s already exists"),
+                       def->name);
+        return NULL;
+    }
+
+    if (!(snap = virDomainSnapshotObjNew()))
+        return NULL;
+    snap->def = def;
+
+    if (virHashAddEntry(snapshots->objs, snap->def->name, snap) < 0) {
+        VIR_FREE(snap);
+        return NULL;
+    }
+
+    return snap;
+}
+
+/* Snapshot Obj List functions */
+static void
+virDomainSnapshotObjListDataFree(void *payload,
+                                 const void *name ATTRIBUTE_UNUSED)
+{
+    virDomainSnapshotObjPtr obj = payload;
+
+    virDomainSnapshotObjFree(obj);
+}
+
+virDomainSnapshotObjListPtr
+virDomainSnapshotObjListNew(void)
+{
+    virDomainSnapshotObjListPtr snapshots;
+    if (VIR_ALLOC(snapshots) < 0) {
+        virReportOOMError();
+        return NULL;
+    }
+    snapshots->objs = virHashCreate(50, virDomainSnapshotObjListDataFree);
+    if (!snapshots->objs) {
+        VIR_FREE(snapshots);
+        return NULL;
+    }
+    return snapshots;
+}
+
+void
+virDomainSnapshotObjListFree(virDomainSnapshotObjListPtr snapshots)
+{
+    if (!snapshots)
+        return;
+    virHashFree(snapshots->objs);
+    VIR_FREE(snapshots);
+}
+
+struct virDomainSnapshotNameData {
+    char **const names;
+    int maxnames;
+    unsigned int flags;
+    int count;
+    bool error;
+};
+
+static void virDomainSnapshotObjListCopyNames(void *payload,
+                                              const void *name ATTRIBUTE_UNUSED,
+                                              void *opaque)
+{
+    virDomainSnapshotObjPtr obj = payload;
+    struct virDomainSnapshotNameData *data = opaque;
+
+    if (data->error)
+        return;
+    /* Caller already sanitized flags.  Filtering on DESCENDANTS was
+     * done by choice of iteration in the caller.  */
+    if ((data->flags & VIR_DOMAIN_SNAPSHOT_LIST_LEAVES) && obj->nchildren)
+        return;
+    if ((data->flags & VIR_DOMAIN_SNAPSHOT_LIST_NO_LEAVES) && !obj->nchildren)
+        return;
+
+    if (data->names && data->count < data->maxnames &&
+        !(data->names[data->count] = strdup(obj->def->name))) {
+        data->error = true;
+        virReportOOMError();
+        return;
+    }
+    data->count++;
+}
+
+int
+virDomainSnapshotObjListGetNames(virDomainSnapshotObjListPtr snapshots,
+                                 virDomainSnapshotObjPtr from,
+                                 char **const names, int maxnames,
+                                 unsigned int flags)
+{
+    struct virDomainSnapshotNameData data = { names, maxnames, flags, 0,
+                                              false };
+    int i;
+
+    if (!from) {
+        /* LIST_ROOTS and LIST_DESCENDANTS have the same bit value,
+         * but opposite semantics.  Toggle here to get the correct
+         * traversal on the metaroot.  */
+        flags ^= VIR_DOMAIN_SNAPSHOT_LIST_ROOTS;
+        from = &snapshots->metaroot;
+    }
+
+    /* We handle LIST_ROOT/LIST_DESCENDANTS directly, mask that bit
+     * out to determine when we must use the filter callback.  */
+    data.flags &= ~VIR_DOMAIN_SNAPSHOT_LIST_DESCENDANTS;
+
+    /* If this common code is being used, we assume that all snapshots
+     * have metadata, and thus can handle METADATA up front as an
+     * all-or-none filter.  XXX This might not always be true, if we
+     * add the ability to track qcow2 internal snapshots without the
+     * use of metadata.  */
+    if ((data.flags & VIR_DOMAIN_SNAPSHOT_FILTERS_METADATA) ==
+        VIR_DOMAIN_SNAPSHOT_LIST_NO_METADATA)
+        return 0;
+    data.flags &= ~VIR_DOMAIN_SNAPSHOT_FILTERS_METADATA;
+
+    /* For ease of coding the visitor, it is easier to zero the LEAVES
+     * group if both bits are set.  */
+    if ((data.flags & VIR_DOMAIN_SNAPSHOT_FILTERS_LEAVES) ==
+        VIR_DOMAIN_SNAPSHOT_FILTERS_LEAVES)
+        data.flags &= ~VIR_DOMAIN_SNAPSHOT_FILTERS_LEAVES;
+
+    if (flags & VIR_DOMAIN_SNAPSHOT_LIST_DESCENDANTS) {
+        if (from->def)
+            virDomainSnapshotForEachDescendant(from,
+                                               virDomainSnapshotObjListCopyNames,
+                                               &data);
+        else if (names || data.flags)
+            virHashForEach(snapshots->objs, virDomainSnapshotObjListCopyNames,
+                           &data);
+        else
+            data.count = virHashSize(snapshots->objs);
+    } else if (names || data.flags) {
+        virDomainSnapshotForEachChild(from,
+                                      virDomainSnapshotObjListCopyNames, &data);
+    } else {
+        data.count = from->nchildren;
+    }
+
+    if (data.error) {
+        for (i = 0; i < data.count; i++)
+            VIR_FREE(names[i]);
+        return -1;
+    }
+
+    return data.count;
+}
+
+int
+virDomainSnapshotObjListNum(virDomainSnapshotObjListPtr snapshots,
+                            virDomainSnapshotObjPtr from,
+                            unsigned int flags)
+{
+    return virDomainSnapshotObjListGetNames(snapshots, from, NULL, 0, flags);
+}
+
+virDomainSnapshotObjPtr
+virDomainSnapshotFindByName(const virDomainSnapshotObjListPtr snapshots,
+                            const char *name)
+{
+    return name ? virHashLookup(snapshots->objs, name) : &snapshots->metaroot;
+}
+
+void virDomainSnapshotObjListRemove(virDomainSnapshotObjListPtr snapshots,
+                                    virDomainSnapshotObjPtr snapshot)
+{
+    virHashRemoveEntry(snapshots->objs, snapshot->def->name);
+}
+
+int
+virDomainSnapshotForEach(virDomainSnapshotObjListPtr snapshots,
+                         virHashIterator iter,
+                         void *data)
+{
+    return virHashForEach(snapshots->objs, iter, data);
+}
+
+/* Run iter(data) on all direct children of snapshot, while ignoring all
+ * other entries in snapshots.  Return the number of children
+ * visited.  No particular ordering is guaranteed.  */
+int
+virDomainSnapshotForEachChild(virDomainSnapshotObjPtr snapshot,
+                              virHashIterator iter,
+                              void *data)
+{
+    virDomainSnapshotObjPtr child = snapshot->first_child;
+
+    while (child) {
+        virDomainSnapshotObjPtr next = child->sibling;
+        (iter)(child, child->def->name, data);
+        child = next;
+    }
+
+    return snapshot->nchildren;
+}
+
+struct snapshot_act_on_descendant {
+    int number;
+    virHashIterator iter;
+    void *data;
+};
+
+static void
+virDomainSnapshotActOnDescendant(void *payload,
+                                 const void *name,
+                                 void *data)
+{
+    virDomainSnapshotObjPtr obj = payload;
+    struct snapshot_act_on_descendant *curr = data;
+
+    curr->number += 1 + virDomainSnapshotForEachDescendant(obj,
+                                                           curr->iter,
+                                                           curr->data);
+    (curr->iter)(payload, name, curr->data);
+}
+
+/* Run iter(data) on all descendants of snapshot, while ignoring all
+ * other entries in snapshots.  Return the number of descendants
+ * visited.  No particular ordering is guaranteed.  */
+int
+virDomainSnapshotForEachDescendant(virDomainSnapshotObjPtr snapshot,
+                                   virHashIterator iter,
+                                   void *data)
+{
+    struct snapshot_act_on_descendant act;
+
+    act.number = 0;
+    act.iter = iter;
+    act.data = data;
+    virDomainSnapshotForEachChild(snapshot,
+                                  virDomainSnapshotActOnDescendant, &act);
+
+    return act.number;
+}
+
+/* Struct and callback function used as a hash table callback; each call
+ * inspects the pre-existing snapshot->def->parent field, and adjusts
+ * the snapshot->parent field as well as the parent's child fields to
+ * wire up the hierarchical relations for the given snapshot.  The error
+ * indicator gets set if a parent is missing or a requested parent would
+ * cause a circular parent chain.  */
+struct snapshot_set_relation {
+    virDomainSnapshotObjListPtr snapshots;
+    int err;
+};
+static void
+virDomainSnapshotSetRelations(void *payload,
+                              const void *name ATTRIBUTE_UNUSED,
+                              void *data)
+{
+    virDomainSnapshotObjPtr obj = payload;
+    struct snapshot_set_relation *curr = data;
+    virDomainSnapshotObjPtr tmp;
+
+    obj->parent = virDomainSnapshotFindByName(curr->snapshots,
+                                              obj->def->parent);
+    if (!obj->parent) {
+        curr->err = -1;
+        obj->parent = &curr->snapshots->metaroot;
+        VIR_WARN("snapshot %s lacks parent", obj->def->name);
+    } else {
+        tmp = obj->parent;
+        while (tmp && tmp->def) {
+            if (tmp == obj) {
+                curr->err = -1;
+                obj->parent = &curr->snapshots->metaroot;
+                VIR_WARN("snapshot %s in circular chain", obj->def->name);
+                break;
+            }
+            tmp = tmp->parent;
+        }
+    }
+    obj->parent->nchildren++;
+    obj->sibling = obj->parent->first_child;
+    obj->parent->first_child = obj;
+}
+
+/* Populate parent link and child count of all snapshots, with all
+ * relations starting as 0/NULL.  Return 0 on success, -1 if a parent
+ * is missing or if a circular relationship was requested.  */
+int
+virDomainSnapshotUpdateRelations(virDomainSnapshotObjListPtr snapshots)
+{
+    struct snapshot_set_relation act = { snapshots, 0 };
+
+    virHashForEach(snapshots->objs, virDomainSnapshotSetRelations, &act);
+    return act.err;
+}
+
+/* Prepare to reparent or delete snapshot, by removing it from its
+ * current listed parent.  Note that when bulk removing all children
+ * of a parent, it is faster to just 0 the count rather than calling
+ * this function on each child.  */
+void
+virDomainSnapshotDropParent(virDomainSnapshotObjPtr snapshot)
+{
+    virDomainSnapshotObjPtr prev = NULL;
+    virDomainSnapshotObjPtr curr = NULL;
+
+    snapshot->parent->nchildren--;
+    curr = snapshot->parent->first_child;
+    while (curr != snapshot) {
+        if (!curr) {
+            VIR_WARN("inconsistent snapshot relations");
+            return;
+        }
+        prev = curr;
+        curr = curr->sibling;
+    }
+    if (prev)
+        prev->sibling = snapshot->sibling;
+    else
+        snapshot->parent->first_child = snapshot->sibling;
+    snapshot->parent = NULL;
+    snapshot->sibling = NULL;
+}
+
+int
+virDomainListSnapshots(virDomainSnapshotObjListPtr snapshots,
+                       virDomainSnapshotObjPtr from,
+                       virDomainPtr dom,
+                       virDomainSnapshotPtr **snaps,
+                       unsigned int flags)
+{
+    int count = virDomainSnapshotObjListNum(snapshots, from, flags);
+    virDomainSnapshotPtr *list;
+    char **names;
+    int ret = -1;
+    int i;
+
+    if (!snaps)
+        return count;
+    if (VIR_ALLOC_N(names, count) < 0 ||
+        VIR_ALLOC_N(list, count + 1) < 0) {
+        virReportOOMError();
+        goto cleanup;
+    }
+
+    if (virDomainSnapshotObjListGetNames(snapshots, from, names, count,
+                                         flags) < 0)
+        goto cleanup;
+    for (i = 0; i < count; i++)
+        if ((list[i] = virGetDomainSnapshot(dom, names[i])) == NULL)
+            goto cleanup;
+
+    ret = count;
+    *snaps = list;
+
+cleanup:
+    for (i = 0; i < count; i++)
+        VIR_FREE(names[i]);
+    VIR_FREE(names);
+    if (ret < 0 && list) {
+        for (i = 0; i < count; i++)
+            virObjectUnref(list[i]);
+        VIR_FREE(list);
+    }
+    return ret;
+}
diff --git a/src/conf/snapshot_conf.h b/src/conf/snapshot_conf.h
new file mode 100644
index 0000000..314c4d1
--- /dev/null
+++ b/src/conf/snapshot_conf.h
@@ -0,0 +1,157 @@
+/*
+ * snapshot_conf.h: domain snapshot XML processing
+ *
+ * Copyright (C) 2006-2012 Red Hat, Inc.
+ * Copyright (C) 2006-2008 Daniel P. Berrange
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Author: Daniel P. Berrange <berrange at redhat.com>
+ */
+
+#ifndef __SNAPSHOT_CONF_H
+# define __SNAPSHOT_CONF_H
+
+# include "internal.h"
+# include "domain_conf.h"
+
+/* Items related to snapshot state */
+
+enum virDomainDiskSnapshot {
+    VIR_DOMAIN_DISK_SNAPSHOT_DEFAULT = 0,
+    VIR_DOMAIN_DISK_SNAPSHOT_NO,
+    VIR_DOMAIN_DISK_SNAPSHOT_INTERNAL,
+    VIR_DOMAIN_DISK_SNAPSHOT_EXTERNAL,
+
+    VIR_DOMAIN_DISK_SNAPSHOT_LAST
+};
+
+enum virDomainSnapshotState {
+    /* Inherit the VIR_DOMAIN_* states from virDomainState.  */
+    VIR_DOMAIN_DISK_SNAPSHOT = VIR_DOMAIN_LAST,
+    VIR_DOMAIN_SNAPSHOT_STATE_LAST
+};
+
+/* Stores disk-snapshot information */
+typedef struct _virDomainSnapshotDiskDef virDomainSnapshotDiskDef;
+typedef virDomainSnapshotDiskDef *virDomainSnapshotDiskDefPtr;
+struct _virDomainSnapshotDiskDef {
+    char *name; /* name matching the <target dev='...' of the domain */
+    int index; /* index within snapshot->dom->disks that matches name */
+    int snapshot; /* enum virDomainDiskSnapshot */
+    char *file; /* new source file when snapshot is external */
+    char *driverType; /* file format type of new file */
+};
+
+/* Stores the complete snapshot metadata */
+typedef struct _virDomainSnapshotDef virDomainSnapshotDef;
+typedef virDomainSnapshotDef *virDomainSnapshotDefPtr;
+struct _virDomainSnapshotDef {
+    /* Public XML.  */
+    char *name;
+    char *description;
+    char *parent;
+    long long creationTime; /* in seconds */
+    int state; /* enum virDomainSnapshotState */
+
+    size_t ndisks; /* should not exceed dom->ndisks */
+    virDomainSnapshotDiskDef *disks;
+
+    virDomainDefPtr dom;
+
+    /* Internal use.  */
+    bool current; /* At most one snapshot in the list should have this set */
+};
+
+struct _virDomainSnapshotObj {
+    virDomainSnapshotDefPtr def; /* non-NULL except for metaroot */
+
+    virDomainSnapshotObjPtr parent; /* non-NULL except for metaroot, before
+                                       virDomainSnapshotUpdateRelations, or
+                                       after virDomainSnapshotDropParent */
+    virDomainSnapshotObjPtr sibling; /* NULL if last child of parent */
+    size_t nchildren;
+    virDomainSnapshotObjPtr first_child; /* NULL if no children */
+};
+
+virDomainSnapshotObjListPtr virDomainSnapshotObjListNew(void);
+void virDomainSnapshotObjListFree(virDomainSnapshotObjListPtr snapshots);
+
+typedef enum {
+    VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE = 1 << 0,
+    VIR_DOMAIN_SNAPSHOT_PARSE_DISKS    = 1 << 1,
+    VIR_DOMAIN_SNAPSHOT_PARSE_INTERNAL = 1 << 2,
+} virDomainSnapshotParseFlags;
+
+virDomainSnapshotDefPtr virDomainSnapshotDefParseString(const char *xmlStr,
+                                                        virCapsPtr caps,
+                                                        unsigned int expectedVirtTypes,
+                                                        unsigned int flags);
+void virDomainSnapshotDefFree(virDomainSnapshotDefPtr def);
+char *virDomainSnapshotDefFormat(const char *domain_uuid,
+                                 virDomainSnapshotDefPtr def,
+                                 unsigned int flags,
+                                 int internal);
+int virDomainSnapshotAlignDisks(virDomainSnapshotDefPtr snapshot,
+                                int default_snapshot,
+                                bool require_match);
+virDomainSnapshotObjPtr virDomainSnapshotAssignDef(virDomainSnapshotObjListPtr snapshots,
+                                                   const virDomainSnapshotDefPtr def);
+
+int virDomainSnapshotObjListGetNames(virDomainSnapshotObjListPtr snapshots,
+                                     virDomainSnapshotObjPtr from,
+                                     char **const names, int maxnames,
+                                     unsigned int flags);
+int virDomainSnapshotObjListNum(virDomainSnapshotObjListPtr snapshots,
+                                virDomainSnapshotObjPtr from,
+                                unsigned int flags);
+virDomainSnapshotObjPtr virDomainSnapshotFindByName(const virDomainSnapshotObjListPtr snapshots,
+                                                    const char *name);
+void virDomainSnapshotObjListRemove(virDomainSnapshotObjListPtr snapshots,
+                                    virDomainSnapshotObjPtr snapshot);
+int virDomainSnapshotForEach(virDomainSnapshotObjListPtr snapshots,
+                             virHashIterator iter,
+                             void *data);
+int virDomainSnapshotForEachChild(virDomainSnapshotObjPtr snapshot,
+                                  virHashIterator iter,
+                                  void *data);
+int virDomainSnapshotForEachDescendant(virDomainSnapshotObjPtr snapshot,
+                                       virHashIterator iter,
+                                       void *data);
+int virDomainSnapshotUpdateRelations(virDomainSnapshotObjListPtr snapshots);
+void virDomainSnapshotDropParent(virDomainSnapshotObjPtr snapshot);
+
+# define VIR_DOMAIN_SNAPSHOT_FILTERS_METADATA           \
+               (VIR_DOMAIN_SNAPSHOT_LIST_METADATA     | \
+                VIR_DOMAIN_SNAPSHOT_LIST_NO_METADATA)
+
+# define VIR_DOMAIN_SNAPSHOT_FILTERS_LEAVES             \
+               (VIR_DOMAIN_SNAPSHOT_LIST_LEAVES       | \
+                VIR_DOMAIN_SNAPSHOT_LIST_NO_LEAVES)
+
+# define VIR_DOMAIN_SNAPSHOT_FILTERS_ALL                \
+               (VIR_DOMAIN_SNAPSHOT_FILTERS_METADATA  | \
+                VIR_DOMAIN_SNAPSHOT_FILTERS_LEAVES)
+
+int virDomainListSnapshots(virDomainSnapshotObjListPtr snapshots,
+                           virDomainSnapshotObjPtr from,
+                           virDomainPtr dom,
+                           virDomainSnapshotPtr **snaps,
+                           unsigned int flags);
+
+VIR_ENUM_DECL(virDomainDiskSnapshot)
+VIR_ENUM_DECL(virDomainSnapshotState)
+
+#endif /* __SNAPSHOT_CONF_H */
diff --git a/src/esx/esx_driver.c b/src/esx/esx_driver.c
index 72a7acc..e57296a 100644
--- a/src/esx/esx_driver.c
+++ b/src/esx/esx_driver.c
@@ -26,6 +26,7 @@

 #include "internal.h"
 #include "domain_conf.h"
+#include "snapshot_conf.h"
 #include "virauth.h"
 #include "util.h"
 #include "memory.h"
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
index ca62f0c..8c32a4d 100644
--- a/src/qemu/qemu_command.c
+++ b/src/qemu/qemu_command.c
@@ -38,6 +38,7 @@
 #include "domain_nwfilter.h"
 #include "domain_audit.h"
 #include "domain_conf.h"
+#include "snapshot_conf.h"
 #include "network/bridge_driver.h"
 #include "virnetdevtap.h"
 #include "base64.h"
diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h
index b96087e..dff53cf 100644
--- a/src/qemu/qemu_domain.h
+++ b/src/qemu/qemu_domain.h
@@ -1,7 +1,7 @@
 /*
  * qemu_domain.h: QEMU domain private state
  *
- * Copyright (C) 2006-2011 Red Hat, Inc.
+ * Copyright (C) 2006-2012 Red Hat, Inc.
  * Copyright (C) 2006 Daniel P. Berrange
  *
  * This library is free software; you can redistribute it and/or
@@ -26,6 +26,7 @@

 # include "threads.h"
 # include "domain_conf.h"
+# include "snapshot_conf.h"
 # include "qemu_monitor.h"
 # include "qemu_agent.h"
 # include "qemu_conf.h"
diff --git a/src/vbox/vbox_tmpl.c b/src/vbox/vbox_tmpl.c
index 4cdb11c..48f371f 100644
--- a/src/vbox/vbox_tmpl.c
+++ b/src/vbox/vbox_tmpl.c
@@ -43,6 +43,7 @@
 #include "internal.h"
 #include "datatypes.h"
 #include "domain_conf.h"
+#include "snapshot_conf.h"
 #include "network_conf.h"
 #include "virterror_internal.h"
 #include "domain_event.h"
-- 
1.7.11.4




More information about the libvir-list mailing list