1 /*
2 * qemu_checkpoint.c: checkpoint related implementation
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library. If not, see
16 * <http://www.gnu.org/licenses/>.
17 */
18
19 #include <config.h>
20
21 #include <sys/types.h>
22
23 #include "qemu_checkpoint.h"
24 #include "qemu_capabilities.h"
25 #include "qemu_monitor.h"
26 #include "qemu_domain.h"
27 #include "qemu_block.h"
28
29 #include "virerror.h"
30 #include "virlog.h"
31 #include "datatypes.h"
32 #include "viralloc.h"
33 #include "domain_conf.h"
34 #include "libvirt_internal.h"
35 #include "virxml.h"
36 #include "virstring.h"
37 #include "virdomaincheckpointobjlist.h"
38 #include "virdomainsnapshotobjlist.h"
39
40 #define VIR_FROM_THIS VIR_FROM_QEMU
41
42 VIR_LOG_INIT("qemu.qemu_checkpoint");
43
44 /**
45 * qemuCheckpointSetCurrent: Set currently active checkpoint
46 *
47 * @vm: domain object
48 * @newcurrent: checkpoint object to set as current/active
49 *
50 * Sets @newcurrent as the 'current' checkpoint of @vm. This helper ensures that
51 * the checkpoint which was 'current' previously is updated.
52 */
53 static void
qemuCheckpointSetCurrent(virDomainObj * vm,virDomainMomentObj * newcurrent)54 qemuCheckpointSetCurrent(virDomainObj *vm,
55 virDomainMomentObj *newcurrent)
56 {
57 qemuDomainObjPrivate *priv = vm->privateData;
58 virQEMUDriver *driver = priv->driver;
59 g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver);
60 virDomainMomentObj *oldcurrent = virDomainCheckpointGetCurrent(vm->checkpoints);
61
62 virDomainCheckpointSetCurrent(vm->checkpoints, newcurrent);
63
64 /* we need to write out metadata for the old checkpoint to update the
65 * 'active' property */
66 if (oldcurrent &&
67 oldcurrent != newcurrent) {
68 if (qemuCheckpointWriteMetadata(vm, oldcurrent, driver->xmlopt, cfg->checkpointDir) < 0)
69 VIR_WARN("failed to update old current checkpoint");
70 }
71 }
72
73
74 /* Looks up the domain object from checkpoint and unlocks the
75 * driver. The returned domain object is locked and ref'd and the
76 * caller must call virDomainObjEndAPI() on it. */
77 virDomainObj *
qemuDomObjFromCheckpoint(virDomainCheckpointPtr checkpoint)78 qemuDomObjFromCheckpoint(virDomainCheckpointPtr checkpoint)
79 {
80 return qemuDomainObjFromDomain(checkpoint->domain);
81 }
82
83
84 /* Looks up checkpoint object from VM and name */
85 virDomainMomentObj *
qemuCheckpointObjFromName(virDomainObj * vm,const char * name)86 qemuCheckpointObjFromName(virDomainObj *vm,
87 const char *name)
88 {
89 virDomainMomentObj *chk = NULL;
90 chk = virDomainCheckpointFindByName(vm->checkpoints, name);
91 if (!chk)
92 virReportError(VIR_ERR_NO_DOMAIN_CHECKPOINT,
93 _("no domain checkpoint with matching name '%s'"),
94 name);
95
96 return chk;
97 }
98
99
100 /* Looks up checkpoint object from VM and checkpointPtr */
101 virDomainMomentObj *
qemuCheckpointObjFromCheckpoint(virDomainObj * vm,virDomainCheckpointPtr checkpoint)102 qemuCheckpointObjFromCheckpoint(virDomainObj *vm,
103 virDomainCheckpointPtr checkpoint)
104 {
105 return qemuCheckpointObjFromName(vm, checkpoint->name);
106 }
107
108
109 int
qemuCheckpointWriteMetadata(virDomainObj * vm,virDomainMomentObj * checkpoint,virDomainXMLOption * xmlopt,const char * checkpointDir)110 qemuCheckpointWriteMetadata(virDomainObj *vm,
111 virDomainMomentObj *checkpoint,
112 virDomainXMLOption *xmlopt,
113 const char *checkpointDir)
114 {
115 unsigned int flags = VIR_DOMAIN_CHECKPOINT_FORMAT_SECURE;
116 virDomainCheckpointDef *def = virDomainCheckpointObjGetDef(checkpoint);
117 g_autofree char *newxml = NULL;
118 g_autofree char *chkDir = NULL;
119 g_autofree char *chkFile = NULL;
120
121 newxml = virDomainCheckpointDefFormat(def, xmlopt, flags);
122 if (newxml == NULL)
123 return -1;
124
125 chkDir = g_strdup_printf("%s/%s", checkpointDir, vm->def->name);
126 if (g_mkdir_with_parents(chkDir, 0777) < 0) {
127 virReportSystemError(errno, _("cannot create checkpoint directory '%s'"),
128 chkDir);
129 return -1;
130 }
131
132 chkFile = g_strdup_printf("%s/%s.xml", chkDir, def->parent.name);
133
134 return virXMLSaveFile(chkFile, NULL, "checkpoint-edit", newxml);
135 }
136
137
138 int
qemuCheckpointDiscardDiskBitmaps(virStorageSource * src,GHashTable * blockNamedNodeData,const char * delbitmap,virJSONValue * actions,const char * diskdst,GSList ** reopenimages)139 qemuCheckpointDiscardDiskBitmaps(virStorageSource *src,
140 GHashTable *blockNamedNodeData,
141 const char *delbitmap,
142 virJSONValue *actions,
143 const char *diskdst,
144 GSList **reopenimages)
145 {
146 virStorageSource *n;
147 bool found = false;
148
149 /* find the backing chain entry with bitmap named '@delbitmap' */
150 for (n = src; virStorageSourceIsBacking(n); n = n->backingStore) {
151 qemuBlockNamedNodeDataBitmap *bitmapdata;
152
153 if (!(bitmapdata = qemuBlockNamedNodeDataGetBitmapByName(blockNamedNodeData,
154 n, delbitmap)))
155 continue;
156
157 found = true;
158
159 if (qemuMonitorTransactionBitmapRemove(actions,
160 n->nodeformat,
161 bitmapdata->name) < 0)
162 return -1;
163
164 if (n != src)
165 *reopenimages = g_slist_prepend(*reopenimages, n);
166 }
167
168 if (!found) {
169 virReportError(VIR_ERR_INTERNAL_ERROR,
170 _("bitmap '%s' not found in backing chain of '%s'"),
171 delbitmap, diskdst);
172 return -1;
173 }
174
175 return 0;
176 }
177
178
179 static int
qemuCheckpointDiscardBitmaps(virDomainObj * vm,virDomainCheckpointDef * chkdef)180 qemuCheckpointDiscardBitmaps(virDomainObj *vm,
181 virDomainCheckpointDef *chkdef)
182 {
183 qemuDomainObjPrivate *priv = vm->privateData;
184 virQEMUDriver *driver = priv->driver;
185 g_autoptr(GHashTable) blockNamedNodeData = NULL;
186 int rc = -1;
187 g_autoptr(virJSONValue) actions = NULL;
188 size_t i;
189 g_autoptr(GSList) reopenimages = NULL;
190 g_autoptr(GSList) relabelimages = NULL;
191 GSList *next;
192
193 actions = virJSONValueNewArray();
194
195 if (!(blockNamedNodeData = qemuBlockGetNamedNodeData(vm, QEMU_ASYNC_JOB_NONE)))
196 return -1;
197
198 for (i = 0; i < chkdef->ndisks; i++) {
199 virDomainCheckpointDiskDef *chkdisk = &chkdef->disks[i];
200 virDomainDiskDef *domdisk = virDomainDiskByTarget(vm->def, chkdisk->name);
201
202 /* domdisk can be missing e.g. when it was unplugged */
203 if (!domdisk)
204 continue;
205
206 if (chkdisk->type != VIR_DOMAIN_CHECKPOINT_TYPE_BITMAP)
207 continue;
208
209 if (!chkdisk->bitmap) {
210 virReportError(VIR_ERR_INVALID_ARG,
211 _("missing bitmap name for disk '%s' of checkpoint '%s'"),
212 chkdisk->name, chkdef->parent.name);
213 return -1;
214 }
215
216 if (qemuCheckpointDiscardDiskBitmaps(domdisk->src, blockNamedNodeData,
217 chkdisk->bitmap,
218 actions, domdisk->dst,
219 &reopenimages) < 0)
220 return -1;
221 }
222
223 /* label any non-top images for read-write access */
224 for (next = reopenimages; next; next = next->next) {
225 virStorageSource *src = next->data;
226
227 if (qemuDomainStorageSourceAccessAllow(driver, vm, src,
228 false, false, false) < 0)
229 goto relabel;
230
231 if (virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_BLOCKDEV_REOPEN) &&
232 qemuBlockReopenReadWrite(vm, src, QEMU_ASYNC_JOB_NONE) < 0)
233 goto relabel;
234
235 relabelimages = g_slist_prepend(relabelimages, src);
236 }
237
238 qemuDomainObjEnterMonitor(driver, vm);
239 rc = qemuMonitorTransaction(priv->mon, &actions);
240 if (qemuDomainObjExitMonitor(driver, vm) < 0)
241 return -1;
242
243 relabel:
244 for (next = relabelimages; next; next = next->next) {
245 virStorageSource *src = next->data;
246
247 if (virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_BLOCKDEV_REOPEN))
248 ignore_value(qemuBlockReopenReadOnly(vm, src, QEMU_ASYNC_JOB_NONE));
249
250 ignore_value(qemuDomainStorageSourceAccessAllow(driver, vm, src,
251 true, false, false));
252 }
253
254 return rc;
255 }
256
257
258 static int
qemuCheckpointDiscard(virQEMUDriver * driver,virDomainObj * vm,virDomainMomentObj * chk,bool update_parent,bool metadata_only)259 qemuCheckpointDiscard(virQEMUDriver *driver,
260 virDomainObj *vm,
261 virDomainMomentObj *chk,
262 bool update_parent,
263 bool metadata_only)
264 {
265 g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver);
266 g_autofree char *chkFile = NULL;
267 bool chkcurrent = chk == virDomainCheckpointGetCurrent(vm->checkpoints);
268
269 if (!metadata_only && !virDomainObjIsActive(vm)) {
270 virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
271 _("cannot remove checkpoint from inactive domain"));
272 return -1;
273 }
274
275 chkFile = g_strdup_printf("%s/%s/%s.xml", cfg->checkpointDir, vm->def->name,
276 chk->def->name);
277
278 if (!metadata_only) {
279 virDomainCheckpointDef *chkdef = virDomainCheckpointObjGetDef(chk);
280 if (qemuCheckpointDiscardBitmaps(vm, chkdef) < 0)
281 return -1;
282 }
283
284 if (chkcurrent) {
285 virDomainMomentObj *parent = NULL;
286
287 virDomainCheckpointSetCurrent(vm->checkpoints, NULL);
288 parent = virDomainCheckpointFindByName(vm->checkpoints,
289 chk->def->parent_name);
290
291 if (update_parent && parent) {
292 virDomainCheckpointSetCurrent(vm->checkpoints, parent);
293 if (qemuCheckpointWriteMetadata(vm, parent,
294 driver->xmlopt,
295 cfg->checkpointDir) < 0) {
296 VIR_WARN("failed to set parent checkpoint '%s' as current",
297 chk->def->parent_name);
298 virDomainCheckpointSetCurrent(vm->checkpoints, NULL);
299 }
300 }
301 }
302
303 if (unlink(chkFile) < 0)
304 VIR_WARN("Failed to unlink %s", chkFile);
305 if (update_parent)
306 virDomainMomentDropParent(chk);
307 virDomainCheckpointObjListRemove(vm->checkpoints, chk);
308
309 return 0;
310 }
311
312
313 int
qemuCheckpointDiscardAllMetadata(virQEMUDriver * driver,virDomainObj * vm)314 qemuCheckpointDiscardAllMetadata(virQEMUDriver *driver,
315 virDomainObj *vm)
316 {
317 virQEMUMomentRemove rem = {
318 .driver = driver,
319 .vm = vm,
320 .metadata_only = true,
321 .momentDiscard = qemuCheckpointDiscard,
322 };
323
324 virDomainCheckpointForEach(vm->checkpoints, qemuDomainMomentDiscardAll,
325 &rem);
326 virDomainCheckpointObjListRemoveAll(vm->checkpoints);
327
328 return rem.err;
329 }
330
331
332 /* Called inside job lock */
333 static int
qemuCheckpointPrepare(virQEMUDriver * driver,virDomainObj * vm,virDomainCheckpointDef * def)334 qemuCheckpointPrepare(virQEMUDriver *driver,
335 virDomainObj *vm,
336 virDomainCheckpointDef *def)
337 {
338 size_t i;
339 g_autofree char *xml = NULL;
340 qemuDomainObjPrivate *priv = vm->privateData;
341
342 /* Easiest way to clone inactive portion of vm->def is via
343 * conversion in and back out of xml. */
344 if (!(xml = qemuDomainDefFormatLive(driver, priv->qemuCaps,
345 vm->def, priv->origCPU,
346 true, true)) ||
347 !(def->parent.dom = virDomainDefParseString(xml, driver->xmlopt,
348 priv->qemuCaps,
349 VIR_DOMAIN_DEF_PARSE_INACTIVE |
350 VIR_DOMAIN_DEF_PARSE_SKIP_VALIDATE)))
351 return -1;
352
353 if (virDomainCheckpointAlignDisks(def) < 0)
354 return -1;
355
356 for (i = 0; i < def->ndisks; i++) {
357 virDomainCheckpointDiskDef *disk = &def->disks[i];
358
359 if (disk->type != VIR_DOMAIN_CHECKPOINT_TYPE_BITMAP)
360 continue;
361
362 if (STRNEQ(disk->bitmap, def->parent.name)) {
363 virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
364 _("bitmap for disk '%s' must match checkpoint name '%s'"),
365 disk->name, def->parent.name);
366 return -1;
367 }
368
369 if (vm->def->disks[i]->src->format != VIR_STORAGE_FILE_QCOW2) {
370 virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
371 _("checkpoint for disk %s unsupported "
372 "for storage type %s"),
373 disk->name,
374 virStorageFileFormatTypeToString(
375 vm->def->disks[i]->src->format));
376 return -1;
377 }
378
379 if (!qemuDomainDiskBlockJobIsSupported(vm, vm->def->disks[i]))
380 return -1;
381 }
382
383 return 0;
384 }
385
386 static int
qemuCheckpointAddActions(virDomainObj * vm,virJSONValue * actions,virDomainCheckpointDef * def)387 qemuCheckpointAddActions(virDomainObj *vm,
388 virJSONValue *actions,
389 virDomainCheckpointDef *def)
390 {
391 size_t i;
392
393 for (i = 0; i < def->ndisks; i++) {
394 virDomainCheckpointDiskDef *chkdisk = &def->disks[i];
395 virDomainDiskDef *domdisk = virDomainDiskByTarget(vm->def, chkdisk->name);
396
397 /* checkpoint definition validator mandates that the corresponding
398 * domdisk should exist */
399 if (!domdisk ||
400 chkdisk->type != VIR_DOMAIN_CHECKPOINT_TYPE_BITMAP)
401 continue;
402
403 if (qemuMonitorTransactionBitmapAdd(actions, domdisk->src->nodeformat,
404 chkdisk->bitmap, true, false, 0) < 0)
405 return -1;
406 }
407 return 0;
408 }
409
410
411 static int
qemuCheckpointRedefineValidateBitmaps(virDomainObj * vm,virDomainCheckpointDef * chkdef)412 qemuCheckpointRedefineValidateBitmaps(virDomainObj *vm,
413 virDomainCheckpointDef *chkdef)
414 {
415 g_autoptr(GHashTable) blockNamedNodeData = NULL;
416 size_t i;
417
418 if (virDomainObjCheckActive(vm) < 0)
419 return -1;
420
421 if (!(blockNamedNodeData = qemuBlockGetNamedNodeData(vm, QEMU_ASYNC_JOB_NONE)))
422 return -1;
423
424 for (i = 0; i < chkdef->ndisks; i++) {
425 virDomainCheckpointDiskDef *chkdisk = chkdef->disks + i;
426 virDomainDiskDef *domdisk;
427
428 if (chkdisk->type != VIR_DOMAIN_CHECKPOINT_TYPE_BITMAP)
429 continue;
430
431 /* we tolerate missing disks due to possible detach */
432 if (!(domdisk = virDomainDiskByTarget(vm->def, chkdisk->name)))
433 continue;
434
435 if (!qemuBlockBitmapChainIsValid(domdisk->src, chkdef->parent.name,
436 blockNamedNodeData)) {
437 virReportError(VIR_ERR_CHECKPOINT_INCONSISTENT,
438 _("missing or broken bitmap '%s' for disk '%s'"),
439 chkdef->parent.name, domdisk->dst);
440 return -1;
441 }
442 }
443
444 return 0;
445 }
446
447
448 static virDomainMomentObj *
qemuCheckpointRedefine(virDomainObj * vm,virDomainCheckpointDef ** def,bool * update_current,bool validate_bitmaps)449 qemuCheckpointRedefine(virDomainObj *vm,
450 virDomainCheckpointDef **def,
451 bool *update_current,
452 bool validate_bitmaps)
453 {
454 if (virDomainCheckpointRedefinePrep(vm, *def, update_current) < 0)
455 return NULL;
456
457 if (validate_bitmaps &&
458 qemuCheckpointRedefineValidateBitmaps(vm, *def) < 0)
459 return NULL;
460
461 return virDomainCheckpointRedefineCommit(vm, def);
462 }
463
464
465 int
qemuCheckpointCreateCommon(virQEMUDriver * driver,virDomainObj * vm,virDomainCheckpointDef ** def,virJSONValue ** actions,virDomainMomentObj ** chk)466 qemuCheckpointCreateCommon(virQEMUDriver *driver,
467 virDomainObj *vm,
468 virDomainCheckpointDef **def,
469 virJSONValue **actions,
470 virDomainMomentObj **chk)
471 {
472 g_autoptr(virJSONValue) tmpactions = NULL;
473 virDomainMomentObj *parent;
474
475 if (qemuCheckpointPrepare(driver, vm, *def) < 0)
476 return -1;
477
478 if ((parent = virDomainCheckpointGetCurrent(vm->checkpoints)))
479 (*def)->parent.parent_name = g_strdup(parent->def->name);
480
481 tmpactions = virJSONValueNewArray();
482
483 if (qemuCheckpointAddActions(vm, tmpactions, *def) < 0)
484 return -1;
485
486 if (!(*chk = virDomainCheckpointAssignDef(vm->checkpoints, *def)))
487 return -1;
488
489 *def = NULL;
490
491 *actions = g_steal_pointer(&tmpactions);
492 return 0;
493 }
494
495
496 /**
497 * qemuCheckpointRollbackMetadata:
498 * @vm: domain object
499 * @chk: checkpoint object
500 *
501 * If @chk is not null remove the @chk object from the list of checkpoints of @vm.
502 */
503 void
qemuCheckpointRollbackMetadata(virDomainObj * vm,virDomainMomentObj * chk)504 qemuCheckpointRollbackMetadata(virDomainObj *vm,
505 virDomainMomentObj *chk)
506 {
507 if (!chk)
508 return;
509
510 virDomainCheckpointObjListRemove(vm->checkpoints, chk);
511 }
512
513
514 static virDomainMomentObj *
qemuCheckpointCreate(virQEMUDriver * driver,virDomainObj * vm,virDomainCheckpointDef ** def)515 qemuCheckpointCreate(virQEMUDriver *driver,
516 virDomainObj *vm,
517 virDomainCheckpointDef **def)
518 {
519 g_autoptr(virJSONValue) actions = NULL;
520 virDomainMomentObj *chk = NULL;
521 int rc;
522
523 if (qemuCheckpointCreateCommon(driver, vm, def, &actions, &chk) < 0)
524 return NULL;
525
526 qemuDomainObjEnterMonitor(driver, vm);
527 rc = qemuMonitorTransaction(qemuDomainGetMonitor(vm), &actions);
528 if (qemuDomainObjExitMonitor(driver, vm) < 0 || rc < 0) {
529 qemuCheckpointRollbackMetadata(vm, chk);
530 return NULL;
531 }
532
533 return chk;
534 }
535
536
537 int
qemuCheckpointCreateFinalize(virQEMUDriver * driver,virDomainObj * vm,virQEMUDriverConfig * cfg,virDomainMomentObj * chk,bool update_current)538 qemuCheckpointCreateFinalize(virQEMUDriver *driver,
539 virDomainObj *vm,
540 virQEMUDriverConfig *cfg,
541 virDomainMomentObj *chk,
542 bool update_current)
543 {
544 if (update_current)
545 qemuCheckpointSetCurrent(vm, chk);
546
547 if (qemuCheckpointWriteMetadata(vm, chk,
548 driver->xmlopt,
549 cfg->checkpointDir) < 0) {
550 /* if writing of metadata fails, error out rather than trying
551 * to silently carry on without completing the checkpoint */
552 virReportError(VIR_ERR_INTERNAL_ERROR,
553 _("unable to save metadata for checkpoint %s"),
554 chk->def->name);
555 qemuCheckpointRollbackMetadata(vm, chk);
556 return -1;
557 }
558
559 virDomainCheckpointLinkParent(vm->checkpoints, chk);
560
561 return 0;
562 }
563
564
565 virDomainCheckpointPtr
qemuCheckpointCreateXML(virDomainPtr domain,virDomainObj * vm,const char * xmlDesc,unsigned int flags)566 qemuCheckpointCreateXML(virDomainPtr domain,
567 virDomainObj *vm,
568 const char *xmlDesc,
569 unsigned int flags)
570 {
571 qemuDomainObjPrivate *priv = vm->privateData;
572 virQEMUDriver *driver = priv->driver;
573 virDomainMomentObj *chk = NULL;
574 virDomainCheckpointPtr checkpoint = NULL;
575 bool update_current = true;
576 bool redefine = flags & VIR_DOMAIN_CHECKPOINT_CREATE_REDEFINE;
577 bool validate_bitmaps = flags & VIR_DOMAIN_CHECKPOINT_CREATE_REDEFINE_VALIDATE;
578 unsigned int parse_flags = 0;
579 g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver);
580 g_autoptr(virDomainCheckpointDef) def = NULL;
581
582 virCheckFlags(VIR_DOMAIN_CHECKPOINT_CREATE_REDEFINE |
583 VIR_DOMAIN_CHECKPOINT_CREATE_REDEFINE_VALIDATE, NULL);
584
585 if (redefine) {
586 parse_flags |= VIR_DOMAIN_CHECKPOINT_PARSE_REDEFINE;
587 update_current = false;
588 }
589
590 if (!redefine) {
591 if (!virDomainObjIsActive(vm)) {
592 virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
593 _("cannot create checkpoint for inactive domain"));
594 return NULL;
595 }
596
597 if (!virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_INCREMENTAL_BACKUP)) {
598 virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
599 _("incremental backup is not supported yet"));
600 return NULL;
601 }
602 }
603
604 if (!(def = virDomainCheckpointDefParseString(xmlDesc, driver->xmlopt,
605 priv->qemuCaps, parse_flags)))
606 return NULL;
607 /* Unlike snapshots, the RNG schema already ensured a sane filename. */
608
609 /* We are going to modify the domain below. */
610 if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MODIFY) < 0)
611 return NULL;
612
613 if (redefine) {
614 chk = qemuCheckpointRedefine(vm, &def, &update_current, validate_bitmaps);
615 } else {
616 chk = qemuCheckpointCreate(driver, vm, &def);
617 }
618
619 if (!chk)
620 goto endjob;
621
622 if (qemuCheckpointCreateFinalize(driver, vm, cfg, chk, update_current) < 0)
623 goto endjob;
624
625 /* If we fail after this point, there's not a whole lot we can do;
626 * we've successfully created the checkpoint, so we have to go
627 * forward the best we can.
628 */
629 checkpoint = virGetDomainCheckpoint(domain, chk->def->name);
630
631 endjob:
632 qemuDomainObjEndJob(driver, vm);
633
634 return checkpoint;
635 }
636
637
638 struct qemuCheckpointDiskMap {
639 virDomainCheckpointDiskDef *chkdisk;
640 virDomainDiskDef *domdisk;
641 };
642
643
644 static int
qemuCheckpointGetXMLDescUpdateSize(virDomainObj * vm,virDomainCheckpointDef * chkdef)645 qemuCheckpointGetXMLDescUpdateSize(virDomainObj *vm,
646 virDomainCheckpointDef *chkdef)
647 {
648 qemuDomainObjPrivate *priv = vm->privateData;
649 virQEMUDriver *driver = priv->driver;
650 g_autoptr(GHashTable) blockNamedNodeData = NULL;
651 g_autofree struct qemuCheckpointDiskMap *diskmap = NULL;
652 g_autoptr(virJSONValue) recoveractions = NULL;
653 g_autoptr(virJSONValue) mergeactions = virJSONValueNewArray();
654 g_autoptr(virJSONValue) cleanupactions = virJSONValueNewArray();
655 int rc = 0;
656 size_t ndisks = 0;
657 size_t i;
658 int ret = -1;
659
660 if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MODIFY) < 0)
661 return -1;
662
663 if (virDomainObjCheckActive(vm) < 0)
664 goto endjob;
665
666 if (!(blockNamedNodeData = qemuBlockGetNamedNodeData(vm, QEMU_ASYNC_JOB_NONE)))
667 goto endjob;
668
669 /* enumerate disks relevant for the checkpoint which are also present in the
670 * domain */
671 diskmap = g_new0(struct qemuCheckpointDiskMap, chkdef->ndisks);
672
673 for (i = 0; i < chkdef->ndisks; i++) {
674 virDomainCheckpointDiskDef *chkdisk = chkdef->disks + i;
675 virDomainDiskDef *domdisk;
676
677 chkdisk->size = 0;
678 chkdisk->sizeValid = false;
679
680 if (chkdisk->type != VIR_DOMAIN_CHECKPOINT_TYPE_BITMAP)
681 continue;
682
683 if (!(domdisk = virDomainDiskByTarget(vm->def, chkdisk->name)))
684 continue;
685
686 if (!qemuBlockBitmapChainIsValid(domdisk->src, chkdef->parent.name, blockNamedNodeData))
687 continue;
688
689 diskmap[ndisks].chkdisk = chkdisk;
690 diskmap[ndisks].domdisk = domdisk;
691 ndisks++;
692 }
693
694 if (ndisks == 0) {
695 ret = 0;
696 goto endjob;
697 }
698
699 /* we need to calculate the merged bitmap to obtain accurate data */
700 for (i = 0; i < ndisks; i++) {
701 virDomainDiskDef *domdisk = diskmap[i].domdisk;
702 g_autoptr(virJSONValue) actions = NULL;
703
704 /* possibly delete leftovers from previous cases */
705 if (qemuBlockNamedNodeDataGetBitmapByName(blockNamedNodeData, domdisk->src,
706 "libvirt-tmp-size-xml")) {
707 if (!recoveractions)
708 recoveractions = virJSONValueNewArray();
709
710 if (qemuMonitorTransactionBitmapRemove(recoveractions,
711 domdisk->src->nodeformat,
712 "libvirt-tmp-size-xml") < 0)
713 goto endjob;
714 }
715
716 if (qemuBlockGetBitmapMergeActions(domdisk->src, NULL, domdisk->src,
717 chkdef->parent.name, "libvirt-tmp-size-xml",
718 NULL, &actions, blockNamedNodeData) < 0)
719 goto endjob;
720
721 if (virJSONValueArrayConcat(mergeactions, actions) < 0)
722 goto endjob;
723
724 if (qemuMonitorTransactionBitmapRemove(cleanupactions,
725 domdisk->src->nodeformat,
726 "libvirt-tmp-size-xml") < 0)
727 goto endjob;
728 }
729
730 qemuDomainObjEnterMonitor(driver, vm);
731
732 if (rc == 0 && recoveractions)
733 rc = qemuMonitorTransaction(priv->mon, &recoveractions);
734
735 if (rc == 0)
736 rc = qemuMonitorTransaction(priv->mon, &mergeactions);
737
738 if (qemuDomainObjExitMonitor(driver, vm) < 0 || rc < 0)
739 goto endjob;
740
741 /* now do a final refresh */
742 virHashFree(blockNamedNodeData);
743 if (!(blockNamedNodeData = qemuBlockGetNamedNodeData(vm, QEMU_ASYNC_JOB_NONE)))
744 goto endjob;
745
746 qemuDomainObjEnterMonitor(driver, vm);
747
748 rc = qemuMonitorTransaction(priv->mon, &cleanupactions);
749
750 if (qemuDomainObjExitMonitor(driver, vm) < 0 || rc < 0)
751 goto endjob;
752
753 /* update disks */
754 for (i = 0; i < ndisks; i++) {
755 virDomainCheckpointDiskDef *chkdisk = diskmap[i].chkdisk;
756 virDomainDiskDef *domdisk = diskmap[i].domdisk;
757 qemuBlockNamedNodeDataBitmap *bitmap;
758
759 if ((bitmap = qemuBlockNamedNodeDataGetBitmapByName(blockNamedNodeData, domdisk->src,
760 "libvirt-tmp-size-xml"))) {
761 chkdisk->size = bitmap->dirtybytes;
762 chkdisk->sizeValid = true;
763 }
764 }
765
766 ret = 0;
767
768 endjob:
769 qemuDomainObjEndJob(driver, vm);
770 return ret;
771 }
772
773
774 char *
qemuCheckpointGetXMLDesc(virDomainObj * vm,virDomainCheckpointPtr checkpoint,unsigned int flags)775 qemuCheckpointGetXMLDesc(virDomainObj *vm,
776 virDomainCheckpointPtr checkpoint,
777 unsigned int flags)
778 {
779 qemuDomainObjPrivate *priv = vm->privateData;
780 virQEMUDriver *driver = priv->driver;
781 virDomainMomentObj *chk = NULL;
782 virDomainCheckpointDef *chkdef;
783 unsigned int format_flags;
784
785 virCheckFlags(VIR_DOMAIN_CHECKPOINT_XML_SECURE |
786 VIR_DOMAIN_CHECKPOINT_XML_NO_DOMAIN |
787 VIR_DOMAIN_CHECKPOINT_XML_SIZE, NULL);
788
789 if (!(chk = qemuCheckpointObjFromCheckpoint(vm, checkpoint)))
790 return NULL;
791
792 chkdef = virDomainCheckpointObjGetDef(chk);
793
794 if (flags & VIR_DOMAIN_CHECKPOINT_XML_SIZE &&
795 qemuCheckpointGetXMLDescUpdateSize(vm, chkdef) < 0)
796 return NULL;
797
798 format_flags = virDomainCheckpointFormatConvertXMLFlags(flags);
799 return virDomainCheckpointDefFormat(chkdef, driver->xmlopt,
800 format_flags);
801 }
802
803
804 struct virQEMUCheckpointReparent {
805 const char *dir;
806 virDomainMomentObj *parent;
807 virDomainObj *vm;
808 virDomainXMLOption *xmlopt;
809 int err;
810 };
811
812
813 static int
qemuCheckpointReparentChildren(void * payload,const char * name G_GNUC_UNUSED,void * data)814 qemuCheckpointReparentChildren(void *payload,
815 const char *name G_GNUC_UNUSED,
816 void *data)
817 {
818 virDomainMomentObj *moment = payload;
819 struct virQEMUCheckpointReparent *rep = data;
820
821 if (rep->err < 0)
822 return 0;
823
824 VIR_FREE(moment->def->parent_name);
825
826 if (rep->parent->def)
827 moment->def->parent_name = g_strdup(rep->parent->def->name);
828
829 rep->err = qemuCheckpointWriteMetadata(rep->vm, moment,
830 rep->xmlopt, rep->dir);
831 return 0;
832 }
833
834
835 int
qemuCheckpointDelete(virDomainObj * vm,virDomainCheckpointPtr checkpoint,unsigned int flags)836 qemuCheckpointDelete(virDomainObj *vm,
837 virDomainCheckpointPtr checkpoint,
838 unsigned int flags)
839 {
840 qemuDomainObjPrivate *priv = vm->privateData;
841 virQEMUDriver *driver = priv->driver;
842 g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver);
843 int ret = -1;
844 virDomainMomentObj *chk = NULL;
845 virQEMUMomentRemove rem;
846 struct virQEMUCheckpointReparent rep;
847 bool metadata_only = !!(flags & VIR_DOMAIN_CHECKPOINT_DELETE_METADATA_ONLY);
848
849 virCheckFlags(VIR_DOMAIN_CHECKPOINT_DELETE_CHILDREN |
850 VIR_DOMAIN_CHECKPOINT_DELETE_METADATA_ONLY |
851 VIR_DOMAIN_CHECKPOINT_DELETE_CHILDREN_ONLY, -1);
852
853 if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MODIFY) < 0)
854 return -1;
855
856 if (!metadata_only) {
857 if (!virDomainObjIsActive(vm)) {
858 virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
859 _("cannot delete checkpoint for inactive domain"));
860 goto endjob;
861 }
862
863 if (!virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_INCREMENTAL_BACKUP)) {
864 virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
865 _("incremental backup is not supported yet"));
866 goto endjob;
867 }
868 }
869
870 if (!(chk = qemuCheckpointObjFromCheckpoint(vm, checkpoint)))
871 goto endjob;
872
873 if (flags & (VIR_DOMAIN_CHECKPOINT_DELETE_CHILDREN |
874 VIR_DOMAIN_CHECKPOINT_DELETE_CHILDREN_ONLY)) {
875 rem.driver = driver;
876 rem.vm = vm;
877 rem.metadata_only = metadata_only;
878 rem.err = 0;
879 rem.current = virDomainCheckpointGetCurrent(vm->checkpoints);
880 rem.found = false;
881 rem.momentDiscard = qemuCheckpointDiscard;
882 virDomainMomentForEachDescendant(chk, qemuDomainMomentDiscardAll,
883 &rem);
884 if (rem.err < 0)
885 goto endjob;
886 if (rem.found) {
887 qemuCheckpointSetCurrent(vm, chk);
888
889 if (flags & VIR_DOMAIN_CHECKPOINT_DELETE_CHILDREN_ONLY) {
890 if (qemuCheckpointWriteMetadata(vm, chk,
891 driver->xmlopt,
892 cfg->checkpointDir) < 0) {
893 virReportError(VIR_ERR_INTERNAL_ERROR,
894 _("failed to set checkpoint '%s' as current"),
895 chk->def->name);
896 virDomainCheckpointSetCurrent(vm->checkpoints, NULL);
897 goto endjob;
898 }
899 }
900 }
901 } else if (chk->nchildren) {
902 rep.dir = cfg->checkpointDir;
903 rep.parent = chk->parent;
904 rep.vm = vm;
905 rep.err = 0;
906 rep.xmlopt = driver->xmlopt;
907 virDomainMomentForEachChild(chk, qemuCheckpointReparentChildren,
908 &rep);
909 if (rep.err < 0)
910 goto endjob;
911 virDomainMomentMoveChildren(chk, chk->parent);
912 }
913
914 if (flags & VIR_DOMAIN_CHECKPOINT_DELETE_CHILDREN_ONLY) {
915 virDomainMomentDropChildren(chk);
916 ret = 0;
917 } else {
918 ret = qemuCheckpointDiscard(driver, vm, chk, true, metadata_only);
919 }
920
921 endjob:
922 qemuDomainObjEndJob(driver, vm);
923 return ret;
924 }
925