diff --git a/src/qemu/qemu_blockjob.c b/src/qemu/qemu_blockjob.c
index ce4056a7b3..b17577e987 100644
--- a/src/qemu/qemu_blockjob.c
+++ b/src/qemu/qemu_blockjob.c
@@ -78,6 +78,11 @@ qemuBlockJobDataDisposeJobdata(qemuBlockJobDataPtr job)
{
if (job->type == QEMU_BLOCKJOB_TYPE_CREATE)
virObjectUnref(job->data.create.src);
+
+ if (job->type == QEMU_BLOCKJOB_TYPE_BACKUP) {
+ virObjectUnref(job->data.backup.store);
+ g_free(job->data.backup.bitmap);
+ }
}
@@ -370,6 +375,34 @@ qemuBlockJobDiskNewCopy(virDomainObjPtr vm,
}
+qemuBlockJobDataPtr
+qemuBlockJobDiskNewBackup(virDomainObjPtr vm,
+ virDomainDiskDefPtr disk,
+ virStorageSourcePtr store,
+ bool deleteStore,
+ const char *bitmap)
+{
+ g_autoptr(qemuBlockJobData) job = NULL;
+ g_autofree char *jobname = NULL;
+
+ jobname = g_strdup_printf("backup-%s-%s", disk->dst, disk->src->nodeformat);
+
+ if (!(job = qemuBlockJobDataNew(QEMU_BLOCKJOB_TYPE_BACKUP, jobname)))
+ return NULL;
+
+ job->data.backup.bitmap = g_strdup(bitmap);
+ job->data.backup.store = virObjectRef(store);
+ job->data.backup.deleteStore = deleteStore;
+
+ /* backup jobs are usually started in bulk by transaction so the caller
+ * shall save the status XML */
+ if (qemuBlockJobRegister(job, vm, disk, false) < 0)
+ return NULL;
+
+ return g_steal_pointer(&job);
+}
+
+
/**
* qemuBlockJobDiskGetJob:
* @disk: disk definition
diff --git a/src/qemu/qemu_blockjob.h b/src/qemu/qemu_blockjob.h
index 4734984c99..52b03aaf9e 100644
--- a/src/qemu/qemu_blockjob.h
+++ b/src/qemu/qemu_blockjob.h
@@ -107,6 +107,16 @@ struct _qemuBlockJobCopyData {
};
+typedef struct _qemuBlockJobBackupData qemuBlockJobBackupData;
+typedef qemuBlockJobBackupData *qemuBlockJobDataBackupPtr;
+
+struct _qemuBlockJobBackupData {
+ virStorageSourcePtr store;
+ bool deleteStore;
+ char *bitmap;
+};
+
+
typedef struct _qemuBlockJobData qemuBlockJobData;
typedef qemuBlockJobData *qemuBlockJobDataPtr;
@@ -124,6 +134,7 @@ struct _qemuBlockJobData {
qemuBlockJobCommitData commit;
qemuBlockJobCreateData create;
qemuBlockJobCopyData copy;
+ qemuBlockJobBackupData backup;
} data;
int type; /* qemuBlockJobType */
@@ -184,6 +195,13 @@ qemuBlockJobDiskNewCopy(virDomainObjPtr vm,
bool shallow,
bool reuse);
+qemuBlockJobDataPtr
+qemuBlockJobDiskNewBackup(virDomainObjPtr vm,
+ virDomainDiskDefPtr disk,
+ virStorageSourcePtr store,
+ bool deleteStore,
+ const char *bitmap);
+
qemuBlockJobDataPtr
qemuBlockJobDiskGetJob(virDomainDiskDefPtr disk)
ATTRIBUTE_NONNULL(1);
diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
index 1be6cfccff..873a397ac6 100644
--- a/src/qemu/qemu_domain.c
+++ b/src/qemu/qemu_domain.c
@@ -2608,6 +2608,18 @@ qemuDomainObjPrivateXMLFormatBlockjobIterator(void *payload,
break;
case QEMU_BLOCKJOB_TYPE_BACKUP:
+ virBufferEscapeString(&childBuf, "\n", job->data.backup.bitmap);
+ if (job->data.backup.store) {
+ if (qemuDomainObjPrivateXMLFormatBlockjobFormatSource(&childBuf,
+ "store",
+ job->data.backup.store,
+ data->xmlopt,
+ false) < 0)
+ return -1;
+
+ if (job->data.backup.deleteStore)
+ virBufferAddLit(&childBuf, "\n");
+ }
break;
case QEMU_BLOCKJOB_TYPE_BROKEN:
@@ -3202,6 +3214,15 @@ qemuDomainObjPrivateXMLParseBlockjobDataSpecific(qemuBlockJobDataPtr job,
break;
case QEMU_BLOCKJOB_TYPE_BACKUP:
+ job->data.backup.bitmap = virXPathString("string(./bitmap/@name)", ctxt);
+
+ if (!(tmp = virXPathNode("./store", ctxt)) ||
+ !(job->data.backup.store = qemuDomainObjPrivateXMLParseBlockjobChain(tmp, ctxt, xmlopt)))
+ goto broken;
+
+ if (virXPathNode("./deleteStore", ctxt))
+ job->data.backup.deleteStore = true;
+
break;
case QEMU_BLOCKJOB_TYPE_BROKEN: