snapshot: Factor out redefine cycle validation
[libvirt/ericb.git] / src / conf / snapshot_conf.c
blob130a9cee03e9a6aa1d80caef87db75beaf1ac44e
1 /*
2 * snapshot_conf.c: domain snapshot XML processing
4 * Copyright (C) 2006-2019 Red Hat, Inc.
5 * Copyright (C) 2006-2008 Daniel P. Berrange
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library. If not, see
19 * <http://www.gnu.org/licenses/>.
22 #include <config.h>
24 #include <fcntl.h>
25 #include <sys/stat.h>
26 #include <unistd.h>
28 #include "configmake.h"
29 #include "internal.h"
30 #include "virbitmap.h"
31 #include "virbuffer.h"
32 #include "count-one-bits.h"
33 #include "datatypes.h"
34 #include "domain_conf.h"
35 #include "virlog.h"
36 #include "viralloc.h"
37 #include "netdev_bandwidth_conf.h"
38 #include "netdev_vport_profile_conf.h"
39 #include "nwfilter_conf.h"
40 #include "secret_conf.h"
41 #include "snapshot_conf.h"
42 #include "virstoragefile.h"
43 #include "viruuid.h"
44 #include "virfile.h"
45 #include "virerror.h"
46 #include "virxml.h"
47 #include "virstring.h"
48 #include "virdomainsnapshotobjlist.h"
50 #define VIR_FROM_THIS VIR_FROM_DOMAIN_SNAPSHOT
52 VIR_LOG_INIT("conf.snapshot_conf");
54 static virClassPtr virDomainSnapshotDefClass;
55 static void virDomainSnapshotDefDispose(void *obj);
57 static int
58 virDomainSnapshotOnceInit(void)
60 if (!VIR_CLASS_NEW(virDomainSnapshotDef, virClassForDomainMomentDef()))
61 return -1;
63 return 0;
66 VIR_ONCE_GLOBAL_INIT(virDomainSnapshot);
68 VIR_ENUM_IMPL(virDomainSnapshotLocation,
69 VIR_DOMAIN_SNAPSHOT_LOCATION_LAST,
70 "default",
71 "no",
72 "internal",
73 "external",
76 /* virDomainSnapshotState is really virDomainState plus one extra state */
77 VIR_ENUM_IMPL(virDomainSnapshotState,
78 VIR_DOMAIN_SNAPSHOT_LAST,
79 "nostate",
80 "running",
81 "blocked",
82 "paused",
83 "shutdown",
84 "shutoff",
85 "crashed",
86 "pmsuspended",
87 "disk-snapshot",
90 /* Snapshot Def functions */
91 static void
92 virDomainSnapshotDiskDefClear(virDomainSnapshotDiskDefPtr disk)
94 VIR_FREE(disk->name);
95 virObjectUnref(disk->src);
96 disk->src = NULL;
99 /* Allocate a new virDomainSnapshotDef; free with virObjectUnref() */
100 virDomainSnapshotDefPtr
101 virDomainSnapshotDefNew(void)
103 virDomainSnapshotDefPtr def;
105 if (virDomainSnapshotInitialize() < 0)
106 return NULL;
108 def = virObjectNew(virDomainSnapshotDefClass);
109 return def;
112 static void
113 virDomainSnapshotDefDispose(void *obj)
115 virDomainSnapshotDefPtr def = obj;
116 size_t i;
118 VIR_FREE(def->file);
119 for (i = 0; i < def->ndisks; i++)
120 virDomainSnapshotDiskDefClear(&def->disks[i]);
121 VIR_FREE(def->disks);
122 virObjectUnref(def->cookie);
125 static int
126 virDomainSnapshotDiskDefParseXML(xmlNodePtr node,
127 xmlXPathContextPtr ctxt,
128 virDomainSnapshotDiskDefPtr def,
129 unsigned int flags,
130 virDomainXMLOptionPtr xmlopt)
132 int ret = -1;
133 char *snapshot = NULL;
134 char *type = NULL;
135 char *driver = NULL;
136 xmlNodePtr cur;
137 xmlNodePtr saved = ctxt->node;
139 ctxt->node = node;
141 if (!(def->src = virStorageSourceNew()))
142 goto cleanup;
144 def->name = virXMLPropString(node, "name");
145 if (!def->name) {
146 virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
147 _("missing name from disk snapshot element"));
148 goto cleanup;
151 snapshot = virXMLPropString(node, "snapshot");
152 if (snapshot) {
153 def->snapshot = virDomainSnapshotLocationTypeFromString(snapshot);
154 if (def->snapshot <= 0) {
155 virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
156 _("unknown disk snapshot setting '%s'"),
157 snapshot);
158 goto cleanup;
162 if ((type = virXMLPropString(node, "type"))) {
163 if ((def->src->type = virStorageTypeFromString(type)) <= 0 ||
164 def->src->type == VIR_STORAGE_TYPE_VOLUME ||
165 def->src->type == VIR_STORAGE_TYPE_DIR) {
166 virReportError(VIR_ERR_XML_ERROR,
167 _("unknown disk snapshot type '%s'"), type);
168 goto cleanup;
170 } else {
171 def->src->type = VIR_STORAGE_TYPE_FILE;
174 if ((cur = virXPathNode("./source", ctxt)) &&
175 virDomainStorageSourceParse(cur, ctxt, def->src, flags, xmlopt) < 0)
176 goto cleanup;
178 if ((driver = virXPathString("string(./driver/@type)", ctxt)) &&
179 (def->src->format = virStorageFileFormatTypeFromString(driver)) <= 0) {
180 virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
181 _("unknown disk snapshot driver '%s'"), driver);
182 goto cleanup;
185 /* validate that the passed path is absolute */
186 if (virStorageSourceIsRelative(def->src)) {
187 virReportError(VIR_ERR_XML_ERROR,
188 _("disk snapshot image path '%s' must be absolute"),
189 def->src->path);
190 goto cleanup;
193 if (!def->snapshot && (def->src->path || def->src->format))
194 def->snapshot = VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL;
196 ret = 0;
197 cleanup:
198 ctxt->node = saved;
200 VIR_FREE(driver);
201 VIR_FREE(snapshot);
202 VIR_FREE(type);
203 if (ret < 0)
204 virDomainSnapshotDiskDefClear(def);
205 return ret;
208 /* flags is bitwise-or of virDomainSnapshotParseFlags.
209 * If flags does not include VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE, then
210 * caps are ignored. If flags does not include
211 * VIR_DOMAIN_SNAPSHOT_PARSE_INTERNAL, then current is ignored.
213 static virDomainSnapshotDefPtr
214 virDomainSnapshotDefParse(xmlXPathContextPtr ctxt,
215 virCapsPtr caps,
216 virDomainXMLOptionPtr xmlopt,
217 bool *current,
218 unsigned int flags)
220 virDomainSnapshotDefPtr def = NULL;
221 virDomainSnapshotDefPtr ret = NULL;
222 xmlNodePtr *nodes = NULL;
223 size_t i;
224 int n;
225 char *creation = NULL, *state = NULL;
226 int active;
227 char *tmp;
228 char *memorySnapshot = NULL;
229 char *memoryFile = NULL;
230 bool offline = !!(flags & VIR_DOMAIN_SNAPSHOT_PARSE_OFFLINE);
231 virSaveCookieCallbacksPtr saveCookie = virDomainXMLOptionGetSaveCookie(xmlopt);
233 if (!(def = virDomainSnapshotDefNew()))
234 return NULL;
236 def->parent.name = virXPathString("string(./name)", ctxt);
237 if (def->parent.name == NULL) {
238 if (flags & VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE) {
239 virReportError(VIR_ERR_XML_ERROR, "%s",
240 _("a redefined snapshot must have a name"));
241 goto cleanup;
245 def->parent.description = virXPathString("string(./description)", ctxt);
247 if (flags & VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE) {
248 if (virXPathLongLong("string(./creationTime)", ctxt,
249 &def->parent.creationTime) < 0) {
250 virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
251 _("missing creationTime from existing snapshot"));
252 goto cleanup;
255 def->parent.parent_name = virXPathString("string(./parent/name)", ctxt);
257 state = virXPathString("string(./state)", ctxt);
258 if (state == NULL) {
259 /* there was no state in an existing snapshot; this
260 * should never happen
262 virReportError(VIR_ERR_XML_ERROR, "%s",
263 _("missing state from existing snapshot"));
264 goto cleanup;
266 def->state = virDomainSnapshotStateTypeFromString(state);
267 if (def->state <= 0) {
268 virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
269 _("Invalid state '%s' in domain snapshot XML"),
270 state);
271 goto cleanup;
273 offline = (def->state == VIR_DOMAIN_SNAPSHOT_SHUTOFF ||
274 def->state == VIR_DOMAIN_SNAPSHOT_DISK_SNAPSHOT);
276 /* Older snapshots were created with just <domain>/<uuid>, and
277 * lack domain/@type. In that case, leave dom NULL, and
278 * clients will have to decide between best effort
279 * initialization or outright failure. */
280 if ((tmp = virXPathString("string(./domain/@type)", ctxt))) {
281 int domainflags = VIR_DOMAIN_DEF_PARSE_INACTIVE |
282 VIR_DOMAIN_DEF_PARSE_SKIP_VALIDATE;
283 xmlNodePtr domainNode = virXPathNode("./domain", ctxt);
285 VIR_FREE(tmp);
286 if (!domainNode) {
287 virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
288 _("missing domain in snapshot"));
289 goto cleanup;
291 def->parent.dom = virDomainDefParseNode(ctxt->node->doc, domainNode,
292 caps, xmlopt, NULL, domainflags);
293 if (!def->parent.dom)
294 goto cleanup;
295 } else {
296 VIR_WARN("parsing older snapshot that lacks domain");
298 } else if (virDomainXMLOptionRunMomentPostParse(xmlopt, &def->parent) < 0) {
299 goto cleanup;
302 memorySnapshot = virXPathString("string(./memory/@snapshot)", ctxt);
303 memoryFile = virXPathString("string(./memory/@file)", ctxt);
304 if (memorySnapshot) {
305 def->memory = virDomainSnapshotLocationTypeFromString(memorySnapshot);
306 if (def->memory <= 0) {
307 virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
308 _("unknown memory snapshot setting '%s'"),
309 memorySnapshot);
310 goto cleanup;
312 if (memoryFile &&
313 def->memory != VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL) {
314 virReportError(VIR_ERR_XML_ERROR,
315 _("memory filename '%s' requires external snapshot"),
316 memoryFile);
317 goto cleanup;
319 if (!memoryFile &&
320 def->memory == VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL) {
321 virReportError(VIR_ERR_XML_ERROR, "%s",
322 _("external memory snapshots require a filename"));
323 goto cleanup;
325 } else if (memoryFile) {
326 def->memory = VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL;
327 } else if (flags & VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE) {
328 def->memory = (offline ?
329 VIR_DOMAIN_SNAPSHOT_LOCATION_NONE :
330 VIR_DOMAIN_SNAPSHOT_LOCATION_INTERNAL);
332 if (offline && def->memory &&
333 def->memory != VIR_DOMAIN_SNAPSHOT_LOCATION_NONE) {
334 virReportError(VIR_ERR_XML_ERROR, "%s",
335 _("memory state cannot be saved with offline or "
336 "disk-only snapshot"));
337 goto cleanup;
339 VIR_STEAL_PTR(def->file, memoryFile);
341 /* verify that memory path is absolute */
342 if (def->file && def->file[0] != '/') {
343 virReportError(VIR_ERR_XML_ERROR,
344 _("memory snapshot file path (%s) must be absolute"),
345 def->file);
346 goto cleanup;
349 if ((n = virXPathNodeSet("./disks/*", ctxt, &nodes)) < 0)
350 goto cleanup;
351 if (flags & VIR_DOMAIN_SNAPSHOT_PARSE_DISKS) {
352 if (n && VIR_ALLOC_N(def->disks, n) < 0)
353 goto cleanup;
354 def->ndisks = n;
355 for (i = 0; i < def->ndisks; i++) {
356 if (virDomainSnapshotDiskDefParseXML(nodes[i], ctxt, &def->disks[i],
357 flags, xmlopt) < 0)
358 goto cleanup;
360 VIR_FREE(nodes);
361 } else if (n) {
362 virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
363 _("unable to handle disk requests in snapshot"));
364 goto cleanup;
367 if (flags & VIR_DOMAIN_SNAPSHOT_PARSE_INTERNAL) {
368 if (!current) {
369 virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
370 _("internal parse requested with NULL current"));
371 goto cleanup;
373 if (virXPathInt("string(./active)", ctxt, &active) < 0) {
374 virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
375 _("Could not find 'active' element"));
376 goto cleanup;
378 *current = active != 0;
381 if (!offline && virSaveCookieParse(ctxt, &def->cookie, saveCookie) < 0)
382 goto cleanup;
384 VIR_STEAL_PTR(ret, def);
386 cleanup:
387 VIR_FREE(creation);
388 VIR_FREE(state);
389 VIR_FREE(nodes);
390 VIR_FREE(memorySnapshot);
391 VIR_FREE(memoryFile);
392 virObjectUnref(def);
394 return ret;
397 virDomainSnapshotDefPtr
398 virDomainSnapshotDefParseNode(xmlDocPtr xml,
399 xmlNodePtr root,
400 virCapsPtr caps,
401 virDomainXMLOptionPtr xmlopt,
402 bool *current,
403 unsigned int flags)
405 xmlXPathContextPtr ctxt = NULL;
406 virDomainSnapshotDefPtr def = NULL;
408 if (!virXMLNodeNameEqual(root, "domainsnapshot")) {
409 virReportError(VIR_ERR_XML_ERROR, "%s", _("domainsnapshot"));
410 goto cleanup;
413 if (flags & VIR_DOMAIN_SNAPSHOT_PARSE_VALIDATE) {
414 VIR_AUTOFREE(char *) schema = NULL;
416 schema = virFileFindResource("domainsnapshot.rng",
417 abs_top_srcdir "/docs/schemas",
418 PKGDATADIR "/schemas");
419 if (!schema)
420 goto cleanup;
421 if (virXMLValidateAgainstSchema(schema, xml) < 0)
422 goto cleanup;
425 ctxt = xmlXPathNewContext(xml);
426 if (ctxt == NULL) {
427 virReportOOMError();
428 goto cleanup;
431 ctxt->node = root;
432 def = virDomainSnapshotDefParse(ctxt, caps, xmlopt, current, flags);
433 cleanup:
434 xmlXPathFreeContext(ctxt);
435 return def;
438 virDomainSnapshotDefPtr
439 virDomainSnapshotDefParseString(const char *xmlStr,
440 virCapsPtr caps,
441 virDomainXMLOptionPtr xmlopt,
442 bool *current,
443 unsigned int flags)
445 virDomainSnapshotDefPtr ret = NULL;
446 xmlDocPtr xml;
447 int keepBlanksDefault = xmlKeepBlanksDefault(0);
449 if ((xml = virXMLParse(NULL, xmlStr, _("(domain_snapshot)")))) {
450 xmlKeepBlanksDefault(keepBlanksDefault);
451 ret = virDomainSnapshotDefParseNode(xml, xmlDocGetRootElement(xml),
452 caps, xmlopt, current, flags);
453 xmlFreeDoc(xml);
455 xmlKeepBlanksDefault(keepBlanksDefault);
457 return ret;
461 /* Perform sanity checking on a redefined snapshot definition. If
462 * @other is non-NULL, this may include swapping def->parent.dom from other
463 * into def. */
465 virDomainSnapshotRedefineValidate(virDomainSnapshotDefPtr def,
466 const unsigned char *domain_uuid,
467 virDomainMomentObjPtr other,
468 virDomainXMLOptionPtr xmlopt,
469 unsigned int flags)
471 int align_location = VIR_DOMAIN_SNAPSHOT_LOCATION_INTERNAL;
472 bool align_match = true;
473 bool external = def->state == VIR_DOMAIN_SNAPSHOT_DISK_SNAPSHOT ||
474 virDomainSnapshotDefIsExternal(def);
476 if ((flags & VIR_DOMAIN_SNAPSHOT_CREATE_DISK_ONLY) && !external) {
477 virReportError(VIR_ERR_INVALID_ARG,
478 _("disk-only flag for snapshot %s requires "
479 "disk-snapshot state"),
480 def->parent.name);
481 return -1;
483 if (def->parent.dom && memcmp(def->parent.dom->uuid, domain_uuid,
484 VIR_UUID_BUFLEN)) {
485 char uuidstr[VIR_UUID_STRING_BUFLEN];
487 virUUIDFormat(domain_uuid, uuidstr);
488 virReportError(VIR_ERR_INVALID_ARG,
489 _("definition for snapshot %s must use uuid %s"),
490 def->parent.name, uuidstr);
491 return -1;
494 if (other) {
495 virDomainSnapshotDefPtr otherdef = virDomainSnapshotObjGetDef(other);
497 if ((otherdef->state == VIR_DOMAIN_SNAPSHOT_RUNNING ||
498 otherdef->state == VIR_DOMAIN_SNAPSHOT_PAUSED) !=
499 (def->state == VIR_DOMAIN_SNAPSHOT_RUNNING ||
500 def->state == VIR_DOMAIN_SNAPSHOT_PAUSED)) {
501 virReportError(VIR_ERR_INVALID_ARG,
502 _("cannot change between online and offline "
503 "snapshot state in snapshot %s"),
504 def->parent.name);
505 return -1;
508 if ((otherdef->state == VIR_DOMAIN_SNAPSHOT_DISK_SNAPSHOT) !=
509 (def->state == VIR_DOMAIN_SNAPSHOT_DISK_SNAPSHOT)) {
510 virReportError(VIR_ERR_INVALID_ARG,
511 _("cannot change between disk only and "
512 "full system in snapshot %s"),
513 def->parent.name);
514 return -1;
517 if (otherdef->parent.dom) {
518 if (def->parent.dom) {
519 if (!virDomainDefCheckABIStability(otherdef->parent.dom,
520 def->parent.dom, xmlopt))
521 return -1;
522 } else {
523 /* Transfer the domain def */
524 VIR_STEAL_PTR(def->parent.dom, otherdef->parent.dom);
529 if (def->parent.dom) {
530 if (external) {
531 align_location = VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL;
532 align_match = false;
534 if (virDomainSnapshotAlignDisks(def, align_location,
535 align_match) < 0)
536 return -1;
540 return 0;
545 * virDomainSnapshotDefAssignExternalNames:
546 * @def: snapshot def object
548 * Generate default external file names for snapshot targets. Returns 0 on
549 * success, -1 on error.
551 static int
552 virDomainSnapshotDefAssignExternalNames(virDomainSnapshotDefPtr def)
554 const char *origpath;
555 char *tmppath;
556 char *tmp;
557 struct stat sb;
558 size_t i;
559 size_t j;
561 for (i = 0; i < def->ndisks; i++) {
562 virDomainSnapshotDiskDefPtr disk = &def->disks[i];
564 if (disk->snapshot != VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL ||
565 disk->src->path)
566 continue;
568 if (disk->src->type != VIR_STORAGE_TYPE_FILE) {
569 virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
570 _("cannot generate external snapshot name "
571 "for disk '%s' on a '%s' device"),
572 disk->name, virStorageTypeToString(disk->src->type));
573 return -1;
576 if (!(origpath = virDomainDiskGetSource(def->parent.dom->disks[i]))) {
577 virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
578 _("cannot generate external snapshot name "
579 "for disk '%s' without source"),
580 disk->name);
581 return -1;
584 if (stat(origpath, &sb) < 0 || !S_ISREG(sb.st_mode)) {
585 virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
586 _("source for disk '%s' is not a regular "
587 "file; refusing to generate external "
588 "snapshot name"),
589 disk->name);
590 return -1;
593 if (VIR_STRDUP(tmppath, origpath) < 0)
594 return -1;
596 /* drop suffix of the file name */
597 if ((tmp = strrchr(tmppath, '.')) && !strchr(tmp, '/'))
598 *tmp = '\0';
600 if (virAsprintf(&disk->src->path, "%s.%s", tmppath, def->parent.name) < 0) {
601 VIR_FREE(tmppath);
602 return -1;
605 VIR_FREE(tmppath);
607 /* verify that we didn't generate a duplicate name */
608 for (j = 0; j < i; j++) {
609 if (STREQ_NULLABLE(disk->src->path, def->disks[j].src->path)) {
610 virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
611 _("cannot generate external snapshot name for "
612 "disk '%s': collision with disk '%s'"),
613 disk->name, def->disks[j].name);
614 return -1;
619 return 0;
623 static int
624 virDomainSnapshotCompareDiskIndex(const void *a, const void *b)
626 const virDomainSnapshotDiskDef *diska = a;
627 const virDomainSnapshotDiskDef *diskb = b;
629 /* Integer overflow shouldn't be a problem here. */
630 return diska->idx - diskb->idx;
633 /* Align def->disks to def->parent.dom. Sort the list of def->disks,
634 * filling in any missing disks or snapshot state defaults given by
635 * the domain, with a fallback to a passed in default. Convert paths
636 * to disk targets for uniformity. Issue an error and return -1 if
637 * any def->disks[n]->name appears more than once or does not map to
638 * dom->disks. If require_match, also ensure that there is no
639 * conflicting requests for both internal and external snapshots. */
641 virDomainSnapshotAlignDisks(virDomainSnapshotDefPtr def,
642 int default_snapshot,
643 bool require_match)
645 int ret = -1;
646 virBitmapPtr map = NULL;
647 size_t i;
648 int ndisks;
650 if (!def->parent.dom) {
651 virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
652 _("missing domain in snapshot"));
653 goto cleanup;
656 if (def->ndisks > def->parent.dom->ndisks) {
657 virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
658 _("too many disk snapshot requests for domain"));
659 goto cleanup;
662 /* Unlikely to have a guest without disks but technically possible. */
663 if (!def->parent.dom->ndisks) {
664 ret = 0;
665 goto cleanup;
668 if (!(map = virBitmapNew(def->parent.dom->ndisks)))
669 goto cleanup;
671 /* Double check requested disks. */
672 for (i = 0; i < def->ndisks; i++) {
673 virDomainSnapshotDiskDefPtr disk = &def->disks[i];
674 int idx = virDomainDiskIndexByName(def->parent.dom, disk->name, false);
675 int disk_snapshot;
677 if (idx < 0) {
678 virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
679 _("no disk named '%s'"), disk->name);
680 goto cleanup;
683 if (virBitmapIsBitSet(map, idx)) {
684 virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
685 _("disk '%s' specified twice"),
686 disk->name);
687 goto cleanup;
689 ignore_value(virBitmapSetBit(map, idx));
690 disk->idx = idx;
692 disk_snapshot = def->parent.dom->disks[idx]->snapshot;
693 if (!disk->snapshot) {
694 if (disk_snapshot &&
695 (!require_match ||
696 disk_snapshot == VIR_DOMAIN_SNAPSHOT_LOCATION_NONE))
697 disk->snapshot = disk_snapshot;
698 else
699 disk->snapshot = default_snapshot;
700 } else if (require_match &&
701 disk->snapshot != default_snapshot &&
702 !(disk->snapshot == VIR_DOMAIN_SNAPSHOT_LOCATION_NONE &&
703 disk_snapshot == VIR_DOMAIN_SNAPSHOT_LOCATION_NONE)) {
704 const char *tmp;
706 tmp = virDomainSnapshotLocationTypeToString(default_snapshot);
707 virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
708 _("disk '%s' must use snapshot mode '%s'"),
709 disk->name, tmp);
710 goto cleanup;
712 if (disk->src->path &&
713 disk->snapshot != VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL) {
714 virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
715 _("file '%s' for disk '%s' requires "
716 "use of external snapshot mode"),
717 disk->src->path, disk->name);
718 goto cleanup;
720 if (STRNEQ(disk->name, def->parent.dom->disks[idx]->dst)) {
721 VIR_FREE(disk->name);
722 if (VIR_STRDUP(disk->name, def->parent.dom->disks[idx]->dst) < 0)
723 goto cleanup;
727 /* Provide defaults for all remaining disks. */
728 ndisks = def->ndisks;
729 if (VIR_EXPAND_N(def->disks, def->ndisks,
730 def->parent.dom->ndisks - def->ndisks) < 0)
731 goto cleanup;
733 for (i = 0; i < def->parent.dom->ndisks; i++) {
734 virDomainSnapshotDiskDefPtr disk;
736 if (virBitmapIsBitSet(map, i))
737 continue;
738 disk = &def->disks[ndisks++];
739 if (!(disk->src = virStorageSourceNew()))
740 goto cleanup;
741 if (VIR_STRDUP(disk->name, def->parent.dom->disks[i]->dst) < 0)
742 goto cleanup;
743 disk->idx = i;
745 /* Don't snapshot empty drives */
746 if (virStorageSourceIsEmpty(def->parent.dom->disks[i]->src))
747 disk->snapshot = VIR_DOMAIN_SNAPSHOT_LOCATION_NONE;
748 else
749 disk->snapshot = def->parent.dom->disks[i]->snapshot;
751 disk->src->type = VIR_STORAGE_TYPE_FILE;
752 if (!disk->snapshot)
753 disk->snapshot = default_snapshot;
756 qsort(&def->disks[0], def->ndisks, sizeof(def->disks[0]),
757 virDomainSnapshotCompareDiskIndex);
759 /* Generate default external file names for external snapshot locations */
760 if (virDomainSnapshotDefAssignExternalNames(def) < 0)
761 goto cleanup;
763 ret = 0;
765 cleanup:
766 virBitmapFree(map);
767 return ret;
771 /* Converts public VIR_DOMAIN_SNAPSHOT_XML_* into
772 * VIR_DOMAIN_SNAPSHOT_FORMAT_* flags, and silently ignores any other
773 * flags. */
774 unsigned int
775 virDomainSnapshotFormatConvertXMLFlags(unsigned int flags)
777 unsigned int formatFlags = 0;
779 if (flags & VIR_DOMAIN_SNAPSHOT_XML_SECURE)
780 formatFlags |= VIR_DOMAIN_SNAPSHOT_FORMAT_SECURE;
782 return formatFlags;
786 static int
787 virDomainSnapshotDiskDefFormat(virBufferPtr buf,
788 virDomainSnapshotDiskDefPtr disk,
789 virDomainXMLOptionPtr xmlopt)
791 int type = disk->src->type;
793 if (!disk->name)
794 return 0;
796 virBufferEscapeString(buf, "<disk name='%s'", disk->name);
797 if (disk->snapshot > 0)
798 virBufferAsprintf(buf, " snapshot='%s'",
799 virDomainSnapshotLocationTypeToString(disk->snapshot));
801 if (!disk->src->path && disk->src->format == 0) {
802 virBufferAddLit(buf, "/>\n");
803 return 0;
806 virBufferAsprintf(buf, " type='%s'>\n", virStorageTypeToString(type));
807 virBufferAdjustIndent(buf, 2);
809 if (disk->src->format > 0)
810 virBufferEscapeString(buf, "<driver type='%s'/>\n",
811 virStorageFileFormatTypeToString(disk->src->format));
812 if (virDomainDiskSourceFormat(buf, disk->src, "source", 0, false, 0,
813 xmlopt) < 0)
814 return -1;
816 virBufferAdjustIndent(buf, -2);
817 virBufferAddLit(buf, "</disk>\n");
818 return 0;
822 /* Append XML describing def into buf. Return 0 on success, or -1 on
823 * failure with buf cleared. */
824 static int
825 virDomainSnapshotDefFormatInternal(virBufferPtr buf,
826 const char *uuidstr,
827 virDomainSnapshotDefPtr def,
828 virCapsPtr caps,
829 virDomainXMLOptionPtr xmlopt,
830 unsigned int flags)
832 size_t i;
833 int domainflags = VIR_DOMAIN_DEF_FORMAT_INACTIVE;
835 if (flags & VIR_DOMAIN_SNAPSHOT_FORMAT_SECURE)
836 domainflags |= VIR_DOMAIN_DEF_FORMAT_SECURE;
838 virBufferAddLit(buf, "<domainsnapshot>\n");
839 virBufferAdjustIndent(buf, 2);
841 virBufferEscapeString(buf, "<name>%s</name>\n", def->parent.name);
842 if (def->parent.description)
843 virBufferEscapeString(buf, "<description>%s</description>\n",
844 def->parent.description);
845 if (def->state)
846 virBufferAsprintf(buf, "<state>%s</state>\n",
847 virDomainSnapshotStateTypeToString(def->state));
849 if (def->parent.parent_name) {
850 virBufferAddLit(buf, "<parent>\n");
851 virBufferAdjustIndent(buf, 2);
852 virBufferEscapeString(buf, "<name>%s</name>\n",
853 def->parent.parent_name);
854 virBufferAdjustIndent(buf, -2);
855 virBufferAddLit(buf, "</parent>\n");
858 if (def->parent.creationTime)
859 virBufferAsprintf(buf, "<creationTime>%lld</creationTime>\n",
860 def->parent.creationTime);
862 if (def->memory) {
863 virBufferAsprintf(buf, "<memory snapshot='%s'",
864 virDomainSnapshotLocationTypeToString(def->memory));
865 virBufferEscapeString(buf, " file='%s'", def->file);
866 virBufferAddLit(buf, "/>\n");
869 if (def->ndisks) {
870 virBufferAddLit(buf, "<disks>\n");
871 virBufferAdjustIndent(buf, 2);
872 for (i = 0; i < def->ndisks; i++) {
873 if (virDomainSnapshotDiskDefFormat(buf, &def->disks[i], xmlopt) < 0)
874 goto error;
876 virBufferAdjustIndent(buf, -2);
877 virBufferAddLit(buf, "</disks>\n");
880 if (def->parent.dom) {
881 if (virDomainDefFormatInternal(def->parent.dom, caps, domainflags, buf,
882 xmlopt) < 0)
883 goto error;
884 } else if (uuidstr) {
885 virBufferAddLit(buf, "<domain>\n");
886 virBufferAdjustIndent(buf, 2);
887 virBufferAsprintf(buf, "<uuid>%s</uuid>\n", uuidstr);
888 virBufferAdjustIndent(buf, -2);
889 virBufferAddLit(buf, "</domain>\n");
892 if (virSaveCookieFormatBuf(buf, def->cookie,
893 virDomainXMLOptionGetSaveCookie(xmlopt)) < 0)
894 goto error;
896 if (flags & VIR_DOMAIN_SNAPSHOT_FORMAT_INTERNAL)
897 virBufferAsprintf(buf, "<active>%d</active>\n",
898 !!(flags & VIR_DOMAIN_SNAPSHOT_FORMAT_CURRENT));
900 virBufferAdjustIndent(buf, -2);
901 virBufferAddLit(buf, "</domainsnapshot>\n");
903 if (virBufferCheckError(buf) < 0)
904 goto error;
906 return 0;
908 error:
909 virBufferFreeAndReset(buf);
910 return -1;
914 char *
915 virDomainSnapshotDefFormat(const char *uuidstr,
916 virDomainSnapshotDefPtr def,
917 virCapsPtr caps,
918 virDomainXMLOptionPtr xmlopt,
919 unsigned int flags)
921 virBuffer buf = VIR_BUFFER_INITIALIZER;
923 virCheckFlags(VIR_DOMAIN_SNAPSHOT_FORMAT_SECURE |
924 VIR_DOMAIN_SNAPSHOT_FORMAT_INTERNAL |
925 VIR_DOMAIN_SNAPSHOT_FORMAT_CURRENT, NULL);
926 if (virDomainSnapshotDefFormatInternal(&buf, uuidstr, def, caps,
927 xmlopt, flags) < 0)
928 return NULL;
930 return virBufferContentAndReset(&buf);
934 bool
935 virDomainSnapshotDefIsExternal(virDomainSnapshotDefPtr def)
937 size_t i;
939 if (def->memory == VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL)
940 return true;
942 for (i = 0; i < def->ndisks; i++) {
943 if (def->disks[i].snapshot == VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL)
944 return true;
947 return false;
950 bool
951 virDomainSnapshotIsExternal(virDomainMomentObjPtr snap)
953 virDomainSnapshotDefPtr def = virDomainSnapshotObjGetDef(snap);
955 return virDomainSnapshotDefIsExternal(def);
959 virDomainSnapshotRedefinePrep(virDomainPtr domain,
960 virDomainObjPtr vm,
961 virDomainSnapshotDefPtr *defptr,
962 virDomainMomentObjPtr *snap,
963 virDomainXMLOptionPtr xmlopt,
964 bool *update_current,
965 unsigned int flags)
967 virDomainSnapshotDefPtr def = *defptr;
968 virDomainMomentObjPtr other;
969 virDomainSnapshotDefPtr otherdef = NULL;
970 bool check_if_stolen;
972 if (virDomainSnapshotCheckCycles(vm->snapshots, def, vm->def->name) < 0)
973 return -1;
975 other = virDomainSnapshotFindByName(vm->snapshots, def->parent.name);
976 if (other)
977 otherdef = virDomainSnapshotObjGetDef(other);
978 check_if_stolen = other && otherdef->parent.dom;
979 if (virDomainSnapshotRedefineValidate(def, domain->uuid, other, xmlopt,
980 flags) < 0) {
981 /* revert any stealing of the snapshot domain definition */
982 if (check_if_stolen && def->parent.dom && !otherdef->parent.dom)
983 VIR_STEAL_PTR(otherdef->parent.dom, def->parent.dom);
984 return -1;
986 if (other) {
987 if (other == virDomainSnapshotGetCurrent(vm->snapshots)) {
988 *update_current = true;
989 virDomainSnapshotSetCurrent(vm->snapshots, NULL);
992 /* Drop and rebuild the parent relationship, but keep all
993 * child relations by reusing snap. */
994 virDomainMomentDropParent(other);
995 virObjectUnref(otherdef);
996 other->def = &(*defptr)->parent;
997 *defptr = NULL;
998 *snap = other;
1001 return 0;