RELEASE=4.2
# also update debian/changelog
-KVMVER=2.6.2
-KVMPKGREL=2
+KVMVER=2.7.0
+KVMPKGREL=1
KVMPACKAGE=pve-qemu-kvm
KVMDIR=qemu-kvm
+pve-qemu-kvm (2.7.0-1) unstable; urgency=medium
+
+ * update to qemu 2.7.0
+
+ -- Proxmox Support Team <support@proxmox.com> Fri, 09 Sep 2016 15:55:35 +0200
+
pve-qemu-kvm (2.6.2-2) unstable; urgency=medium
* fix CVE-2016-7466: memory leak in usb_xhci_exit
-From 3f89d6f1f3ba9b316e3f17c78c8c6f06a8f9c974 Mon Sep 17 00:00:00 2001
+From 603c472d61c354c30bc898b0e9ff1914302cbca9 Mon Sep 17 00:00:00 2001
From: Wolfgang Bumiller <w.bumiller@proxmox.com>
Date: Mon, 4 Jul 2016 15:02:26 +0200
-Subject: [PATCH] Revert "target-i386: disable LINT0 after reset"
+Subject: [PATCH 1/3] Revert "target-i386: disable LINT0 after reset"
This reverts commit b8eb5512fd8a115f164edbbe897cdf8884920ccb.
---
-see https://bugs.launchpad.net/qemu/+bug/1488363?comments=all
hw/intc/apic_common.c | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/hw/intc/apic_common.c b/hw/intc/apic_common.c
-index ad959c4..0264a88 100644
+index 14ac43c..1ed0511 100644
--- a/hw/intc/apic_common.c
+++ b/hw/intc/apic_common.c
-@@ -242,6 +242,15 @@ static void apic_reset_common(DeviceState *dev)
+@@ -246,6 +246,15 @@ static void apic_reset_common(DeviceState *dev)
info->vapic_base_update(s);
apic_init_reset(dev);
--- /dev/null
+From 1313d27fc347633d0cf6fc2ff8cbe17a740dd658 Mon Sep 17 00:00:00 2001
+From: Li Qiang <liqiang6-s@360.cn>
+Date: Thu, 11 Aug 2016 00:42:20 +0530
+Subject: [PATCH 2/3] net: vmxnet: initialise local tx descriptor
+
+In Vmxnet3 device emulator while processing transmit(tx) queue,
+when it reaches end of packet, it calls vmxnet3_complete_packet.
+In that local 'txcq_descr' object is not initialised, which could
+leak host memory bytes a guest.
+
+Reported-by: Li Qiang <liqiang6-s@360.cn>
+Signed-off-by: Prasad J Pandit <pjp@fedoraproject.org>
+---
+ hw/net/vmxnet3.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/hw/net/vmxnet3.c b/hw/net/vmxnet3.c
+index 90f6943..92f6af9 100644
+--- a/hw/net/vmxnet3.c
++++ b/hw/net/vmxnet3.c
+@@ -531,6 +531,7 @@ static void vmxnet3_complete_packet(VMXNET3State *s, int qidx, uint32_t tx_ridx)
+
+ VMXNET3_RING_DUMP(VMW_RIPRN, "TXC", qidx, &s->txq_descr[qidx].comp_ring);
+
++ memset(&txcq_descr, 0, sizeof(txcq_descr));
+ txcq_descr.txdIdx = tx_ridx;
+ txcq_descr.gen = vmxnet3_ring_curr_gen(&s->txq_descr[qidx].comp_ring);
+
+--
+2.1.4
+
+++ /dev/null
-From 89636d45af75c102a7b893d8cc34a51be76e88b5 Mon Sep 17 00:00:00 2001
-From: Li Qiang <liqiang6-s@360.cn>
-Date: Mon, 8 Aug 2016 18:08:31 +0530
-Subject: [PATCH 2/5] net: vmxnet3: check for device_active before write
-
-Vmxnet3 device emulator does not check if the device is active,
-before using it for write. It leads to a use after free issue,
-if the vmxnet3_io_bar0_write routine is called after the device is
-deactivated. Add check to avoid it.
-
-Reported-by: Li Qiang <liqiang6-s@360.cn>
-Signed-off-by: Prasad J Pandit <pjp@fedoraproject.org>
-Acked-by: Dmitry Fleytman <dmitry@daynix.com>
-Signed-off-by: Jason Wang <jasowang@redhat.com>
----
- hw/net/vmxnet3.c | 4 ++++
- 1 file changed, 4 insertions(+)
-
-diff --git a/hw/net/vmxnet3.c b/hw/net/vmxnet3.c
-index 20f26b7..a6ce16e 100644
---- a/hw/net/vmxnet3.c
-+++ b/hw/net/vmxnet3.c
-@@ -1158,6 +1158,10 @@ vmxnet3_io_bar0_write(void *opaque, hwaddr addr,
- {
- VMXNET3State *s = opaque;
-
-+ if (!s->device_active) {
-+ return;
-+ }
-+
- if (VMW_IS_MULTIREG_ADDR(addr, VMXNET3_REG_TXPROD,
- VMXNET3_DEVICE_MAX_TX_QUEUES, VMXNET3_REG_ALIGN)) {
- int tx_queue_idx =
---
-2.1.4
-
+++ /dev/null
-From 10cf6bf50d000a1b0dad1d5f2b931d1d1b1ff7f3 Mon Sep 17 00:00:00 2001
-From: Paolo Bonzini <pbonzini@redhat.com>
-Date: Mon, 20 Jun 2016 16:32:39 +0200
-Subject: [PATCH 2/2] scsi: esp: fix migration
-
-Commit 926cde5 ("scsi: esp: make cmdbuf big enough for maximum CDB size",
-2016-06-16) changed the size of a migrated field. Split it in two
-parts, and only migrate the second part in a new vmstate version.
-
-Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
----
- hw/scsi/esp.c | 5 +++--
- include/migration/vmstate.h | 5 ++++-
- 2 files changed, 7 insertions(+), 3 deletions(-)
-
-diff --git a/hw/scsi/esp.c b/hw/scsi/esp.c
-index e533522..8cff94b 100644
---- a/hw/scsi/esp.c
-+++ b/hw/scsi/esp.c
-@@ -573,7 +573,7 @@ static bool esp_mem_accepts(void *opaque, hwaddr addr,
-
- const VMStateDescription vmstate_esp = {
- .name ="esp",
-- .version_id = 3,
-+ .version_id = 4,
- .minimum_version_id = 3,
- .fields = (VMStateField[]) {
- VMSTATE_BUFFER(rregs, ESPState),
-@@ -584,7 +584,8 @@ const VMStateDescription vmstate_esp = {
- VMSTATE_BUFFER(ti_buf, ESPState),
- VMSTATE_UINT32(status, ESPState),
- VMSTATE_UINT32(dma, ESPState),
-- VMSTATE_BUFFER(cmdbuf, ESPState),
-+ VMSTATE_PARTIAL_BUFFER(cmdbuf, ESPState, 16),
-+ VMSTATE_BUFFER_START_MIDDLE_V(cmdbuf, ESPState, 16, 4),
- VMSTATE_UINT32(cmdlen, ESPState),
- VMSTATE_UINT32(do_cmd, ESPState),
- VMSTATE_UINT32(dma_left, ESPState),
-diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h
-index 84ee355..853a2bd 100644
---- a/include/migration/vmstate.h
-+++ b/include/migration/vmstate.h
-@@ -888,8 +888,11 @@ extern const VMStateInfo vmstate_info_bitmap;
- #define VMSTATE_PARTIAL_BUFFER(_f, _s, _size) \
- VMSTATE_STATIC_BUFFER(_f, _s, 0, NULL, 0, _size)
-
-+#define VMSTATE_BUFFER_START_MIDDLE_V(_f, _s, _start, _v) \
-+ VMSTATE_STATIC_BUFFER(_f, _s, _v, NULL, _start, sizeof(typeof_field(_s, _f)))
-+
- #define VMSTATE_BUFFER_START_MIDDLE(_f, _s, _start) \
-- VMSTATE_STATIC_BUFFER(_f, _s, 0, NULL, _start, sizeof(typeof_field(_s, _f)))
-+ VMSTATE_BUFFER_START_MIDDLE_V(_f, _s, _start, 0)
-
- #define VMSTATE_PARTIAL_VBUFFER(_f, _s, _size) \
- VMSTATE_VBUFFER(_f, _s, 0, NULL, 0, _size)
---
-2.1.4
-
--- /dev/null
+From 2705772316ff905f3ed08871c602fca1c636f332 Mon Sep 17 00:00:00 2001
+From: Peter Lieven <pl@kamp.de>
+Date: Thu, 30 Jun 2016 11:49:40 +0200
+Subject: [PATCH 3/3] net: limit allocation in nc_sendv_compat
+
+we only need to allocate enough memory to hold the packet. This might be
+less than NET_BUFSIZE. Additionally fail early if the packet is larger
+than NET_BUFSIZE.
+
+Signed-off-by: Peter Lieven <pl@kamp.de>
+---
+ net/net.c | 8 ++++++--
+ 1 file changed, 6 insertions(+), 2 deletions(-)
+
+diff --git a/net/net.c b/net/net.c
+index c94d93d..2ac46a6 100644
+--- a/net/net.c
++++ b/net/net.c
+@@ -690,9 +690,13 @@ static ssize_t nc_sendv_compat(NetClientState *nc, const struct iovec *iov,
+ buffer = iov[0].iov_base;
+ offset = iov[0].iov_len;
+ } else {
+- buf = g_new(uint8_t, NET_BUFSIZE);
++ offset = iov_size(iov, iovcnt);
++ if (offset > NET_BUFSIZE) {
++ return -1;
++ }
++ buf = g_malloc(offset);
+ buffer = buf;
+- offset = iov_to_buf(iov, iovcnt, 0, buf, NET_BUFSIZE);
++ offset = iov_to_buf(iov, iovcnt, 0, buf, offset);
+ }
+
+ if (flags & QEMU_NET_PACKET_FLAG_RAW && nc->info->receive_raw) {
+--
+2.1.4
+
+++ /dev/null
-From 4fa993ee1f127eee2862f1779565aea0b760647a Mon Sep 17 00:00:00 2001
-From: Li Qiang <liqiang6-s@360.cn>
-Date: Thu, 11 Aug 2016 00:42:20 +0530
-Subject: [PATCH 5/5] net: vmxnet: initialise local tx descriptor
-
-In Vmxnet3 device emulator while processing transmit(tx) queue,
-when it reaches end of packet, it calls vmxnet3_complete_packet.
-In that local 'txcq_descr' object is not initialised, which could
-leak host memory bytes a guest.
-
-Reported-by: Li Qiang <liqiang6-s@360.cn>
-Signed-off-by: Prasad J Pandit <pjp@fedoraproject.org>
----
- hw/net/vmxnet3.c | 1 +
- 1 file changed, 1 insertion(+)
-
-diff --git a/hw/net/vmxnet3.c b/hw/net/vmxnet3.c
-index a6ce16e..360290d 100644
---- a/hw/net/vmxnet3.c
-+++ b/hw/net/vmxnet3.c
-@@ -529,6 +529,7 @@ static void vmxnet3_complete_packet(VMXNET3State *s, int qidx, uint32_t tx_ridx)
-
- VMXNET3_RING_DUMP(VMW_RIPRN, "TXC", qidx, &s->txq_descr[qidx].comp_ring);
-
-+ memset(&txcq_descr, 0, sizeof(txcq_descr));
- txcq_descr.txdIdx = tx_ridx;
- txcq_descr.gen = vmxnet3_ring_curr_gen(&s->txq_descr[qidx].comp_ring);
-
---
-2.1.4
-
+++ /dev/null
-From 1419429813905c95e2648516a8a23ad43e2c7297 Mon Sep 17 00:00:00 2001
-From: Peter Lieven <pl@kamp.de>
-Date: Thu, 30 Jun 2016 11:49:40 +0200
-Subject: [PATCH 09/10] net: limit allocation in nc_sendv_compat
-
-we only need to allocate enough memory to hold the packet. This might be
-less than NET_BUFSIZE. Additionally fail early if the packet is larger
-than NET_BUFSIZE.
-
-Signed-off-by: Peter Lieven <pl@kamp.de>
----
- net/net.c | 8 ++++++--
- 1 file changed, 6 insertions(+), 2 deletions(-)
-
-diff --git a/net/net.c b/net/net.c
-index 2b9de86..af36a2a 100644
---- a/net/net.c
-+++ b/net/net.c
-@@ -692,9 +692,13 @@ static ssize_t nc_sendv_compat(NetClientState *nc, const struct iovec *iov,
- buffer = iov[0].iov_base;
- offset = iov[0].iov_len;
- } else {
-- buf = g_new(uint8_t, NET_BUFSIZE);
-+ offset = iov_size(iov, iovcnt);
-+ if (offset > NET_BUFSIZE) {
-+ return -1;
-+ }
-+ buf = g_malloc(offset);
- buffer = buf;
-- offset = iov_to_buf(iov, iovcnt, 0, buf, NET_BUFSIZE);
-+ offset = iov_to_buf(iov, iovcnt, 0, buf, offset);
- }
-
- if (flags & QEMU_NET_PACKET_FLAG_RAW && nc->info->receive_raw) {
---
-2.1.4
-
+++ /dev/null
-From 3f8bf5846151f173361966cb4869ab5a1306ad37 Mon Sep 17 00:00:00 2001
-From: Prasad J Pandit <pjp@fedoraproject.org>
-Date: Wed, 27 Jul 2016 21:07:56 +0530
-Subject: [PATCH] virtio: check vring descriptor buffer length
-
-virtio back end uses set of buffers to facilitate I/O operations.
-An infinite loop unfolds in virtqueue_pop() if a buffer was
-of zero size. Add check to avoid it.
-
-Reported-by: Li Qiang <liqiang6-s@360.cn>
-Signed-off-by: Prasad J Pandit <pjp@fedoraproject.org>
-Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
-Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
-Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
----
- hw/virtio/virtio.c | 5 +++++
- 1 file changed, 5 insertions(+)
-
-diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c
-index 30ede3d..8de896c 100644
---- a/hw/virtio/virtio.c
-+++ b/hw/virtio/virtio.c
-@@ -457,6 +457,11 @@ static void virtqueue_map_desc(unsigned int *p_num_sg, hwaddr *addr, struct iove
- unsigned num_sg = *p_num_sg;
- assert(num_sg <= max_num_sg);
-
-+ if (!sz) {
-+ error_report("virtio: zero sized buffers are not allowed");
-+ exit(1);
-+ }
-+
- while (sz) {
- hwaddr len = sz;
-
---
-2.1.4
-
--- /dev/null
+From 7c7e45d2bd1a77fbe89dff83fabf89a46479111f Mon Sep 17 00:00:00 2001
+From: Prasad J Pandit <pjp@fedoraproject.org>
+Date: Thu, 1 Sep 2016 16:30:51 +0530
+Subject: [PATCH 4/6] scsi: check page count while initialising descriptor
+ rings
+
+Vmware Paravirtual SCSI emulation uses command descriptors to
+process SCSI commands. These descriptors come with their ring
+buffers. A guest could set the page count for these rings to
+an arbitrary value, leading to infinite loop or OOB access.
+Add check to avoid it.
+
+Reported-by: Tom Victor <vv474172261@gmail.com>
+Reported-by: Li Qiang <liqiang6-s@360.cn>
+Signed-off-by: Prasad J Pandit <pjp@fedoraproject.org>
+---
+ hw/scsi/vmw_pvscsi.c | 19 +++++++++----------
+ 1 file changed, 9 insertions(+), 10 deletions(-)
+
+diff --git a/hw/scsi/vmw_pvscsi.c b/hw/scsi/vmw_pvscsi.c
+index 2d7528d..22f872c 100644
+--- a/hw/scsi/vmw_pvscsi.c
++++ b/hw/scsi/vmw_pvscsi.c
+@@ -153,7 +153,7 @@ pvscsi_log2(uint32_t input)
+ return log;
+ }
+
+-static int
++static void
+ pvscsi_ring_init_data(PVSCSIRingInfo *m, PVSCSICmdDescSetupRings *ri)
+ {
+ int i;
+@@ -161,10 +161,6 @@ pvscsi_ring_init_data(PVSCSIRingInfo *m, PVSCSICmdDescSetupRings *ri)
+ uint32_t req_ring_size, cmp_ring_size;
+ m->rs_pa = ri->ringsStatePPN << VMW_PAGE_SHIFT;
+
+- if ((ri->reqRingNumPages > PVSCSI_SETUP_RINGS_MAX_NUM_PAGES)
+- || (ri->cmpRingNumPages > PVSCSI_SETUP_RINGS_MAX_NUM_PAGES)) {
+- return -1;
+- }
+ req_ring_size = ri->reqRingNumPages * PVSCSI_MAX_NUM_REQ_ENTRIES_PER_PAGE;
+ cmp_ring_size = ri->cmpRingNumPages * PVSCSI_MAX_NUM_CMP_ENTRIES_PER_PAGE;
+ txr_len_log2 = pvscsi_log2(req_ring_size - 1);
+@@ -196,8 +192,6 @@ pvscsi_ring_init_data(PVSCSIRingInfo *m, PVSCSICmdDescSetupRings *ri)
+
+ /* Flush ring state page changes */
+ smp_wmb();
+-
+- return 0;
+ }
+
+ static int
+@@ -747,7 +741,7 @@ pvscsi_dbg_dump_tx_rings_config(PVSCSICmdDescSetupRings *rc)
+
+ trace_pvscsi_tx_rings_num_pages("Confirm Ring", rc->cmpRingNumPages);
+ for (i = 0; i < rc->cmpRingNumPages; i++) {
+- trace_pvscsi_tx_rings_ppn("Confirm Ring", rc->reqRingPPNs[i]);
++ trace_pvscsi_tx_rings_ppn("Confirm Ring", rc->cmpRingPPNs[i]);
+ }
+ }
+
+@@ -780,11 +774,16 @@ pvscsi_on_cmd_setup_rings(PVSCSIState *s)
+
+ trace_pvscsi_on_cmd_arrived("PVSCSI_CMD_SETUP_RINGS");
+
+- pvscsi_dbg_dump_tx_rings_config(rc);
+- if (pvscsi_ring_init_data(&s->rings, rc) < 0) {
++ if (!rc->reqRingNumPages
++ || rc->reqRingNumPages > PVSCSI_SETUP_RINGS_MAX_NUM_PAGES
++ || !rc->cmpRingNumPages
++ || rc->cmpRingNumPages > PVSCSI_SETUP_RINGS_MAX_NUM_PAGES) {
+ return PVSCSI_COMMAND_PROCESSING_FAILED;
+ }
+
++ pvscsi_dbg_dump_tx_rings_config(rc);
++ pvscsi_ring_init_data(&s->rings, rc);
++
+ s->rings_info_valid = TRUE;
+ return PVSCSI_COMMAND_PROCESSING_SUCCEEDED;
+ }
+--
+2.1.4
+
--- /dev/null
+From a0e2a28c08472ad3468c2f75d20fe1ca02ef8c0b Mon Sep 17 00:00:00 2001
+From: Prasad J Pandit <pjp@fedoraproject.org>
+Date: Wed, 31 Aug 2016 17:36:07 +0530
+Subject: [PATCH 6/6] scsi: mptconfig: fix an assert expression
+
+When LSI SAS1068 Host Bus emulator builds configuration page
+headers, mptsas_config_pack() should assert that the size
+fits in a byte. However, the size is expressed in 32-bit
+units, so up to 1020 bytes fit. The assertion was only
+allowing replies up to 252 bytes, so fix it.
+
+Suggested-by: Paolo Bonzini <pbonzini@redhat.com>
+Signed-off-by: Prasad J Pandit <pjp@fedoraproject.org>
+Message-Id: <1472645167-30765-2-git-send-email-ppandit@redhat.com>
+Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
+---
+ hw/scsi/mptconfig.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/hw/scsi/mptconfig.c b/hw/scsi/mptconfig.c
+index 7071854..3e4f400 100644
+--- a/hw/scsi/mptconfig.c
++++ b/hw/scsi/mptconfig.c
+@@ -158,7 +158,7 @@ static size_t mptsas_config_pack(uint8_t **data, const char *fmt, ...)
+ va_end(ap);
+
+ if (data) {
+- assert(ret < 256 && (ret % 4) == 0);
++ assert(ret / 4 < 256 && (ret % 4) == 0);
+ stb_p(*data + 1, ret / 4);
+ }
+ return ret;
+--
+2.1.4
+
--- /dev/null
+From d251157ac1928191af851d199a9ff255d330bec9 Mon Sep 17 00:00:00 2001
+From: Prasad J Pandit <pjp@fedoraproject.org>
+Date: Wed, 14 Sep 2016 15:09:12 +0530
+Subject: [PATCH] scsi: pvscsi: limit process IO loop to ring size
+
+Vmware Paravirtual SCSI emulator while processing IO requests
+could run into an infinite loop if 'pvscsi_ring_pop_req_descr'
+always returned positive value. Limit IO loop to the ring size.
+
+Cc: qemu-stable@nongnu.org
+Reported-by: Li Qiang <liqiang6-s@360.cn>
+Signed-off-by: Prasad J Pandit <pjp@fedoraproject.org>
+Message-Id: <1473845952-30785-1-git-send-email-ppandit@redhat.com>
+Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
+---
+ hw/scsi/vmw_pvscsi.c | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+diff --git a/hw/scsi/vmw_pvscsi.c b/hw/scsi/vmw_pvscsi.c
+index babac5a..a5ce7de 100644
+--- a/hw/scsi/vmw_pvscsi.c
++++ b/hw/scsi/vmw_pvscsi.c
+@@ -247,8 +247,11 @@ static hwaddr
+ pvscsi_ring_pop_req_descr(PVSCSIRingInfo *mgr)
+ {
+ uint32_t ready_ptr = RS_GET_FIELD(mgr, reqProdIdx);
++ uint32_t ring_size = PVSCSI_MAX_NUM_PAGES_REQ_RING
++ * PVSCSI_MAX_NUM_REQ_ENTRIES_PER_PAGE;
+
+- if (ready_ptr != mgr->consumed_ptr) {
++ if (ready_ptr != mgr->consumed_ptr
++ && ready_ptr - mgr->consumed_ptr < ring_size) {
+ uint32_t next_ready_ptr =
+ mgr->consumed_ptr++ & mgr->txr_len_mask;
+ uint32_t next_ready_page =
+--
+2.1.4
+
-From 77b365c29e9bf143ee4c024daa9f6f0a13213376 Mon Sep 17 00:00:00 2001
+From 1723b5e7962eb077353bab0772ca8114774b6c60 Mon Sep 17 00:00:00 2001
From: Prasad J Pandit <pjp@fedoraproject.org>
Date: Mon, 19 Sep 2016 23:55:45 +0530
-Subject: [PATCH 1/5] virtio: add check for descriptor's mapped address
+Subject: [PATCH 4/7] virtio: add check for descriptor's mapped address
virtio back end uses set of buffers to facilitate I/O operations.
If its size is too large, 'cpu_physical_memory_map' could return
1 file changed, 5 insertions(+)
diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c
-index 111ad8e..75f5ada 100644
+index 74c085c..eabe573 100644
--- a/hw/virtio/virtio.c
+++ b/hw/virtio/virtio.c
-@@ -471,6 +471,11 @@ static void virtqueue_map_desc(unsigned int *p_num_sg, hwaddr *addr, struct iove
+@@ -473,6 +473,11 @@ static void virtqueue_map_desc(unsigned int *p_num_sg, hwaddr *addr, struct iove
}
iov[num_sg].iov_base = cpu_physical_memory_map(pa, &len, is_write);
--- /dev/null
+From 670e56d3ed2918b3861d9216f2c0540d9e9ae0d5 Mon Sep 17 00:00:00 2001
+From: Li Qiang <liqiang6-s@360.cn>
+Date: Mon, 12 Sep 2016 18:14:11 +0530
+Subject: [PATCH] scsi: mptsas: use g_new0 to allocate MPTSASRequest object
+
+When processing IO request in mptsas, it uses g_new to allocate
+a 'req' object. If an error occurs before 'req->sreq' is
+allocated, It could lead to an OOB write in mptsas_free_request
+function. Use g_new0 to avoid it.
+
+Reported-by: Li Qiang <liqiang6-s@360.cn>
+Signed-off-by: Prasad J Pandit <pjp@fedoraproject.org>
+Message-Id: <1473684251-17476-1-git-send-email-ppandit@redhat.com>
+Cc: qemu-stable@nongnu.org
+Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
+---
+ hw/scsi/mptsas.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/hw/scsi/mptsas.c b/hw/scsi/mptsas.c
+index 0e0a22f..eaae1bb 100644
+--- a/hw/scsi/mptsas.c
++++ b/hw/scsi/mptsas.c
+@@ -304,7 +304,7 @@ static int mptsas_process_scsi_io_request(MPTSASState *s,
+ goto bad;
+ }
+
+- req = g_new(MPTSASRequest, 1);
++ req = g_new0(MPTSASRequest, 1);
+ QTAILQ_INSERT_TAIL(&s->pending, req, next);
+ req->scsi_io = *scsi_io;
+ req->dev = s;
+--
+2.1.4
+
-From 53102ff7c9c928e2c778a6440f7039ee29dc5acf Mon Sep 17 00:00:00 2001
+From 3798522afcf58abbce6de67446fcae7a34ae919d Mon Sep 17 00:00:00 2001
From: Prasad J Pandit <pjp@fedoraproject.org>
Date: Thu, 22 Sep 2016 16:01:38 +0530
-Subject: [PATCH 3/5] net: imx: limit buffer descriptor count
+Subject: [PATCH 5/7] net: imx: limit buffer descriptor count
i.MX Fast Ethernet Controller uses buffer descriptors to manage
data flow to/fro receive & transmit queues. While transmitting
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/hw/net/imx_fec.c b/hw/net/imx_fec.c
-index e60e338..547fa99 100644
+index 1c415ab..1d74827 100644
--- a/hw/net/imx_fec.c
+++ b/hw/net/imx_fec.c
-@@ -94,6 +94,8 @@ static const VMStateDescription vmstate_imx_fec = {
+@@ -220,6 +220,8 @@ static const VMStateDescription vmstate_imx_eth = {
#define PHY_INT_PARFAULT (1 << 2)
#define PHY_INT_AUTONEG_PAGE (1 << 1)
+#define IMX_MAX_DESC 1024
+
- static void imx_fec_update(IMXFECState *s);
+ static void imx_eth_update(IMXFECState *s);
/*
-@@ -264,12 +266,12 @@ static void imx_fec_update(IMXFECState *s)
+@@ -402,12 +404,12 @@ static void imx_eth_update(IMXFECState *s)
static void imx_fec_do_tx(IMXFECState *s)
{
- int frame_size = 0;
+ int frame_size = 0, descnt = 0;
- uint8_t frame[FEC_MAX_FRAME_SIZE];
+ uint8_t frame[ENET_MAX_FRAME_SIZE];
uint8_t *ptr = frame;
uint32_t addr = s->tx_descriptor;
-From 50e74d1c748bde8d667e452d4d7cac3d8f869520 Mon Sep 17 00:00:00 2001
+From 94087c0cbe014b4a60d96930d7cb43d54a05c701 Mon Sep 17 00:00:00 2001
From: Prasad J Pandit <pjp@fedoraproject.org>
Date: Thu, 22 Sep 2016 16:02:37 +0530
-Subject: [PATCH 4/5] net: mcf: limit buffer descriptor count
+Subject: [PATCH 6/7] net: mcf: limit buffer descriptor count
ColdFire Fast Ethernet Controller uses buffer descriptors to manage
data flow to/fro receive & transmit queues. While transmitting
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/hw/net/mcf_fec.c b/hw/net/mcf_fec.c
-index 7c0398e..6d3418e 100644
+index 0ee8ad9..d31fea1 100644
--- a/hw/net/mcf_fec.c
+++ b/hw/net/mcf_fec.c
@@ -23,6 +23,7 @@ do { printf("mcf_fec: " fmt , ## __VA_ARGS__); } while (0)
-From 9d315a91caeb359045b2c730294bba3889911127 Mon Sep 17 00:00:00 2001
+From ed825b783750cbe88aa67bbe83cf662082828efa Mon Sep 17 00:00:00 2001
From: Prasad J Pandit <pjp@fedoraproject.org>
Date: Fri, 30 Sep 2016 00:27:33 +0530
-Subject: [PATCH 5/5] net: pcnet: check rx/tx descriptor ring length
+Subject: [PATCH 7/7] net: pcnet: check rx/tx descriptor ring length
The AMD PC-Net II emulator has set of control and status(CSR)
registers. Of these, CSR76 and CSR78 hold receive and transmit
--- /dev/null
+From 594fa98211f92ab07ee6d6b6a9eda93a416a1f57 Mon Sep 17 00:00:00 2001
+From: Li Qiang <liqiang6-s@360.cn>
+Date: Sun, 18 Sep 2016 19:07:11 -0700
+Subject: [PATCH 1/2] virtio-gpu: fix memory leak in
+ virtio_gpu_resource_create_2d
+
+In virtio gpu resource create dispatch, if the pixman format is zero
+it doesn't free the resource object allocated previously. Thus leading
+a host memory leak issue. This patch avoid this.
+
+Signed-off-by: Li Qiang <liqiang6-s@360.cn>
+---
+ hw/display/virtio-gpu.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c
+index 7fe6ed8..5b6d17b 100644
+--- a/hw/display/virtio-gpu.c
++++ b/hw/display/virtio-gpu.c
+@@ -333,6 +333,7 @@ static void virtio_gpu_resource_create_2d(VirtIOGPU *g,
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: host couldn't handle guest format %d\n",
+ __func__, c2d.format);
++ g_free(res);
+ cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER;
+ return;
+ }
+--
+2.1.4
+
--- /dev/null
+From 91a16e6e51a4e046d59379fc83b9dfc1e860e9c7 Mon Sep 17 00:00:00 2001
+From: Li Qiang <liqiang6-s@360.cn>
+Date: Sat, 8 Oct 2016 11:58:03 +0300
+Subject: [PATCH 2/2] usb: ehci: fix memory leak in ehci_process_itd
+
+While processing isochronous transfer descriptors(iTD), if the page
+select(PG) field value is out of bands it will return. In this
+situation the ehci's sg list is not freed thus leading to a memory
+leak issue. This patch avoid this.
+
+Signed-off-by: Li Qiang <liqiang6-s@360.cn>
+Reviewed-by: Thomas Huth <thuth@redhat.com>
+Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
+---
+ hw/usb/hcd-ehci.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c
+index b093db7..f4ece9a 100644
+--- a/hw/usb/hcd-ehci.c
++++ b/hw/usb/hcd-ehci.c
+@@ -1426,6 +1426,7 @@ static int ehci_process_itd(EHCIState *ehci,
+ if (off + len > 4096) {
+ /* transfer crosses page border */
+ if (pg == 6) {
++ qemu_sglist_destroy(&ehci->isgl);
+ return -1; /* avoid page pg + 1 */
+ }
+ ptr2 = (itd->bufptr[pg + 1] & ITD_BUFPTR_MASK);
+--
+2.1.4
+
-From 0dc734b8304318e7a4f9a0ca756f72fc7d0f543e Mon Sep 17 00:00:00 2001
+From 9e0dc08fa276f0f758d9c5fe1e47ef96ee59b069 Mon Sep 17 00:00:00 2001
From: Wolfgang Bumiller <w.bumiller@proxmox.com>
Date: Wed, 9 Dec 2015 14:15:49 +0100
-Subject: [PATCH 01/55] fr-ca keymap corrections
+Subject: [PATCH 01/41] fr-ca keymap corrections
---
pc-bios/keymaps/fr-ca | 9 +++++++++
-From 896141c8e486d45be140e53b395f81866b7af776 Mon Sep 17 00:00:00 2001
+From 3f71a11efda66b9145afa0015af1d5a2bc5e5bb2 Mon Sep 17 00:00:00 2001
From: Wolfgang Bumiller <w.bumiller@proxmox.com>
Date: Wed, 9 Dec 2015 14:16:49 +0100
-Subject: [PATCH 02/55] Adjust network script path to /etc/kvm/
+Subject: [PATCH 02/41] Adjust network script path to /etc/kvm/
---
include/net/net.h | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/include/net/net.h b/include/net/net.h
-index 73e4c46..4267008 100644
+index e8d9e9e..375e81d 100644
--- a/include/net/net.h
+++ b/include/net/net.h
-@@ -198,8 +198,9 @@ void qmp_netdev_add(QDict *qdict, QObject **ret, Error **errp);
+@@ -216,8 +216,9 @@ void qmp_netdev_add(QDict *qdict, QObject **ret, Error **errp);
int net_hub_id_for_client(NetClientState *nc, int *id);
NetClientState *net_hub_port_find(int hub_id);
-From 4d774ad7b71342c5e3ba2f1f5ac9d051eb835270 Mon Sep 17 00:00:00 2001
+From 4429be6a1a4ba5320f4ce61ac8f874bfc4ec196e Mon Sep 17 00:00:00 2001
From: Wolfgang Bumiller <w.bumiller@proxmox.com>
Date: Wed, 9 Dec 2015 14:17:38 +0100
-Subject: [PATCH 03/55] vnc: altgr emulation
+Subject: [PATCH 03/41] vnc: altgr emulation
---
- ui/vnc.c | 24 +++++++++++++++++++++++-
- 1 file changed, 23 insertions(+), 1 deletion(-)
+ ui/vnc.c | 26 +++++++++++++++++++++++++-
+ 1 file changed, 25 insertions(+), 1 deletion(-)
diff --git a/ui/vnc.c b/ui/vnc.c
-index 3e89dad..88a5824 100644
+index d1087c9..8d7d435 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
-@@ -1711,6 +1711,10 @@ static void kbd_leds(void *opaque, int ledstate)
+@@ -1729,6 +1729,10 @@ static void kbd_leds(void *opaque, int ledstate)
static void do_key_event(VncState *vs, int down, int keycode, int sym)
{
/* QEMU console switch */
switch(keycode) {
case 0x2a: /* Left Shift */
-@@ -1791,7 +1795,24 @@ static void do_key_event(VncState *vs, int down, int keycode, int sym)
+@@ -1809,8 +1813,27 @@ static void do_key_event(VncState *vs, int down, int keycode, int sym)
}
if (qemu_console_is_graphic(NULL)) {
+ if (emul_altgr) {
+ reset_keys(vs);
+ qemu_input_event_send_key_number(vs->vd->dcl.con, 0xb8, true);
++ qemu_input_event_send_key_delay(vs->vd->key_delay_ms);
+ }
+
qemu_input_event_send_key_number(vs->vd->dcl.con, keycode, down);
+ qemu_input_event_send_key_delay(vs->vd->key_delay_ms);
+
+ if (emul_altgr) {
+ qemu_input_event_send_key_number(vs->vd->dcl.con, 0xb8, false);
++ qemu_input_event_send_key_delay(vs->vd->key_delay_ms);
+ }
+
} else {
bool numlock = vs->modifiers_state[0x45];
bool control = (vs->modifiers_state[0x1d] ||
-@@ -1930,7 +1951,8 @@ static void key_event(VncState *vs, int down, uint32_t sym)
+@@ -1950,7 +1973,8 @@ static void key_event(VncState *vs, int down, uint32_t sym)
lsym = lsym - 'A' + 'a';
}
-From bf9ce905d69ec3ac335d69a5ac578a52a96da204 Mon Sep 17 00:00:00 2001
+From 060fc3faed82c4f1bfe7fea3bea8b7bd39f4de81 Mon Sep 17 00:00:00 2001
From: Wolfgang Bumiller <w.bumiller@proxmox.com>
Date: Wed, 9 Dec 2015 14:18:46 +0100
-Subject: [PATCH 04/55] qemu-img: return success on info without snapshots
+Subject: [PATCH 04/41] qemu-img: return success on info without snapshots
---
qemu-img.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/qemu-img.c b/qemu-img.c
-index 46f2a6d..418e2d6 100644
+index f204d041..99be68f 100644
--- a/qemu-img.c
+++ b/qemu-img.c
-@@ -2361,7 +2361,8 @@ static int img_info(int argc, char **argv)
+@@ -2389,7 +2389,8 @@ static int img_info(int argc, char **argv)
list = collect_image_info_list(image_opts, filename, fmt, chain);
if (!list) {
-From c727dd8049e617a814e6b537648e0145190084e2 Mon Sep 17 00:00:00 2001
+From ebe8b0bb12b1725e9a56e5e65fe5b295d5ac90aa Mon Sep 17 00:00:00 2001
From: Wolfgang Bumiller <w.bumiller@proxmox.com>
Date: Wed, 9 Dec 2015 14:27:05 +0100
-Subject: [PATCH 05/55] use kvm by default
+Subject: [PATCH 05/41] use kvm by default
---
accel.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/accel.c b/accel.c
-index 0510b90..afe9a0d 100644
+index 403eb5e..dd2ebea 100644
--- a/accel.c
+++ b/accel.c
-@@ -88,8 +88,8 @@ int configure_accelerator(MachineState *ms)
+@@ -88,8 +88,8 @@ void configure_accelerator(MachineState *ms)
p = qemu_opt_get(qemu_get_machine_opts(), "accel");
if (p == NULL) {
-From 08cbc3ac32dfefc5557bf62ea230a10ad46083b1 Mon Sep 17 00:00:00 2001
+From ff496e015b9b53c2d49a0110d247223872d5a582 Mon Sep 17 00:00:00 2001
From: Wolfgang Bumiller <w.bumiller@proxmox.com>
Date: Wed, 9 Dec 2015 14:27:49 +0100
-Subject: [PATCH 06/55] virtio-balloon: fix query
+Subject: [PATCH 06/41] virtio-balloon: fix query
Actually provide memory information via the query-balloon
command.
4 files changed, 94 insertions(+), 5 deletions(-)
diff --git a/hmp.c b/hmp.c
-index d510236..fe80757 100644
+index cc2056e..aa1395d 100644
--- a/hmp.c
+++ b/hmp.c
-@@ -686,7 +686,35 @@ void hmp_info_balloon(Monitor *mon, const QDict *qdict)
+@@ -704,7 +704,35 @@ void hmp_info_balloon(Monitor *mon, const QDict *qdict)
return;
}
qapi_free_BalloonInfo(info);
}
diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c
-index 9dbe681..50b20f1 100644
+index 5af429a..d87b971 100644
--- a/hw/virtio/virtio-balloon.c
+++ b/hw/virtio/virtio-balloon.c
-@@ -379,8 +379,37 @@ static uint64_t virtio_balloon_get_features(VirtIODevice *vdev, uint64_t f,
+@@ -376,8 +376,37 @@ static uint64_t virtio_balloon_get_features(VirtIODevice *vdev, uint64_t f,
static void virtio_balloon_stat(void *opaque, BalloonInfo *info)
{
VirtIOBalloon *dev = opaque;
static void virtio_balloon_to_target(void *opaque, ram_addr_t target)
diff --git a/qapi-schema.json b/qapi-schema.json
-index 54634c4..639f7b6 100644
+index 5658723..4bf7222 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
-@@ -1217,10 +1217,29 @@
+@@ -1278,10 +1278,29 @@
#
# @actual: the number of bytes the balloon currently contains
#
##
# @query-balloon:
diff --git a/qmp-commands.hx b/qmp-commands.hx
-index de896a5..9388578 100644
+index 6866264..6de28d4 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
-@@ -3815,6 +3815,13 @@ Make an asynchronous request for balloon info. When the request completes a
+@@ -3854,6 +3854,13 @@ Make an asynchronous request for balloon info. When the request completes a
json-object will be returned containing the following data:
- "actual": current balloon value in bytes (json-int)
Example:
-@@ -3822,6 +3829,12 @@ Example:
+@@ -3861,6 +3868,12 @@ Example:
<- {
"return":{
"actual":1073741824,
-From cfeb66f21383c31c70e69165c80e65c1901e7fa1 Mon Sep 17 00:00:00 2001
+From 058dbe22c0c172032264e25faa45278569a326ff Mon Sep 17 00:00:00 2001
From: Wolfgang Bumiller <w.bumiller@proxmox.com>
Date: Wed, 9 Dec 2015 14:30:21 +0100
-Subject: [PATCH 07/55] set the CPU model to kvm64/32 instead of qemu64/32
+Subject: [PATCH 07/41] set the CPU model to kvm64/32 instead of qemu64/32
---
hw/i386/pc.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/hw/i386/pc.c b/hw/i386/pc.c
-index 99437e0..1225a99 100644
+index 022dd1b..ba8a5a1 100644
--- a/hw/i386/pc.c
+++ b/hw/i386/pc.c
-@@ -1111,9 +1111,9 @@ void pc_cpus_init(PCMachineState *pcms)
+@@ -1160,9 +1160,9 @@ void pc_cpus_init(PCMachineState *pcms)
/* init CPUs */
if (machine->cpu_model == NULL) {
#ifdef TARGET_X86_64
-From 7ec55edfacec8698e6fde6e8be0163e57526be2b Mon Sep 17 00:00:00 2001
+From a9503229ca99454a94045c1bf0cd285228ed2863 Mon Sep 17 00:00:00 2001
From: Wolfgang Bumiller <w.bumiller@proxmox.com>
Date: Wed, 9 Dec 2015 14:31:18 +0100
-Subject: [PATCH 08/55] qapi: modify query machines
+Subject: [PATCH 08/41] qapi: modify query machines
provide '*is-current' in MachineInfo struct
---
2 files changed, 8 insertions(+), 1 deletion(-)
diff --git a/qapi-schema.json b/qapi-schema.json
-index 639f7b6..0e97da1 100644
+index 4bf7222..63507f5 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
-@@ -2941,6 +2941,8 @@
+@@ -3027,6 +3027,8 @@
#
# @default: #optional whether the machine is default
#
# @cpu-max: maximum number of CPUs supported by the machine type
# (since 1.5.0)
#
-@@ -2948,7 +2950,7 @@
+@@ -3036,7 +3038,7 @@
##
{ 'struct': 'MachineInfo',
'data': { 'name': 'str', '*alias': 'str',
-- '*is-default': 'bool', 'cpu-max': 'int' } }
-+ '*is-default': 'bool', '*is-current': 'bool', 'cpu-max': 'int' } }
+- '*is-default': 'bool', 'cpu-max': 'int',
++ '*is-default': 'bool', '*is-current': 'bool', 'cpu-max': 'int',
+ 'hotpluggable-cpus': 'bool'} }
##
- # @query-machines:
diff --git a/vl.c b/vl.c
-index 5db5dc2..feeb2c4 100644
+index b3c80d5..bdf4fdc 100644
--- a/vl.c
+++ b/vl.c
-@@ -1538,6 +1538,11 @@ MachineInfoList *qmp_query_machines(Error **errp)
- info->name = g_strdup(mc->name);
+@@ -1508,6 +1508,11 @@ MachineInfoList *qmp_query_machines(Error **errp)
info->cpu_max = !mc->max_cpus ? 1 : mc->max_cpus;
+ info->hotpluggable_cpus = !!mc->query_hotpluggable_cpus;
+ if (strcmp(mc->name, MACHINE_GET_CLASS(current_machine)->name) == 0) {
+ info->has_is_current = true;
-From 63b3b17a2ae5d373f49191bb0828b5dc9128b04b Mon Sep 17 00:00:00 2001
+From 42cf33b03e7f20cd9fe3f7cefbc3cc39f7a7cdeb Mon Sep 17 00:00:00 2001
From: Wolfgang Bumiller <w.bumiller@proxmox.com>
Date: Wed, 9 Dec 2015 14:32:11 +0100
-Subject: [PATCH 09/55] qapi: modify spice query
+Subject: [PATCH 09/41] qapi: modify spice query
Provide the last ticket in the SpiceInfo struct optionally.
---
2 files changed, 8 insertions(+)
diff --git a/qapi-schema.json b/qapi-schema.json
-index 0e97da1..f9ccfe4 100644
+index 63507f5..518c2ea 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
-@@ -1192,11 +1192,14 @@
+@@ -1253,11 +1253,14 @@
#
# @channels: a list of @SpiceChannel for each active spice channel
#
-From 46cd19688c9134e5fa0eef9d31d005430bdae29a Mon Sep 17 00:00:00 2001
+From bc64f439e741d3d5d615fb9314f0e4b471eaeba1 Mon Sep 17 00:00:00 2001
From: Wolfgang Bumiller <w.bumiller@proxmox.com>
Date: Wed, 9 Dec 2015 14:33:34 +0100
-Subject: [PATCH 10/55] ui/spice: default to pve certs unless otherwise
+Subject: [PATCH 10/41] ui/spice: default to pve certs unless otherwise
specified
---
-From 1ef80c8a5ce360bbff1a9daa1c191e9238855408 Mon Sep 17 00:00:00 2001
+From 9cb2f2d61ebf1c28b19bc2df78879431b735f460 Mon Sep 17 00:00:00 2001
From: Dietmar Maurer <dietmar@proxmox.com>
Date: Tue, 13 Nov 2012 11:11:38 +0100
-Subject: [PATCH 11/55] introduce new vma archive format
+Subject: [PATCH 11/41] introduce new vma archive format
This is a very simple archive format, see docs/specs/vma_spec.txt
---
Makefile | 3 +-
Makefile.objs | 1 +
- vma-reader.c | 799 ++++++++++++++++++++++++++++++++++++++++++++++++++++
- vma-writer.c | 876 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- vma.c | 582 ++++++++++++++++++++++++++++++++++++++
+ vma-reader.c | 797 +++++++++++++++++++++++++++++++++++++++++++++++++++++
+ vma-writer.c | 870 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ vma.c | 585 +++++++++++++++++++++++++++++++++++++++
vma.h | 146 ++++++++++
- 6 files changed, 2406 insertions(+), 1 deletion(-)
+ 6 files changed, 2401 insertions(+), 1 deletion(-)
create mode 100644 vma-reader.c
create mode 100644 vma-writer.c
create mode 100644 vma.c
create mode 100644 vma.h
diff --git a/Makefile b/Makefile
-index 1d076a9..4e41399 100644
+index 50b4b3a..d92d905 100644
--- a/Makefile
+++ b/Makefile
-@@ -167,7 +167,7 @@ ifneq ($(wildcard config-host.mak),)
- include $(SRC_PATH)/tests/Makefile
+@@ -165,7 +165,7 @@ ifneq ($(wildcard config-host.mak),)
+ include $(SRC_PATH)/tests/Makefile.include
endif
-all: $(DOCS) $(TOOLS) $(HELPERS-y) recurse-all modules
+all: $(DOCS) $(TOOLS) vma$(EXESUF) $(HELPERS-y) recurse-all modules
- config-host.h: config-host.h-timestamp
- config-host.h-timestamp: config-host.mak
-@@ -237,6 +237,7 @@ qemu-img.o: qemu-img-cmds.h
+ qemu-version.h: FORCE
+ $(call quiet-command, \
+@@ -256,6 +256,7 @@ qemu-img.o: qemu-img-cmds.h
qemu-img$(EXESUF): qemu-img.o $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) libqemuutil.a libqemustub.a
qemu-nbd$(EXESUF): qemu-nbd.o $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) libqemuutil.a libqemustub.a
qemu-io$(EXESUF): qemu-io.o $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) libqemuutil.a libqemustub.a
qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o libqemuutil.a libqemustub.a
diff --git a/Makefile.objs b/Makefile.objs
-index 8f705f6..5fd0bc4 100644
+index 6d5ddcf..845edd0 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -15,6 +15,7 @@ block-obj-$(CONFIG_POSIX) += aio-posix.o
diff --git a/vma-reader.c b/vma-reader.c
new file mode 100644
-index 0000000..bc36cba
+index 0000000..51dd8fe
--- /dev/null
+++ b/vma-reader.c
-@@ -0,0 +1,799 @@
+@@ -0,0 +1,797 @@
+/*
+ * VMA: Virtual Machine Archive
+ *
+ *
+ */
+
-+#include <stdio.h>
-+#include <errno.h>
-+#include <unistd.h>
-+#include <stdio.h>
-+#include <string.h>
-+#include <sys/types.h>
-+#include <sys/stat.h>
-+#include <fcntl.h>
++#include "qemu/osdep.h"
+#include <glib.h>
+#include <uuid/uuid.h>
+
+#include "qemu/ratelimit.h"
+#include "vma.h"
+#include "block/block.h"
-+
-+#define BITS_PER_LONG (sizeof(unsigned long) * CHAR_BIT)
++#include "sysemu/block-backend.h"
+
+static unsigned char zero_vma_block[VMA_BLOCK_SIZE];
+
+ assert(vmar->rstate[dev_id].bs == NULL);
+
+ int64_t size = bdrv_getlength(bs);
-+ if (size != vmar->devinfo[dev_id].size) {
++ int64_t size_diff = size - vmar->devinfo[dev_id].size;
++
++ /* storage types can have different size restrictions, so it
++ * is not always possible to create an image with exact size.
++ * So we tolerate a size difference up to 4MB.
++ */
++ if ((size_diff < 0) || (size_diff > 4*1024*1024)) {
+ error_setg(errp, "vma_reader_register_bs for stream %s failed - "
+ "unexpected size %zd != %zd", vmar->devinfo[dev_id].devname,
+ size, vmar->devinfo[dev_id].size);
+
diff --git a/vma-writer.c b/vma-writer.c
new file mode 100644
-index 0000000..8a3fa1c
+index 0000000..b0cf529
--- /dev/null
+++ b/vma-writer.c
-@@ -0,0 +1,876 @@
+@@ -0,0 +1,870 @@
+/*
+ * VMA: Virtual Machine Archive
+ *
+ *
+ */
+
-+#include <stdio.h>
-+#include <errno.h>
-+#include <unistd.h>
-+#include <stdio.h>
-+#include <string.h>
-+#include <sys/types.h>
-+#include <sys/stat.h>
-+#include <fcntl.h>
++#include "qemu/osdep.h"
+#include <glib.h>
+#include <uuid/uuid.h>
+
-+#include "qemu-common.h"
+#include "vma.h"
+#include "block/block.h"
+#include "monitor/monitor.h"
+#include "qemu/main-loop.h"
++#include "qemu/coroutine.h"
++#include "qemu/cutils.h"
+
+#define DEBUG_VMA 0
+
+ VmaWriter *vmaw = opaque;
+
+ DPRINTF("vma_co_continue_write\n");
-+ qemu_coroutine_enter(vmaw->co_writer, NULL);
++ qemu_coroutine_enter(vmaw->co_writer);
+}
+
+static ssize_t coroutine_fn
+
+ vmaw->co_writer = qemu_coroutine_self();
+
-+ aio_set_fd_handler(qemu_get_aio_context(), vmaw->fd, NULL, vma_co_continue_write, vmaw);
++ aio_set_fd_handler(qemu_get_aio_context(), vmaw->fd, false, NULL, vma_co_continue_write, vmaw);
+
+ DPRINTF("vma_co_write wait until writable\n");
+ qemu_coroutine_yield();
+ }
+ }
+
-+ aio_set_fd_handler(qemu_get_aio_context(), vmaw->fd, NULL, NULL, NULL);
++ aio_set_fd_handler(qemu_get_aio_context(), vmaw->fd, false, NULL, NULL, NULL);
+
+ vmaw->co_writer = NULL;
+
+}
diff --git a/vma.c b/vma.c
new file mode 100644
-index 0000000..86c117b
+index 0000000..8014090
--- /dev/null
+++ b/vma.c
-@@ -0,0 +1,582 @@
+@@ -0,0 +1,585 @@
+/*
+ * VMA: Virtual Machine Archive
+ *
+ *
+ */
+
-+#include <stdio.h>
-+#include <errno.h>
-+#include <unistd.h>
-+#include <stdio.h>
-+#include <string.h>
-+#include <sys/types.h>
-+#include <sys/stat.h>
-+#include <fcntl.h>
++#include "qemu/osdep.h"
+#include <glib.h>
+
+#include "vma.h"
+#include "qemu-common.h"
+#include "qemu/error-report.h"
+#include "qemu/main-loop.h"
++#include "sysemu/char.h" /* qstring_from_str */
+
+static void help(void)
+{
+ }
+
+ BlockDriverState *bs = bdrv_new();
-+ if (errp || bdrv_open(&bs, devfn, NULL, NULL, flags, NULL, &errp)) {
++ if (errp || bdrv_open(&bs, devfn, NULL, NULL, flags, &errp)) {
+ g_error("can't open file %s - %s", devfn,
+ error_get_pretty(errp));
+ }
+
+static int create_archive(int argc, char **argv)
+{
-+ int i, c, res;
++ int i, c;
+ int verbose = 0;
+ const char *archivename;
+ GList *config_files = NULL;
+ char *devname = NULL;
+ path = extract_devname(path, &devname, ind++);
+
-+ BlockDriver *drv = NULL;
+ Error *errp = NULL;
-+ BlockDriverState *bs = bdrv_new();
++ BlockDriverState *bs;
+
-+ res = bdrv_open(&bs, path, NULL, NULL, BDRV_O_CACHE_WB , drv, &errp);
-+ if (res < 0) {
++ bs = bdrv_open(path, NULL, NULL, 0, &errp);
++ if (!bs) {
+ unlink(archivename);
+ g_error("bdrv_open '%s' failed - %s", path, error_get_pretty(errp));
+ }
+ job->vmaw = vmaw;
+ job->dev_id = dev_id;
+
-+ Coroutine *co = qemu_coroutine_create(backup_run);
-+ qemu_coroutine_enter(co, job);
++ Coroutine *co = qemu_coroutine_create(backup_run, job);
++ qemu_coroutine_enter(co);
+ }
+
+ VmaStatus vmastat;
+ if (vmastat.closed) {
+ break;
+ }
++ } else {
++ Coroutine *co = qemu_coroutine_create(backup_run_empty, vmaw);
++ qemu_coroutine_enter(co);
++ while (1) {
++ main_loop_wait(false);
++ vma_writer_get_status(vmaw, &vmastat);
++ if (vmastat.closed) {
++ break;
++ }
++ }
+ }
+
+ bdrv_drain_all();
-From 5a03baace683a297371dbe36795a517d68fc2727 Mon Sep 17 00:00:00 2001
+From 6dad22d8b527594d14987d1118f95179c0ff5aaa Mon Sep 17 00:00:00 2001
From: Dietmar Maurer <dietmar@proxmox.com>
Date: Mon, 11 Mar 2013 07:07:46 +0100
-Subject: [PATCH 12/55] vma: add verify command
+Subject: [PATCH 12/41] vma: add verify command
Users wants to verify the archive after backup.
3 files changed, 147 insertions(+), 30 deletions(-)
diff --git a/vma-reader.c b/vma-reader.c
-index bc36cba..9d92c6a 100644
+index 51dd8fe..2aafb26 100644
--- a/vma-reader.c
+++ b/vma-reader.c
-@@ -53,6 +53,8 @@ struct VmaReader {
+@@ -45,6 +45,8 @@ struct VmaReader {
time_t start_time;
int64_t cluster_count;
int64_t clusters_read;
int clusters_read_per;
};
-@@ -433,6 +435,27 @@ VmaDeviceInfo *vma_reader_get_device_info(VmaReader *vmar, guint8 dev_id)
+@@ -425,6 +427,27 @@ VmaDeviceInfo *vma_reader_get_device_info(VmaReader *vmar, guint8 dev_id)
return NULL;
}
int vma_reader_register_bs(VmaReader *vmar, guint8 dev_id, BlockDriverState *bs,
bool write_zeroes, Error **errp)
{
-@@ -449,17 +472,7 @@ int vma_reader_register_bs(VmaReader *vmar, guint8 dev_id, BlockDriverState *bs,
+@@ -447,17 +470,7 @@ int vma_reader_register_bs(VmaReader *vmar, guint8 dev_id, BlockDriverState *bs,
return -1;
}
return 0;
}
-@@ -526,9 +539,10 @@ static int restore_write_data(VmaReader *vmar, guint8 dev_id,
+@@ -524,9 +537,10 @@ static int restore_write_data(VmaReader *vmar, guint8 dev_id,
}
return 0;
}
{
assert(vmar);
assert(buf);
-@@ -553,7 +567,7 @@ static int restore_extent(VmaReader *vmar, unsigned char *buf,
+@@ -551,7 +565,7 @@ static int restore_extent(VmaReader *vmar, unsigned char *buf,
if (dev_id != vmar->vmstate_stream) {
bs = rstate->bs;
error_setg(errp, "got wrong dev id %d", dev_id);
return -1;
}
-@@ -609,10 +623,13 @@ static int restore_extent(VmaReader *vmar, unsigned char *buf,
+@@ -607,10 +621,13 @@ static int restore_extent(VmaReader *vmar, unsigned char *buf,
return -1;
}
}
start += VMA_CLUSTER_SIZE;
-@@ -642,26 +659,37 @@ static int restore_extent(VmaReader *vmar, unsigned char *buf,
+@@ -640,26 +657,37 @@ static int restore_extent(VmaReader *vmar, unsigned char *buf,
return -1;
}
}
}
}
-@@ -679,8 +707,9 @@ static int restore_extent(VmaReader *vmar, unsigned char *buf,
+@@ -677,8 +705,9 @@ static int restore_extent(VmaReader *vmar, unsigned char *buf,
return 0;
}
{
assert(vmar);
assert(vmar->head_data);
-@@ -747,7 +776,7 @@ int vma_reader_restore(VmaReader *vmar, int vmstate_fd, bool verbose,
+@@ -745,7 +774,7 @@ int vma_reader_restore(VmaReader *vmar, int vmstate_fd, bool verbose,
}
if (restore_extent(vmar, buf, extent_size, vmstate_fd, verbose,
return -1;
}
-@@ -794,6 +823,38 @@ int vma_reader_restore(VmaReader *vmar, int vmstate_fd, bool verbose,
+@@ -792,6 +821,38 @@ int vma_reader_restore(VmaReader *vmar, int vmstate_fd, bool verbose,
}
}
+}
+
diff --git a/vma.c b/vma.c
-index 86c117b..3ad2f18 100644
+index 8014090..d55874a 100644
--- a/vma.c
+++ b/vma.c
-@@ -34,6 +34,7 @@ static void help(void)
+@@ -28,6 +28,7 @@ static void help(void)
"vma list <filename>\n"
"vma create <filename> [-c config] <archive> pathname ...\n"
"vma extract <filename> [-r <fifo>] <targetdir>\n"
;
printf("%s", help_msg);
-@@ -338,6 +339,58 @@ static int extract_content(int argc, char **argv)
+@@ -332,6 +333,58 @@ static int extract_content(int argc, char **argv)
return ret;
}
typedef struct BackupJob {
BlockDriverState *bs;
int64_t len;
-@@ -575,6 +628,8 @@ int main(int argc, char **argv)
+@@ -578,6 +631,8 @@ int main(int argc, char **argv)
return create_archive(argc, argv);
} else if (!strcmp(cmdname, "extract")) {
return extract_content(argc, argv);
-From 4cf741ef0ec95b62dab484f5630e0f5fd608220a Mon Sep 17 00:00:00 2001
+From fbe4db594bee11864b82c5839425348bc4133c9b Mon Sep 17 00:00:00 2001
From: Wolfgang Bumiller <w.bumiller@proxmox.com>
Date: Wed, 9 Dec 2015 14:46:49 +0100
-Subject: [PATCH 13/55] vma: add 'config' command to dump the config
+Subject: [PATCH 13/41] vma: add 'config' command to dump the config
---
vma.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 64 insertions(+)
diff --git a/vma.c b/vma.c
-index 3ad2f18..6a33352 100644
+index d55874a..79bdd00 100644
--- a/vma.c
+++ b/vma.c
-@@ -32,6 +32,7 @@ static void help(void)
+@@ -26,6 +26,7 @@ static void help(void)
"usage: vma command [command options]\n"
"\n"
"vma list <filename>\n"
"vma create <filename> [-c config] <archive> pathname ...\n"
"vma extract <filename> [-r <fifo>] <targetdir>\n"
"vma verify <filename> [-v]\n"
-@@ -601,6 +602,67 @@ static int create_archive(int argc, char **argv)
+@@ -604,6 +605,67 @@ static int create_archive(int argc, char **argv)
return 0;
}
int main(int argc, char **argv)
{
const char *cmdname;
-@@ -630,6 +692,8 @@ int main(int argc, char **argv)
+@@ -633,6 +695,8 @@ int main(int argc, char **argv)
return extract_content(argc, argv);
} else if (!strcmp(cmdname, "verify")) {
return verify_content(argc, argv);
--- /dev/null
+From dbdd959063e8f5d13c3051607a0fb7a2e0e178e6 Mon Sep 17 00:00:00 2001
+From: Wolfgang Bumiller <w.bumiller@proxmox.com>
+Date: Wed, 9 Dec 2015 15:04:57 +0100
+Subject: [PATCH 14/41] backup: modify job api
+
+Introduces a BackupDump function callback and a pause_count
+for backup_start. For a dump-backup the target parameter
+can now be NULL so access to target needs to be guarded now.
+---
+ block/backup.c | 82 +++++++++++++++++++++++++++++++----------------
+ blockdev.c | 6 ++--
+ include/block/block_int.h | 5 +++
+ 3 files changed, 63 insertions(+), 30 deletions(-)
+
+diff --git a/block/backup.c b/block/backup.c
+index 2c05323..f3c0ba3 100644
+--- a/block/backup.c
++++ b/block/backup.c
+@@ -41,6 +41,7 @@ typedef struct BackupBlockJob {
+ BdrvDirtyBitmap *sync_bitmap;
+ MirrorSyncMode sync_mode;
+ RateLimit limit;
++ BackupDumpFunc *dump_cb;
+ BlockdevOnError on_source_error;
+ BlockdevOnError on_target_error;
+ CoRwlock flush_rwlock;
+@@ -149,12 +150,23 @@ static int coroutine_fn backup_do_cow(BackupBlockJob *job,
+ goto out;
+ }
+
++ int64_t start_sec = start * sectors_per_cluster;
+ if (buffer_is_zero(iov.iov_base, iov.iov_len)) {
+- ret = blk_co_pwrite_zeroes(job->target, start * job->cluster_size,
+- bounce_qiov.size, BDRV_REQ_MAY_UNMAP);
++ if (job->dump_cb) {
++ ret = job->dump_cb(job->common.opaque, job->target, start_sec, n, NULL);
++ }
++ if (job->target) {
++ ret = blk_co_pwrite_zeroes(job->target, start * job->cluster_size,
++ bounce_qiov.size, BDRV_REQ_MAY_UNMAP);
++ }
+ } else {
+- ret = blk_co_pwritev(job->target, start * job->cluster_size,
+- bounce_qiov.size, &bounce_qiov, 0);
++ if (job->dump_cb) {
++ ret = job->dump_cb(job->common.opaque, job->target, start_sec, n, bounce_buffer);
++ }
++ if (job->target) {
++ ret = blk_co_pwritev(job->target, start * job->cluster_size,
++ bounce_qiov.size, &bounce_qiov, 0);
++ }
+ }
+ if (ret < 0) {
+ trace_backup_do_cow_write_fail(job, start, ret);
+@@ -268,9 +280,11 @@ static BlockErrorAction backup_error_action(BackupBlockJob *job,
+ if (read) {
+ return block_job_error_action(&job->common, job->on_source_error,
+ true, error);
+- } else {
++ } else if (job->target) {
+ return block_job_error_action(&job->common, job->on_target_error,
+ false, error);
++ } else {
++ return BLOCK_ERROR_ACTION_REPORT;
+ }
+ }
+
+@@ -393,6 +407,7 @@ static void coroutine_fn backup_run(void *opaque)
+
+ job->done_bitmap = bitmap_new(end);
+
++
+ job->before_write.notify = backup_before_write_notify;
+ bdrv_add_before_write_notifier(bs, &job->before_write);
+
+@@ -467,7 +482,9 @@ static void coroutine_fn backup_run(void *opaque)
+ qemu_co_rwlock_unlock(&job->flush_rwlock);
+ g_free(job->done_bitmap);
+
+- bdrv_op_unblock_all(blk_bs(target), job->common.blocker);
++ if (target) {
++ bdrv_op_unblock_all(blk_bs(target), job->common.blocker);
++ }
+
+ data = g_malloc(sizeof(*data));
+ data->ret = ret;
+@@ -479,7 +496,9 @@ void backup_start(const char *job_id, BlockDriverState *bs,
+ MirrorSyncMode sync_mode, BdrvDirtyBitmap *sync_bitmap,
+ BlockdevOnError on_source_error,
+ BlockdevOnError on_target_error,
++ BackupDumpFunc *dump_cb,
+ BlockCompletionFunc *cb, void *opaque,
++ int pause_count,
+ BlockJobTxn *txn, Error **errp)
+ {
+ int64_t len;
+@@ -488,7 +507,7 @@ void backup_start(const char *job_id, BlockDriverState *bs,
+ int ret;
+
+ assert(bs);
+- assert(target);
++ assert(target || dump_cb);
+
+ if (bs == target) {
+ error_setg(errp, "Source and target cannot be the same");
+@@ -501,7 +520,7 @@ void backup_start(const char *job_id, BlockDriverState *bs,
+ return;
+ }
+
+- if (!bdrv_is_inserted(target)) {
++ if (target && !bdrv_is_inserted(target)) {
+ error_setg(errp, "Device is not inserted: %s",
+ bdrv_get_device_name(target));
+ return;
+@@ -511,7 +530,7 @@ void backup_start(const char *job_id, BlockDriverState *bs,
+ return;
+ }
+
+- if (bdrv_op_is_blocked(target, BLOCK_OP_TYPE_BACKUP_TARGET, errp)) {
++ if (target && bdrv_op_is_blocked(target, BLOCK_OP_TYPE_BACKUP_TARGET, errp)) {
+ return;
+ }
+
+@@ -547,34 +566,43 @@ void backup_start(const char *job_id, BlockDriverState *bs,
+ goto error;
+ }
+
+- job->target = blk_new();
+- blk_insert_bs(job->target, target);
++ if (target) {
++ job->target = blk_new();
++ blk_insert_bs(job->target, target);
++ }
+
++ job->dump_cb = dump_cb;
+ job->on_source_error = on_source_error;
+ job->on_target_error = on_target_error;
+ job->sync_mode = sync_mode;
+ job->sync_bitmap = sync_mode == MIRROR_SYNC_MODE_INCREMENTAL ?
+ sync_bitmap : NULL;
+
+- /* If there is no backing file on the target, we cannot rely on COW if our
+- * backup cluster size is smaller than the target cluster size. Even for
+- * targets with a backing file, try to avoid COW if possible. */
+- ret = bdrv_get_info(target, &bdi);
+- if (ret < 0 && !target->backing) {
+- error_setg_errno(errp, -ret,
+- "Couldn't determine the cluster size of the target image, "
+- "which has no backing file");
+- error_append_hint(errp,
+- "Aborting, since this may create an unusable destination image\n");
+- goto error;
+- } else if (ret < 0 && target->backing) {
+- /* Not fatal; just trudge on ahead. */
+- job->cluster_size = BACKUP_CLUSTER_SIZE_DEFAULT;
++ if (target) {
++ /* If there is no backing file on the target, we cannot rely on COW if our
++ * backup cluster size is smaller than the target cluster size. Even for
++ * targets with a backing file, try to avoid COW if possible. */
++ ret = bdrv_get_info(target, &bdi);
++ if (ret < 0 && !target->backing) {
++ error_setg_errno(errp, -ret,
++ "Couldn't determine the cluster size of the target image, "
++ "which has no backing file");
++ error_append_hint(errp,
++ "Aborting, since this may create an unusable destination image\n");
++ goto error;
++ } else if (ret < 0 && target->backing) {
++ /* Not fatal; just trudge on ahead. */
++ job->cluster_size = BACKUP_CLUSTER_SIZE_DEFAULT;
++ } else {
++ job->cluster_size = MAX(BACKUP_CLUSTER_SIZE_DEFAULT, bdi.cluster_size);
++ }
++
++ bdrv_op_block_all(target, job->common.blocker);
+ } else {
+- job->cluster_size = MAX(BACKUP_CLUSTER_SIZE_DEFAULT, bdi.cluster_size);
++ job->cluster_size = BACKUP_CLUSTER_SIZE_DEFAULT;
+ }
+
+- bdrv_op_block_all(target, job->common.blocker);
++ job->common.pause_count = pause_count;
+ job->common.len = len;
+ job->common.co = qemu_coroutine_create(backup_run, job);
+ block_job_txn_add_job(txn, &job->common);
+diff --git a/blockdev.c b/blockdev.c
+index 2161400..5e3707d 100644
+--- a/blockdev.c
++++ b/blockdev.c
+@@ -3277,8 +3277,8 @@ static void do_drive_backup(const char *job_id, const char *device,
+ }
+
+ backup_start(job_id, bs, target_bs, speed, sync, bmap,
+- on_source_error, on_target_error,
+- block_job_cb, bs, txn, &local_err);
++ on_source_error, on_target_error, NULL,
++ block_job_cb, bs, 0, txn, &local_err);
+ bdrv_unref(target_bs);
+ if (local_err != NULL) {
+ error_propagate(errp, local_err);
+@@ -3371,7 +3371,7 @@ void do_blockdev_backup(const char *job_id, const char *device,
+ }
+ }
+ backup_start(job_id, bs, target_bs, speed, sync, NULL, on_source_error,
+- on_target_error, block_job_cb, bs, txn, &local_err);
++ on_target_error, NULL, block_job_cb, bs, 0, txn, &local_err);
+ if (local_err != NULL) {
+ error_propagate(errp, local_err);
+ }
+diff --git a/include/block/block_int.h b/include/block/block_int.h
+index 1e939de..db4650e 100644
+--- a/include/block/block_int.h
++++ b/include/block/block_int.h
+@@ -59,6 +59,9 @@
+
+ #define BLOCK_PROBE_BUF_SIZE 512
+
++typedef int BackupDumpFunc(void *opaque, BlockDriverState *bs,
++ int64_t sector_num, int n_sectors, unsigned char *buf);
++
+ enum BdrvTrackedRequestType {
+ BDRV_TRACKED_READ,
+ BDRV_TRACKED_WRITE,
+@@ -767,7 +770,9 @@ void backup_start(const char *job_id, BlockDriverState *bs,
+ MirrorSyncMode sync_mode, BdrvDirtyBitmap *sync_bitmap,
+ BlockdevOnError on_source_error,
+ BlockdevOnError on_target_error,
++ BackupDumpFunc *dump_cb,
+ BlockCompletionFunc *cb, void *opaque,
++ int pause_count,
+ BlockJobTxn *txn, Error **errp);
+
+ void hmp_drive_add_node(Monitor *mon, const char *optstr);
+--
+2.1.4
+
+++ /dev/null
-From b60a9ba781fc9e4f1a8c7d177f20933340cf4fc4 Mon Sep 17 00:00:00 2001
-From: Dietmar Maurer <dietmar@proxmox.com>
-Date: Tue, 26 Mar 2013 06:21:16 +0100
-Subject: [PATCH 14/55] vma restore: tolerate a size difference up to 4M
-
-Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
----
- vma-reader.c | 8 +++++++-
- 1 file changed, 7 insertions(+), 1 deletion(-)
-
-diff --git a/vma-reader.c b/vma-reader.c
-index 9d92c6a..d9f43fe 100644
---- a/vma-reader.c
-+++ b/vma-reader.c
-@@ -465,7 +465,13 @@ int vma_reader_register_bs(VmaReader *vmar, guint8 dev_id, BlockDriverState *bs,
- assert(vmar->rstate[dev_id].bs == NULL);
-
- int64_t size = bdrv_getlength(bs);
-- if (size != vmar->devinfo[dev_id].size) {
-+ int64_t size_diff = size - vmar->devinfo[dev_id].size;
-+
-+ /* storage types can have different size restrictions, so it
-+ * is not always possible to create an image with exact size.
-+ * So we tolerate a size difference up to 4MB.
-+ */
-+ if ((size_diff < 0) || (size_diff > 4*1024*1024)) {
- error_setg(errp, "vma_reader_register_bs for stream %s failed - "
- "unexpected size %zd != %zd", vmar->devinfo[dev_id].devname,
- size, vmar->devinfo[dev_id].size);
---
-2.1.4
-
--- /dev/null
+From 83fc57d04020d8d9aebd7b9676bee0be099421d8 Mon Sep 17 00:00:00 2001
+From: Wolfgang Bumiller <w.bumiller@proxmox.com>
+Date: Wed, 9 Dec 2015 15:20:56 +0100
+Subject: [PATCH 15/41] backup: add pve monitor commands
+
+---
+ blockdev.c | 439 ++++++++++++++++++++++++++++++++++++++++++++++
+ blockjob.c | 3 +-
+ hmp-commands-info.hx | 13 ++
+ hmp-commands.hx | 29 +++
+ hmp.c | 61 +++++++
+ hmp.h | 3 +
+ include/block/block_int.h | 2 +-
+ qapi-schema.json | 89 ++++++++++
+ qmp-commands.hx | 18 ++
+ 9 files changed, 655 insertions(+), 2 deletions(-)
+
+diff --git a/blockdev.c b/blockdev.c
+index 5e3707d..5417bb0 100644
+--- a/blockdev.c
++++ b/blockdev.c
+@@ -52,6 +52,7 @@
+ #include "sysemu/arch_init.h"
+ #include "qemu/cutils.h"
+ #include "qemu/help_option.h"
++#include "vma.h"
+
+ static QTAILQ_HEAD(, BlockDriverState) monitor_bdrv_states =
+ QTAILQ_HEAD_INITIALIZER(monitor_bdrv_states);
+@@ -2976,6 +2977,444 @@ static void block_job_cb(void *opaque, int ret)
+ }
+ }
+
++/* PVE backup related function */
++
++static struct PVEBackupState {
++ Error *error;
++ bool cancel;
++ uuid_t uuid;
++ char uuid_str[37];
++ int64_t speed;
++ time_t start_time;
++ time_t end_time;
++ char *backup_file;
++ VmaWriter *vmaw;
++ GList *di_list;
++ size_t total;
++ size_t transferred;
++ size_t zero_bytes;
++} backup_state;
++
++typedef struct PVEBackupDevInfo {
++ BlockDriverState *bs;
++ size_t size;
++ uint8_t dev_id;
++ //bool started;
++ bool completed;
++} PVEBackupDevInfo;
++
++static void pvebackup_run_next_job(void);
++
++static int pvebackup_dump_cb(void *opaque, BlockBackend *target,
++ int64_t sector_num, int n_sectors,
++ unsigned char *buf)
++{
++ PVEBackupDevInfo *di = opaque;
++
++ if (sector_num & 0x7f) {
++ if (!backup_state.error) {
++ error_setg(&backup_state.error,
++ "got unaligned write inside backup dump "
++ "callback (sector %ld)", sector_num);
++ }
++ return -1; // not aligned to cluster size
++ }
++
++ int64_t cluster_num = sector_num >> 7;
++ int size = n_sectors * BDRV_SECTOR_SIZE;
++
++ int ret = -1;
++
++ if (backup_state.vmaw) {
++ size_t zero_bytes = 0;
++ ret = vma_writer_write(backup_state.vmaw, di->dev_id, cluster_num,
++ buf, &zero_bytes);
++ backup_state.zero_bytes += zero_bytes;
++ } else {
++ ret = size;
++ if (!buf) {
++ backup_state.zero_bytes += size;
++ }
++ }
++
++ backup_state.transferred += size;
++
++ return ret;
++}
++
++static void pvebackup_cleanup(void)
++{
++ backup_state.end_time = time(NULL);
++
++ if (backup_state.vmaw) {
++ Error *local_err = NULL;
++ vma_writer_close(backup_state.vmaw, &local_err);
++ error_propagate(&backup_state.error, local_err);
++ backup_state.vmaw = NULL;
++ }
++
++ if (backup_state.di_list) {
++ GList *l = backup_state.di_list;
++ while (l) {
++ PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data;
++ l = g_list_next(l);
++ g_free(di);
++ }
++ g_list_free(backup_state.di_list);
++ backup_state.di_list = NULL;
++ }
++}
++
++static void pvebackup_complete_cb(void *opaque, int ret)
++{
++ PVEBackupDevInfo *di = opaque;
++
++ assert(backup_state.vmaw);
++
++ di->completed = true;
++
++ if (ret < 0 && !backup_state.error) {
++ error_setg(&backup_state.error, "job failed with err %d - %s",
++ ret, strerror(-ret));
++ }
++
++ BlockDriverState *bs = di->bs;
++
++ di->bs = NULL;
++
++ vma_writer_close_stream(backup_state.vmaw, di->dev_id);
++
++ block_job_cb(bs, ret);
++
++ if (!backup_state.cancel) {
++ pvebackup_run_next_job();
++ }
++}
++
++static void pvebackup_cancel(void *opaque)
++{
++ backup_state.cancel = true;
++
++ if (!backup_state.error) {
++ error_setg(&backup_state.error, "backup cancelled");
++ }
++
++ /* drain all i/o (awake jobs waiting for aio) */
++ bdrv_drain_all();
++
++ GList *l = backup_state.di_list;
++ while (l) {
++ PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data;
++ l = g_list_next(l);
++ if (!di->completed && di->bs) {
++ BlockJob *job = di->bs->job;
++ if (job) {
++ if (!di->completed) {
++ block_job_cancel_sync(job);
++ }
++ }
++ }
++ }
++
++ pvebackup_cleanup();
++}
++
++void qmp_backup_cancel(Error **errp)
++{
++ Coroutine *co = qemu_coroutine_create(pvebackup_cancel, NULL);
++ qemu_coroutine_enter(co);
++
++ while (backup_state.vmaw) {
++ /* vma writer use main aio context */
++ aio_poll(qemu_get_aio_context(), true);
++ }
++}
++
++bool block_job_should_pause(BlockJob *job);
++static void pvebackup_run_next_job(void)
++{
++ GList *l = backup_state.di_list;
++ while (l) {
++ PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data;
++ l = g_list_next(l);
++ if (!di->completed && di->bs && di->bs->job) {
++ BlockJob *job = di->bs->job;
++ if (block_job_should_pause(job)) {
++ bool cancel = backup_state.error || backup_state.cancel;
++ if (cancel) {
++ block_job_cancel(job);
++ } else {
++ block_job_resume(job);
++ }
++ }
++ return;
++ }
++ }
++
++ pvebackup_cleanup();
++}
++
++UuidInfo *qmp_backup(const char *backup_file, bool has_format,
++ BackupFormat format,
++ bool has_config_file, const char *config_file,
++ bool has_devlist, const char *devlist,
++ bool has_speed, int64_t speed, Error **errp)
++{
++ BlockBackend *blk;
++ BlockDriverState *bs = NULL;
++ Error *local_err = NULL;
++ uuid_t uuid;
++ VmaWriter *vmaw = NULL;
++ gchar **devs = NULL;
++ GList *di_list = NULL;
++ GList *l;
++ UuidInfo *uuid_info;
++
++ if (backup_state.di_list) {
++ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
++ "previous backup not finished");
++ return NULL;
++ }
++
++ /* Todo: try to auto-detect format based on file name */
++ format = has_format ? format : BACKUP_FORMAT_VMA;
++
++ if (format != BACKUP_FORMAT_VMA) {
++ error_set(errp, ERROR_CLASS_GENERIC_ERROR, "unknown backup format");
++ return NULL;
++ }
++
++ if (has_devlist) {
++ devs = g_strsplit_set(devlist, ",;:", -1);
++
++ gchar **d = devs;
++ while (d && *d) {
++ blk = blk_by_name(*d);
++ if (blk) {
++ bs = blk_bs(blk);
++ if (bdrv_is_read_only(bs)) {
++ error_setg(errp, "Node '%s' is read only", *d);
++ goto err;
++ }
++ if (!bdrv_is_inserted(bs)) {
++ error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, *d);
++ goto err;
++ }
++ PVEBackupDevInfo *di = g_new0(PVEBackupDevInfo, 1);
++ di->bs = bs;
++ di_list = g_list_append(di_list, di);
++ } else {
++ error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
++ "Device '%s' not found", *d);
++ goto err;
++ }
++ d++;
++ }
++
++ } else {
++ BdrvNextIterator it;
++
++ bs = NULL;
++ for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
++ if (!bdrv_is_inserted(bs) || bdrv_is_read_only(bs)) {
++ continue;
++ }
++
++ PVEBackupDevInfo *di = g_new0(PVEBackupDevInfo, 1);
++ di->bs = bs;
++ di_list = g_list_append(di_list, di);
++ }
++ }
++
++ if (!di_list) {
++ error_set(errp, ERROR_CLASS_GENERIC_ERROR, "empty device list");
++ goto err;
++ }
++
++ size_t total = 0;
++
++ l = di_list;
++ while (l) {
++ PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data;
++ l = g_list_next(l);
++ if (bdrv_op_is_blocked(di->bs, BLOCK_OP_TYPE_BACKUP_SOURCE, errp)) {
++ goto err;
++ }
++
++ ssize_t size = bdrv_getlength(di->bs);
++ if (size < 0) {
++ error_setg_errno(errp, -di->size, "bdrv_getlength failed");
++ goto err;
++ }
++ di->size = size;
++ total += size;
++ }
++
++ uuid_generate(uuid);
++
++ vmaw = vma_writer_create(backup_file, uuid, &local_err);
++ if (!vmaw) {
++ if (local_err) {
++ error_propagate(errp, local_err);
++ }
++ goto err;
++ }
++
++ /* register all devices for vma writer */
++ l = di_list;
++ while (l) {
++ PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data;
++ l = g_list_next(l);
++
++ const char *devname = bdrv_get_device_name(di->bs);
++ di->dev_id = vma_writer_register_stream(vmaw, devname, di->size);
++ if (di->dev_id <= 0) {
++ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
++ "register_stream failed");
++ goto err;
++ }
++ }
++
++ /* add configuration file to archive */
++ if (has_config_file) {
++ char *cdata = NULL;
++ gsize clen = 0;
++ GError *err = NULL;
++ if (!g_file_get_contents(config_file, &cdata, &clen, &err)) {
++ error_setg(errp, "unable to read file '%s'", config_file);
++ goto err;
++ }
++
++ const char *basename = g_path_get_basename(config_file);
++ if (vma_writer_add_config(vmaw, basename, cdata, clen) != 0) {
++ error_setg(errp, "unable to add config data to vma archive");
++ g_free(cdata);
++ goto err;
++ }
++ g_free(cdata);
++ }
++
++ /* initialize global backup_state now */
++
++ backup_state.cancel = false;
++
++ if (backup_state.error) {
++ error_free(backup_state.error);
++ backup_state.error = NULL;
++ }
++
++ backup_state.speed = (has_speed && speed > 0) ? speed : 0;
++
++ backup_state.start_time = time(NULL);
++ backup_state.end_time = 0;
++
++ if (backup_state.backup_file) {
++ g_free(backup_state.backup_file);
++ }
++ backup_state.backup_file = g_strdup(backup_file);
++
++ backup_state.vmaw = vmaw;
++
++ uuid_copy(backup_state.uuid, uuid);
++ uuid_unparse_lower(uuid, backup_state.uuid_str);
++
++ backup_state.di_list = di_list;
++
++ backup_state.total = total;
++ backup_state.transferred = 0;
++ backup_state.zero_bytes = 0;
++
++ /* start all jobs (paused state) */
++ l = di_list;
++ while (l) {
++ PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data;
++ l = g_list_next(l);
++
++ backup_start(NULL, di->bs, NULL, speed, MIRROR_SYNC_MODE_FULL, NULL,
++ BLOCKDEV_ON_ERROR_REPORT, BLOCKDEV_ON_ERROR_REPORT,
++ pvebackup_dump_cb, pvebackup_complete_cb, di,
++ 1, NULL, &local_err);
++ if (local_err != NULL) {
++ error_setg(&backup_state.error, "backup_job_create failed");
++ pvebackup_cancel(NULL);
++ }
++ }
++
++ if (!backup_state.error) {
++ pvebackup_run_next_job(); // run one job
++ }
++
++ uuid_info = g_malloc0(sizeof(*uuid_info));
++ uuid_info->UUID = g_strdup(backup_state.uuid_str);
++ return uuid_info;
++
++err:
++
++ l = di_list;
++ while (l) {
++ g_free(l->data);
++ l = g_list_next(l);
++ }
++ g_list_free(di_list);
++
++ if (devs) {
++ g_strfreev(devs);
++ }
++
++ if (vmaw) {
++ Error *err = NULL;
++ vma_writer_close(vmaw, &err);
++ unlink(backup_file);
++ }
++
++ return NULL;
++}
++
++BackupStatus *qmp_query_backup(Error **errp)
++{
++ BackupStatus *info = g_malloc0(sizeof(*info));
++
++ if (!backup_state.start_time) {
++ /* not started, return {} */
++ return info;
++ }
++
++ info->has_status = true;
++ info->has_start_time = true;
++ info->start_time = backup_state.start_time;
++
++ if (backup_state.backup_file) {
++ info->has_backup_file = true;
++ info->backup_file = g_strdup(backup_state.backup_file);
++ }
++
++ info->has_uuid = true;
++ info->uuid = g_strdup(backup_state.uuid_str);
++
++ if (backup_state.end_time) {
++ if (backup_state.error) {
++ info->status = g_strdup("error");
++ info->has_errmsg = true;
++ info->errmsg = g_strdup(error_get_pretty(backup_state.error));
++ } else {
++ info->status = g_strdup("done");
++ }
++ info->has_end_time = true;
++ info->end_time = backup_state.end_time;
++ } else {
++ info->status = g_strdup("active");
++ }
++
++ info->has_total = true;
++ info->total = backup_state.total;
++ info->has_zero_bytes = true;
++ info->zero_bytes = backup_state.zero_bytes;
++ info->has_transferred = true;
++ info->transferred = backup_state.transferred;
++
++ return info;
++}
++
+ void qmp_block_stream(bool has_job_id, const char *job_id, const char *device,
+ bool has_base, const char *base,
+ bool has_backing_file, const char *backing_file,
+diff --git a/blockjob.c b/blockjob.c
+index a5ba3be..a550458 100644
+--- a/blockjob.c
++++ b/blockjob.c
+@@ -331,7 +331,8 @@ void block_job_pause(BlockJob *job)
+ job->pause_count++;
+ }
+
+-static bool block_job_should_pause(BlockJob *job)
++bool block_job_should_pause(BlockJob *job);
++bool block_job_should_pause(BlockJob *job)
+ {
+ return job->pause_count > 0;
+ }
+diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx
+index 74446c6..7616fe2 100644
+--- a/hmp-commands-info.hx
++++ b/hmp-commands-info.hx
+@@ -502,6 +502,19 @@ STEXI
+ Show CPU statistics.
+ ETEXI
+
++ {
++ .name = "backup",
++ .args_type = "",
++ .params = "",
++ .help = "show backup status",
++ .mhandler.cmd = hmp_info_backup,
++ },
++
++STEXI
++@item info backup
++show backup status
++ETEXI
++
+ #if defined(CONFIG_SLIRP)
+ {
+ .name = "usernet",
+diff --git a/hmp-commands.hx b/hmp-commands.hx
+index 848efee..8f2f3e0 100644
+--- a/hmp-commands.hx
++++ b/hmp-commands.hx
+@@ -87,6 +87,35 @@ STEXI
+ Copy data from a backing file into a block device.
+ ETEXI
+
++ {
++ .name = "backup",
++ .args_type = "backupfile:s,speed:o?,devlist:s?",
++ .params = "backupfile [speed [devlist]]",
++ .help = "create a VM Backup.",
++ .mhandler.cmd = hmp_backup,
++ },
++
++STEXI
++@item backup
++@findex backup
++Create a VM backup.
++ETEXI
++
++ {
++ .name = "backup_cancel",
++ .args_type = "",
++ .params = "",
++ .help = "cancel the current VM backup",
++ .mhandler.cmd = hmp_backup_cancel,
++ },
++
++STEXI
++@item backup_cancel
++@findex backup_cancel
++Cancel the current VM backup.
++
++ETEXI
++
+ {
+ .name = "block_job_set_speed",
+ .args_type = "device:B,speed:o",
+diff --git a/hmp.c b/hmp.c
+index aa1395d..88cb4e5 100644
+--- a/hmp.c
++++ b/hmp.c
+@@ -149,6 +149,44 @@ void hmp_info_mice(Monitor *mon, const QDict *qdict)
+ qapi_free_MouseInfoList(mice_list);
+ }
+
++void hmp_info_backup(Monitor *mon, const QDict *qdict)
++{
++ BackupStatus *info;
++
++ info = qmp_query_backup(NULL);
++ if (info->has_status) {
++ if (info->has_errmsg) {
++ monitor_printf(mon, "Backup status: %s - %s\n",
++ info->status, info->errmsg);
++ } else {
++ monitor_printf(mon, "Backup status: %s\n", info->status);
++ }
++ }
++
++ if (info->has_backup_file) {
++ monitor_printf(mon, "Start time: %s", ctime(&info->start_time));
++ if (info->end_time) {
++ monitor_printf(mon, "End time: %s", ctime(&info->end_time));
++ }
++
++ int per = (info->has_total && info->total &&
++ info->has_transferred && info->transferred) ?
++ (info->transferred * 100)/info->total : 0;
++ int zero_per = (info->has_total && info->total &&
++ info->has_zero_bytes && info->zero_bytes) ?
++ (info->zero_bytes * 100)/info->total : 0;
++ monitor_printf(mon, "Backup file: %s\n", info->backup_file);
++ monitor_printf(mon, "Backup uuid: %s\n", info->uuid);
++ monitor_printf(mon, "Total size: %zd\n", info->total);
++ monitor_printf(mon, "Transferred bytes: %zd (%d%%)\n",
++ info->transferred, per);
++ monitor_printf(mon, "Zero bytes: %zd (%d%%)\n",
++ info->zero_bytes, zero_per);
++ }
++
++ qapi_free_BackupStatus(info);
++}
++
+ void hmp_info_migrate(Monitor *mon, const QDict *qdict)
+ {
+ MigrationInfo *info;
+@@ -1492,6 +1530,29 @@ void hmp_block_stream(Monitor *mon, const QDict *qdict)
+ hmp_handle_error(mon, &error);
+ }
+
++void hmp_backup_cancel(Monitor *mon, const QDict *qdict)
++{
++ Error *error = NULL;
++
++ qmp_backup_cancel(&error);
++
++ hmp_handle_error(mon, &error);
++}
++
++void hmp_backup(Monitor *mon, const QDict *qdict)
++{
++ Error *error = NULL;
++
++ const char *backup_file = qdict_get_str(qdict, "backupfile");
++ const char *devlist = qdict_get_try_str(qdict, "devlist");
++ int64_t speed = qdict_get_try_int(qdict, "speed", 0);
++
++ qmp_backup(backup_file, true, BACKUP_FORMAT_VMA, false, NULL, !!devlist,
++ devlist, qdict_haskey(qdict, "speed"), speed, &error);
++
++ hmp_handle_error(mon, &error);
++}
++
+ void hmp_block_job_set_speed(Monitor *mon, const QDict *qdict)
+ {
+ Error *error = NULL;
+diff --git a/hmp.h b/hmp.h
+index 0876ec0..9a4c1f6 100644
+--- a/hmp.h
++++ b/hmp.h
+@@ -30,6 +30,7 @@ void hmp_info_migrate(Monitor *mon, const QDict *qdict);
+ void hmp_info_migrate_capabilities(Monitor *mon, const QDict *qdict);
+ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict);
+ void hmp_info_migrate_cache_size(Monitor *mon, const QDict *qdict);
++void hmp_info_backup(Monitor *mon, const QDict *qdict);
+ void hmp_info_cpus(Monitor *mon, const QDict *qdict);
+ void hmp_info_block(Monitor *mon, const QDict *qdict);
+ void hmp_info_blockstats(Monitor *mon, const QDict *qdict);
+@@ -76,6 +77,8 @@ void hmp_eject(Monitor *mon, const QDict *qdict);
+ void hmp_change(Monitor *mon, const QDict *qdict);
+ void hmp_block_set_io_throttle(Monitor *mon, const QDict *qdict);
+ void hmp_block_stream(Monitor *mon, const QDict *qdict);
++void hmp_backup(Monitor *mon, const QDict *qdict);
++void hmp_backup_cancel(Monitor *mon, const QDict *qdict);
+ void hmp_block_job_set_speed(Monitor *mon, const QDict *qdict);
+ void hmp_block_job_cancel(Monitor *mon, const QDict *qdict);
+ void hmp_block_job_pause(Monitor *mon, const QDict *qdict);
+diff --git a/include/block/block_int.h b/include/block/block_int.h
+index db4650e..0f79b51 100644
+--- a/include/block/block_int.h
++++ b/include/block/block_int.h
+@@ -59,7 +59,7 @@
+
+ #define BLOCK_PROBE_BUF_SIZE 512
+
+-typedef int BackupDumpFunc(void *opaque, BlockDriverState *bs,
++typedef int BackupDumpFunc(void *opaque, BlockBackend *be,
+ int64_t sector_num, int n_sectors, unsigned char *buf);
+
+ enum BdrvTrackedRequestType {
+diff --git a/qapi-schema.json b/qapi-schema.json
+index 518c2ea..89d9ea6 100644
+--- a/qapi-schema.json
++++ b/qapi-schema.json
+@@ -356,6 +356,95 @@
+ ##
+ { 'command': 'query-events', 'returns': ['EventInfo'] }
+
++# @BackupStatus:
++#
++# Detailed backup status.
++#
++# @status: #optional string describing the current backup status.
++# This can be 'active', 'done', 'error'. If this field is not
++# returned, no backup process has been initiated
++#
++# @errmsg: #optional error message (only returned if status is 'error')
++#
++# @total: #optional total amount of bytes involved in the backup process
++#
++# @transferred: #optional amount of bytes already backed up.
++#
++# @zero-bytes: #optional amount of 'zero' bytes detected.
++#
++# @start-time: #optional time (epoch) when backup job started.
++#
++# @end-time: #optional time (epoch) when backup job finished.
++#
++# @backupfile: #optional backup file name
++#
++# @uuid: #optional uuid for this backup job
++#
++##
++{ 'struct': 'BackupStatus',
++ 'data': {'*status': 'str', '*errmsg': 'str', '*total': 'int',
++ '*transferred': 'int', '*zero-bytes': 'int',
++ '*start-time': 'int', '*end-time': 'int',
++ '*backup-file': 'str', '*uuid': 'str' } }
++
++##
++# @BackupFormat
++#
++# An enumeration of supported backup formats.
++#
++# @vma: Proxmox vma backup format
++##
++{ 'enum': 'BackupFormat',
++ 'data': [ 'vma' ] }
++
++##
++# @backup:
++#
++# Starts a VM backup.
++#
++# @backup-file: the backup file name
++#
++# @format: format of the backup file
++#
++# @config-filename: #optional name of a configuration file to include into
++# the backup archive.
++#
++# @speed: #optional the maximum speed, in bytes per second
++#
++# @devlist: #optional list of block device names (separated by ',', ';'
++# or ':'). By default the backup includes all writable block devices.
++#
++# Returns: the uuid of the backup job
++#
++##
++{ 'command': 'backup', 'data': { 'backup-file': 'str',
++ '*format': 'BackupFormat',
++ '*config-file': 'str',
++ '*devlist': 'str', '*speed': 'int' },
++ 'returns': 'UuidInfo' }
++
++##
++# @query-backup
++#
++# Returns information about current/last backup task.
++#
++# Returns: @BackupStatus
++#
++##
++{ 'command': 'query-backup', 'returns': 'BackupStatus' }
++
++##
++# @backup-cancel
++#
++# Cancel the current executing backup process.
++#
++# Returns: nothing on success
++#
++# Notes: This command succeeds even if there is no backup process running.
++#
++##
++{ 'command': 'backup-cancel' }
++
+ ##
+ # @MigrationStats
+ #
+diff --git a/qmp-commands.hx b/qmp-commands.hx
+index 6de28d4..a8e8522 100644
+--- a/qmp-commands.hx
++++ b/qmp-commands.hx
+@@ -1314,6 +1314,24 @@ Example:
+ EQMP
+
+ {
++ .name = "backup",
++ .args_type = "backup-file:s,format:s?,config-file:F?,speed:o?,devlist:s?",
++ .mhandler.cmd_new = qmp_marshal_backup,
++ },
++
++ {
++ .name = "backup-cancel",
++ .args_type = "",
++ .mhandler.cmd_new = qmp_marshal_backup_cancel,
++ },
++
++ {
++ .name = "query-backup",
++ .args_type = "",
++ .mhandler.cmd_new = qmp_marshal_query_backup,
++ },
++
++ {
+ .name = "block-job-set-speed",
+ .args_type = "device:B,speed:o",
+ .mhandler.cmd_new = qmp_marshal_block_job_set_speed,
+--
+2.1.4
+
+++ /dev/null
-From 621e6e6521a550fd3be48ee33b0d1e9a39c0ed5c Mon Sep 17 00:00:00 2001
-From: Wolfgang Bumiller <w.bumiller@proxmox.com>
-Date: Wed, 9 Dec 2015 15:04:57 +0100
-Subject: [PATCH 15/55] backup: modify job api
-
-Introduces a BackupDump function callback and a pause_count
-for backup_start. For a dump-backup the target parameter
-can now be NULL so access to target needs to be guarded now.
----
- block/backup.c | 90 ++++++++++++++++++++++++++++++-----------------
- blockdev.c | 6 ++--
- include/block/block_int.h | 5 +++
- 3 files changed, 65 insertions(+), 36 deletions(-)
-
-diff --git a/block/backup.c b/block/backup.c
-index 370c285..c38b520 100644
---- a/block/backup.c
-+++ b/block/backup.c
-@@ -41,6 +41,7 @@ typedef struct BackupBlockJob {
- BdrvDirtyBitmap *sync_bitmap;
- MirrorSyncMode sync_mode;
- RateLimit limit;
-+ BackupDumpFunc *dump_cb;
- BlockdevOnError on_source_error;
- BlockdevOnError on_target_error;
- CoRwlock flush_rwlock;
-@@ -153,14 +154,21 @@ static int coroutine_fn backup_do_cow(BlockDriverState *bs,
- goto out;
- }
-
-+ int64_t start_sec = start * sectors_per_cluster;
- if (buffer_is_zero(iov.iov_base, iov.iov_len)) {
-- ret = bdrv_co_write_zeroes(job->target,
-- start * sectors_per_cluster,
-- n, BDRV_REQ_MAY_UNMAP);
-+ if (job->dump_cb) {
-+ ret = job->dump_cb(job->common.opaque, job->target, start_sec, n, NULL);
-+ }
-+ if (job->target) {
-+ ret = bdrv_co_write_zeroes(job->target, start_sec, n, BDRV_REQ_MAY_UNMAP);
-+ }
- } else {
-- ret = bdrv_co_writev(job->target,
-- start * sectors_per_cluster, n,
-- &bounce_qiov);
-+ if (job->dump_cb) {
-+ ret = job->dump_cb(job->common.opaque, job->target, start_sec, n, bounce_buffer);
-+ }
-+ if (job->target) {
-+ ret = bdrv_co_writev(job->target, start_sec, n, &bounce_qiov);
-+ }
- }
- if (ret < 0) {
- trace_backup_do_cow_write_fail(job, start, ret);
-@@ -222,7 +230,7 @@ static void backup_iostatus_reset(BlockJob *job)
- {
- BackupBlockJob *s = container_of(job, BackupBlockJob, common);
-
-- if (s->target->blk) {
-+ if (s->target && s->target->blk) {
- blk_iostatus_reset(s->target->blk);
- }
- }
-@@ -274,9 +282,11 @@ static BlockErrorAction backup_error_action(BackupBlockJob *job,
- if (read) {
- return block_job_error_action(&job->common, job->common.bs,
- job->on_source_error, true, error);
-- } else {
-+ } else if (job->target) {
- return block_job_error_action(&job->common, job->target,
- job->on_target_error, false, error);
-+ } else {
-+ return BLOCK_ERROR_ACTION_REPORT;
- }
- }
-
-@@ -404,9 +414,12 @@ static void coroutine_fn backup_run(void *opaque)
-
- job->done_bitmap = bitmap_new(end);
-
-- if (target->blk) {
-- blk_set_on_error(target->blk, on_target_error, on_target_error);
-- blk_iostatus_enable(target->blk);
-+ if (target) {
-+ bdrv_set_enable_write_cache(target, true);
-+ if (target->blk) {
-+ blk_set_on_error(target->blk, on_target_error, on_target_error);
-+ blk_iostatus_enable(target->blk);
-+ }
- }
-
- bdrv_add_before_write_notifier(bs, &before_write);
-@@ -484,10 +497,12 @@ static void coroutine_fn backup_run(void *opaque)
- qemu_co_rwlock_unlock(&job->flush_rwlock);
- g_free(job->done_bitmap);
-
-- if (target->blk) {
-- blk_iostatus_disable(target->blk);
-+ if (target) {
-+ if (target->blk) {
-+ blk_iostatus_disable(target->blk);
-+ }
-+ bdrv_op_unblock_all(target, job->common.blocker);
- }
-- bdrv_op_unblock_all(target, job->common.blocker);
-
- data = g_malloc(sizeof(*data));
- data->ret = ret;
-@@ -499,7 +514,9 @@ void backup_start(BlockDriverState *bs, BlockDriverState *target,
- BdrvDirtyBitmap *sync_bitmap,
- BlockdevOnError on_source_error,
- BlockdevOnError on_target_error,
-+ BackupDumpFunc *dump_cb,
- BlockCompletionFunc *cb, void *opaque,
-+ int pause_count,
- BlockJobTxn *txn, Error **errp)
- {
- int64_t len;
-@@ -508,7 +525,7 @@ void backup_start(BlockDriverState *bs, BlockDriverState *target,
- int ret;
-
- assert(bs);
-- assert(target);
-+ assert(target || dump_cb);
- assert(cb);
-
- if (bs == target) {
-@@ -529,7 +546,7 @@ void backup_start(BlockDriverState *bs, BlockDriverState *target,
- return;
- }
-
-- if (!bdrv_is_inserted(target)) {
-+ if (target && !bdrv_is_inserted(target)) {
- error_setg(errp, "Device is not inserted: %s",
- bdrv_get_device_name(target));
- return;
-@@ -539,7 +556,7 @@ void backup_start(BlockDriverState *bs, BlockDriverState *target,
- return;
- }
-
-- if (bdrv_op_is_blocked(target, BLOCK_OP_TYPE_BACKUP_TARGET, errp)) {
-+ if (target && bdrv_op_is_blocked(target, BLOCK_OP_TYPE_BACKUP_TARGET, errp)) {
- return;
- }
-
-@@ -574,6 +591,7 @@ void backup_start(BlockDriverState *bs, BlockDriverState *target,
- goto error;
- }
-
-+ job->dump_cb = dump_cb;
- job->on_source_error = on_source_error;
- job->on_target_error = on_target_error;
- job->target = target;
-@@ -581,25 +599,31 @@ void backup_start(BlockDriverState *bs, BlockDriverState *target,
- job->sync_bitmap = sync_mode == MIRROR_SYNC_MODE_INCREMENTAL ?
- sync_bitmap : NULL;
-
-- /* If there is no backing file on the target, we cannot rely on COW if our
-- * backup cluster size is smaller than the target cluster size. Even for
-- * targets with a backing file, try to avoid COW if possible. */
-- ret = bdrv_get_info(job->target, &bdi);
-- if (ret < 0 && !target->backing) {
-- error_setg_errno(errp, -ret,
-- "Couldn't determine the cluster size of the target image, "
-- "which has no backing file");
-- error_append_hint(errp,
-- "Aborting, since this may create an unusable destination image\n");
-- goto error;
-- } else if (ret < 0 && target->backing) {
-- /* Not fatal; just trudge on ahead. */
-- job->cluster_size = BACKUP_CLUSTER_SIZE_DEFAULT;
-+ if (target) {
-+ /* If there is no backing file on the target, we cannot rely on COW if our
-+ * backup cluster size is smaller than the target cluster size. Even for
-+ * targets with a backing file, try to avoid COW if possible. */
-+ ret = bdrv_get_info(job->target, &bdi);
-+ if (ret < 0 && !target->backing) {
-+ error_setg_errno(errp, -ret,
-+ "Couldn't determine the cluster size of the target image, "
-+ "which has no backing file");
-+ error_append_hint(errp,
-+ "Aborting, since this may create an unusable destination image\n");
-+ goto error;
-+ } else if (ret < 0 && target->backing) {
-+ /* Not fatal; just trudge on ahead. */
-+ job->cluster_size = BACKUP_CLUSTER_SIZE_DEFAULT;
-+ } else {
-+ job->cluster_size = MAX(BACKUP_CLUSTER_SIZE_DEFAULT, bdi.cluster_size);
-+ }
-+
-+ bdrv_op_block_all(target, job->common.blocker);
- } else {
-- job->cluster_size = MAX(BACKUP_CLUSTER_SIZE_DEFAULT, bdi.cluster_size);
-+ job->cluster_size = BACKUP_CLUSTER_SIZE_DEFAULT;
- }
-
-- bdrv_op_block_all(target, job->common.blocker);
-+ job->common.pause_count = pause_count;
- job->common.len = len;
- job->common.co = qemu_coroutine_create(backup_run);
- block_job_txn_add_job(txn, &job->common);
-diff --git a/blockdev.c b/blockdev.c
-index 260a6f5..dfc5204 100644
---- a/blockdev.c
-+++ b/blockdev.c
-@@ -3292,8 +3292,8 @@ static void do_drive_backup(const char *device, const char *target,
- }
-
- backup_start(bs, target_bs, speed, sync, bmap,
-- on_source_error, on_target_error,
-- block_job_cb, bs, txn, &local_err);
-+ on_source_error, on_target_error, NULL,
-+ block_job_cb, bs, 0, txn, &local_err);
- if (local_err != NULL) {
- bdrv_unref(target_bs);
- error_propagate(errp, local_err);
-@@ -3382,7 +3382,7 @@ void do_blockdev_backup(const char *device, const char *target,
- bdrv_ref(target_bs);
- bdrv_set_aio_context(target_bs, aio_context);
- backup_start(bs, target_bs, speed, sync, NULL, on_source_error,
-- on_target_error, block_job_cb, bs, txn, &local_err);
-+ on_target_error, NULL, block_job_cb, bs, 0, txn, &local_err);
- if (local_err != NULL) {
- bdrv_unref(target_bs);
- error_propagate(errp, local_err);
-diff --git a/include/block/block_int.h b/include/block/block_int.h
-index 10d8759..2dd866b 100644
---- a/include/block/block_int.h
-+++ b/include/block/block_int.h
-@@ -60,6 +60,9 @@
-
- #define BLOCK_PROBE_BUF_SIZE 512
-
-+typedef int BackupDumpFunc(void *opaque, BlockDriverState *bs,
-+ int64_t sector_num, int n_sectors, unsigned char *buf);
-+
- enum BdrvTrackedRequestType {
- BDRV_TRACKED_READ,
- BDRV_TRACKED_WRITE,
-@@ -703,7 +706,9 @@ void backup_start(BlockDriverState *bs, BlockDriverState *target,
- BdrvDirtyBitmap *sync_bitmap,
- BlockdevOnError on_source_error,
- BlockdevOnError on_target_error,
-+ BackupDumpFunc *dump_cb,
- BlockCompletionFunc *cb, void *opaque,
-+ int pause_count,
- BlockJobTxn *txn, Error **errp);
-
- void hmp_drive_add_node(Monitor *mon, const char *optstr);
---
-2.1.4
-
+++ /dev/null
-From 30e5d63f13a3f7adcd1f473fa0238c3e2e915114 Mon Sep 17 00:00:00 2001
-From: Wolfgang Bumiller <w.bumiller@proxmox.com>
-Date: Wed, 9 Dec 2015 15:20:56 +0100
-Subject: [PATCH 16/55] backup: add pve monitor commands
-
----
- blockdev.c | 438 +++++++++++++++++++++++++++++++++++++++++++++++++++
- hmp-commands-info.hx | 13 ++
- hmp-commands.hx | 29 ++++
- hmp.c | 61 +++++++
- hmp.h | 3 +
- qapi-schema.json | 89 +++++++++++
- qmp-commands.hx | 18 +++
- 7 files changed, 651 insertions(+)
-
-diff --git a/blockdev.c b/blockdev.c
-index dfc5204..115524f 100644
---- a/blockdev.c
-+++ b/blockdev.c
-@@ -52,6 +52,7 @@
- #include "sysemu/arch_init.h"
- #include "qemu/cutils.h"
- #include "qemu/help_option.h"
-+#include "vma.h"
-
- static QTAILQ_HEAD(, BlockDriverState) monitor_bdrv_states =
- QTAILQ_HEAD_INITIALIZER(monitor_bdrv_states);
-@@ -2989,6 +2990,443 @@ static void block_job_cb(void *opaque, int ret)
- }
- }
-
-+/* PVE backup related function */
-+
-+static struct PVEBackupState {
-+ Error *error;
-+ bool cancel;
-+ uuid_t uuid;
-+ char uuid_str[37];
-+ int64_t speed;
-+ time_t start_time;
-+ time_t end_time;
-+ char *backup_file;
-+ VmaWriter *vmaw;
-+ GList *di_list;
-+ size_t total;
-+ size_t transferred;
-+ size_t zero_bytes;
-+} backup_state;
-+
-+typedef struct PVEBackupDevInfo {
-+ BlockDriverState *bs;
-+ size_t size;
-+ uint8_t dev_id;
-+ //bool started;
-+ bool completed;
-+} PVEBackupDevInfo;
-+
-+static void pvebackup_run_next_job(void);
-+
-+static int pvebackup_dump_cb(void *opaque, BlockDriverState *target,
-+ int64_t sector_num, int n_sectors,
-+ unsigned char *buf)
-+{
-+ PVEBackupDevInfo *di = opaque;
-+
-+ if (sector_num & 0x7f) {
-+ if (!backup_state.error) {
-+ error_setg(&backup_state.error,
-+ "got unaligned write inside backup dump "
-+ "callback (sector %ld)", sector_num);
-+ }
-+ return -1; // not aligned to cluster size
-+ }
-+
-+ int64_t cluster_num = sector_num >> 7;
-+ int size = n_sectors * BDRV_SECTOR_SIZE;
-+
-+ int ret = -1;
-+
-+ if (backup_state.vmaw) {
-+ size_t zero_bytes = 0;
-+ ret = vma_writer_write(backup_state.vmaw, di->dev_id, cluster_num,
-+ buf, &zero_bytes);
-+ backup_state.zero_bytes += zero_bytes;
-+ } else {
-+ ret = size;
-+ if (!buf) {
-+ backup_state.zero_bytes += size;
-+ }
-+ }
-+
-+ backup_state.transferred += size;
-+
-+ return ret;
-+}
-+
-+static void pvebackup_cleanup(void)
-+{
-+ backup_state.end_time = time(NULL);
-+
-+ if (backup_state.vmaw) {
-+ Error *local_err = NULL;
-+ vma_writer_close(backup_state.vmaw, &local_err);
-+ error_propagate(&backup_state.error, local_err);
-+ backup_state.vmaw = NULL;
-+ }
-+
-+ if (backup_state.di_list) {
-+ GList *l = backup_state.di_list;
-+ while (l) {
-+ PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data;
-+ l = g_list_next(l);
-+ g_free(di);
-+ }
-+ g_list_free(backup_state.di_list);
-+ backup_state.di_list = NULL;
-+ }
-+}
-+
-+static void pvebackup_complete_cb(void *opaque, int ret)
-+{
-+ PVEBackupDevInfo *di = opaque;
-+
-+ assert(backup_state.vmaw);
-+
-+ di->completed = true;
-+
-+ if (ret < 0 && !backup_state.error) {
-+ error_setg(&backup_state.error, "job failed with err %d - %s",
-+ ret, strerror(-ret));
-+ }
-+
-+ BlockDriverState *bs = di->bs;
-+
-+ di->bs = NULL;
-+
-+ vma_writer_close_stream(backup_state.vmaw, di->dev_id);
-+
-+ block_job_cb(bs, ret);
-+
-+ if (!backup_state.cancel) {
-+ pvebackup_run_next_job();
-+ }
-+}
-+
-+static void pvebackup_cancel(void *opaque)
-+{
-+ backup_state.cancel = true;
-+
-+ if (!backup_state.error) {
-+ error_setg(&backup_state.error, "backup cancelled");
-+ }
-+
-+ /* drain all i/o (awake jobs waiting for aio) */
-+ bdrv_drain_all();
-+
-+ GList *l = backup_state.di_list;
-+ while (l) {
-+ PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data;
-+ l = g_list_next(l);
-+ if (!di->completed && di->bs) {
-+ BlockJob *job = di->bs->job;
-+ if (job) {
-+ if (!di->completed) {
-+ block_job_cancel_sync(job);
-+ }
-+ }
-+ }
-+ }
-+
-+ pvebackup_cleanup();
-+}
-+
-+void qmp_backup_cancel(Error **errp)
-+{
-+ Coroutine *co = qemu_coroutine_create(pvebackup_cancel);
-+ qemu_coroutine_enter(co, NULL);
-+
-+ while (backup_state.vmaw) {
-+ /* vma writer use main aio context */
-+ aio_poll(qemu_get_aio_context(), true);
-+ }
-+}
-+
-+static void pvebackup_run_next_job(void)
-+{
-+ GList *l = backup_state.di_list;
-+ while (l) {
-+ PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data;
-+ l = g_list_next(l);
-+ if (!di->completed && di->bs && di->bs->job) {
-+ BlockJob *job = di->bs->job;
-+ if (block_job_is_paused(job)) {
-+ bool cancel = backup_state.error || backup_state.cancel;
-+ if (cancel) {
-+ block_job_cancel(job);
-+ } else {
-+ block_job_resume(job);
-+ }
-+ }
-+ return;
-+ }
-+ }
-+
-+ pvebackup_cleanup();
-+}
-+
-+UuidInfo *qmp_backup(const char *backup_file, bool has_format,
-+ BackupFormat format,
-+ bool has_config_file, const char *config_file,
-+ bool has_devlist, const char *devlist,
-+ bool has_speed, int64_t speed, Error **errp)
-+{
-+ BlockBackend *blk;
-+ BlockDriverState *bs = NULL;
-+ Error *local_err = NULL;
-+ uuid_t uuid;
-+ VmaWriter *vmaw = NULL;
-+ gchar **devs = NULL;
-+ GList *di_list = NULL;
-+ GList *l;
-+ UuidInfo *uuid_info;
-+
-+ if (backup_state.di_list) {
-+ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
-+ "previous backup not finished");
-+ return NULL;
-+ }
-+
-+ /* Todo: try to auto-detect format based on file name */
-+ format = has_format ? format : BACKUP_FORMAT_VMA;
-+
-+ if (format != BACKUP_FORMAT_VMA) {
-+ error_set(errp, ERROR_CLASS_GENERIC_ERROR, "unknown backup format");
-+ return NULL;
-+ }
-+
-+ if (has_devlist) {
-+ devs = g_strsplit_set(devlist, ",;:", -1);
-+
-+ gchar **d = devs;
-+ while (d && *d) {
-+ blk = blk_by_name(*d);
-+ if (blk) {
-+ bs = blk_bs(blk);
-+ if (bdrv_is_read_only(bs)) {
-+ error_setg(errp, "Node '%s' is read only", *d);
-+ goto err;
-+ }
-+ if (!bdrv_is_inserted(bs)) {
-+ error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, *d);
-+ goto err;
-+ }
-+ PVEBackupDevInfo *di = g_new0(PVEBackupDevInfo, 1);
-+ di->bs = bs;
-+ di_list = g_list_append(di_list, di);
-+ } else {
-+ error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
-+ "Device '%s' not found", *d);
-+ goto err;
-+ }
-+ d++;
-+ }
-+
-+ } else {
-+
-+ bs = NULL;
-+ while ((bs = bdrv_next(bs))) {
-+
-+ if (!bdrv_is_inserted(bs) || bdrv_is_read_only(bs)) {
-+ continue;
-+ }
-+
-+ PVEBackupDevInfo *di = g_new0(PVEBackupDevInfo, 1);
-+ di->bs = bs;
-+ di_list = g_list_append(di_list, di);
-+ }
-+ }
-+
-+ if (!di_list) {
-+ error_set(errp, ERROR_CLASS_GENERIC_ERROR, "empty device list");
-+ goto err;
-+ }
-+
-+ size_t total = 0;
-+
-+ l = di_list;
-+ while (l) {
-+ PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data;
-+ l = g_list_next(l);
-+ if (bdrv_op_is_blocked(di->bs, BLOCK_OP_TYPE_BACKUP_SOURCE, errp)) {
-+ goto err;
-+ }
-+
-+ ssize_t size = bdrv_getlength(di->bs);
-+ if (size < 0) {
-+ error_setg_errno(errp, -di->size, "bdrv_getlength failed");
-+ goto err;
-+ }
-+ di->size = size;
-+ total += size;
-+ }
-+
-+ uuid_generate(uuid);
-+
-+ vmaw = vma_writer_create(backup_file, uuid, &local_err);
-+ if (!vmaw) {
-+ if (local_err) {
-+ error_propagate(errp, local_err);
-+ }
-+ goto err;
-+ }
-+
-+ /* register all devices for vma writer */
-+ l = di_list;
-+ while (l) {
-+ PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data;
-+ l = g_list_next(l);
-+
-+ const char *devname = bdrv_get_device_name(di->bs);
-+ di->dev_id = vma_writer_register_stream(vmaw, devname, di->size);
-+ if (di->dev_id <= 0) {
-+ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
-+ "register_stream failed");
-+ goto err;
-+ }
-+ }
-+
-+ /* add configuration file to archive */
-+ if (has_config_file) {
-+ char *cdata = NULL;
-+ gsize clen = 0;
-+ GError *err = NULL;
-+ if (!g_file_get_contents(config_file, &cdata, &clen, &err)) {
-+ error_setg(errp, "unable to read file '%s'", config_file);
-+ goto err;
-+ }
-+
-+ const char *basename = g_path_get_basename(config_file);
-+ if (vma_writer_add_config(vmaw, basename, cdata, clen) != 0) {
-+ error_setg(errp, "unable to add config data to vma archive");
-+ g_free(cdata);
-+ goto err;
-+ }
-+ g_free(cdata);
-+ }
-+
-+ /* initialize global backup_state now */
-+
-+ backup_state.cancel = false;
-+
-+ if (backup_state.error) {
-+ error_free(backup_state.error);
-+ backup_state.error = NULL;
-+ }
-+
-+ backup_state.speed = (has_speed && speed > 0) ? speed : 0;
-+
-+ backup_state.start_time = time(NULL);
-+ backup_state.end_time = 0;
-+
-+ if (backup_state.backup_file) {
-+ g_free(backup_state.backup_file);
-+ }
-+ backup_state.backup_file = g_strdup(backup_file);
-+
-+ backup_state.vmaw = vmaw;
-+
-+ uuid_copy(backup_state.uuid, uuid);
-+ uuid_unparse_lower(uuid, backup_state.uuid_str);
-+
-+ backup_state.di_list = di_list;
-+
-+ backup_state.total = total;
-+ backup_state.transferred = 0;
-+ backup_state.zero_bytes = 0;
-+
-+ /* start all jobs (paused state) */
-+ l = di_list;
-+ while (l) {
-+ PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data;
-+ l = g_list_next(l);
-+
-+ backup_start(di->bs, NULL, speed, MIRROR_SYNC_MODE_FULL, NULL,
-+ BLOCKDEV_ON_ERROR_REPORT, BLOCKDEV_ON_ERROR_REPORT,
-+ pvebackup_dump_cb, pvebackup_complete_cb, di,
-+ 1, &local_err);
-+ if (local_err != NULL) {
-+ error_setg(&backup_state.error, "backup_job_create failed");
-+ pvebackup_cancel(NULL);
-+ }
-+ }
-+
-+ if (!backup_state.error) {
-+ pvebackup_run_next_job(); // run one job
-+ }
-+
-+ uuid_info = g_malloc0(sizeof(*uuid_info));
-+ uuid_info->UUID = g_strdup(backup_state.uuid_str);
-+ return uuid_info;
-+
-+err:
-+
-+ l = di_list;
-+ while (l) {
-+ g_free(l->data);
-+ l = g_list_next(l);
-+ }
-+ g_list_free(di_list);
-+
-+ if (devs) {
-+ g_strfreev(devs);
-+ }
-+
-+ if (vmaw) {
-+ Error *err = NULL;
-+ vma_writer_close(vmaw, &err);
-+ unlink(backup_file);
-+ }
-+
-+ return NULL;
-+}
-+
-+BackupStatus *qmp_query_backup(Error **errp)
-+{
-+ BackupStatus *info = g_malloc0(sizeof(*info));
-+
-+ if (!backup_state.start_time) {
-+ /* not started, return {} */
-+ return info;
-+ }
-+
-+ info->has_status = true;
-+ info->has_start_time = true;
-+ info->start_time = backup_state.start_time;
-+
-+ if (backup_state.backup_file) {
-+ info->has_backup_file = true;
-+ info->backup_file = g_strdup(backup_state.backup_file);
-+ }
-+
-+ info->has_uuid = true;
-+ info->uuid = g_strdup(backup_state.uuid_str);
-+
-+ if (backup_state.end_time) {
-+ if (backup_state.error) {
-+ info->status = g_strdup("error");
-+ info->has_errmsg = true;
-+ info->errmsg = g_strdup(error_get_pretty(backup_state.error));
-+ } else {
-+ info->status = g_strdup("done");
-+ }
-+ info->has_end_time = true;
-+ info->end_time = backup_state.end_time;
-+ } else {
-+ info->status = g_strdup("active");
-+ }
-+
-+ info->has_total = true;
-+ info->total = backup_state.total;
-+ info->has_zero_bytes = true;
-+ info->zero_bytes = backup_state.zero_bytes;
-+ info->has_transferred = true;
-+ info->transferred = backup_state.transferred;
-+
-+ return info;
-+}
-+
- void qmp_block_stream(const char *device,
- bool has_base, const char *base,
- bool has_backing_file, const char *backing_file,
-diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx
-index 52539c3..f872262 100644
---- a/hmp-commands-info.hx
-+++ b/hmp-commands-info.hx
-@@ -502,6 +502,19 @@ STEXI
- Show CPU statistics.
- ETEXI
-
-+ {
-+ .name = "backup",
-+ .args_type = "",
-+ .params = "",
-+ .help = "show backup status",
-+ .mhandler.cmd = hmp_info_backup,
-+ },
-+
-+STEXI
-+@item info backup
-+show backup status
-+ETEXI
-+
- #if defined(CONFIG_SLIRP)
- {
- .name = "usernet",
-diff --git a/hmp-commands.hx b/hmp-commands.hx
-index 4f4f60a..63c44f5 100644
---- a/hmp-commands.hx
-+++ b/hmp-commands.hx
-@@ -87,6 +87,35 @@ STEXI
- Copy data from a backing file into a block device.
- ETEXI
-
-+ {
-+ .name = "backup",
-+ .args_type = "backupfile:s,speed:o?,devlist:s?",
-+ .params = "backupfile [speed [devlist]]",
-+ .help = "create a VM Backup.",
-+ .mhandler.cmd = hmp_backup,
-+ },
-+
-+STEXI
-+@item backup
-+@findex backup
-+Create a VM backup.
-+ETEXI
-+
-+ {
-+ .name = "backup_cancel",
-+ .args_type = "",
-+ .params = "",
-+ .help = "cancel the current VM backup",
-+ .mhandler.cmd = hmp_backup_cancel,
-+ },
-+
-+STEXI
-+@item backup_cancel
-+@findex backup_cancel
-+Cancel the current VM backup.
-+
-+ETEXI
-+
- {
- .name = "block_job_set_speed",
- .args_type = "device:B,speed:o",
-diff --git a/hmp.c b/hmp.c
-index fe80757..2d4bf97 100644
---- a/hmp.c
-+++ b/hmp.c
-@@ -148,6 +148,44 @@ void hmp_info_mice(Monitor *mon, const QDict *qdict)
- qapi_free_MouseInfoList(mice_list);
- }
-
-+void hmp_info_backup(Monitor *mon, const QDict *qdict)
-+{
-+ BackupStatus *info;
-+
-+ info = qmp_query_backup(NULL);
-+ if (info->has_status) {
-+ if (info->has_errmsg) {
-+ monitor_printf(mon, "Backup status: %s - %s\n",
-+ info->status, info->errmsg);
-+ } else {
-+ monitor_printf(mon, "Backup status: %s\n", info->status);
-+ }
-+ }
-+
-+ if (info->has_backup_file) {
-+ monitor_printf(mon, "Start time: %s", ctime(&info->start_time));
-+ if (info->end_time) {
-+ monitor_printf(mon, "End time: %s", ctime(&info->end_time));
-+ }
-+
-+ int per = (info->has_total && info->total &&
-+ info->has_transferred && info->transferred) ?
-+ (info->transferred * 100)/info->total : 0;
-+ int zero_per = (info->has_total && info->total &&
-+ info->has_zero_bytes && info->zero_bytes) ?
-+ (info->zero_bytes * 100)/info->total : 0;
-+ monitor_printf(mon, "Backup file: %s\n", info->backup_file);
-+ monitor_printf(mon, "Backup uuid: %s\n", info->uuid);
-+ monitor_printf(mon, "Total size: %zd\n", info->total);
-+ monitor_printf(mon, "Transferred bytes: %zd (%d%%)\n",
-+ info->transferred, per);
-+ monitor_printf(mon, "Zero bytes: %zd (%d%%)\n",
-+ info->zero_bytes, zero_per);
-+ }
-+
-+ qapi_free_BackupStatus(info);
-+}
-+
- void hmp_info_migrate(Monitor *mon, const QDict *qdict)
- {
- MigrationInfo *info;
-@@ -1476,6 +1514,29 @@ void hmp_block_stream(Monitor *mon, const QDict *qdict)
- hmp_handle_error(mon, &error);
- }
-
-+void hmp_backup_cancel(Monitor *mon, const QDict *qdict)
-+{
-+ Error *error = NULL;
-+
-+ qmp_backup_cancel(&error);
-+
-+ hmp_handle_error(mon, &error);
-+}
-+
-+void hmp_backup(Monitor *mon, const QDict *qdict)
-+{
-+ Error *error = NULL;
-+
-+ const char *backup_file = qdict_get_str(qdict, "backupfile");
-+ const char *devlist = qdict_get_try_str(qdict, "devlist");
-+ int64_t speed = qdict_get_try_int(qdict, "speed", 0);
-+
-+ qmp_backup(backup_file, true, BACKUP_FORMAT_VMA, false, NULL, !!devlist,
-+ devlist, qdict_haskey(qdict, "speed"), speed, &error);
-+
-+ hmp_handle_error(mon, &error);
-+}
-+
- void hmp_block_job_set_speed(Monitor *mon, const QDict *qdict)
- {
- Error *error = NULL;
-diff --git a/hmp.h b/hmp.h
-index 093d65f..c708b3f 100644
---- a/hmp.h
-+++ b/hmp.h
-@@ -30,6 +30,7 @@ void hmp_info_migrate(Monitor *mon, const QDict *qdict);
- void hmp_info_migrate_capabilities(Monitor *mon, const QDict *qdict);
- void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict);
- void hmp_info_migrate_cache_size(Monitor *mon, const QDict *qdict);
-+void hmp_info_backup(Monitor *mon, const QDict *qdict);
- void hmp_info_cpus(Monitor *mon, const QDict *qdict);
- void hmp_info_block(Monitor *mon, const QDict *qdict);
- void hmp_info_blockstats(Monitor *mon, const QDict *qdict);
-@@ -76,6 +77,8 @@ void hmp_eject(Monitor *mon, const QDict *qdict);
- void hmp_change(Monitor *mon, const QDict *qdict);
- void hmp_block_set_io_throttle(Monitor *mon, const QDict *qdict);
- void hmp_block_stream(Monitor *mon, const QDict *qdict);
-+void hmp_backup(Monitor *mon, const QDict *qdict);
-+void hmp_backup_cancel(Monitor *mon, const QDict *qdict);
- void hmp_block_job_set_speed(Monitor *mon, const QDict *qdict);
- void hmp_block_job_cancel(Monitor *mon, const QDict *qdict);
- void hmp_block_job_pause(Monitor *mon, const QDict *qdict);
-diff --git a/qapi-schema.json b/qapi-schema.json
-index f9ccfe4..4f673d4 100644
---- a/qapi-schema.json
-+++ b/qapi-schema.json
-@@ -356,6 +356,95 @@
- ##
- { 'command': 'query-events', 'returns': ['EventInfo'] }
-
-+# @BackupStatus:
-+#
-+# Detailed backup status.
-+#
-+# @status: #optional string describing the current backup status.
-+# This can be 'active', 'done', 'error'. If this field is not
-+# returned, no backup process has been initiated
-+#
-+# @errmsg: #optional error message (only returned if status is 'error')
-+#
-+# @total: #optional total amount of bytes involved in the backup process
-+#
-+# @transferred: #optional amount of bytes already backed up.
-+#
-+# @zero-bytes: #optional amount of 'zero' bytes detected.
-+#
-+# @start-time: #optional time (epoch) when backup job started.
-+#
-+# @end-time: #optional time (epoch) when backup job finished.
-+#
-+# @backupfile: #optional backup file name
-+#
-+# @uuid: #optional uuid for this backup job
-+#
-+##
-+{ 'struct': 'BackupStatus',
-+ 'data': {'*status': 'str', '*errmsg': 'str', '*total': 'int',
-+ '*transferred': 'int', '*zero-bytes': 'int',
-+ '*start-time': 'int', '*end-time': 'int',
-+ '*backup-file': 'str', '*uuid': 'str' } }
-+
-+##
-+# @BackupFormat
-+#
-+# An enumeration of supported backup formats.
-+#
-+# @vma: Proxmox vma backup format
-+##
-+{ 'enum': 'BackupFormat',
-+ 'data': [ 'vma' ] }
-+
-+##
-+# @backup:
-+#
-+# Starts a VM backup.
-+#
-+# @backup-file: the backup file name
-+#
-+# @format: format of the backup file
-+#
-+# @config-filename: #optional name of a configuration file to include into
-+# the backup archive.
-+#
-+# @speed: #optional the maximum speed, in bytes per second
-+#
-+# @devlist: #optional list of block device names (separated by ',', ';'
-+# or ':'). By default the backup includes all writable block devices.
-+#
-+# Returns: the uuid of the backup job
-+#
-+##
-+{ 'command': 'backup', 'data': { 'backup-file': 'str',
-+ '*format': 'BackupFormat',
-+ '*config-file': 'str',
-+ '*devlist': 'str', '*speed': 'int' },
-+ 'returns': 'UuidInfo' }
-+
-+##
-+# @query-backup
-+#
-+# Returns information about current/last backup task.
-+#
-+# Returns: @BackupStatus
-+#
-+##
-+{ 'command': 'query-backup', 'returns': 'BackupStatus' }
-+
-+##
-+# @backup-cancel
-+#
-+# Cancel the current executing backup process.
-+#
-+# Returns: nothing on success
-+#
-+# Notes: This command succeeds even if there is no backup process running.
-+#
-+##
-+{ 'command': 'backup-cancel' }
-+
- ##
- # @MigrationStats
- #
-diff --git a/qmp-commands.hx b/qmp-commands.hx
-index 9388578..3f03c0d 100644
---- a/qmp-commands.hx
-+++ b/qmp-commands.hx
-@@ -1279,6 +1279,24 @@ Example:
- EQMP
-
- {
-+ .name = "backup",
-+ .args_type = "backup-file:s,format:s?,config-file:F?,speed:o?,devlist:s?",
-+ .mhandler.cmd_new = qmp_marshal_input_backup,
-+ },
-+
-+ {
-+ .name = "backup-cancel",
-+ .args_type = "",
-+ .mhandler.cmd_new = qmp_marshal_input_backup_cancel,
-+ },
-+
-+ {
-+ .name = "query-backup",
-+ .args_type = "",
-+ .mhandler.cmd_new = qmp_marshal_input_query_backup,
-+ },
-+
-+ {
- .name = "block-job-set-speed",
- .args_type = "device:B,speed:o",
- .mhandler.cmd_new = qmp_marshal_block_job_set_speed,
---
-2.1.4
-
--- /dev/null
+From 996797aac7aa6bc93ed934a14fa3f8425e711318 Mon Sep 17 00:00:00 2001
+From: Wolfgang Bumiller <w.bumiller@proxmox.com>
+Date: Wed, 9 Dec 2015 15:21:54 +0100
+Subject: [PATCH 16/41] backup: vma: add dir format
+
+---
+ blockdev.c | 124 +++++++++++++++++++++++++++++++++++++++++--------------
+ hmp-commands.hx | 8 ++--
+ hmp.c | 4 +-
+ qapi-schema.json | 2 +-
+ vma.c | 2 +-
+ 5 files changed, 103 insertions(+), 37 deletions(-)
+
+diff --git a/blockdev.c b/blockdev.c
+index 5417bb0..d8b1db8 100644
+--- a/blockdev.c
++++ b/blockdev.c
+@@ -3001,6 +3001,8 @@ typedef struct PVEBackupDevInfo {
+ uint8_t dev_id;
+ //bool started;
+ bool completed;
++ char targetfile[PATH_MAX];
++ BlockDriverState *target;
+ } PVEBackupDevInfo;
+
+ static void pvebackup_run_next_job(void);
+@@ -3069,8 +3071,6 @@ static void pvebackup_complete_cb(void *opaque, int ret)
+ {
+ PVEBackupDevInfo *di = opaque;
+
+- assert(backup_state.vmaw);
+-
+ di->completed = true;
+
+ if (ret < 0 && !backup_state.error) {
+@@ -3081,8 +3081,11 @@ static void pvebackup_complete_cb(void *opaque, int ret)
+ BlockDriverState *bs = di->bs;
+
+ di->bs = NULL;
++ di->target = NULL;
+
+- vma_writer_close_stream(backup_state.vmaw, di->dev_id);
++ if (backup_state.vmaw) {
++ vma_writer_close_stream(backup_state.vmaw, di->dev_id);
++ }
+
+ block_job_cb(bs, ret);
+
+@@ -3162,6 +3165,7 @@ UuidInfo *qmp_backup(const char *backup_file, bool has_format,
+ {
+ BlockBackend *blk;
+ BlockDriverState *bs = NULL;
++ const char *backup_dir = NULL;
+ Error *local_err = NULL;
+ uuid_t uuid;
+ VmaWriter *vmaw = NULL;
+@@ -3179,11 +3183,6 @@ UuidInfo *qmp_backup(const char *backup_file, bool has_format,
+ /* Todo: try to auto-detect format based on file name */
+ format = has_format ? format : BACKUP_FORMAT_VMA;
+
+- if (format != BACKUP_FORMAT_VMA) {
+- error_set(errp, ERROR_CLASS_GENERIC_ERROR, "unknown backup format");
+- return NULL;
+- }
+-
+ if (has_devlist) {
+ devs = g_strsplit_set(devlist, ",;:", -1);
+
+@@ -3252,27 +3251,62 @@ UuidInfo *qmp_backup(const char *backup_file, bool has_format,
+
+ uuid_generate(uuid);
+
+- vmaw = vma_writer_create(backup_file, uuid, &local_err);
+- if (!vmaw) {
+- if (local_err) {
+- error_propagate(errp, local_err);
++ if (format == BACKUP_FORMAT_VMA) {
++ vmaw = vma_writer_create(backup_file, uuid, &local_err);
++ if (!vmaw) {
++ if (local_err) {
++ error_propagate(errp, local_err);
++ }
++ goto err;
+ }
+- goto err;
+- }
+
+- /* register all devices for vma writer */
+- l = di_list;
+- while (l) {
+- PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data;
+- l = g_list_next(l);
++ /* register all devices for vma writer */
++ l = di_list;
++ while (l) {
++ PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data;
++ l = g_list_next(l);
+
+- const char *devname = bdrv_get_device_name(di->bs);
+- di->dev_id = vma_writer_register_stream(vmaw, devname, di->size);
+- if (di->dev_id <= 0) {
+- error_set(errp, ERROR_CLASS_GENERIC_ERROR,
+- "register_stream failed");
++ const char *devname = bdrv_get_device_name(di->bs);
++ di->dev_id = vma_writer_register_stream(vmaw, devname, di->size);
++ if (di->dev_id <= 0) {
++ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
++ "register_stream failed");
++ goto err;
++ }
++ }
++ } else if (format == BACKUP_FORMAT_DIR) {
++ if (mkdir(backup_file, 0640) != 0) {
++ error_setg_errno(errp, errno, "can't create directory '%s'\n",
++ backup_file);
+ goto err;
+ }
++ backup_dir = backup_file;
++
++ l = di_list;
++ while (l) {
++ PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data;
++ l = g_list_next(l);
++
++ const char *devname = bdrv_get_device_name(di->bs);
++ snprintf(di->targetfile, PATH_MAX, "%s/%s.raw", backup_dir, devname);
++
++ int flags = BDRV_O_RDWR;
++ bdrv_img_create(di->targetfile, "raw", NULL, NULL, NULL,
++ di->size, flags, &local_err, false);
++ if (local_err) {
++ error_propagate(errp, local_err);
++ goto err;
++ }
++
++ di->target = bdrv_open(di->targetfile, NULL, NULL, flags, &local_err);
++ if (!di->target) {
++ error_propagate(errp, local_err);
++ goto err;
++ }
++ }
++ } else {
++ error_set(errp, ERROR_CLASS_GENERIC_ERROR, "unknown backup format");
++ goto err;
+ }
+
+ /* add configuration file to archive */
+@@ -3285,12 +3319,27 @@ UuidInfo *qmp_backup(const char *backup_file, bool has_format,
+ goto err;
+ }
+
+- const char *basename = g_path_get_basename(config_file);
+- if (vma_writer_add_config(vmaw, basename, cdata, clen) != 0) {
+- error_setg(errp, "unable to add config data to vma archive");
+- g_free(cdata);
+- goto err;
++ char *basename = g_path_get_basename(config_file);
++
++ if (format == BACKUP_FORMAT_VMA) {
++ if (vma_writer_add_config(vmaw, basename, cdata, clen) != 0) {
++ error_setg(errp, "unable to add config data to vma archive");
++ g_free(cdata);
++ g_free(basename);
++ goto err;
++ }
++ } else if (format == BACKUP_FORMAT_DIR) {
++ char config_path[PATH_MAX];
++ snprintf(config_path, PATH_MAX, "%s/%s", backup_dir, basename);
++ if (!g_file_set_contents(config_path, cdata, clen, &err)) {
++ error_setg(errp, "unable to write config file '%s'", config_path);
++ g_free(cdata);
++ g_free(basename);
++ goto err;
++ }
+ }
++
++ g_free(basename);
+ g_free(cdata);
+ }
+
+@@ -3330,7 +3379,7 @@ UuidInfo *qmp_backup(const char *backup_file, bool has_format,
+ PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data;
+ l = g_list_next(l);
+
+- backup_start(NULL, di->bs, NULL, speed, MIRROR_SYNC_MODE_FULL, NULL,
++ backup_start(NULL, di->bs, di->target, speed, MIRROR_SYNC_MODE_FULL, NULL,
+ BLOCKDEV_ON_ERROR_REPORT, BLOCKDEV_ON_ERROR_REPORT,
+ pvebackup_dump_cb, pvebackup_complete_cb, di,
+ 1, NULL, &local_err);
+@@ -3352,8 +3401,17 @@ err:
+
+ l = di_list;
+ while (l) {
+- g_free(l->data);
++ PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data;
+ l = g_list_next(l);
++
++ if (di->target) {
++ bdrv_unref(di->target);
++ }
++
++ if (di->targetfile[0]) {
++ unlink(di->targetfile);
++ }
++ g_free(di);
+ }
+ g_list_free(di_list);
+
+@@ -3367,6 +3425,10 @@ err:
+ unlink(backup_file);
+ }
+
++ if (backup_dir) {
++ rmdir(backup_dir);
++ }
++
+ return NULL;
+ }
+
+diff --git a/hmp-commands.hx b/hmp-commands.hx
+index 8f2f3e0..0e20ef9 100644
+--- a/hmp-commands.hx
++++ b/hmp-commands.hx
+@@ -89,9 +89,11 @@ ETEXI
+
+ {
+ .name = "backup",
+- .args_type = "backupfile:s,speed:o?,devlist:s?",
+- .params = "backupfile [speed [devlist]]",
+- .help = "create a VM Backup.",
++ .args_type = "directory:-d,backupfile:s,speed:o?,devlist:s?",
++ .params = "[-d] backupfile [speed [devlist]]",
++ .help = "create a VM Backup."
++ "\n\t\t\t Use -d to dump data into a directory instead"
++ "\n\t\t\t of using VMA format.",
+ .mhandler.cmd = hmp_backup,
+ },
+
+diff --git a/hmp.c b/hmp.c
+index 88cb4e5..c292354 100644
+--- a/hmp.c
++++ b/hmp.c
+@@ -1543,11 +1543,13 @@ void hmp_backup(Monitor *mon, const QDict *qdict)
+ {
+ Error *error = NULL;
+
++ int dir = qdict_get_try_bool(qdict, "directory", 0);
+ const char *backup_file = qdict_get_str(qdict, "backupfile");
+ const char *devlist = qdict_get_try_str(qdict, "devlist");
+ int64_t speed = qdict_get_try_int(qdict, "speed", 0);
+
+- qmp_backup(backup_file, true, BACKUP_FORMAT_VMA, false, NULL, !!devlist,
++ qmp_backup(backup_file, true, dir ? BACKUP_FORMAT_DIR : BACKUP_FORMAT_VMA,
++ false, NULL, !!devlist,
+ devlist, qdict_haskey(qdict, "speed"), speed, &error);
+
+ hmp_handle_error(mon, &error);
+diff --git a/qapi-schema.json b/qapi-schema.json
+index 89d9ea6..147137d 100644
+--- a/qapi-schema.json
++++ b/qapi-schema.json
+@@ -395,7 +395,7 @@
+ # @vma: Proxmox vma backup format
+ ##
+ { 'enum': 'BackupFormat',
+- 'data': [ 'vma' ] }
++ 'data': [ 'vma', 'dir' ] }
+
+ ##
+ # @backup:
+diff --git a/vma.c b/vma.c
+index 79bdd00..c88a4358 100644
+--- a/vma.c
++++ b/vma.c
+@@ -263,7 +263,7 @@ static int extract_content(int argc, char **argv)
+ g_free(statefn);
+ } else if (di) {
+ char *devfn = NULL;
+- int flags = BDRV_O_RDWR|BDRV_O_CACHE_WB;
++ int flags = BDRV_O_RDWR;
+ bool write_zero = true;
+
+ if (readmap) {
+--
+2.1.4
+
--- /dev/null
+From 042b664cc0237d51f188f7feed962df1e8a26910 Mon Sep 17 00:00:00 2001
+From: Wolfgang Bumiller <w.bumiller@proxmox.com>
+Date: Wed, 9 Dec 2015 15:22:19 +0100
+Subject: [PATCH 17/41] backup: do not return errors in dump callback
+
+---
+ blockdev.c | 26 ++++++++++++++++++++------
+ 1 file changed, 20 insertions(+), 6 deletions(-)
+
+diff --git a/blockdev.c b/blockdev.c
+index d8b1db8..fb71cdc 100644
+--- a/blockdev.c
++++ b/blockdev.c
+@@ -3013,6 +3013,11 @@ static int pvebackup_dump_cb(void *opaque, BlockBackend *target,
+ {
+ PVEBackupDevInfo *di = opaque;
+
++ int size = n_sectors * BDRV_SECTOR_SIZE;
++ if (backup_state.cancel) {
++ return size; // return success
++ }
++
+ if (sector_num & 0x7f) {
+ if (!backup_state.error) {
+ error_setg(&backup_state.error,
+@@ -3023,7 +3028,6 @@ static int pvebackup_dump_cb(void *opaque, BlockBackend *target,
+ }
+
+ int64_t cluster_num = sector_num >> 7;
+- int size = n_sectors * BDRV_SECTOR_SIZE;
+
+ int ret = -1;
+
+@@ -3031,17 +3035,27 @@ static int pvebackup_dump_cb(void *opaque, BlockBackend *target,
+ size_t zero_bytes = 0;
+ ret = vma_writer_write(backup_state.vmaw, di->dev_id, cluster_num,
+ buf, &zero_bytes);
+- backup_state.zero_bytes += zero_bytes;
++ if (ret < 0) {
++ if (!backup_state.error) {
++ error_setg(&backup_state.error, "vma_writer_write error %d", ret);
++ }
++ if (di->bs && di->bs->job) {
++ block_job_cancel(di->bs->job);
++ }
++ } else {
++ backup_state.zero_bytes += zero_bytes;
++ backup_state.transferred += size;
++ }
+ } else {
+- ret = size;
+ if (!buf) {
+ backup_state.zero_bytes += size;
+ }
++ backup_state.transferred += size;
+ }
+
+- backup_state.transferred += size;
++ // Note: always return success, because we want that writes succeed anyways.
+
+- return ret;
++ return size;
+ }
+
+ static void pvebackup_cleanup(void)
+@@ -3113,7 +3127,7 @@ static void pvebackup_cancel(void *opaque)
+ BlockJob *job = di->bs->job;
+ if (job) {
+ if (!di->completed) {
+- block_job_cancel_sync(job);
++ block_job_cancel_sync(job);
+ }
+ }
+ }
+--
+2.1.4
+
+++ /dev/null
-From 0ea55080ddb9b9503dae8c35671a76fa5578572f Mon Sep 17 00:00:00 2001
-From: Wolfgang Bumiller <w.bumiller@proxmox.com>
-Date: Wed, 9 Dec 2015 15:21:54 +0100
-Subject: [PATCH 17/55] backup: vma: add dir format
-
----
- blockdev.c | 126 +++++++++++++++++++++++++++++++++++++++++--------------
- hmp-commands.hx | 8 ++--
- hmp.c | 4 +-
- qapi-schema.json | 2 +-
- 4 files changed, 104 insertions(+), 36 deletions(-)
-
-diff --git a/blockdev.c b/blockdev.c
-index 115524f..0537a53 100644
---- a/blockdev.c
-+++ b/blockdev.c
-@@ -3014,6 +3014,8 @@ typedef struct PVEBackupDevInfo {
- uint8_t dev_id;
- //bool started;
- bool completed;
-+ char targetfile[PATH_MAX];
-+ BlockDriverState *target;
- } PVEBackupDevInfo;
-
- static void pvebackup_run_next_job(void);
-@@ -3082,8 +3084,6 @@ static void pvebackup_complete_cb(void *opaque, int ret)
- {
- PVEBackupDevInfo *di = opaque;
-
-- assert(backup_state.vmaw);
--
- di->completed = true;
-
- if (ret < 0 && !backup_state.error) {
-@@ -3094,8 +3094,11 @@ static void pvebackup_complete_cb(void *opaque, int ret)
- BlockDriverState *bs = di->bs;
-
- di->bs = NULL;
-+ di->target = NULL;
-
-- vma_writer_close_stream(backup_state.vmaw, di->dev_id);
-+ if (backup_state.vmaw) {
-+ vma_writer_close_stream(backup_state.vmaw, di->dev_id);
-+ }
-
- block_job_cb(bs, ret);
-
-@@ -3174,6 +3177,7 @@ UuidInfo *qmp_backup(const char *backup_file, bool has_format,
- {
- BlockBackend *blk;
- BlockDriverState *bs = NULL;
-+ const char *backup_dir = NULL;
- Error *local_err = NULL;
- uuid_t uuid;
- VmaWriter *vmaw = NULL;
-@@ -3191,11 +3195,6 @@ UuidInfo *qmp_backup(const char *backup_file, bool has_format,
- /* Todo: try to auto-detect format based on file name */
- format = has_format ? format : BACKUP_FORMAT_VMA;
-
-- if (format != BACKUP_FORMAT_VMA) {
-- error_set(errp, ERROR_CLASS_GENERIC_ERROR, "unknown backup format");
-- return NULL;
-- }
--
- if (has_devlist) {
- devs = g_strsplit_set(devlist, ",;:", -1);
-
-@@ -3264,27 +3263,63 @@ UuidInfo *qmp_backup(const char *backup_file, bool has_format,
-
- uuid_generate(uuid);
-
-- vmaw = vma_writer_create(backup_file, uuid, &local_err);
-- if (!vmaw) {
-- if (local_err) {
-- error_propagate(errp, local_err);
-+ if (format == BACKUP_FORMAT_VMA) {
-+ vmaw = vma_writer_create(backup_file, uuid, &local_err);
-+ if (!vmaw) {
-+ if (local_err) {
-+ error_propagate(errp, local_err);
-+ }
-+ goto err;
- }
-- goto err;
-- }
-
-- /* register all devices for vma writer */
-- l = di_list;
-- while (l) {
-- PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data;
-- l = g_list_next(l);
-+ /* register all devices for vma writer */
-+ l = di_list;
-+ while (l) {
-+ PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data;
-+ l = g_list_next(l);
-
-- const char *devname = bdrv_get_device_name(di->bs);
-- di->dev_id = vma_writer_register_stream(vmaw, devname, di->size);
-- if (di->dev_id <= 0) {
-- error_set(errp, ERROR_CLASS_GENERIC_ERROR,
-- "register_stream failed");
-+ const char *devname = bdrv_get_device_name(di->bs);
-+ di->dev_id = vma_writer_register_stream(vmaw, devname, di->size);
-+ if (di->dev_id <= 0) {
-+ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
-+ "register_stream failed");
-+ goto err;
-+ }
-+ }
-+ } else if (format == BACKUP_FORMAT_DIR) {
-+ if (mkdir(backup_file, 0640) != 0) {
-+ error_setg_errno(errp, errno, "can't create directory '%s'\n",
-+ backup_file);
- goto err;
- }
-+ backup_dir = backup_file;
-+
-+ l = di_list;
-+ while (l) {
-+ PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data;
-+ l = g_list_next(l);
-+
-+ const char *devname = bdrv_get_device_name(di->bs);
-+ snprintf(di->targetfile, PATH_MAX, "%s/%s.raw", backup_dir, devname);
-+
-+ int flags = BDRV_O_RDWR|BDRV_O_CACHE_WB;
-+ bdrv_img_create(di->targetfile, "raw", NULL, NULL, NULL,
-+ di->size, flags, &local_err, false);
-+ if (local_err) {
-+ error_propagate(errp, local_err);
-+ goto err;
-+ }
-+
-+ di->target = bdrv_new();
-+ if (bdrv_open(&di->target, di->targetfile, NULL, NULL, flags, NULL, &local_err) < 0) {
-+ bdrv_unref(di->target);
-+ error_propagate(errp, local_err);
-+ goto err;
-+ }
-+ }
-+ } else {
-+ error_set(errp, ERROR_CLASS_GENERIC_ERROR, "unknown backup format");
-+ goto err;
- }
-
- /* add configuration file to archive */
-@@ -3297,12 +3332,27 @@ UuidInfo *qmp_backup(const char *backup_file, bool has_format,
- goto err;
- }
-
-- const char *basename = g_path_get_basename(config_file);
-- if (vma_writer_add_config(vmaw, basename, cdata, clen) != 0) {
-- error_setg(errp, "unable to add config data to vma archive");
-- g_free(cdata);
-- goto err;
-+ char *basename = g_path_get_basename(config_file);
-+
-+ if (format == BACKUP_FORMAT_VMA) {
-+ if (vma_writer_add_config(vmaw, basename, cdata, clen) != 0) {
-+ error_setg(errp, "unable to add config data to vma archive");
-+ g_free(cdata);
-+ g_free(basename);
-+ goto err;
-+ }
-+ } else if (format == BACKUP_FORMAT_DIR) {
-+ char config_path[PATH_MAX];
-+ snprintf(config_path, PATH_MAX, "%s/%s", backup_dir, basename);
-+ if (!g_file_set_contents(config_path, cdata, clen, &err)) {
-+ error_setg(errp, "unable to write config file '%s'", config_path);
-+ g_free(cdata);
-+ g_free(basename);
-+ goto err;
-+ }
- }
-+
-+ g_free(basename);
- g_free(cdata);
- }
-
-@@ -3342,10 +3392,11 @@ UuidInfo *qmp_backup(const char *backup_file, bool has_format,
- PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data;
- l = g_list_next(l);
-
-- backup_start(di->bs, NULL, speed, MIRROR_SYNC_MODE_FULL, NULL,
-+ backup_start(di->bs, di->target, speed, MIRROR_SYNC_MODE_FULL, NULL,
- BLOCKDEV_ON_ERROR_REPORT, BLOCKDEV_ON_ERROR_REPORT,
- pvebackup_dump_cb, pvebackup_complete_cb, di,
- 1, &local_err);
-+
- if (local_err != NULL) {
- error_setg(&backup_state.error, "backup_job_create failed");
- pvebackup_cancel(NULL);
-@@ -3364,8 +3415,17 @@ err:
-
- l = di_list;
- while (l) {
-- g_free(l->data);
-+ PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data;
- l = g_list_next(l);
-+
-+ if (di->target) {
-+ bdrv_unref(di->target);
-+ }
-+
-+ if (di->targetfile[0]) {
-+ unlink(di->targetfile);
-+ }
-+ g_free(di);
- }
- g_list_free(di_list);
-
-@@ -3379,6 +3439,10 @@ err:
- unlink(backup_file);
- }
-
-+ if (backup_dir) {
-+ rmdir(backup_dir);
-+ }
-+
- return NULL;
- }
-
-diff --git a/hmp-commands.hx b/hmp-commands.hx
-index 63c44f5..8a94e36 100644
---- a/hmp-commands.hx
-+++ b/hmp-commands.hx
-@@ -89,9 +89,11 @@ ETEXI
-
- {
- .name = "backup",
-- .args_type = "backupfile:s,speed:o?,devlist:s?",
-- .params = "backupfile [speed [devlist]]",
-- .help = "create a VM Backup.",
-+ .args_type = "directory:-d,backupfile:s,speed:o?,devlist:s?",
-+ .params = "[-d] backupfile [speed [devlist]]",
-+ .help = "create a VM Backup."
-+ "\n\t\t\t Use -d to dump data into a directory instead"
-+ "\n\t\t\t of using VMA format.",
- .mhandler.cmd = hmp_backup,
- },
-
-diff --git a/hmp.c b/hmp.c
-index 2d4bf97..040909e 100644
---- a/hmp.c
-+++ b/hmp.c
-@@ -1527,11 +1527,13 @@ void hmp_backup(Monitor *mon, const QDict *qdict)
- {
- Error *error = NULL;
-
-+ int dir = qdict_get_try_bool(qdict, "directory", 0);
- const char *backup_file = qdict_get_str(qdict, "backupfile");
- const char *devlist = qdict_get_try_str(qdict, "devlist");
- int64_t speed = qdict_get_try_int(qdict, "speed", 0);
-
-- qmp_backup(backup_file, true, BACKUP_FORMAT_VMA, false, NULL, !!devlist,
-+ qmp_backup(backup_file, true, dir ? BACKUP_FORMAT_DIR : BACKUP_FORMAT_VMA,
-+ false, NULL, !!devlist,
- devlist, qdict_haskey(qdict, "speed"), speed, &error);
-
- hmp_handle_error(mon, &error);
-diff --git a/qapi-schema.json b/qapi-schema.json
-index 4f673d4..3427d50 100644
---- a/qapi-schema.json
-+++ b/qapi-schema.json
-@@ -395,7 +395,7 @@
- # @vma: Proxmox vma backup format
- ##
- { 'enum': 'BackupFormat',
-- 'data': [ 'vma' ] }
-+ 'data': [ 'vma', 'dir' ] }
-
- ##
- # @backup:
---
-2.1.4
-
+++ /dev/null
-From 0db7b5b9c1799414788a43c3181c5b965d1b4be7 Mon Sep 17 00:00:00 2001
-From: Wolfgang Bumiller <w.bumiller@proxmox.com>
-Date: Wed, 9 Dec 2015 15:22:19 +0100
-Subject: [PATCH 18/55] backup: do not return errors in dump callback
-
----
- blockdev.c | 26 ++++++++++++++++++++------
- 1 file changed, 20 insertions(+), 6 deletions(-)
-
-diff --git a/blockdev.c b/blockdev.c
-index 0537a53..157cc44 100644
---- a/blockdev.c
-+++ b/blockdev.c
-@@ -3026,6 +3026,11 @@ static int pvebackup_dump_cb(void *opaque, BlockDriverState *target,
- {
- PVEBackupDevInfo *di = opaque;
-
-+ int size = n_sectors * BDRV_SECTOR_SIZE;
-+ if (backup_state.cancel) {
-+ return size; // return success
-+ }
-+
- if (sector_num & 0x7f) {
- if (!backup_state.error) {
- error_setg(&backup_state.error,
-@@ -3036,7 +3041,6 @@ static int pvebackup_dump_cb(void *opaque, BlockDriverState *target,
- }
-
- int64_t cluster_num = sector_num >> 7;
-- int size = n_sectors * BDRV_SECTOR_SIZE;
-
- int ret = -1;
-
-@@ -3044,17 +3048,27 @@ static int pvebackup_dump_cb(void *opaque, BlockDriverState *target,
- size_t zero_bytes = 0;
- ret = vma_writer_write(backup_state.vmaw, di->dev_id, cluster_num,
- buf, &zero_bytes);
-- backup_state.zero_bytes += zero_bytes;
-+ if (ret < 0) {
-+ if (!backup_state.error) {
-+ error_setg(&backup_state.error, "vma_writer_write error %d", ret);
-+ }
-+ if (di->bs && di->bs->job) {
-+ block_job_cancel(di->bs->job);
-+ }
-+ } else {
-+ backup_state.zero_bytes += zero_bytes;
-+ backup_state.transferred += size;
-+ }
- } else {
-- ret = size;
- if (!buf) {
- backup_state.zero_bytes += size;
- }
-+ backup_state.transferred += size;
- }
-
-- backup_state.transferred += size;
-+ // Note: always return success, because we want that writes succeed anyways.
-
-- return ret;
-+ return size;
- }
-
- static void pvebackup_cleanup(void)
-@@ -3126,7 +3140,7 @@ static void pvebackup_cancel(void *opaque)
- BlockJob *job = di->bs->job;
- if (job) {
- if (!di->completed) {
-- block_job_cancel_sync(job);
-+ block_job_cancel_sync(job);
- }
- }
- }
---
-2.1.4
-
--- /dev/null
+From 7f74494cc36b70074bbdf8aaaca7eb7d0ad6c5e5 Mon Sep 17 00:00:00 2001
+From: Wolfgang Bumiller <w.bumiller@proxmox.com>
+Date: Wed, 9 Dec 2015 15:39:36 +0100
+Subject: [PATCH 18/41] backup: vma: correctly propagate error
+
+---
+ blockdev.c | 2 +-
+ vma-writer.c | 7 +++++++
+ vma.h | 1 +
+ 3 files changed, 9 insertions(+), 1 deletion(-)
+
+diff --git a/blockdev.c b/blockdev.c
+index fb71cdc..2e51913 100644
+--- a/blockdev.c
++++ b/blockdev.c
+@@ -3037,7 +3037,7 @@ static int pvebackup_dump_cb(void *opaque, BlockBackend *target,
+ buf, &zero_bytes);
+ if (ret < 0) {
+ if (!backup_state.error) {
+- error_setg(&backup_state.error, "vma_writer_write error %d", ret);
++ vma_writer_error_propagate(backup_state.vmaw, &backup_state.error);
+ }
+ if (di->bs && di->bs->job) {
+ block_job_cancel(di->bs->job);
+diff --git a/vma-writer.c b/vma-writer.c
+index b0cf529..689e988 100644
+--- a/vma-writer.c
++++ b/vma-writer.c
+@@ -792,6 +792,13 @@ vma_writer_write(VmaWriter *vmaw, uint8_t dev_id, int64_t cluster_num,
+ return transferred;
+ }
+
++void vma_writer_error_propagate(VmaWriter *vmaw, Error **errp)
++{
++ if (vmaw->status < 0 && *errp == NULL) {
++ error_setg(errp, "%s", vmaw->errmsg);
++ }
++}
++
+ int vma_writer_close(VmaWriter *vmaw, Error **errp)
+ {
+ g_assert(vmaw != NULL);
+diff --git a/vma.h b/vma.h
+index 9bb6ea4..98377e4 100644
+--- a/vma.h
++++ b/vma.h
+@@ -116,6 +116,7 @@ typedef struct VmaDeviceInfo {
+
+ VmaWriter *vma_writer_create(const char *filename, uuid_t uuid, Error **errp);
+ int vma_writer_close(VmaWriter *vmaw, Error **errp);
++void vma_writer_error_propagate(VmaWriter *vmaw, Error **errp);
+ void vma_writer_destroy(VmaWriter *vmaw);
+ int vma_writer_add_config(VmaWriter *vmaw, const char *name, gpointer data,
+ size_t len);
+--
+2.1.4
+
+++ /dev/null
-From c6b074069e25ec722879f09672df89a446ef4f0a Mon Sep 17 00:00:00 2001
-From: Wolfgang Bumiller <w.bumiller@proxmox.com>
-Date: Wed, 9 Dec 2015 15:39:36 +0100
-Subject: [PATCH 19/55] backup: vma: correctly propagate error
-
----
- blockdev.c | 2 +-
- vma-writer.c | 7 +++++++
- vma.h | 1 +
- 3 files changed, 9 insertions(+), 1 deletion(-)
-
-diff --git a/blockdev.c b/blockdev.c
-index 157cc44..0ea8189 100644
---- a/blockdev.c
-+++ b/blockdev.c
-@@ -3050,7 +3050,7 @@ static int pvebackup_dump_cb(void *opaque, BlockDriverState *target,
- buf, &zero_bytes);
- if (ret < 0) {
- if (!backup_state.error) {
-- error_setg(&backup_state.error, "vma_writer_write error %d", ret);
-+ vma_writer_error_propagate(backup_state.vmaw, &backup_state.error);
- }
- if (di->bs && di->bs->job) {
- block_job_cancel(di->bs->job);
-diff --git a/vma-writer.c b/vma-writer.c
-index 8a3fa1c..425c4b9 100644
---- a/vma-writer.c
-+++ b/vma-writer.c
-@@ -798,6 +798,13 @@ vma_writer_write(VmaWriter *vmaw, uint8_t dev_id, int64_t cluster_num,
- return transferred;
- }
-
-+void vma_writer_error_propagate(VmaWriter *vmaw, Error **errp)
-+{
-+ if (vmaw->status < 0 && *errp == NULL) {
-+ error_setg(errp, "%s", vmaw->errmsg);
-+ }
-+}
-+
- int vma_writer_close(VmaWriter *vmaw, Error **errp)
- {
- g_assert(vmaw != NULL);
-diff --git a/vma.h b/vma.h
-index 9bb6ea4..98377e4 100644
---- a/vma.h
-+++ b/vma.h
-@@ -116,6 +116,7 @@ typedef struct VmaDeviceInfo {
-
- VmaWriter *vma_writer_create(const char *filename, uuid_t uuid, Error **errp);
- int vma_writer_close(VmaWriter *vmaw, Error **errp);
-+void vma_writer_error_propagate(VmaWriter *vmaw, Error **errp);
- void vma_writer_destroy(VmaWriter *vmaw);
- int vma_writer_add_config(VmaWriter *vmaw, const char *name, gpointer data,
- size_t len);
---
-2.1.4
-
--- /dev/null
+From 12c8cbb8628da0b828492005639bbbd170d26409 Mon Sep 17 00:00:00 2001
+From: Wolfgang Bumiller <w.bumiller@proxmox.com>
+Date: Wed, 9 Dec 2015 15:40:00 +0100
+Subject: [PATCH 19/41] backup: vma: remove async queue
+
+---
+ blockdev.c | 6 ++
+ vma-writer.c | 179 +++++++++++------------------------------------------------
+ 2 files changed, 38 insertions(+), 147 deletions(-)
+
+diff --git a/blockdev.c b/blockdev.c
+index 2e51913..1491c2d 100644
+--- a/blockdev.c
++++ b/blockdev.c
+@@ -3116,6 +3116,11 @@ static void pvebackup_cancel(void *opaque)
+ error_setg(&backup_state.error, "backup cancelled");
+ }
+
++ if (backup_state.vmaw) {
++ /* make sure vma writer does not block anymore */
++ vma_writer_set_error(backup_state.vmaw, "backup cancelled");
++ }
++
+ /* drain all i/o (awake jobs waiting for aio) */
+ bdrv_drain_all();
+
+@@ -3128,6 +3133,7 @@ static void pvebackup_cancel(void *opaque)
+ if (job) {
+ if (!di->completed) {
+ block_job_cancel_sync(job);
++ bdrv_drain_all(); /* drain all i/o (awake jobs waiting for aio) */
+ }
+ }
+ }
+diff --git a/vma-writer.c b/vma-writer.c
+index 689e988..6d3119d 100644
+--- a/vma-writer.c
++++ b/vma-writer.c
+@@ -28,14 +28,8 @@
+ do { if (DEBUG_VMA) { printf("vma: " fmt, ## __VA_ARGS__); } } while (0)
+
+ #define WRITE_BUFFERS 5
+-
+-typedef struct VmaAIOCB VmaAIOCB;
+-struct VmaAIOCB {
+- unsigned char buffer[VMA_MAX_EXTENT_SIZE];
+- VmaWriter *vmaw;
+- size_t bytes;
+- Coroutine *co;
+-};
++#define HEADER_CLUSTERS 8
++#define HEADERBUF_SIZE (VMA_CLUSTER_SIZE*HEADER_CLUSTERS)
+
+ struct VmaWriter {
+ int fd;
+@@ -47,16 +41,14 @@ struct VmaWriter {
+ bool closed;
+
+ /* we always write extents */
+- unsigned char outbuf[VMA_MAX_EXTENT_SIZE];
++ unsigned char *outbuf;
+ int outbuf_pos; /* in bytes */
+ int outbuf_count; /* in VMA_BLOCKS */
+ uint64_t outbuf_block_info[VMA_BLOCKS_PER_EXTENT];
+
+- VmaAIOCB *aiocbs[WRITE_BUFFERS];
+- CoQueue wqueue;
++ unsigned char *headerbuf;
+
+ GChecksum *md5csum;
+- CoMutex writer_lock;
+ CoMutex flush_lock;
+ Coroutine *co_writer;
+
+@@ -217,38 +209,39 @@ static void vma_co_continue_write(void *opaque)
+ }
+
+ static ssize_t coroutine_fn
+-vma_co_write(VmaWriter *vmaw, const void *buf, size_t bytes)
++vma_queue_write(VmaWriter *vmaw, const void *buf, size_t bytes)
+ {
+- size_t done = 0;
+- ssize_t ret;
++ DPRINTF("vma_queue_write enter %zd\n", bytes);
+
+- /* atomic writes (we cannot interleave writes) */
+- qemu_co_mutex_lock(&vmaw->writer_lock);
++ assert(vmaw);
++ assert(buf);
++ assert(bytes <= VMA_MAX_EXTENT_SIZE);
+
+- DPRINTF("vma_co_write enter %zd\n", bytes);
++ size_t done = 0;
++ ssize_t ret;
+
+ assert(vmaw->co_writer == NULL);
+
+ vmaw->co_writer = qemu_coroutine_self();
+
+- aio_set_fd_handler(qemu_get_aio_context(), vmaw->fd, false, NULL, vma_co_continue_write, vmaw);
+-
+- DPRINTF("vma_co_write wait until writable\n");
+- qemu_coroutine_yield();
+- DPRINTF("vma_co_write starting %zd\n", bytes);
+-
+ while (done < bytes) {
++ aio_set_fd_handler(qemu_get_aio_context(), vmaw->fd, false, NULL, vma_co_continue_write, vmaw);
++ qemu_coroutine_yield();
++ aio_set_fd_handler(qemu_get_aio_context(), vmaw->fd, false, NULL, NULL, NULL);
++ if (vmaw->status < 0) {
++ DPRINTF("vma_queue_write detected canceled backup\n");
++ done = -1;
++ break;
++ }
+ ret = write(vmaw->fd, buf + done, bytes - done);
+ if (ret > 0) {
+ done += ret;
+- DPRINTF("vma_co_write written %zd %zd\n", done, ret);
++ DPRINTF("vma_queue_write written %zd %zd\n", done, ret);
+ } else if (ret < 0) {
+ if (errno == EAGAIN || errno == EWOULDBLOCK) {
+- DPRINTF("vma_co_write yield %zd\n", done);
+- qemu_coroutine_yield();
+- DPRINTF("vma_co_write restart %zd\n", done);
+- } else {
+- vma_writer_set_error(vmaw, "vma_co_write write error - %s",
++ /* try again */
++ } else {
++ vma_writer_set_error(vmaw, "vma_queue_write: write error - %s",
+ g_strerror(errno));
+ done = -1; /* always return failure for partial writes */
+ break;
+@@ -258,102 +251,9 @@ vma_co_write(VmaWriter *vmaw, const void *buf, size_t bytes)
+ }
+ }
+
+- aio_set_fd_handler(qemu_get_aio_context(), vmaw->fd, false, NULL, NULL, NULL);
+-
+ vmaw->co_writer = NULL;
+-
+- qemu_co_mutex_unlock(&vmaw->writer_lock);
+-
+- DPRINTF("vma_co_write leave %zd\n", done);
+- return done;
+-}
+-
+-static void coroutine_fn vma_co_writer_task(void *opaque)
+-{
+- VmaAIOCB *cb = opaque;
+-
+- DPRINTF("vma_co_writer_task start\n");
+-
+- int64_t done = vma_co_write(cb->vmaw, cb->buffer, cb->bytes);
+- DPRINTF("vma_co_writer_task write done %zd\n", done);
+-
+- if (done != cb->bytes) {
+- DPRINTF("vma_co_writer_task failed write %zd %zd", cb->bytes, done);
+- vma_writer_set_error(cb->vmaw, "vma_co_writer_task failed write %zd",
+- done);
+- }
+-
+- cb->bytes = 0;
+-
+- qemu_co_queue_next(&cb->vmaw->wqueue);
+-
+- DPRINTF("vma_co_writer_task end\n");
+-}
+-
+-static void coroutine_fn vma_queue_flush(VmaWriter *vmaw)
+-{
+- DPRINTF("vma_queue_flush enter\n");
+-
+- assert(vmaw);
+-
+- while (1) {
+- int i;
+- VmaAIOCB *cb = NULL;
+- for (i = 0; i < WRITE_BUFFERS; i++) {
+- if (vmaw->aiocbs[i]->bytes) {
+- cb = vmaw->aiocbs[i];
+- DPRINTF("FOUND USED AIO BUFFER %d %zd\n", i,
+- vmaw->aiocbs[i]->bytes);
+- break;
+- }
+- }
+- if (!cb) {
+- break;
+- }
+- qemu_co_queue_wait(&vmaw->wqueue);
+- }
+-
+- DPRINTF("vma_queue_flush leave\n");
+-}
+-
+-/**
+- * NOTE: pipe buffer size in only 4096 bytes on linux (see 'ulimit -a')
+- * So we need to create a coroutione to allow 'parallel' execution.
+- */
+-static ssize_t coroutine_fn
+-vma_queue_write(VmaWriter *vmaw, const void *buf, size_t bytes)
+-{
+- DPRINTF("vma_queue_write enter %zd\n", bytes);
+-
+- assert(vmaw);
+- assert(buf);
+- assert(bytes <= VMA_MAX_EXTENT_SIZE);
+-
+- VmaAIOCB *cb = NULL;
+- while (!cb) {
+- int i;
+- for (i = 0; i < WRITE_BUFFERS; i++) {
+- if (!vmaw->aiocbs[i]->bytes) {
+- cb = vmaw->aiocbs[i];
+- break;
+- }
+- }
+- if (!cb) {
+- qemu_co_queue_wait(&vmaw->wqueue);
+- }
+- }
+-
+- memcpy(cb->buffer, buf, bytes);
+- cb->bytes = bytes;
+- cb->vmaw = vmaw;
+-
+- DPRINTF("vma_queue_write start %zd\n", bytes);
+- cb->co = qemu_coroutine_create(vma_co_writer_task);
+- qemu_coroutine_enter(cb->co, cb);
+-
+- DPRINTF("vma_queue_write leave\n");
+-
+- return bytes;
++
++ return (done == bytes) ? bytes : -1;
+ }
+
+ VmaWriter *vma_writer_create(const char *filename, uuid_t uuid, Error **errp)
+@@ -420,20 +320,16 @@ VmaWriter *vma_writer_create(const char *filename, uuid_t uuid, Error **errp)
+ }
+
+ /* we use O_DIRECT, so we need to align IO buffers */
+- int i;
+- for (i = 0; i < WRITE_BUFFERS; i++) {
+- vmaw->aiocbs[i] = qemu_memalign(512, sizeof(VmaAIOCB));
+- memset(vmaw->aiocbs[i], 0, sizeof(VmaAIOCB));
+- }
++
++ vmaw->outbuf = qemu_memalign(512, VMA_MAX_EXTENT_SIZE);
++ vmaw->headerbuf = qemu_memalign(512, HEADERBUF_SIZE);
+
+ vmaw->outbuf_count = 0;
+ vmaw->outbuf_pos = VMA_EXTENT_HEADER_SIZE;
+
+ vmaw->header_blob_table_pos = 1; /* start at pos 1 */
+
+- qemu_co_mutex_init(&vmaw->writer_lock);
+ qemu_co_mutex_init(&vmaw->flush_lock);
+- qemu_co_queue_init(&vmaw->wqueue);
+
+ uuid_copy(vmaw->uuid, uuid);
+
+@@ -460,8 +356,7 @@ err:
+ static int coroutine_fn vma_write_header(VmaWriter *vmaw)
+ {
+ assert(vmaw);
+- int header_clusters = 8;
+- char buf[65536*header_clusters];
++ unsigned char *buf = vmaw->headerbuf;
+ VmaHeader *head = (VmaHeader *)buf;
+
+ int i;
+@@ -472,7 +367,7 @@ static int coroutine_fn vma_write_header(VmaWriter *vmaw)
+ return vmaw->status;
+ }
+
+- memset(buf, 0, sizeof(buf));
++ memset(buf, 0, HEADERBUF_SIZE);
+
+ head->magic = VMA_MAGIC;
+ head->version = GUINT32_TO_BE(1); /* v1 */
+@@ -507,7 +402,7 @@ static int coroutine_fn vma_write_header(VmaWriter *vmaw)
+ uint32_t header_size = sizeof(VmaHeader) + vmaw->header_blob_table_size;
+ head->header_size = GUINT32_TO_BE(header_size);
+
+- if (header_size > sizeof(buf)) {
++ if (header_size > HEADERBUF_SIZE) {
+ return -1; /* just to be sure */
+ }
+
+@@ -805,13 +700,7 @@ int vma_writer_close(VmaWriter *vmaw, Error **errp)
+
+ int i;
+
+- vma_queue_flush(vmaw);
+-
+- /* this should not happen - just to be sure */
+- while (!qemu_co_queue_empty(&vmaw->wqueue)) {
+- DPRINTF("vma_writer_close wait\n");
+- co_aio_sleep_ns(qemu_get_aio_context(), QEMU_CLOCK_REALTIME, 1000000);
+- }
++ assert(vmaw->co_writer == NULL);
+
+ if (vmaw->cmd) {
+ if (pclose(vmaw->cmd) < 0) {
+@@ -869,9 +758,5 @@ void vma_writer_destroy(VmaWriter *vmaw)
+ g_checksum_free(vmaw->md5csum);
+ }
+
+- for (i = 0; i < WRITE_BUFFERS; i++) {
+- free(vmaw->aiocbs[i]);
+- }
+-
+ g_free(vmaw);
+ }
+--
+2.1.4
+
+++ /dev/null
-From 850534378187f82c34d962d35654dde6a47d956c Mon Sep 17 00:00:00 2001
-From: Wolfgang Bumiller <w.bumiller@proxmox.com>
-Date: Wed, 9 Dec 2015 15:40:00 +0100
-Subject: [PATCH 20/55] backup: vma: remove async queue
-
----
- blockdev.c | 6 ++
- vma-writer.c | 179 +++++++++++------------------------------------------------
- 2 files changed, 38 insertions(+), 147 deletions(-)
-
-diff --git a/blockdev.c b/blockdev.c
-index 0ea8189..55c80c0 100644
---- a/blockdev.c
-+++ b/blockdev.c
-@@ -3129,6 +3129,11 @@ static void pvebackup_cancel(void *opaque)
- error_setg(&backup_state.error, "backup cancelled");
- }
-
-+ if (backup_state.vmaw) {
-+ /* make sure vma writer does not block anymore */
-+ vma_writer_set_error(backup_state.vmaw, "backup cancelled");
-+ }
-+
- /* drain all i/o (awake jobs waiting for aio) */
- bdrv_drain_all();
-
-@@ -3141,6 +3146,7 @@ static void pvebackup_cancel(void *opaque)
- if (job) {
- if (!di->completed) {
- block_job_cancel_sync(job);
-+ bdrv_drain_all(); /* drain all i/o (awake jobs waiting for aio) */
- }
- }
- }
-diff --git a/vma-writer.c b/vma-writer.c
-index 425c4b9..2558fe1 100644
---- a/vma-writer.c
-+++ b/vma-writer.c
-@@ -34,14 +34,8 @@
- do { if (DEBUG_VMA) { printf("vma: " fmt, ## __VA_ARGS__); } } while (0)
-
- #define WRITE_BUFFERS 5
--
--typedef struct VmaAIOCB VmaAIOCB;
--struct VmaAIOCB {
-- unsigned char buffer[VMA_MAX_EXTENT_SIZE];
-- VmaWriter *vmaw;
-- size_t bytes;
-- Coroutine *co;
--};
-+#define HEADER_CLUSTERS 8
-+#define HEADERBUF_SIZE (VMA_CLUSTER_SIZE*HEADER_CLUSTERS)
-
- struct VmaWriter {
- int fd;
-@@ -53,16 +47,14 @@ struct VmaWriter {
- bool closed;
-
- /* we always write extents */
-- unsigned char outbuf[VMA_MAX_EXTENT_SIZE];
-+ unsigned char *outbuf;
- int outbuf_pos; /* in bytes */
- int outbuf_count; /* in VMA_BLOCKS */
- uint64_t outbuf_block_info[VMA_BLOCKS_PER_EXTENT];
-
-- VmaAIOCB *aiocbs[WRITE_BUFFERS];
-- CoQueue wqueue;
-+ unsigned char *headerbuf;
-
- GChecksum *md5csum;
-- CoMutex writer_lock;
- CoMutex flush_lock;
- Coroutine *co_writer;
-
-@@ -223,38 +215,39 @@ static void vma_co_continue_write(void *opaque)
- }
-
- static ssize_t coroutine_fn
--vma_co_write(VmaWriter *vmaw, const void *buf, size_t bytes)
-+vma_queue_write(VmaWriter *vmaw, const void *buf, size_t bytes)
- {
-- size_t done = 0;
-- ssize_t ret;
-+ DPRINTF("vma_queue_write enter %zd\n", bytes);
-
-- /* atomic writes (we cannot interleave writes) */
-- qemu_co_mutex_lock(&vmaw->writer_lock);
-+ assert(vmaw);
-+ assert(buf);
-+ assert(bytes <= VMA_MAX_EXTENT_SIZE);
-
-- DPRINTF("vma_co_write enter %zd\n", bytes);
-+ size_t done = 0;
-+ ssize_t ret;
-
- assert(vmaw->co_writer == NULL);
-
- vmaw->co_writer = qemu_coroutine_self();
-
-- aio_set_fd_handler(qemu_get_aio_context(), vmaw->fd, NULL, vma_co_continue_write, vmaw);
--
-- DPRINTF("vma_co_write wait until writable\n");
-- qemu_coroutine_yield();
-- DPRINTF("vma_co_write starting %zd\n", bytes);
--
- while (done < bytes) {
-+ aio_set_fd_handler(qemu_get_aio_context(), vmaw->fd, NULL, vma_co_continue_write, vmaw);
-+ qemu_coroutine_yield();
-+ aio_set_fd_handler(qemu_get_aio_context(), vmaw->fd, NULL, NULL, NULL);
-+ if (vmaw->status < 0) {
-+ DPRINTF("vma_queue_write detected canceled backup\n");
-+ done = -1;
-+ break;
-+ }
- ret = write(vmaw->fd, buf + done, bytes - done);
- if (ret > 0) {
- done += ret;
-- DPRINTF("vma_co_write written %zd %zd\n", done, ret);
-+ DPRINTF("vma_queue_write written %zd %zd\n", done, ret);
- } else if (ret < 0) {
- if (errno == EAGAIN || errno == EWOULDBLOCK) {
-- DPRINTF("vma_co_write yield %zd\n", done);
-- qemu_coroutine_yield();
-- DPRINTF("vma_co_write restart %zd\n", done);
-- } else {
-- vma_writer_set_error(vmaw, "vma_co_write write error - %s",
-+ /* try again */
-+ } else {
-+ vma_writer_set_error(vmaw, "vma_queue_write: write error - %s",
- g_strerror(errno));
- done = -1; /* always return failure for partial writes */
- break;
-@@ -264,102 +257,9 @@ vma_co_write(VmaWriter *vmaw, const void *buf, size_t bytes)
- }
- }
-
-- aio_set_fd_handler(qemu_get_aio_context(), vmaw->fd, NULL, NULL, NULL);
--
- vmaw->co_writer = NULL;
--
-- qemu_co_mutex_unlock(&vmaw->writer_lock);
--
-- DPRINTF("vma_co_write leave %zd\n", done);
-- return done;
--}
--
--static void coroutine_fn vma_co_writer_task(void *opaque)
--{
-- VmaAIOCB *cb = opaque;
--
-- DPRINTF("vma_co_writer_task start\n");
--
-- int64_t done = vma_co_write(cb->vmaw, cb->buffer, cb->bytes);
-- DPRINTF("vma_co_writer_task write done %zd\n", done);
--
-- if (done != cb->bytes) {
-- DPRINTF("vma_co_writer_task failed write %zd %zd", cb->bytes, done);
-- vma_writer_set_error(cb->vmaw, "vma_co_writer_task failed write %zd",
-- done);
-- }
--
-- cb->bytes = 0;
--
-- qemu_co_queue_next(&cb->vmaw->wqueue);
--
-- DPRINTF("vma_co_writer_task end\n");
--}
--
--static void coroutine_fn vma_queue_flush(VmaWriter *vmaw)
--{
-- DPRINTF("vma_queue_flush enter\n");
--
-- assert(vmaw);
--
-- while (1) {
-- int i;
-- VmaAIOCB *cb = NULL;
-- for (i = 0; i < WRITE_BUFFERS; i++) {
-- if (vmaw->aiocbs[i]->bytes) {
-- cb = vmaw->aiocbs[i];
-- DPRINTF("FOUND USED AIO BUFFER %d %zd\n", i,
-- vmaw->aiocbs[i]->bytes);
-- break;
-- }
-- }
-- if (!cb) {
-- break;
-- }
-- qemu_co_queue_wait(&vmaw->wqueue);
-- }
--
-- DPRINTF("vma_queue_flush leave\n");
--}
--
--/**
-- * NOTE: pipe buffer size in only 4096 bytes on linux (see 'ulimit -a')
-- * So we need to create a coroutione to allow 'parallel' execution.
-- */
--static ssize_t coroutine_fn
--vma_queue_write(VmaWriter *vmaw, const void *buf, size_t bytes)
--{
-- DPRINTF("vma_queue_write enter %zd\n", bytes);
--
-- assert(vmaw);
-- assert(buf);
-- assert(bytes <= VMA_MAX_EXTENT_SIZE);
--
-- VmaAIOCB *cb = NULL;
-- while (!cb) {
-- int i;
-- for (i = 0; i < WRITE_BUFFERS; i++) {
-- if (!vmaw->aiocbs[i]->bytes) {
-- cb = vmaw->aiocbs[i];
-- break;
-- }
-- }
-- if (!cb) {
-- qemu_co_queue_wait(&vmaw->wqueue);
-- }
-- }
--
-- memcpy(cb->buffer, buf, bytes);
-- cb->bytes = bytes;
-- cb->vmaw = vmaw;
--
-- DPRINTF("vma_queue_write start %zd\n", bytes);
-- cb->co = qemu_coroutine_create(vma_co_writer_task);
-- qemu_coroutine_enter(cb->co, cb);
--
-- DPRINTF("vma_queue_write leave\n");
--
-- return bytes;
-+
-+ return (done == bytes) ? bytes : -1;
- }
-
- VmaWriter *vma_writer_create(const char *filename, uuid_t uuid, Error **errp)
-@@ -426,20 +326,16 @@ VmaWriter *vma_writer_create(const char *filename, uuid_t uuid, Error **errp)
- }
-
- /* we use O_DIRECT, so we need to align IO buffers */
-- int i;
-- for (i = 0; i < WRITE_BUFFERS; i++) {
-- vmaw->aiocbs[i] = qemu_memalign(512, sizeof(VmaAIOCB));
-- memset(vmaw->aiocbs[i], 0, sizeof(VmaAIOCB));
-- }
-+
-+ vmaw->outbuf = qemu_memalign(512, VMA_MAX_EXTENT_SIZE);
-+ vmaw->headerbuf = qemu_memalign(512, HEADERBUF_SIZE);
-
- vmaw->outbuf_count = 0;
- vmaw->outbuf_pos = VMA_EXTENT_HEADER_SIZE;
-
- vmaw->header_blob_table_pos = 1; /* start at pos 1 */
-
-- qemu_co_mutex_init(&vmaw->writer_lock);
- qemu_co_mutex_init(&vmaw->flush_lock);
-- qemu_co_queue_init(&vmaw->wqueue);
-
- uuid_copy(vmaw->uuid, uuid);
-
-@@ -466,8 +362,7 @@ err:
- static int coroutine_fn vma_write_header(VmaWriter *vmaw)
- {
- assert(vmaw);
-- int header_clusters = 8;
-- char buf[65536*header_clusters];
-+ unsigned char *buf = vmaw->headerbuf;
- VmaHeader *head = (VmaHeader *)buf;
-
- int i;
-@@ -478,7 +373,7 @@ static int coroutine_fn vma_write_header(VmaWriter *vmaw)
- return vmaw->status;
- }
-
-- memset(buf, 0, sizeof(buf));
-+ memset(buf, 0, HEADERBUF_SIZE);
-
- head->magic = VMA_MAGIC;
- head->version = GUINT32_TO_BE(1); /* v1 */
-@@ -513,7 +408,7 @@ static int coroutine_fn vma_write_header(VmaWriter *vmaw)
- uint32_t header_size = sizeof(VmaHeader) + vmaw->header_blob_table_size;
- head->header_size = GUINT32_TO_BE(header_size);
-
-- if (header_size > sizeof(buf)) {
-+ if (header_size > HEADERBUF_SIZE) {
- return -1; /* just to be sure */
- }
-
-@@ -811,13 +706,7 @@ int vma_writer_close(VmaWriter *vmaw, Error **errp)
-
- int i;
-
-- vma_queue_flush(vmaw);
--
-- /* this should not happen - just to be sure */
-- while (!qemu_co_queue_empty(&vmaw->wqueue)) {
-- DPRINTF("vma_writer_close wait\n");
-- co_aio_sleep_ns(qemu_get_aio_context(), QEMU_CLOCK_REALTIME, 1000000);
-- }
-+ assert(vmaw->co_writer == NULL);
-
- if (vmaw->cmd) {
- if (pclose(vmaw->cmd) < 0) {
-@@ -875,9 +764,5 @@ void vma_writer_destroy(VmaWriter *vmaw)
- g_checksum_free(vmaw->md5csum);
- }
-
-- for (i = 0; i < WRITE_BUFFERS; i++) {
-- free(vmaw->aiocbs[i]);
-- }
--
- g_free(vmaw);
- }
---
-2.1.4
-
--- /dev/null
+From e201ffde70d82ae541d7aa14ea16e307fd3b3f76 Mon Sep 17 00:00:00 2001
+From: Wolfgang Bumiller <w.bumiller@proxmox.com>
+Date: Wed, 9 Dec 2015 15:40:42 +0100
+Subject: [PATCH 20/41] backup: vma: run flush inside coroutine
+
+---
+ blockdev.c | 10 +++++++++-
+ vma-writer.c | 4 ++++
+ 2 files changed, 13 insertions(+), 1 deletion(-)
+
+diff --git a/blockdev.c b/blockdev.c
+index 1491c2d..f3c0c58 100644
+--- a/blockdev.c
++++ b/blockdev.c
+@@ -3081,6 +3081,13 @@ static void pvebackup_cleanup(void)
+ }
+ }
+
++static void coroutine_fn backup_close_vma_stream(void *opaque)
++{
++ PVEBackupDevInfo *di = opaque;
++
++ vma_writer_close_stream(backup_state.vmaw, di->dev_id);
++}
++
+ static void pvebackup_complete_cb(void *opaque, int ret)
+ {
+ PVEBackupDevInfo *di = opaque;
+@@ -3098,7 +3105,8 @@ static void pvebackup_complete_cb(void *opaque, int ret)
+ di->target = NULL;
+
+ if (backup_state.vmaw) {
+- vma_writer_close_stream(backup_state.vmaw, di->dev_id);
++ Coroutine *co = qemu_coroutine_create(backup_close_vma_stream, di);
++ qemu_coroutine_enter(co);
+ }
+
+ block_job_cb(bs, ret);
+diff --git a/vma-writer.c b/vma-writer.c
+index 6d3119d..79b7fd4 100644
+--- a/vma-writer.c
++++ b/vma-writer.c
+@@ -700,6 +700,10 @@ int vma_writer_close(VmaWriter *vmaw, Error **errp)
+
+ int i;
+
++ while (vmaw->co_writer) {
++ aio_poll(qemu_get_aio_context(), true);
++ }
++
+ assert(vmaw->co_writer == NULL);
+
+ if (vmaw->cmd) {
+--
+2.1.4
+
--- /dev/null
+From 120827907beae1a28dbdc98e5d3ebf43ce7156da Mon Sep 17 00:00:00 2001
+From: Wolfgang Bumiller <w.bumiller@proxmox.com>
+Date: Wed, 9 Dec 2015 15:41:13 +0100
+Subject: [PATCH 21/41] backup: do not use bdrv_drain_all
+
+---
+ blockdev.c | 6 +-----
+ 1 file changed, 1 insertion(+), 5 deletions(-)
+
+diff --git a/blockdev.c b/blockdev.c
+index f3c0c58..2371cf3 100644
+--- a/blockdev.c
++++ b/blockdev.c
+@@ -3129,9 +3129,6 @@ static void pvebackup_cancel(void *opaque)
+ vma_writer_set_error(backup_state.vmaw, "backup cancelled");
+ }
+
+- /* drain all i/o (awake jobs waiting for aio) */
+- bdrv_drain_all();
+-
+ GList *l = backup_state.di_list;
+ while (l) {
+ PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data;
+@@ -3140,8 +3137,7 @@ static void pvebackup_cancel(void *opaque)
+ BlockJob *job = di->bs->job;
+ if (job) {
+ if (!di->completed) {
+- block_job_cancel_sync(job);
+- bdrv_drain_all(); /* drain all i/o (awake jobs waiting for aio) */
++ block_job_cancel_sync(job);
+ }
+ }
+ }
+--
+2.1.4
+
+++ /dev/null
-From 60e4cc553c6ed53dd86e91ac143ad3aa8edcdbe7 Mon Sep 17 00:00:00 2001
-From: Wolfgang Bumiller <w.bumiller@proxmox.com>
-Date: Wed, 9 Dec 2015 15:40:42 +0100
-Subject: [PATCH 21/55] backup: vma: run flush inside coroutine
-
----
- blockdev.c | 10 +++++++++-
- vma-writer.c | 4 ++++
- 2 files changed, 13 insertions(+), 1 deletion(-)
-
-diff --git a/blockdev.c b/blockdev.c
-index 55c80c0..2c51b95 100644
---- a/blockdev.c
-+++ b/blockdev.c
-@@ -3094,6 +3094,13 @@ static void pvebackup_cleanup(void)
- }
- }
-
-+static void coroutine_fn backup_close_vma_stream(void *opaque)
-+{
-+ PVEBackupDevInfo *di = opaque;
-+
-+ vma_writer_close_stream(backup_state.vmaw, di->dev_id);
-+}
-+
- static void pvebackup_complete_cb(void *opaque, int ret)
- {
- PVEBackupDevInfo *di = opaque;
-@@ -3111,7 +3118,8 @@ static void pvebackup_complete_cb(void *opaque, int ret)
- di->target = NULL;
-
- if (backup_state.vmaw) {
-- vma_writer_close_stream(backup_state.vmaw, di->dev_id);
-+ Coroutine *co = qemu_coroutine_create(backup_close_vma_stream);
-+ qemu_coroutine_enter(co, di);
- }
-
- block_job_cb(bs, ret);
-diff --git a/vma-writer.c b/vma-writer.c
-index 2558fe1..3c77e98 100644
---- a/vma-writer.c
-+++ b/vma-writer.c
-@@ -706,6 +706,10 @@ int vma_writer_close(VmaWriter *vmaw, Error **errp)
-
- int i;
-
-+ while (vmaw->co_writer) {
-+ aio_poll(qemu_get_aio_context(), true);
-+ }
-+
- assert(vmaw->co_writer == NULL);
-
- if (vmaw->cmd) {
---
-2.1.4
-
+++ /dev/null
-From 5848b90ad434c9c697e32cdd00a34708cfce24c5 Mon Sep 17 00:00:00 2001
-From: Wolfgang Bumiller <w.bumiller@proxmox.com>
-Date: Wed, 9 Dec 2015 15:41:13 +0100
-Subject: [PATCH 22/55] backup: do not use bdrv_drain_all
-
----
- blockdev.c | 6 +-----
- 1 file changed, 1 insertion(+), 5 deletions(-)
-
-diff --git a/blockdev.c b/blockdev.c
-index 2c51b95..0131e92 100644
---- a/blockdev.c
-+++ b/blockdev.c
-@@ -3142,9 +3142,6 @@ static void pvebackup_cancel(void *opaque)
- vma_writer_set_error(backup_state.vmaw, "backup cancelled");
- }
-
-- /* drain all i/o (awake jobs waiting for aio) */
-- bdrv_drain_all();
--
- GList *l = backup_state.di_list;
- while (l) {
- PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data;
-@@ -3153,8 +3150,7 @@ static void pvebackup_cancel(void *opaque)
- BlockJob *job = di->bs->job;
- if (job) {
- if (!di->completed) {
-- block_job_cancel_sync(job);
-- bdrv_drain_all(); /* drain all i/o (awake jobs waiting for aio) */
-+ block_job_cancel_sync(job);
- }
- }
- }
---
-2.1.4
-
--- /dev/null
+From 3c93adeb6d594c56619fd34dc345dffa3139c2cb Mon Sep 17 00:00:00 2001
+From: Wolfgang Bumiller <w.bumiller@proxmox.com>
+Date: Wed, 9 Dec 2015 16:04:32 +0100
+Subject: [PATCH 22/41] internal snapshot async
+
+---
+ Makefile.objs | 1 +
+ block.c | 2 +-
+ hmp-commands-info.hx | 13 ++
+ hmp-commands.hx | 32 +++
+ hmp.c | 57 ++++++
+ hmp.h | 5 +
+ include/block/block.h | 1 +
+ include/sysemu/sysemu.h | 5 +-
+ migration/savevm.c | 12 +-
+ qapi-schema.json | 46 +++++
+ qemu-options.hx | 13 ++
+ qmp-commands.hx | 30 +++
+ savevm-async.c | 526 ++++++++++++++++++++++++++++++++++++++++++++++++
+ vl.c | 8 +
+ 14 files changed, 743 insertions(+), 8 deletions(-)
+ create mode 100644 savevm-async.c
+
+diff --git a/Makefile.objs b/Makefile.objs
+index 845edd0..7d9d2d7 100644
+--- a/Makefile.objs
++++ b/Makefile.objs
+@@ -53,6 +53,7 @@ common-obj-$(CONFIG_LINUX) += fsdev/
+ common-obj-y += migration/
+ common-obj-y += qemu-char.o #aio.o
+ common-obj-y += page_cache.o
++common-obj-y += savevm-async.o
+
+ common-obj-$(CONFIG_SPICE) += spice-qemu-char.o
+
+diff --git a/block.c b/block.c
+index 30d64e6..95c1d32 100644
+--- a/block.c
++++ b/block.c
+@@ -2288,7 +2288,7 @@ void bdrv_replace_in_backing_chain(BlockDriverState *old, BlockDriverState *new)
+ bdrv_unref(old);
+ }
+
+-static void bdrv_delete(BlockDriverState *bs)
++void bdrv_delete(BlockDriverState *bs)
+ {
+ assert(!bs->job);
+ assert(bdrv_op_blocker_is_empty(bs));
+diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx
+index 7616fe2..3046f9d 100644
+--- a/hmp-commands-info.hx
++++ b/hmp-commands-info.hx
+@@ -588,6 +588,19 @@ Show current migration xbzrle cache size.
+ ETEXI
+
+ {
++ .name = "savevm",
++ .args_type = "",
++ .params = "",
++ .help = "show savevm status",
++ .mhandler.cmd = hmp_info_savevm,
++ },
++
++STEXI
++@item info savevm
++show savevm status
++ETEXI
++
++ {
+ .name = "balloon",
+ .args_type = "",
+ .params = "",
+diff --git a/hmp-commands.hx b/hmp-commands.hx
+index 0e20ef9..4d735cb 100644
+--- a/hmp-commands.hx
++++ b/hmp-commands.hx
+@@ -1791,3 +1791,35 @@ ETEXI
+ STEXI
+ @end table
+ ETEXI
++
++ {
++ .name = "savevm-start",
++ .args_type = "statefile:s?",
++ .params = "[statefile]",
++ .help = "Prepare for snapshot and halt VM. Save VM state to statefile.",
++ .mhandler.cmd = hmp_savevm_start,
++ },
++
++ {
++ .name = "snapshot-drive",
++ .args_type = "device:s,name:s",
++ .params = "device name",
++ .help = "Create internal snapshot.",
++ .mhandler.cmd = hmp_snapshot_drive,
++ },
++
++ {
++ .name = "delete-drive-snapshot",
++ .args_type = "device:s,name:s",
++ .params = "device name",
++ .help = "Delete internal snapshot.",
++ .mhandler.cmd = hmp_delete_drive_snapshot,
++ },
++
++ {
++ .name = "savevm-end",
++ .args_type = "",
++ .params = "",
++ .help = "Resume VM after snaphot.",
++ .mhandler.cmd = hmp_savevm_end,
++ },
+diff --git a/hmp.c b/hmp.c
+index c292354..7bd319f 100644
+--- a/hmp.c
++++ b/hmp.c
+@@ -2116,6 +2116,63 @@ void hmp_info_memory_devices(Monitor *mon, const QDict *qdict)
+ qapi_free_MemoryDeviceInfoList(info_list);
+ }
+
++void hmp_savevm_start(Monitor *mon, const QDict *qdict)
++{
++ Error *errp = NULL;
++ const char *statefile = qdict_get_try_str(qdict, "statefile");
++
++ qmp_savevm_start(statefile != NULL, statefile, &errp);
++ hmp_handle_error(mon, &errp);
++}
++
++void hmp_snapshot_drive(Monitor *mon, const QDict *qdict)
++{
++ Error *errp = NULL;
++ const char *name = qdict_get_str(qdict, "name");
++ const char *device = qdict_get_str(qdict, "device");
++
++ qmp_snapshot_drive(device, name, &errp);
++ hmp_handle_error(mon, &errp);
++}
++
++void hmp_delete_drive_snapshot(Monitor *mon, const QDict *qdict)
++{
++ Error *errp = NULL;
++ const char *name = qdict_get_str(qdict, "name");
++ const char *device = qdict_get_str(qdict, "device");
++
++ qmp_delete_drive_snapshot(device, name, &errp);
++ hmp_handle_error(mon, &errp);
++}
++
++void hmp_savevm_end(Monitor *mon, const QDict *qdict)
++{
++ Error *errp = NULL;
++
++ qmp_savevm_end(&errp);
++ hmp_handle_error(mon, &errp);
++}
++
++void hmp_info_savevm(Monitor *mon, const QDict *qdict)
++{
++ SaveVMInfo *info;
++ info = qmp_query_savevm(NULL);
++
++ if (info->has_status) {
++ monitor_printf(mon, "savevm status: %s\n", info->status);
++ monitor_printf(mon, "total time: %" PRIu64 " milliseconds\n",
++ info->total_time);
++ } else {
++ monitor_printf(mon, "savevm status: not running\n");
++ }
++ if (info->has_bytes) {
++ monitor_printf(mon, "Bytes saved: %"PRIu64"\n", info->bytes);
++ }
++ if (info->has_error) {
++ monitor_printf(mon, "Error: %s\n", info->error);
++ }
++}
++
+ void hmp_info_iothreads(Monitor *mon, const QDict *qdict)
+ {
+ IOThreadInfoList *info_list = qmp_query_iothreads(NULL);
+diff --git a/hmp.h b/hmp.h
+index 9a4c1f6..b74ddbf 100644
+--- a/hmp.h
++++ b/hmp.h
+@@ -26,6 +26,7 @@ void hmp_info_status(Monitor *mon, const QDict *qdict);
+ void hmp_info_uuid(Monitor *mon, const QDict *qdict);
+ void hmp_info_chardev(Monitor *mon, const QDict *qdict);
+ void hmp_info_mice(Monitor *mon, const QDict *qdict);
++void hmp_info_savevm(Monitor *mon, const QDict *qdict);
+ void hmp_info_migrate(Monitor *mon, const QDict *qdict);
+ void hmp_info_migrate_capabilities(Monitor *mon, const QDict *qdict);
+ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict);
+@@ -92,6 +93,10 @@ void hmp_netdev_add(Monitor *mon, const QDict *qdict);
+ void hmp_netdev_del(Monitor *mon, const QDict *qdict);
+ void hmp_getfd(Monitor *mon, const QDict *qdict);
+ void hmp_closefd(Monitor *mon, const QDict *qdict);
++void hmp_savevm_start(Monitor *mon, const QDict *qdict);
++void hmp_snapshot_drive(Monitor *mon, const QDict *qdict);
++void hmp_delete_drive_snapshot(Monitor *mon, const QDict *qdict);
++void hmp_savevm_end(Monitor *mon, const QDict *qdict);
+ void hmp_sendkey(Monitor *mon, const QDict *qdict);
+ void hmp_screendump(Monitor *mon, const QDict *qdict);
+ void hmp_nbd_server_start(Monitor *mon, const QDict *qdict);
+diff --git a/include/block/block.h b/include/block/block.h
+index 11c162d..6822b91 100644
+--- a/include/block/block.h
++++ b/include/block/block.h
+@@ -256,6 +256,7 @@ BlockDriverState *bdrv_find_backing_image(BlockDriverState *bs,
+ int bdrv_get_backing_file_depth(BlockDriverState *bs);
+ void bdrv_refresh_filename(BlockDriverState *bs);
+ int bdrv_truncate(BlockDriverState *bs, int64_t offset);
++void bdrv_delete(BlockDriverState *bs);
+ int64_t bdrv_nb_sectors(BlockDriverState *bs);
+ int64_t bdrv_getlength(BlockDriverState *bs);
+ int64_t bdrv_get_allocated_file_size(BlockDriverState *bs);
+diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h
+index ee7c760..4875441 100644
+--- a/include/sysemu/sysemu.h
++++ b/include/sysemu/sysemu.h
+@@ -79,6 +79,7 @@ void qemu_remove_machine_init_done_notifier(Notifier *notify);
+
+ void hmp_savevm(Monitor *mon, const QDict *qdict);
+ int load_vmstate(const char *name);
++int load_state_from_blockdev(const char *filename);
+ void hmp_delvm(Monitor *mon, const QDict *qdict);
+ void hmp_info_snapshots(Monitor *mon, const QDict *qdict);
+
+@@ -106,13 +107,13 @@ enum qemu_vm_cmd {
+ #define MAX_VM_CMD_PACKAGED_SIZE (1ul << 24)
+
+ bool qemu_savevm_state_blocked(Error **errp);
+-void qemu_savevm_state_begin(QEMUFile *f,
++int qemu_savevm_state_begin(QEMUFile *f,
+ const MigrationParams *params);
+ void qemu_savevm_state_header(QEMUFile *f);
+ int qemu_savevm_state_iterate(QEMUFile *f, bool postcopy);
+ void qemu_savevm_state_cleanup(void);
+ void qemu_savevm_state_complete_postcopy(QEMUFile *f);
+-void qemu_savevm_state_complete_precopy(QEMUFile *f, bool iterable_only);
++int qemu_savevm_state_complete_precopy(QEMUFile *f, bool iterable_only);
+ void qemu_savevm_state_pending(QEMUFile *f, uint64_t max_size,
+ uint64_t *res_non_postcopiable,
+ uint64_t *res_postcopiable);
+diff --git a/migration/savevm.c b/migration/savevm.c
+index 33a2911..b1bdfb6 100644
+--- a/migration/savevm.c
++++ b/migration/savevm.c
+@@ -879,11 +879,11 @@ void qemu_savevm_state_header(QEMUFile *f)
+
+ }
+
+-void qemu_savevm_state_begin(QEMUFile *f,
++int qemu_savevm_state_begin(QEMUFile *f,
+ const MigrationParams *params)
+ {
+ SaveStateEntry *se;
+- int ret;
++ int ret = 0;
+
+ trace_savevm_state_begin();
+ QTAILQ_FOREACH(se, &savevm_state.handlers, entry) {
+@@ -911,6 +911,7 @@ void qemu_savevm_state_begin(QEMUFile *f,
+ break;
+ }
+ }
++ return ret;
+ }
+
+ /*
+@@ -1014,7 +1015,7 @@ void qemu_savevm_state_complete_postcopy(QEMUFile *f)
+ qemu_fflush(f);
+ }
+
+-void qemu_savevm_state_complete_precopy(QEMUFile *f, bool iterable_only)
++int qemu_savevm_state_complete_precopy(QEMUFile *f, bool iterable_only)
+ {
+ QJSON *vmdesc;
+ int vmdesc_len;
+@@ -1048,12 +1049,12 @@ void qemu_savevm_state_complete_precopy(QEMUFile *f, bool iterable_only)
+ save_section_footer(f, se);
+ if (ret < 0) {
+ qemu_file_set_error(f, ret);
+- return;
++ return ret;
+ }
+ }
+
+ if (iterable_only) {
+- return;
++ return ret;
+ }
+
+ vmdesc = qjson_new();
+@@ -1100,6 +1101,7 @@ void qemu_savevm_state_complete_precopy(QEMUFile *f, bool iterable_only)
+ qjson_destroy(vmdesc);
+
+ qemu_fflush(f);
++ return qemu_file_get_error(f);
+ }
+
+ /* Give an estimate of the amount left to be transferred,
+diff --git a/qapi-schema.json b/qapi-schema.json
+index 147137d..0c0faf7 100644
+--- a/qapi-schema.json
++++ b/qapi-schema.json
+@@ -594,6 +594,42 @@
+ '*cpu-throttle-percentage': 'int',
+ '*error-desc': 'str'} }
+
++
++# @SaveVMInfo
++#
++# Information about current migration process.
++#
++# @status: #optional string describing the current savevm status.
++# This can be 'active', 'completed', 'failed'.
++# If this field is not returned, no savevm process
++# has been initiated
++#
++# @error: #optional string containing error message is status is failed.
++#
++# @total-time: #optional total amount of milliseconds since savevm started.
++# If savevm has ended, it returns the total save time
++#
++# @bytes: #optional total amount of data transfered
++#
++# Since: 1.3
++##
++{ 'struct': 'SaveVMInfo',
++ 'data': {'*status': 'str', '*error': 'str',
++ '*total-time': 'int', '*bytes': 'int'} }
++
++##
++# @query-savevm
++#
++# Returns information about current savevm process.
++#
++# Returns: @SaveVMInfo
++#
++# Since: 1.3
++##
++{ 'command': 'query-savevm', 'returns': 'SaveVMInfo' }
++
++##
++
+ ##
+ # @query-migrate
+ #
+@@ -3286,8 +3322,18 @@
+ #
+ # Since: 1.2.0
+ ##
++
+ { 'command': 'query-target', 'returns': 'TargetInfo' }
+
++{ 'command': 'savevm-start', 'data': { '*statefile': 'str' } }
++
++{ 'command': 'snapshot-drive', 'data': { 'device': 'str', 'name': 'str' } }
++
++{ 'command': 'delete-drive-snapshot', 'data': { 'device': 'str', 'name': 'str' } }
++
++{ 'command': 'savevm-end' }
++
++
+ ##
+ # @QKeyCode:
+ #
+diff --git a/qemu-options.hx b/qemu-options.hx
+index a71aaf8..37fad3b 100644
+--- a/qemu-options.hx
++++ b/qemu-options.hx
+@@ -3302,6 +3302,19 @@ STEXI
+ Start right away with a saved state (@code{loadvm} in monitor)
+ ETEXI
+
++DEF("loadstate", HAS_ARG, QEMU_OPTION_loadstate, \
++ "-loadstate file\n" \
++ " start right away with a saved state\n",
++ QEMU_ARCH_ALL)
++STEXI
++@item -loadstate @var{file}
++@findex -loadstate
++Start right away with a saved state. This option does not rollback
++disk state like @code{loadvm}, so user must make sure that disk
++have correct state. @var{file} can be any valid device URL. See the section
++for "Device URL Syntax" for more information.
++ETEXI
++
+ #ifndef _WIN32
+ DEF("daemonize", 0, QEMU_OPTION_daemonize, \
+ "-daemonize daemonize QEMU after initializing\n", QEMU_ARCH_ALL)
+diff --git a/qmp-commands.hx b/qmp-commands.hx
+index a8e8522..6342cd2 100644
+--- a/qmp-commands.hx
++++ b/qmp-commands.hx
+@@ -4904,6 +4904,36 @@ Example:
+ EQMP
+
+ {
++ .name = "savevm-start",
++ .args_type = "statefile:s?",
++ .mhandler.cmd_new = qmp_marshal_savevm_start,
++ },
++
++ {
++ .name = "snapshot-drive",
++ .args_type = "device:s,name:s",
++ .mhandler.cmd_new = qmp_marshal_snapshot_drive,
++ },
++
++ {
++ .name = "delete-drive-snapshot",
++ .args_type = "device:s,name:s",
++ .mhandler.cmd_new = qmp_marshal_delete_drive_snapshot,
++ },
++
++ {
++ .name = "savevm-end",
++ .args_type = "",
++ .mhandler.cmd_new = qmp_marshal_savevm_end,
++ },
++
++ {
++ .name = "query-savevm",
++ .args_type = "",
++ .mhandler.cmd_new = qmp_marshal_query_savevm,
++ },
++
++ {
+ .name = "query-rocker",
+ .args_type = "name:s",
+ .mhandler.cmd_new = qmp_marshal_query_rocker,
+diff --git a/savevm-async.c b/savevm-async.c
+new file mode 100644
+index 0000000..ae7ea84
+--- /dev/null
++++ b/savevm-async.c
+@@ -0,0 +1,526 @@
++#include "qemu/osdep.h"
++#include "qemu-common.h"
++#include "qapi/qmp/qerror.h"
++#include "qemu/error-report.h"
++#include "sysemu/sysemu.h"
++#include "qmp-commands.h"
++#include "qemu-options.h"
++#include "migration/qemu-file.h"
++#include "qom/qom-qobject.h"
++#include "migration/migration.h"
++#include "block/snapshot.h"
++#include "block/qapi.h"
++#include "block/block.h"
++#include "qemu/timer.h"
++#include "sysemu/block-backend.h"
++#include "qapi/qmp/qstring.h"
++#include "qemu/rcu.h"
++#include "qemu/thread.h"
++#include "qemu/cutils.h"
++
++/* #define DEBUG_SAVEVM_STATE */
++
++#ifdef DEBUG_SAVEVM_STATE
++#define DPRINTF(fmt, ...) \
++ do { printf("savevm-async: " fmt, ## __VA_ARGS__); } while (0)
++#else
++#define DPRINTF(fmt, ...) \
++ do { } while (0)
++#endif
++
++enum {
++ SAVE_STATE_DONE,
++ SAVE_STATE_ERROR,
++ SAVE_STATE_ACTIVE,
++ SAVE_STATE_COMPLETED,
++ SAVE_STATE_CANCELLED
++};
++
++
++static struct SnapshotState {
++ BlockDriverState *bs;
++ size_t bs_pos;
++ int state;
++ Error *error;
++ Error *blocker;
++ int saved_vm_running;
++ QEMUFile *file;
++ int64_t total_time;
++} snap_state;
++
++SaveVMInfo *qmp_query_savevm(Error **errp)
++{
++ SaveVMInfo *info = g_malloc0(sizeof(*info));
++ struct SnapshotState *s = &snap_state;
++
++ if (s->state != SAVE_STATE_DONE) {
++ info->has_bytes = true;
++ info->bytes = s->bs_pos;
++ switch (s->state) {
++ case SAVE_STATE_ERROR:
++ info->has_status = true;
++ info->status = g_strdup("failed");
++ info->has_total_time = true;
++ info->total_time = s->total_time;
++ if (s->error) {
++ info->has_error = true;
++ info->error = g_strdup(error_get_pretty(s->error));
++ }
++ break;
++ case SAVE_STATE_ACTIVE:
++ info->has_status = true;
++ info->status = g_strdup("active");
++ info->has_total_time = true;
++ info->total_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME)
++ - s->total_time;
++ break;
++ case SAVE_STATE_COMPLETED:
++ info->has_status = true;
++ info->status = g_strdup("completed");
++ info->has_total_time = true;
++ info->total_time = s->total_time;
++ break;
++ }
++ }
++
++ return info;
++}
++
++static int save_snapshot_cleanup(void)
++{
++ int ret = 0;
++
++ DPRINTF("save_snapshot_cleanup\n");
++
++ snap_state.total_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME) -
++ snap_state.total_time;
++
++ if (snap_state.file) {
++ ret = qemu_fclose(snap_state.file);
++ }
++
++ if (snap_state.bs) {
++ /* try to truncate, but ignore errors (will fail on block devices).
++ * note: bdrv_read() need whole blocks, so we round up
++ */
++ size_t size = (snap_state.bs_pos + BDRV_SECTOR_SIZE) & BDRV_SECTOR_MASK;
++ bdrv_truncate(snap_state.bs, size);
++ bdrv_op_unblock_all(snap_state.bs, snap_state.blocker);
++ error_free(snap_state.blocker);
++ snap_state.blocker = NULL;
++ bdrv_unref(snap_state.bs);
++ snap_state.bs = NULL;
++ }
++
++ return ret;
++}
++
++static void save_snapshot_error(const char *fmt, ...)
++{
++ va_list ap;
++ char *msg;
++
++ va_start(ap, fmt);
++ msg = g_strdup_vprintf(fmt, ap);
++ va_end(ap);
++
++ DPRINTF("save_snapshot_error: %s\n", msg);
++
++ if (!snap_state.error) {
++ error_set(&snap_state.error, ERROR_CLASS_GENERIC_ERROR, "%s", msg);
++ }
++
++ g_free (msg);
++
++ snap_state.state = SAVE_STATE_ERROR;
++
++ save_snapshot_cleanup();
++}
++
++static void save_snapshot_completed(void)
++{
++ DPRINTF("save_snapshot_completed\n");
++
++ if (save_snapshot_cleanup() < 0) {
++ snap_state.state = SAVE_STATE_ERROR;
++ } else {
++ snap_state.state = SAVE_STATE_COMPLETED;
++ }
++}
++
++static int block_state_close(void *opaque)
++{
++ snap_state.file = NULL;
++ return bdrv_flush(snap_state.bs);
++}
++
++static int block_state_put_buffer(void *opaque, const uint8_t *buf,
++ int64_t pos, int size)
++{
++ int ret;
++
++ assert(pos == snap_state.bs_pos);
++
++ if ((ret = bdrv_pwrite(snap_state.bs, snap_state.bs_pos, buf, size)) > 0) {
++ snap_state.bs_pos += ret;
++ }
++
++ return ret;
++}
++
++static int store_and_stop(void) {
++ if (global_state_store()) {
++ save_snapshot_error("Error saving global state");
++ return 1;
++ }
++ if (runstate_is_running()) {
++ vm_stop(RUN_STATE_SAVE_VM);
++ }
++ return 0;
++}
++
++static void process_savevm_co(void *opaque)
++{
++ int ret;
++ int64_t maxlen;
++ MigrationParams params = {
++ .blk = 0,
++ .shared = 0
++ };
++
++ snap_state.state = SAVE_STATE_ACTIVE;
++
++ qemu_mutex_unlock_iothread();
++ qemu_savevm_state_header(snap_state.file);
++ ret = qemu_savevm_state_begin(snap_state.file, ¶ms);
++ qemu_mutex_lock_iothread();
++
++ if (ret < 0) {
++ save_snapshot_error("qemu_savevm_state_begin failed");
++ return;
++ }
++
++ while (snap_state.state == SAVE_STATE_ACTIVE) {
++ uint64_t pending_size;
++
++ pending_size = qemu_savevm_state_pending(snap_state.file, 0);
++
++ if (pending_size) {
++ ret = qemu_savevm_state_iterate(snap_state.file);
++ if (ret < 0) {
++ save_snapshot_error("qemu_savevm_state_iterate error %d", ret);
++ break;
++ }
++ DPRINTF("savevm inerate pending size %lu ret %d\n", pending_size, ret);
++ } else {
++ DPRINTF("done iterating\n");
++ if (store_and_stop())
++ break;
++ DPRINTF("savevm inerate finished\n");
++ qemu_savevm_state_complete_precopy(snap_state.file);
++ DPRINTF("save complete\n");
++ save_snapshot_completed();
++ break;
++ }
++
++ /* stop the VM if we get to the end of available space,
++ * or if pending_size is just a few MB
++ */
++ maxlen = bdrv_getlength(snap_state.bs) - 30*1024*1024;
++ if ((pending_size < 100000) ||
++ ((snap_state.bs_pos + pending_size) >= maxlen)) {
++ if (store_and_stop())
++ break;
++ }
++ }
++
++ if(snap_state.state == SAVE_STATE_CANCELLED) {
++ save_snapshot_completed();
++ Error *errp = NULL;
++ qmp_savevm_end(&errp);
++ }
++
++}
++
++static const QEMUFileOps block_file_ops = {
++ .put_buffer = block_state_put_buffer,
++ .close = block_state_close,
++};
++
++
++void qmp_savevm_start(bool has_statefile, const char *statefile, Error **errp)
++{
++ BlockDriver *drv = NULL;
++ Error *local_err = NULL;
++
++ int bdrv_oflags = BDRV_O_RDWR;
++ int ret;
++
++ if (snap_state.state != SAVE_STATE_DONE) {
++ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
++ "VM snapshot already started\n");
++ return;
++ }
++
++ /* initialize snapshot info */
++ snap_state.saved_vm_running = runstate_is_running();
++ snap_state.bs_pos = 0;
++ snap_state.total_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
++ snap_state.blocker = NULL;
++
++ if (snap_state.error) {
++ error_free(snap_state.error);
++ snap_state.error = NULL;
++ }
++
++ if (!has_statefile) {
++ vm_stop(RUN_STATE_SAVE_VM);
++ snap_state.state = SAVE_STATE_COMPLETED;
++ return;
++ }
++
++ if (qemu_savevm_state_blocked(errp)) {
++ return;
++ }
++
++ /* Open the image */
++ snap_state.bs = bdrv_new();
++
++ QDict *options = NULL;
++ options = qdict_new();
++ qdict_put(options, "driver", qstring_from_str("raw"));
++ ret = bdrv_open(&snap_state.bs, statefile, NULL, options, bdrv_oflags, drv, &local_err);
++ if (ret < 0) {
++ error_set(errp, ERROR_CLASS_GENERIC_ERROR, "failed to open '%s'", statefile);
++ goto restart;
++ }
++
++ snap_state.file = qemu_fopen_ops(&snap_state, &block_file_ops);
++
++ if (!snap_state.file) {
++ error_set(errp, ERROR_CLASS_GENERIC_ERROR, "failed to open '%s'", statefile);
++ goto restart;
++ }
++
++
++ error_setg(&snap_state.blocker, "block device is in use by savevm");
++ bdrv_op_block_all(snap_state.bs, snap_state.blocker);
++
++ Coroutine *co = qemu_coroutine_create(process_savevm_co);
++ qemu_coroutine_enter(co);
++
++ return;
++
++restart:
++
++ save_snapshot_error("setup failed");
++
++ if (snap_state.saved_vm_running) {
++ vm_start();
++ }
++}
++
++void qmp_savevm_end(Error **errp)
++{
++ if (snap_state.state == SAVE_STATE_DONE) {
++ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
++ "VM snapshot not started\n");
++ return;
++ }
++
++ if (snap_state.state == SAVE_STATE_ACTIVE) {
++ snap_state.state = SAVE_STATE_CANCELLED;
++ return;
++ }
++
++ if (snap_state.saved_vm_running) {
++ vm_start();
++ }
++
++ snap_state.state = SAVE_STATE_DONE;
++}
++
++void qmp_snapshot_drive(const char *device, const char *name, Error **errp)
++{
++ BlockBackend *blk;
++ BlockDriverState *bs;
++ QEMUSnapshotInfo sn1, *sn = &sn1;
++ int ret;
++#ifdef _WIN32
++ struct _timeb tb;
++#else
++ struct timeval tv;
++#endif
++
++ if (snap_state.state != SAVE_STATE_COMPLETED) {
++ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
++ "VM snapshot not ready/started\n");
++ return;
++ }
++
++ blk = blk_by_name(device);
++ if (!blk) {
++ error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
++ "Device '%s' not found", device);
++ return;
++ }
++
++ bs = blk_bs(blk);
++ if (!bdrv_is_inserted(bs)) {
++ error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, device);
++ return;
++ }
++
++ if (bdrv_is_read_only(bs)) {
++ error_setg(errp, "Node '%s' is read only", device);
++ return;
++ }
++
++ if (!bdrv_can_snapshot(bs)) {
++ error_setg(errp, QERR_UNSUPPORTED);
++ return;
++ }
++
++ if (bdrv_snapshot_find(bs, sn, name) >= 0) {
++ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
++ "snapshot '%s' already exists", name);
++ return;
++ }
++
++ sn = &sn1;
++ memset(sn, 0, sizeof(*sn));
++
++#ifdef _WIN32
++ _ftime(&tb);
++ sn->date_sec = tb.time;
++ sn->date_nsec = tb.millitm * 1000000;
++#else
++ gettimeofday(&tv, NULL);
++ sn->date_sec = tv.tv_sec;
++ sn->date_nsec = tv.tv_usec * 1000;
++#endif
++ sn->vm_clock_nsec = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
++
++ pstrcpy(sn->name, sizeof(sn->name), name);
++
++ sn->vm_state_size = 0; /* do not save state */
++
++ ret = bdrv_snapshot_create(bs, sn);
++ if (ret < 0) {
++ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
++ "Error while creating snapshot on '%s'\n", device);
++ return;
++ }
++}
++
++void qmp_delete_drive_snapshot(const char *device, const char *name,
++ Error **errp)
++{
++ BlockBackend *blk;
++ BlockDriverState *bs;
++ QEMUSnapshotInfo sn1, *sn = &sn1;
++ Error *local_err = NULL;
++
++ int ret;
++
++ blk = blk_by_name(device);
++ if (!blk) {
++ error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
++ "Device '%s' not found", device);
++ return;
++ }
++
++ bs = blk_bs(blk);
++ if (bdrv_is_read_only(bs)) {
++ error_setg(errp, "Node '%s' is read only", device);
++ return;
++ }
++
++ if (!bdrv_can_snapshot(bs)) {
++ error_setg(errp, QERR_UNSUPPORTED);
++ return;
++ }
++
++ if (bdrv_snapshot_find(bs, sn, name) < 0) {
++ /* return success if snapshot does not exists */
++ return;
++ }
++
++ ret = bdrv_snapshot_delete(bs, NULL, name, &local_err);
++ if (ret < 0) {
++ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
++ "Error while deleting snapshot on '%s'\n", device);
++ return;
++ }
++}
++
++static int loadstate_get_buffer(void *opaque, uint8_t *buf, int64_t pos,
++ int size)
++{
++ BlockDriverState *bs = (BlockDriverState *)opaque;
++ int64_t maxlen = bdrv_getlength(bs);
++ if (pos > maxlen) {
++ return -EIO;
++ }
++ if ((pos + size) > maxlen) {
++ size = maxlen - pos - 1;
++ }
++ if (size == 0) {
++ return 0;
++ }
++ return bdrv_pread(bs, pos, buf, size);
++}
++
++static const QEMUFileOps loadstate_file_ops = {
++ .get_buffer = loadstate_get_buffer,
++};
++
++int load_state_from_blockdev(const char *filename)
++{
++ BlockDriverState *bs = NULL;
++ BlockDriver *drv = NULL;
++ Error *local_err = NULL;
++ Error *blocker = NULL;
++
++ QEMUFile *f;
++ int ret;
++
++ bs = bdrv_new();
++ ret = bdrv_open(&bs, filename, NULL, NULL, 0, drv, &local_err);
++ error_setg(&blocker, "block device is in use by load state");
++ bdrv_op_block_all(bs, blocker);
++
++ if (ret < 0) {
++ error_report("Could not open VM state file");
++ goto the_end;
++ }
++
++ /* restore the VM state */
++ f = qemu_fopen_ops(bs, &loadstate_file_ops);
++ if (!f) {
++ error_report("Could not open VM state file");
++ ret = -EINVAL;
++ goto the_end;
++ }
++
++ qemu_system_reset(VMRESET_SILENT);
++ migration_incoming_state_new(f);
++ ret = qemu_loadvm_state(f);
++
++ qemu_fclose(f);
++ migration_incoming_state_destroy();
++ if (ret < 0) {
++ error_report("Error %d while loading VM state", ret);
++ goto the_end;
++ }
++
++ ret = 0;
++
++ the_end:
++ if (bs) {
++ bdrv_op_unblock_all(bs, blocker);
++ error_free(blocker);
++ bdrv_unref(bs);
++ }
++ return ret;
++}
+diff --git a/vl.c b/vl.c
+index bdf4fdc..85033e3 100644
+--- a/vl.c
++++ b/vl.c
+@@ -2948,6 +2948,7 @@ int main(int argc, char **argv, char **envp)
+ int optind;
+ const char *optarg;
+ const char *loadvm = NULL;
++ const char *loadstate = NULL;
+ MachineClass *machine_class;
+ const char *cpu_model;
+ const char *vga_model = NULL;
+@@ -3589,6 +3590,9 @@ int main(int argc, char **argv, char **envp)
+ case QEMU_OPTION_loadvm:
+ loadvm = optarg;
+ break;
++ case QEMU_OPTION_loadstate:
++ loadstate = optarg;
++ break;
+ case QEMU_OPTION_full_screen:
+ full_screen = 1;
+ break;
+@@ -4583,6 +4587,10 @@ int main(int argc, char **argv, char **envp)
+ if (load_vmstate(loadvm) < 0) {
+ autostart = 0;
+ }
++ } else if (loadstate) {
++ if (load_state_from_blockdev(loadstate) < 0) {
++ autostart = 0;
++ }
+ }
+
+ qdev_prop_check_globals();
+--
+2.1.4
+
--- /dev/null
+From 4e3757e9cb0235a3ff0093f982dc3a2b4f4bdfe4 Mon Sep 17 00:00:00 2001
+From: Wolfgang Bumiller <w.bumiller@proxmox.com>
+Date: Wed, 9 Dec 2015 16:31:51 +0100
+Subject: [PATCH 23/41] backup: vma: allow empty backups
+
+---
+ vma-reader.c | 29 ++++++++++++-------------
+ vma-writer.c | 30 ++++++++++++++++----------
+ vma.c | 70 ++++++++++++++++++++++++++++++++++++------------------------
+ vma.h | 1 +
+ 4 files changed, 76 insertions(+), 54 deletions(-)
+
+diff --git a/vma-reader.c b/vma-reader.c
+index 2aafb26..78f1de9 100644
+--- a/vma-reader.c
++++ b/vma-reader.c
+@@ -326,11 +326,6 @@ static int vma_reader_read_head(VmaReader *vmar, Error **errp)
+ }
+ }
+
+- if (!count) {
+- error_setg(errp, "vma does not contain data");
+- return -1;
+- }
+-
+ for (i = 0; i < VMA_MAX_CONFIGS; i++) {
+ uint32_t name_ptr = GUINT32_FROM_BE(h->config_names[i]);
+ uint32_t data_ptr = GUINT32_FROM_BE(h->config_data[i]);
+@@ -822,16 +817,20 @@ static int vma_reader_restore_full(VmaReader *vmar, int vmstate_fd,
+ }
+
+ if (verbose) {
+- printf("total bytes read %zd, sparse bytes %zd (%.3g%%)\n",
+- vmar->clusters_read*VMA_CLUSTER_SIZE,
+- vmar->zero_cluster_data,
+- (double)(100.0*vmar->zero_cluster_data)/
+- (vmar->clusters_read*VMA_CLUSTER_SIZE));
+-
+- int64_t datasize = vmar->clusters_read*VMA_CLUSTER_SIZE-vmar->zero_cluster_data;
+- if (datasize) { // this does not make sense for empty files
+- printf("space reduction due to 4K zero blocks %.3g%%\n",
+- (double)(100.0*vmar->partial_zero_cluster_data) / datasize);
++ if (vmar->clusters_read) {
++ printf("total bytes read %zd, sparse bytes %zd (%.3g%%)\n",
++ vmar->clusters_read*VMA_CLUSTER_SIZE,
++ vmar->zero_cluster_data,
++ (double)(100.0*vmar->zero_cluster_data)/
++ (vmar->clusters_read*VMA_CLUSTER_SIZE));
++
++ int64_t datasize = vmar->clusters_read*VMA_CLUSTER_SIZE-vmar->zero_cluster_data;
++ if (datasize) { // this does not make sense for empty files
++ printf("space reduction due to 4K zero blocks %.3g%%\n",
++ (double)(100.0*vmar->partial_zero_cluster_data) / datasize);
++ }
++ } else {
++ printf("vma archive contains no image data\n");
+ }
+ }
+ return ret;
+diff --git a/vma-writer.c b/vma-writer.c
+index 79b7fd4..0d26fc6 100644
+--- a/vma-writer.c
++++ b/vma-writer.c
+@@ -252,7 +252,7 @@ vma_queue_write(VmaWriter *vmaw, const void *buf, size_t bytes)
+ }
+
+ vmaw->co_writer = NULL;
+-
++
+ return (done == bytes) ? bytes : -1;
+ }
+
+@@ -376,10 +376,6 @@ static int coroutine_fn vma_write_header(VmaWriter *vmaw)
+ time_t ctime = time(NULL);
+ head->ctime = GUINT64_TO_BE(ctime);
+
+- if (!vmaw->stream_count) {
+- return -1;
+- }
+-
+ for (i = 0; i < VMA_MAX_CONFIGS; i++) {
+ head->config_names[i] = GUINT32_TO_BE(vmaw->config_names[i]);
+ head->config_data[i] = GUINT32_TO_BE(vmaw->config_data[i]);
+@@ -496,6 +492,23 @@ static int vma_count_open_streams(VmaWriter *vmaw)
+ return open_drives;
+ }
+
++
++/**
++ * You need to call this if the vma archive does not contain
++ * any data stream.
++ */
++int coroutine_fn
++vma_writer_flush_output(VmaWriter *vmaw)
++{
++ qemu_co_mutex_lock(&vmaw->flush_lock);
++ int ret = vma_writer_flush(vmaw);
++ qemu_co_mutex_unlock(&vmaw->flush_lock);
++ if (ret < 0) {
++ vma_writer_set_error(vmaw, "vma_writer_flush_header failed");
++ }
++ return ret;
++}
++
+ /**
+ * all jobs should call this when there is no more data
+ * Returns: number of remaining stream (0 ==> finished)
+@@ -523,12 +536,7 @@ vma_writer_close_stream(VmaWriter *vmaw, uint8_t dev_id)
+
+ if (open_drives <= 0) {
+ DPRINTF("vma_writer_set_status all drives completed\n");
+- qemu_co_mutex_lock(&vmaw->flush_lock);
+- int ret = vma_writer_flush(vmaw);
+- qemu_co_mutex_unlock(&vmaw->flush_lock);
+- if (ret < 0) {
+- vma_writer_set_error(vmaw, "vma_writer_close_stream: flush failed");
+- }
++ vma_writer_flush_output(vmaw);
+ }
+
+ return open_drives;
+diff --git a/vma.c b/vma.c
+index c88a4358..08e4725 100644
+--- a/vma.c
++++ b/vma.c
+@@ -27,7 +27,7 @@ static void help(void)
+ "\n"
+ "vma list <filename>\n"
+ "vma config <filename> [-c config]\n"
+- "vma create <filename> [-c config] <archive> pathname ...\n"
++ "vma create <filename> [-c config] pathname ...\n"
+ "vma extract <filename> [-r <fifo>] <targetdir>\n"
+ "vma verify <filename> [-v]\n"
+ ;
+@@ -395,6 +395,18 @@ typedef struct BackupJob {
+
+ #define BACKUP_SECTORS_PER_CLUSTER (VMA_CLUSTER_SIZE / BDRV_SECTOR_SIZE)
+
++static void coroutine_fn backup_run_empty(void *opaque)
++{
++ VmaWriter *vmaw = (VmaWriter *)opaque;
++
++ vma_writer_flush_output(vmaw);
++
++ Error *err = NULL;
++ if (vma_writer_close(vmaw, &err) != 0) {
++ g_warning("vma_writer_close failed %s", error_get_pretty(err));
++ }
++}
++
+ static void coroutine_fn backup_run(void *opaque)
+ {
+ BackupJob *job = (BackupJob *)opaque;
+@@ -468,8 +480,8 @@ static int create_archive(int argc, char **argv)
+ }
+
+
+- /* make sure we have archive name and at least one path */
+- if ((optind + 2) > argc) {
++ /* make sure we an archive name */
++ if ((optind + 1) > argc) {
+ help();
+ }
+
+@@ -504,11 +516,11 @@ static int create_archive(int argc, char **argv)
+ l = g_list_next(l);
+ }
+
+- int ind = 0;
++ int devcount = 0;
+ while (optind < argc) {
+ const char *path = argv[optind++];
+ char *devname = NULL;
+- path = extract_devname(path, &devname, ind++);
++ path = extract_devname(path, &devname, devcount++);
+
+ Error *errp = NULL;
+ BlockDriverState *bs;
+@@ -539,37 +551,39 @@ static int create_archive(int argc, char **argv)
+ int percent = 0;
+ int last_percent = -1;
+
+- while (1) {
+- main_loop_wait(false);
+- vma_writer_get_status(vmaw, &vmastat);
++ if (devcount) {
++ while (1) {
++ main_loop_wait(false);
++ vma_writer_get_status(vmaw, &vmastat);
++
++ if (verbose) {
+
+- if (verbose) {
++ uint64_t total = 0;
++ uint64_t transferred = 0;
++ uint64_t zero_bytes = 0;
+
+- uint64_t total = 0;
+- uint64_t transferred = 0;
+- uint64_t zero_bytes = 0;
++ int i;
++ for (i = 0; i < 256; i++) {
++ if (vmastat.stream_info[i].size) {
++ total += vmastat.stream_info[i].size;
++ transferred += vmastat.stream_info[i].transferred;
++ zero_bytes += vmastat.stream_info[i].zero_bytes;
++ }
++ }
++ percent = (transferred*100)/total;
++ if (percent != last_percent) {
++ fprintf(stderr, "progress %d%% %zd/%zd %zd\n", percent,
++ transferred, total, zero_bytes);
++ fflush(stderr);
+
+- int i;
+- for (i = 0; i < 256; i++) {
+- if (vmastat.stream_info[i].size) {
+- total += vmastat.stream_info[i].size;
+- transferred += vmastat.stream_info[i].transferred;
+- zero_bytes += vmastat.stream_info[i].zero_bytes;
++ last_percent = percent;
+ }
+ }
+- percent = (transferred*100)/total;
+- if (percent != last_percent) {
+- fprintf(stderr, "progress %d%% %zd/%zd %zd\n", percent,
+- transferred, total, zero_bytes);
+- fflush(stderr);
+
+- last_percent = percent;
++ if (vmastat.closed) {
++ break;
+ }
+ }
+-
+- if (vmastat.closed) {
+- break;
+- }
+ } else {
+ Coroutine *co = qemu_coroutine_create(backup_run_empty, vmaw);
+ qemu_coroutine_enter(co);
+diff --git a/vma.h b/vma.h
+index 98377e4..365ceb2 100644
+--- a/vma.h
++++ b/vma.h
+@@ -128,6 +128,7 @@ int64_t coroutine_fn vma_writer_write(VmaWriter *vmaw, uint8_t dev_id,
+ size_t *zero_bytes);
+
+ int coroutine_fn vma_writer_close_stream(VmaWriter *vmaw, uint8_t dev_id);
++int coroutine_fn vma_writer_flush_output(VmaWriter *vmaw);
+
+ int vma_writer_get_status(VmaWriter *vmaw, VmaStatus *status);
+ void vma_writer_set_error(VmaWriter *vmaw, const char *fmt, ...);
+--
+2.1.4
+
+++ /dev/null
-From a53c35c25e703bc08198ec6ee02a86c79dae8aaa Mon Sep 17 00:00:00 2001
-From: Wolfgang Bumiller <w.bumiller@proxmox.com>
-Date: Wed, 9 Dec 2015 16:04:32 +0100
-Subject: [PATCH 23/55] internal snapshot async
-
----
- Makefile.objs | 1 +
- block.c | 2 +-
- hmp-commands-info.hx | 13 ++
- hmp-commands.hx | 32 +++
- hmp.c | 57 ++++++
- hmp.h | 5 +
- include/block/block.h | 1 +
- include/sysemu/sysemu.h | 5 +-
- migration/savevm.c | 12 +-
- qapi-schema.json | 46 +++++
- qemu-options.hx | 13 ++
- qmp-commands.hx | 30 +++
- savevm-async.c | 522 ++++++++++++++++++++++++++++++++++++++++++++++++
- vl.c | 8 +
- 14 files changed, 739 insertions(+), 8 deletions(-)
- create mode 100644 savevm-async.c
-
-diff --git a/Makefile.objs b/Makefile.objs
-index 5fd0bc4..c3367dd 100644
---- a/Makefile.objs
-+++ b/Makefile.objs
-@@ -54,6 +54,7 @@ common-obj-y += migration/
- common-obj-y += qemu-char.o #aio.o
- common-obj-y += page_cache.o
- common-obj-y += qjson.o
-+common-obj-y += savevm-async.o
-
- common-obj-$(CONFIG_SPICE) += spice-qemu-char.o
-
-diff --git a/block.c b/block.c
-index d4939b4..bee5a8d 100644
---- a/block.c
-+++ b/block.c
-@@ -2332,7 +2332,7 @@ void bdrv_replace_in_backing_chain(BlockDriverState *old, BlockDriverState *new)
- bdrv_unref(old);
- }
-
--static void bdrv_delete(BlockDriverState *bs)
-+void bdrv_delete(BlockDriverState *bs)
- {
- assert(!bs->job);
- assert(bdrv_op_blocker_is_empty(bs));
-diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx
-index f872262..c213ef7 100644
---- a/hmp-commands-info.hx
-+++ b/hmp-commands-info.hx
-@@ -588,6 +588,19 @@ Show current migration xbzrle cache size.
- ETEXI
-
- {
-+ .name = "savevm",
-+ .args_type = "",
-+ .params = "",
-+ .help = "show savevm status",
-+ .mhandler.cmd = hmp_info_savevm,
-+ },
-+
-+STEXI
-+@item info savevm
-+show savevm status
-+ETEXI
-+
-+ {
- .name = "balloon",
- .args_type = "",
- .params = "",
-diff --git a/hmp-commands.hx b/hmp-commands.hx
-index 8a94e36..2c0009a 100644
---- a/hmp-commands.hx
-+++ b/hmp-commands.hx
-@@ -1790,3 +1790,35 @@ ETEXI
- STEXI
- @end table
- ETEXI
-+
-+ {
-+ .name = "savevm-start",
-+ .args_type = "statefile:s?",
-+ .params = "[statefile]",
-+ .help = "Prepare for snapshot and halt VM. Save VM state to statefile.",
-+ .mhandler.cmd = hmp_savevm_start,
-+ },
-+
-+ {
-+ .name = "snapshot-drive",
-+ .args_type = "device:s,name:s",
-+ .params = "device name",
-+ .help = "Create internal snapshot.",
-+ .mhandler.cmd = hmp_snapshot_drive,
-+ },
-+
-+ {
-+ .name = "delete-drive-snapshot",
-+ .args_type = "device:s,name:s",
-+ .params = "device name",
-+ .help = "Delete internal snapshot.",
-+ .mhandler.cmd = hmp_delete_drive_snapshot,
-+ },
-+
-+ {
-+ .name = "savevm-end",
-+ .args_type = "",
-+ .params = "",
-+ .help = "Resume VM after snaphot.",
-+ .mhandler.cmd = hmp_savevm_end,
-+ },
-diff --git a/hmp.c b/hmp.c
-index 040909e..78644a7 100644
---- a/hmp.c
-+++ b/hmp.c
-@@ -2093,6 +2093,63 @@ void hmp_info_memory_devices(Monitor *mon, const QDict *qdict)
- qapi_free_MemoryDeviceInfoList(info_list);
- }
-
-+void hmp_savevm_start(Monitor *mon, const QDict *qdict)
-+{
-+ Error *errp = NULL;
-+ const char *statefile = qdict_get_try_str(qdict, "statefile");
-+
-+ qmp_savevm_start(statefile != NULL, statefile, &errp);
-+ hmp_handle_error(mon, &errp);
-+}
-+
-+void hmp_snapshot_drive(Monitor *mon, const QDict *qdict)
-+{
-+ Error *errp = NULL;
-+ const char *name = qdict_get_str(qdict, "name");
-+ const char *device = qdict_get_str(qdict, "device");
-+
-+ qmp_snapshot_drive(device, name, &errp);
-+ hmp_handle_error(mon, &errp);
-+}
-+
-+void hmp_delete_drive_snapshot(Monitor *mon, const QDict *qdict)
-+{
-+ Error *errp = NULL;
-+ const char *name = qdict_get_str(qdict, "name");
-+ const char *device = qdict_get_str(qdict, "device");
-+
-+ qmp_delete_drive_snapshot(device, name, &errp);
-+ hmp_handle_error(mon, &errp);
-+}
-+
-+void hmp_savevm_end(Monitor *mon, const QDict *qdict)
-+{
-+ Error *errp = NULL;
-+
-+ qmp_savevm_end(&errp);
-+ hmp_handle_error(mon, &errp);
-+}
-+
-+void hmp_info_savevm(Monitor *mon, const QDict *qdict)
-+{
-+ SaveVMInfo *info;
-+ info = qmp_query_savevm(NULL);
-+
-+ if (info->has_status) {
-+ monitor_printf(mon, "savevm status: %s\n", info->status);
-+ monitor_printf(mon, "total time: %" PRIu64 " milliseconds\n",
-+ info->total_time);
-+ } else {
-+ monitor_printf(mon, "savevm status: not running\n");
-+ }
-+ if (info->has_bytes) {
-+ monitor_printf(mon, "Bytes saved: %"PRIu64"\n", info->bytes);
-+ }
-+ if (info->has_error) {
-+ monitor_printf(mon, "Error: %s\n", info->error);
-+ }
-+}
-+
- void hmp_info_iothreads(Monitor *mon, const QDict *qdict)
- {
- IOThreadInfoList *info_list = qmp_query_iothreads(NULL);
-diff --git a/hmp.h b/hmp.h
-index c708b3f..99bc949 100644
---- a/hmp.h
-+++ b/hmp.h
-@@ -26,6 +26,7 @@ void hmp_info_status(Monitor *mon, const QDict *qdict);
- void hmp_info_uuid(Monitor *mon, const QDict *qdict);
- void hmp_info_chardev(Monitor *mon, const QDict *qdict);
- void hmp_info_mice(Monitor *mon, const QDict *qdict);
-+void hmp_info_savevm(Monitor *mon, const QDict *qdict);
- void hmp_info_migrate(Monitor *mon, const QDict *qdict);
- void hmp_info_migrate_capabilities(Monitor *mon, const QDict *qdict);
- void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict);
-@@ -92,6 +93,10 @@ void hmp_netdev_add(Monitor *mon, const QDict *qdict);
- void hmp_netdev_del(Monitor *mon, const QDict *qdict);
- void hmp_getfd(Monitor *mon, const QDict *qdict);
- void hmp_closefd(Monitor *mon, const QDict *qdict);
-+void hmp_savevm_start(Monitor *mon, const QDict *qdict);
-+void hmp_snapshot_drive(Monitor *mon, const QDict *qdict);
-+void hmp_delete_drive_snapshot(Monitor *mon, const QDict *qdict);
-+void hmp_savevm_end(Monitor *mon, const QDict *qdict);
- void hmp_sendkey(Monitor *mon, const QDict *qdict);
- void hmp_screendump(Monitor *mon, const QDict *qdict);
- void hmp_nbd_server_start(Monitor *mon, const QDict *qdict);
-diff --git a/include/block/block.h b/include/block/block.h
-index 3a73137..fcc9cac 100644
---- a/include/block/block.h
-+++ b/include/block/block.h
-@@ -266,6 +266,7 @@ BlockDriverState *bdrv_find_backing_image(BlockDriverState *bs,
- int bdrv_get_backing_file_depth(BlockDriverState *bs);
- void bdrv_refresh_filename(BlockDriverState *bs);
- int bdrv_truncate(BlockDriverState *bs, int64_t offset);
-+void bdrv_delete(BlockDriverState *bs);
- int64_t bdrv_nb_sectors(BlockDriverState *bs);
- int64_t bdrv_getlength(BlockDriverState *bs);
- int64_t bdrv_get_allocated_file_size(BlockDriverState *bs);
-diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h
-index 38fb3ca..d4790a6 100644
---- a/include/sysemu/sysemu.h
-+++ b/include/sysemu/sysemu.h
-@@ -78,6 +78,7 @@ void qemu_add_machine_init_done_notifier(Notifier *notify);
-
- void hmp_savevm(Monitor *mon, const QDict *qdict);
- int load_vmstate(const char *name);
-+int load_state_from_blockdev(const char *filename);
- void hmp_delvm(Monitor *mon, const QDict *qdict);
- void hmp_info_snapshots(Monitor *mon, const QDict *qdict);
-
-@@ -105,13 +106,13 @@ enum qemu_vm_cmd {
- #define MAX_VM_CMD_PACKAGED_SIZE (1ul << 24)
-
- bool qemu_savevm_state_blocked(Error **errp);
--void qemu_savevm_state_begin(QEMUFile *f,
-+int qemu_savevm_state_begin(QEMUFile *f,
- const MigrationParams *params);
- void qemu_savevm_state_header(QEMUFile *f);
- int qemu_savevm_state_iterate(QEMUFile *f, bool postcopy);
- void qemu_savevm_state_cleanup(void);
- void qemu_savevm_state_complete_postcopy(QEMUFile *f);
--void qemu_savevm_state_complete_precopy(QEMUFile *f, bool iterable_only);
-+int qemu_savevm_state_complete_precopy(QEMUFile *f, bool iterable_only);
- void qemu_savevm_state_pending(QEMUFile *f, uint64_t max_size,
- uint64_t *res_non_postcopiable,
- uint64_t *res_postcopiable);
-diff --git a/migration/savevm.c b/migration/savevm.c
-index 8346649..d6560f5 100644
---- a/migration/savevm.c
-+++ b/migration/savevm.c
-@@ -896,11 +896,11 @@ void qemu_savevm_state_header(QEMUFile *f)
-
- }
-
--void qemu_savevm_state_begin(QEMUFile *f,
-+int qemu_savevm_state_begin(QEMUFile *f,
- const MigrationParams *params)
- {
- SaveStateEntry *se;
-- int ret;
-+ int ret = 0;
-
- trace_savevm_state_begin();
- QTAILQ_FOREACH(se, &savevm_state.handlers, entry) {
-@@ -928,6 +928,7 @@ void qemu_savevm_state_begin(QEMUFile *f,
- break;
- }
- }
-+ return ret;
- }
-
- /*
-@@ -1031,7 +1032,7 @@ void qemu_savevm_state_complete_postcopy(QEMUFile *f)
- qemu_fflush(f);
- }
-
--void qemu_savevm_state_complete_precopy(QEMUFile *f, bool iterable_only)
-+int qemu_savevm_state_complete_precopy(QEMUFile *f, bool iterable_only)
- {
- QJSON *vmdesc;
- int vmdesc_len;
-@@ -1065,12 +1066,12 @@ void qemu_savevm_state_complete_precopy(QEMUFile *f, bool iterable_only)
- save_section_footer(f, se);
- if (ret < 0) {
- qemu_file_set_error(f, ret);
-- return;
-+ return ret;
- }
- }
-
- if (iterable_only) {
-- return;
-+ return ret;
- }
-
- vmdesc = qjson_new();
-@@ -1117,6 +1118,7 @@ void qemu_savevm_state_complete_precopy(QEMUFile *f, bool iterable_only)
- object_unref(OBJECT(vmdesc));
-
- qemu_fflush(f);
-+ return qemu_file_get_error(f);
- }
-
- /* Give an estimate of the amount left to be transferred,
-diff --git a/qapi-schema.json b/qapi-schema.json
-index 3427d50..2392139 100644
---- a/qapi-schema.json
-+++ b/qapi-schema.json
-@@ -585,6 +585,42 @@
- '*setup-time': 'int',
- '*x-cpu-throttle-percentage': 'int'} }
-
-+
-+# @SaveVMInfo
-+#
-+# Information about current migration process.
-+#
-+# @status: #optional string describing the current savevm status.
-+# This can be 'active', 'completed', 'failed'.
-+# If this field is not returned, no savevm process
-+# has been initiated
-+#
-+# @error: #optional string containing error message is status is failed.
-+#
-+# @total-time: #optional total amount of milliseconds since savevm started.
-+# If savevm has ended, it returns the total save time
-+#
-+# @bytes: #optional total amount of data transfered
-+#
-+# Since: 1.3
-+##
-+{ 'struct': 'SaveVMInfo',
-+ 'data': {'*status': 'str', '*error': 'str',
-+ '*total-time': 'int', '*bytes': 'int'} }
-+
-+##
-+# @query-savevm
-+#
-+# Returns information about current savevm process.
-+#
-+# Returns: @SaveVMInfo
-+#
-+# Since: 1.3
-+##
-+{ 'command': 'query-savevm', 'returns': 'SaveVMInfo' }
-+
-+##
-+
- ##
- # @query-migrate
- #
-@@ -3197,8 +3233,18 @@
- #
- # Since: 1.2.0
- ##
-+
- { 'command': 'query-target', 'returns': 'TargetInfo' }
-
-+{ 'command': 'savevm-start', 'data': { '*statefile': 'str' } }
-+
-+{ 'command': 'snapshot-drive', 'data': { 'device': 'str', 'name': 'str' } }
-+
-+{ 'command': 'delete-drive-snapshot', 'data': { 'device': 'str', 'name': 'str' } }
-+
-+{ 'command': 'savevm-end' }
-+
-+
- ##
- # @QKeyCode:
- #
-diff --git a/qemu-options.hx b/qemu-options.hx
-index 6106520..20ab9ac 100644
---- a/qemu-options.hx
-+++ b/qemu-options.hx
-@@ -3270,6 +3270,19 @@ STEXI
- Start right away with a saved state (@code{loadvm} in monitor)
- ETEXI
-
-+DEF("loadstate", HAS_ARG, QEMU_OPTION_loadstate, \
-+ "-loadstate file\n" \
-+ " start right away with a saved state\n",
-+ QEMU_ARCH_ALL)
-+STEXI
-+@item -loadstate @var{file}
-+@findex -loadstate
-+Start right away with a saved state. This option does not rollback
-+disk state like @code{loadvm}, so user must make sure that disk
-+have correct state. @var{file} can be any valid device URL. See the section
-+for "Device URL Syntax" for more information.
-+ETEXI
-+
- #ifndef _WIN32
- DEF("daemonize", 0, QEMU_OPTION_daemonize, \
- "-daemonize daemonize QEMU after initializing\n", QEMU_ARCH_ALL)
-diff --git a/qmp-commands.hx b/qmp-commands.hx
-index 3f03c0d..437ddd6 100644
---- a/qmp-commands.hx
-+++ b/qmp-commands.hx
-@@ -4783,6 +4783,36 @@ Example:
- EQMP
-
- {
-+ .name = "savevm-start",
-+ .args_type = "statefile:s?",
-+ .mhandler.cmd_new = qmp_marshal_input_savevm_start,
-+ },
-+
-+ {
-+ .name = "snapshot-drive",
-+ .args_type = "device:s,name:s",
-+ .mhandler.cmd_new = qmp_marshal_input_snapshot_drive,
-+ },
-+
-+ {
-+ .name = "delete-drive-snapshot",
-+ .args_type = "device:s,name:s",
-+ .mhandler.cmd_new = qmp_marshal_input_delete_drive_snapshot,
-+ },
-+
-+ {
-+ .name = "savevm-end",
-+ .args_type = "",
-+ .mhandler.cmd_new = qmp_marshal_input_savevm_end,
-+ },
-+
-+ {
-+ .name = "query-savevm",
-+ .args_type = "",
-+ .mhandler.cmd_new = qmp_marshal_input_query_savevm,
-+ },
-+
-+ {
- .name = "query-rocker",
- .args_type = "name:s",
- .mhandler.cmd_new = qmp_marshal_query_rocker,
-diff --git a/savevm-async.c b/savevm-async.c
-new file mode 100644
-index 0000000..8117443
---- /dev/null
-+++ b/savevm-async.c
-@@ -0,0 +1,522 @@
-+#include "qemu-common.h"
-+#include "qapi/qmp/qerror.h"
-+#include "qemu/error-report.h"
-+#include "sysemu/sysemu.h"
-+#include "qmp-commands.h"
-+#include "qemu-options.h"
-+#include "migration/qemu-file.h"
-+#include "qom/qom-qobject.h"
-+#include "migration/migration.h"
-+#include "block/snapshot.h"
-+#include "block/qapi.h"
-+#include "block/block.h"
-+#include "qemu/timer.h"
-+#include "sysemu/block-backend.h"
-+#include "qapi/qmp/qstring.h"
-+
-+/* #define DEBUG_SAVEVM_STATE */
-+
-+#ifdef DEBUG_SAVEVM_STATE
-+#define DPRINTF(fmt, ...) \
-+ do { printf("savevm-async: " fmt, ## __VA_ARGS__); } while (0)
-+#else
-+#define DPRINTF(fmt, ...) \
-+ do { } while (0)
-+#endif
-+
-+enum {
-+ SAVE_STATE_DONE,
-+ SAVE_STATE_ERROR,
-+ SAVE_STATE_ACTIVE,
-+ SAVE_STATE_COMPLETED,
-+ SAVE_STATE_CANCELLED
-+};
-+
-+
-+static struct SnapshotState {
-+ BlockDriverState *bs;
-+ size_t bs_pos;
-+ int state;
-+ Error *error;
-+ Error *blocker;
-+ int saved_vm_running;
-+ QEMUFile *file;
-+ int64_t total_time;
-+} snap_state;
-+
-+SaveVMInfo *qmp_query_savevm(Error **errp)
-+{
-+ SaveVMInfo *info = g_malloc0(sizeof(*info));
-+ struct SnapshotState *s = &snap_state;
-+
-+ if (s->state != SAVE_STATE_DONE) {
-+ info->has_bytes = true;
-+ info->bytes = s->bs_pos;
-+ switch (s->state) {
-+ case SAVE_STATE_ERROR:
-+ info->has_status = true;
-+ info->status = g_strdup("failed");
-+ info->has_total_time = true;
-+ info->total_time = s->total_time;
-+ if (s->error) {
-+ info->has_error = true;
-+ info->error = g_strdup(error_get_pretty(s->error));
-+ }
-+ break;
-+ case SAVE_STATE_ACTIVE:
-+ info->has_status = true;
-+ info->status = g_strdup("active");
-+ info->has_total_time = true;
-+ info->total_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME)
-+ - s->total_time;
-+ break;
-+ case SAVE_STATE_COMPLETED:
-+ info->has_status = true;
-+ info->status = g_strdup("completed");
-+ info->has_total_time = true;
-+ info->total_time = s->total_time;
-+ break;
-+ }
-+ }
-+
-+ return info;
-+}
-+
-+static int save_snapshot_cleanup(void)
-+{
-+ int ret = 0;
-+
-+ DPRINTF("save_snapshot_cleanup\n");
-+
-+ snap_state.total_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME) -
-+ snap_state.total_time;
-+
-+ if (snap_state.file) {
-+ ret = qemu_fclose(snap_state.file);
-+ }
-+
-+ if (snap_state.bs) {
-+ /* try to truncate, but ignore errors (will fail on block devices).
-+ * note: bdrv_read() need whole blocks, so we round up
-+ */
-+ size_t size = (snap_state.bs_pos + BDRV_SECTOR_SIZE) & BDRV_SECTOR_MASK;
-+ bdrv_truncate(snap_state.bs, size);
-+ bdrv_op_unblock_all(snap_state.bs, snap_state.blocker);
-+ error_free(snap_state.blocker);
-+ snap_state.blocker = NULL;
-+ bdrv_unref(snap_state.bs);
-+ snap_state.bs = NULL;
-+ }
-+
-+ return ret;
-+}
-+
-+static void save_snapshot_error(const char *fmt, ...)
-+{
-+ va_list ap;
-+ char *msg;
-+
-+ va_start(ap, fmt);
-+ msg = g_strdup_vprintf(fmt, ap);
-+ va_end(ap);
-+
-+ DPRINTF("save_snapshot_error: %s\n", msg);
-+
-+ if (!snap_state.error) {
-+ error_set(&snap_state.error, ERROR_CLASS_GENERIC_ERROR, "%s", msg);
-+ }
-+
-+ g_free (msg);
-+
-+ snap_state.state = SAVE_STATE_ERROR;
-+
-+ save_snapshot_cleanup();
-+}
-+
-+static void save_snapshot_completed(void)
-+{
-+ DPRINTF("save_snapshot_completed\n");
-+
-+ if (save_snapshot_cleanup() < 0) {
-+ snap_state.state = SAVE_STATE_ERROR;
-+ } else {
-+ snap_state.state = SAVE_STATE_COMPLETED;
-+ }
-+}
-+
-+static int block_state_close(void *opaque)
-+{
-+ snap_state.file = NULL;
-+ return bdrv_flush(snap_state.bs);
-+}
-+
-+static int block_state_put_buffer(void *opaque, const uint8_t *buf,
-+ int64_t pos, int size)
-+{
-+ int ret;
-+
-+ assert(pos == snap_state.bs_pos);
-+
-+ if ((ret = bdrv_pwrite(snap_state.bs, snap_state.bs_pos, buf, size)) > 0) {
-+ snap_state.bs_pos += ret;
-+ }
-+
-+ return ret;
-+}
-+
-+static int store_and_stop(void) {
-+ if (global_state_store()) {
-+ save_snapshot_error("Error saving global state");
-+ return 1;
-+ }
-+ if (runstate_is_running()) {
-+ vm_stop(RUN_STATE_SAVE_VM);
-+ }
-+ return 0;
-+}
-+
-+static void process_savevm_co(void *opaque)
-+{
-+ int ret;
-+ int64_t maxlen;
-+ MigrationParams params = {
-+ .blk = 0,
-+ .shared = 0
-+ };
-+
-+ snap_state.state = SAVE_STATE_ACTIVE;
-+
-+ qemu_mutex_unlock_iothread();
-+ qemu_savevm_state_header(snap_state.file);
-+ ret = qemu_savevm_state_begin(snap_state.file, ¶ms);
-+ qemu_mutex_lock_iothread();
-+
-+ if (ret < 0) {
-+ save_snapshot_error("qemu_savevm_state_begin failed");
-+ return;
-+ }
-+
-+ while (snap_state.state == SAVE_STATE_ACTIVE) {
-+ uint64_t pending_size;
-+
-+ pending_size = qemu_savevm_state_pending(snap_state.file, 0);
-+
-+ if (pending_size) {
-+ ret = qemu_savevm_state_iterate(snap_state.file);
-+ if (ret < 0) {
-+ save_snapshot_error("qemu_savevm_state_iterate error %d", ret);
-+ break;
-+ }
-+ DPRINTF("savevm inerate pending size %lu ret %d\n", pending_size, ret);
-+ } else {
-+ DPRINTF("done iterating\n");
-+ if (store_and_stop())
-+ break;
-+ DPRINTF("savevm inerate finished\n");
-+ qemu_savevm_state_complete_precopy(snap_state.file);
-+ DPRINTF("save complete\n");
-+ save_snapshot_completed();
-+ break;
-+ }
-+
-+ /* stop the VM if we get to the end of available space,
-+ * or if pending_size is just a few MB
-+ */
-+ maxlen = bdrv_getlength(snap_state.bs) - 30*1024*1024;
-+ if ((pending_size < 100000) ||
-+ ((snap_state.bs_pos + pending_size) >= maxlen)) {
-+ if (store_and_stop())
-+ break;
-+ }
-+ }
-+
-+ if(snap_state.state == SAVE_STATE_CANCELLED) {
-+ save_snapshot_completed();
-+ Error *errp = NULL;
-+ qmp_savevm_end(&errp);
-+ }
-+
-+}
-+
-+static const QEMUFileOps block_file_ops = {
-+ .put_buffer = block_state_put_buffer,
-+ .close = block_state_close,
-+};
-+
-+
-+void qmp_savevm_start(bool has_statefile, const char *statefile, Error **errp)
-+{
-+ BlockDriver *drv = NULL;
-+ Error *local_err = NULL;
-+
-+ int bdrv_oflags = BDRV_O_CACHE_WB | BDRV_O_RDWR;
-+ int ret;
-+
-+ if (snap_state.state != SAVE_STATE_DONE) {
-+ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
-+ "VM snapshot already started\n");
-+ return;
-+ }
-+
-+ /* initialize snapshot info */
-+ snap_state.saved_vm_running = runstate_is_running();
-+ snap_state.bs_pos = 0;
-+ snap_state.total_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
-+ snap_state.blocker = NULL;
-+
-+ if (snap_state.error) {
-+ error_free(snap_state.error);
-+ snap_state.error = NULL;
-+ }
-+
-+ if (!has_statefile) {
-+ vm_stop(RUN_STATE_SAVE_VM);
-+ snap_state.state = SAVE_STATE_COMPLETED;
-+ return;
-+ }
-+
-+ if (qemu_savevm_state_blocked(errp)) {
-+ return;
-+ }
-+
-+ /* Open the image */
-+ snap_state.bs = bdrv_new();
-+
-+ QDict *options = NULL;
-+ options = qdict_new();
-+ qdict_put(options, "driver", qstring_from_str("raw"));
-+ ret = bdrv_open(&snap_state.bs, statefile, NULL, options, bdrv_oflags, drv, &local_err);
-+ if (ret < 0) {
-+ error_set(errp, ERROR_CLASS_GENERIC_ERROR, "failed to open '%s'", statefile);
-+ goto restart;
-+ }
-+
-+ snap_state.file = qemu_fopen_ops(&snap_state, &block_file_ops);
-+
-+ if (!snap_state.file) {
-+ error_set(errp, ERROR_CLASS_GENERIC_ERROR, "failed to open '%s'", statefile);
-+ goto restart;
-+ }
-+
-+
-+ error_setg(&snap_state.blocker, "block device is in use by savevm");
-+ bdrv_op_block_all(snap_state.bs, snap_state.blocker);
-+
-+ Coroutine *co = qemu_coroutine_create(process_savevm_co);
-+ qemu_coroutine_enter(co, NULL);
-+
-+ return;
-+
-+restart:
-+
-+ save_snapshot_error("setup failed");
-+
-+ if (snap_state.saved_vm_running) {
-+ vm_start();
-+ }
-+}
-+
-+void qmp_savevm_end(Error **errp)
-+{
-+ if (snap_state.state == SAVE_STATE_DONE) {
-+ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
-+ "VM snapshot not started\n");
-+ return;
-+ }
-+
-+ if (snap_state.state == SAVE_STATE_ACTIVE) {
-+ snap_state.state = SAVE_STATE_CANCELLED;
-+ return;
-+ }
-+
-+ if (snap_state.saved_vm_running) {
-+ vm_start();
-+ }
-+
-+ snap_state.state = SAVE_STATE_DONE;
-+}
-+
-+void qmp_snapshot_drive(const char *device, const char *name, Error **errp)
-+{
-+ BlockBackend *blk;
-+ BlockDriverState *bs;
-+ QEMUSnapshotInfo sn1, *sn = &sn1;
-+ int ret;
-+#ifdef _WIN32
-+ struct _timeb tb;
-+#else
-+ struct timeval tv;
-+#endif
-+
-+ if (snap_state.state != SAVE_STATE_COMPLETED) {
-+ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
-+ "VM snapshot not ready/started\n");
-+ return;
-+ }
-+
-+ blk = blk_by_name(device);
-+ if (!blk) {
-+ error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
-+ "Device '%s' not found", device);
-+ return;
-+ }
-+
-+ bs = blk_bs(blk);
-+ if (!bdrv_is_inserted(bs)) {
-+ error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, device);
-+ return;
-+ }
-+
-+ if (bdrv_is_read_only(bs)) {
-+ error_setg(errp, "Node '%s' is read only", device);
-+ return;
-+ }
-+
-+ if (!bdrv_can_snapshot(bs)) {
-+ error_setg(errp, QERR_UNSUPPORTED);
-+ return;
-+ }
-+
-+ if (bdrv_snapshot_find(bs, sn, name) >= 0) {
-+ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
-+ "snapshot '%s' already exists", name);
-+ return;
-+ }
-+
-+ sn = &sn1;
-+ memset(sn, 0, sizeof(*sn));
-+
-+#ifdef _WIN32
-+ _ftime(&tb);
-+ sn->date_sec = tb.time;
-+ sn->date_nsec = tb.millitm * 1000000;
-+#else
-+ gettimeofday(&tv, NULL);
-+ sn->date_sec = tv.tv_sec;
-+ sn->date_nsec = tv.tv_usec * 1000;
-+#endif
-+ sn->vm_clock_nsec = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
-+
-+ pstrcpy(sn->name, sizeof(sn->name), name);
-+
-+ sn->vm_state_size = 0; /* do not save state */
-+
-+ ret = bdrv_snapshot_create(bs, sn);
-+ if (ret < 0) {
-+ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
-+ "Error while creating snapshot on '%s'\n", device);
-+ return;
-+ }
-+}
-+
-+void qmp_delete_drive_snapshot(const char *device, const char *name,
-+ Error **errp)
-+{
-+ BlockBackend *blk;
-+ BlockDriverState *bs;
-+ QEMUSnapshotInfo sn1, *sn = &sn1;
-+ Error *local_err = NULL;
-+
-+ int ret;
-+
-+ blk = blk_by_name(device);
-+ if (!blk) {
-+ error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
-+ "Device '%s' not found", device);
-+ return;
-+ }
-+
-+ bs = blk_bs(blk);
-+ if (bdrv_is_read_only(bs)) {
-+ error_setg(errp, "Node '%s' is read only", device);
-+ return;
-+ }
-+
-+ if (!bdrv_can_snapshot(bs)) {
-+ error_setg(errp, QERR_UNSUPPORTED);
-+ return;
-+ }
-+
-+ if (bdrv_snapshot_find(bs, sn, name) < 0) {
-+ /* return success if snapshot does not exists */
-+ return;
-+ }
-+
-+ ret = bdrv_snapshot_delete(bs, NULL, name, &local_err);
-+ if (ret < 0) {
-+ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
-+ "Error while deleting snapshot on '%s'\n", device);
-+ return;
-+ }
-+}
-+
-+static int loadstate_get_buffer(void *opaque, uint8_t *buf, int64_t pos,
-+ int size)
-+{
-+ BlockDriverState *bs = (BlockDriverState *)opaque;
-+ int64_t maxlen = bdrv_getlength(bs);
-+ if (pos > maxlen) {
-+ return -EIO;
-+ }
-+ if ((pos + size) > maxlen) {
-+ size = maxlen - pos - 1;
-+ }
-+ if (size == 0) {
-+ return 0;
-+ }
-+ return bdrv_pread(bs, pos, buf, size);
-+}
-+
-+static const QEMUFileOps loadstate_file_ops = {
-+ .get_buffer = loadstate_get_buffer,
-+};
-+
-+int load_state_from_blockdev(const char *filename)
-+{
-+ BlockDriverState *bs = NULL;
-+ BlockDriver *drv = NULL;
-+ Error *local_err = NULL;
-+ Error *blocker = NULL;
-+
-+ QEMUFile *f;
-+ int ret;
-+
-+ bs = bdrv_new();
-+ ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_CACHE_WB, drv, &local_err);
-+ error_setg(&blocker, "block device is in use by load state");
-+ bdrv_op_block_all(bs, blocker);
-+
-+ if (ret < 0) {
-+ error_report("Could not open VM state file");
-+ goto the_end;
-+ }
-+
-+ /* restore the VM state */
-+ f = qemu_fopen_ops(bs, &loadstate_file_ops);
-+ if (!f) {
-+ error_report("Could not open VM state file");
-+ ret = -EINVAL;
-+ goto the_end;
-+ }
-+
-+ qemu_system_reset(VMRESET_SILENT);
-+ migration_incoming_state_new(f);
-+ ret = qemu_loadvm_state(f);
-+
-+ qemu_fclose(f);
-+ migration_incoming_state_destroy();
-+ if (ret < 0) {
-+ error_report("Error %d while loading VM state", ret);
-+ goto the_end;
-+ }
-+
-+ ret = 0;
-+
-+ the_end:
-+ if (bs) {
-+ bdrv_op_unblock_all(bs, blocker);
-+ error_free(blocker);
-+ bdrv_unref(bs);
-+ }
-+ return ret;
-+}
-diff --git a/vl.c b/vl.c
-index feeb2c4..ba29593 100644
---- a/vl.c
-+++ b/vl.c
-@@ -2979,6 +2979,7 @@ int main(int argc, char **argv, char **envp)
- int optind;
- const char *optarg;
- const char *loadvm = NULL;
-+ const char *loadstate = NULL;
- MachineClass *machine_class;
- const char *cpu_model;
- const char *vga_model = NULL;
-@@ -3612,6 +3613,9 @@ int main(int argc, char **argv, char **envp)
- case QEMU_OPTION_loadvm:
- loadvm = optarg;
- break;
-+ case QEMU_OPTION_loadstate:
-+ loadstate = optarg;
-+ break;
- case QEMU_OPTION_full_screen:
- full_screen = 1;
- break;
-@@ -4640,6 +4644,10 @@ int main(int argc, char **argv, char **envp)
- if (load_vmstate(loadvm) < 0) {
- autostart = 0;
- }
-+ } else if (loadstate) {
-+ if (load_state_from_blockdev(loadstate) < 0) {
-+ autostart = 0;
-+ }
- }
-
- qdev_prop_check_globals();
---
-2.1.4
-
+++ /dev/null
-From 7136f6d83dee1ee791c142ab0bb3d406589e9e13 Mon Sep 17 00:00:00 2001
-From: Wolfgang Bumiller <w.bumiller@proxmox.com>
-Date: Wed, 9 Dec 2015 16:31:51 +0100
-Subject: [PATCH 24/55] backup: vma: allow empty backups
-
----
- vma-reader.c | 29 ++++++++++-----------
- vma-writer.c | 30 ++++++++++++++--------
- vma.c | 84 ++++++++++++++++++++++++++++++++++++++----------------------
- vma.h | 1 +
- 4 files changed, 88 insertions(+), 56 deletions(-)
-
-diff --git a/vma-reader.c b/vma-reader.c
-index d9f43fe..5d0d3ea 100644
---- a/vma-reader.c
-+++ b/vma-reader.c
-@@ -334,11 +334,6 @@ static int vma_reader_read_head(VmaReader *vmar, Error **errp)
- }
- }
-
-- if (!count) {
-- error_setg(errp, "vma does not contain data");
-- return -1;
-- }
--
- for (i = 0; i < VMA_MAX_CONFIGS; i++) {
- uint32_t name_ptr = GUINT32_FROM_BE(h->config_names[i]);
- uint32_t data_ptr = GUINT32_FROM_BE(h->config_data[i]);
-@@ -830,16 +825,20 @@ static int vma_reader_restore_full(VmaReader *vmar, int vmstate_fd,
- }
-
- if (verbose) {
-- printf("total bytes read %zd, sparse bytes %zd (%.3g%%)\n",
-- vmar->clusters_read*VMA_CLUSTER_SIZE,
-- vmar->zero_cluster_data,
-- (double)(100.0*vmar->zero_cluster_data)/
-- (vmar->clusters_read*VMA_CLUSTER_SIZE));
--
-- int64_t datasize = vmar->clusters_read*VMA_CLUSTER_SIZE-vmar->zero_cluster_data;
-- if (datasize) { // this does not make sense for empty files
-- printf("space reduction due to 4K zero blocks %.3g%%\n",
-- (double)(100.0*vmar->partial_zero_cluster_data) / datasize);
-+ if (vmar->clusters_read) {
-+ printf("total bytes read %zd, sparse bytes %zd (%.3g%%)\n",
-+ vmar->clusters_read*VMA_CLUSTER_SIZE,
-+ vmar->zero_cluster_data,
-+ (double)(100.0*vmar->zero_cluster_data)/
-+ (vmar->clusters_read*VMA_CLUSTER_SIZE));
-+
-+ int64_t datasize = vmar->clusters_read*VMA_CLUSTER_SIZE-vmar->zero_cluster_data;
-+ if (datasize) { // this does not make sense for empty files
-+ printf("space reduction due to 4K zero blocks %.3g%%\n",
-+ (double)(100.0*vmar->partial_zero_cluster_data) / datasize);
-+ }
-+ } else {
-+ printf("vma archive contains no image data\n");
- }
- }
- return ret;
-diff --git a/vma-writer.c b/vma-writer.c
-index 3c77e98..5cc4564 100644
---- a/vma-writer.c
-+++ b/vma-writer.c
-@@ -258,7 +258,7 @@ vma_queue_write(VmaWriter *vmaw, const void *buf, size_t bytes)
- }
-
- vmaw->co_writer = NULL;
--
-+
- return (done == bytes) ? bytes : -1;
- }
-
-@@ -382,10 +382,6 @@ static int coroutine_fn vma_write_header(VmaWriter *vmaw)
- time_t ctime = time(NULL);
- head->ctime = GUINT64_TO_BE(ctime);
-
-- if (!vmaw->stream_count) {
-- return -1;
-- }
--
- for (i = 0; i < VMA_MAX_CONFIGS; i++) {
- head->config_names[i] = GUINT32_TO_BE(vmaw->config_names[i]);
- head->config_data[i] = GUINT32_TO_BE(vmaw->config_data[i]);
-@@ -502,6 +498,23 @@ static int vma_count_open_streams(VmaWriter *vmaw)
- return open_drives;
- }
-
-+
-+/**
-+ * You need to call this if the vma archive does not contain
-+ * any data stream.
-+ */
-+int coroutine_fn
-+vma_writer_flush_output(VmaWriter *vmaw)
-+{
-+ qemu_co_mutex_lock(&vmaw->flush_lock);
-+ int ret = vma_writer_flush(vmaw);
-+ qemu_co_mutex_unlock(&vmaw->flush_lock);
-+ if (ret < 0) {
-+ vma_writer_set_error(vmaw, "vma_writer_flush_header failed");
-+ }
-+ return ret;
-+}
-+
- /**
- * all jobs should call this when there is no more data
- * Returns: number of remaining stream (0 ==> finished)
-@@ -529,12 +542,7 @@ vma_writer_close_stream(VmaWriter *vmaw, uint8_t dev_id)
-
- if (open_drives <= 0) {
- DPRINTF("vma_writer_set_status all drives completed\n");
-- qemu_co_mutex_lock(&vmaw->flush_lock);
-- int ret = vma_writer_flush(vmaw);
-- qemu_co_mutex_unlock(&vmaw->flush_lock);
-- if (ret < 0) {
-- vma_writer_set_error(vmaw, "vma_writer_close_stream: flush failed");
-- }
-+ vma_writer_flush_output(vmaw);
- }
-
- return open_drives;
-diff --git a/vma.c b/vma.c
-index 6a33352..00f20b0 100644
---- a/vma.c
-+++ b/vma.c
-@@ -33,7 +33,7 @@ static void help(void)
- "\n"
- "vma list <filename>\n"
- "vma config <filename> [-c config]\n"
-- "vma create <filename> [-c config] <archive> pathname ...\n"
-+ "vma create <filename> [-c config] pathname ...\n"
- "vma extract <filename> [-r <fifo>] <targetdir>\n"
- "vma verify <filename> [-v]\n"
- ;
-@@ -401,6 +401,18 @@ typedef struct BackupJob {
-
- #define BACKUP_SECTORS_PER_CLUSTER (VMA_CLUSTER_SIZE / BDRV_SECTOR_SIZE)
-
-+static void coroutine_fn backup_run_empty(void *opaque)
-+{
-+ VmaWriter *vmaw = (VmaWriter *)opaque;
-+
-+ vma_writer_flush_output(vmaw);
-+
-+ Error *err = NULL;
-+ if (vma_writer_close(vmaw, &err) != 0) {
-+ g_warning("vma_writer_close failed %s", error_get_pretty(err));
-+ }
-+}
-+
- static void coroutine_fn backup_run(void *opaque)
- {
- BackupJob *job = (BackupJob *)opaque;
-@@ -474,8 +486,8 @@ static int create_archive(int argc, char **argv)
- }
-
-
-- /* make sure we have archive name and at least one path */
-- if ((optind + 2) > argc) {
-+ /* make sure we an archive name */
-+ if ((optind + 1) > argc) {
- help();
- }
-
-@@ -510,11 +522,11 @@ static int create_archive(int argc, char **argv)
- l = g_list_next(l);
- }
-
-- int ind = 0;
-+ int devcount = 0;
- while (optind < argc) {
- const char *path = argv[optind++];
- char *devname = NULL;
-- path = extract_devname(path, &devname, ind++);
-+ path = extract_devname(path, &devname, devcount++);
-
- BlockDriver *drv = NULL;
- Error *errp = NULL;
-@@ -546,37 +558,49 @@ static int create_archive(int argc, char **argv)
- int percent = 0;
- int last_percent = -1;
-
-- while (1) {
-- main_loop_wait(false);
-- vma_writer_get_status(vmaw, &vmastat);
--
-- if (verbose) {
--
-- uint64_t total = 0;
-- uint64_t transferred = 0;
-- uint64_t zero_bytes = 0;
-+ if (devcount) {
-+ while (1) {
-+ main_loop_wait(false);
-+ vma_writer_get_status(vmaw, &vmastat);
-+
-+ if (verbose) {
-+
-+ uint64_t total = 0;
-+ uint64_t transferred = 0;
-+ uint64_t zero_bytes = 0;
-+
-+ int i;
-+ for (i = 0; i < 256; i++) {
-+ if (vmastat.stream_info[i].size) {
-+ total += vmastat.stream_info[i].size;
-+ transferred += vmastat.stream_info[i].transferred;
-+ zero_bytes += vmastat.stream_info[i].zero_bytes;
-+ }
-+ }
-+ percent = (transferred*100)/total;
-+ if (percent != last_percent) {
-+ fprintf(stderr, "progress %d%% %zd/%zd %zd\n", percent,
-+ transferred, total, zero_bytes);
-+ fflush(stderr);
-
-- int i;
-- for (i = 0; i < 256; i++) {
-- if (vmastat.stream_info[i].size) {
-- total += vmastat.stream_info[i].size;
-- transferred += vmastat.stream_info[i].transferred;
-- zero_bytes += vmastat.stream_info[i].zero_bytes;
-+ last_percent = percent;
- }
- }
-- percent = (transferred*100)/total;
-- if (percent != last_percent) {
-- fprintf(stderr, "progress %d%% %zd/%zd %zd\n", percent,
-- transferred, total, zero_bytes);
-- fflush(stderr);
-
-- last_percent = percent;
-+ if (vmastat.closed) {
-+ break;
- }
- }
--
-- if (vmastat.closed) {
-- break;
-- }
-+ } else {
-+ Coroutine *co = qemu_coroutine_create(backup_run_empty);
-+ qemu_coroutine_enter(co, vmaw);
-+ while (1) {
-+ main_loop_wait(false);
-+ vma_writer_get_status(vmaw, &vmastat);
-+ if (vmastat.closed) {
-+ break;
-+ }
-+ }
- }
-
- bdrv_drain_all();
-diff --git a/vma.h b/vma.h
-index 98377e4..365ceb2 100644
---- a/vma.h
-+++ b/vma.h
-@@ -128,6 +128,7 @@ int64_t coroutine_fn vma_writer_write(VmaWriter *vmaw, uint8_t dev_id,
- size_t *zero_bytes);
-
- int coroutine_fn vma_writer_close_stream(VmaWriter *vmaw, uint8_t dev_id);
-+int coroutine_fn vma_writer_flush_output(VmaWriter *vmaw);
-
- int vma_writer_get_status(VmaWriter *vmaw, VmaStatus *status);
- void vma_writer_set_error(VmaWriter *vmaw, const char *fmt, ...);
---
-2.1.4
-
--- /dev/null
+From 7e58a86c836514745590e4f4d280841c4808a022 Mon Sep 17 00:00:00 2001
+From: Wolfgang Bumiller <w.bumiller@proxmox.com>
+Date: Wed, 9 Dec 2015 16:34:41 +0100
+Subject: [PATCH 24/41] qmp: add get_link_status
+
+---
+ net/net.c | 27 +++++++++++++++++++++++++++
+ qapi-schema.json | 15 +++++++++++++++
+ qmp-commands.hx | 23 +++++++++++++++++++++++
+ scripts/qapi.py | 2 ++
+ 4 files changed, 67 insertions(+)
+
+diff --git a/net/net.c b/net/net.c
+index d51cb29..c94d93d 100644
+--- a/net/net.c
++++ b/net/net.c
+@@ -1362,6 +1362,33 @@ void hmp_info_network(Monitor *mon, const QDict *qdict)
+ }
+ }
+
++int64_t qmp_get_link_status(const char *name, Error **errp)
++{
++ NetClientState *ncs[MAX_QUEUE_NUM];
++ NetClientState *nc;
++ int queues;
++ bool ret;
++
++ queues = qemu_find_net_clients_except(name, ncs,
++ NET_CLIENT_DRIVER__MAX,
++ MAX_QUEUE_NUM);
++
++ if (queues == 0) {
++ error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
++ "Device '%s' not found", name);
++ return (int64_t) -1;
++ }
++
++ nc = ncs[0];
++ ret = ncs[0]->link_down;
++
++ if (nc->peer->info->type == NET_CLIENT_DRIVER_NIC) {
++ ret = ncs[0]->peer->link_down;
++ }
++
++ return (int64_t) ret ? 0 : 1;
++}
++
+ void qmp_set_link(const char *name, bool up, Error **errp)
+ {
+ NetClientState *ncs[MAX_QUEUE_NUM];
+diff --git a/qapi-schema.json b/qapi-schema.json
+index 0c0faf7..d75e932 100644
+--- a/qapi-schema.json
++++ b/qapi-schema.json
+@@ -1786,6 +1786,21 @@
+ { 'command': 'set_link', 'data': {'name': 'str', 'up': 'bool'} }
+
+ ##
++# @get_link_status
++#
++# Get the current link state of the nics or nic.
++#
++# @name: name of the nic you get the state of
++#
++# Return: If link is up 1
++# If link is down 0
++# If an error occure an empty string.
++#
++# Notes: this is an Proxmox VE extension and not offical part of Qemu.
++##
++{ 'command': 'get_link_status', 'data': {'name': 'str'}, 'returns': 'int'}
++
++##
+ # @balloon:
+ #
+ # Request the balloon driver to change its balloon size.
+diff --git a/qmp-commands.hx b/qmp-commands.hx
+index 6342cd2..a84932a 100644
+--- a/qmp-commands.hx
++++ b/qmp-commands.hx
+@@ -1883,6 +1883,29 @@ Example:
+ EQMP
+
+ {
++ .name = "get_link_status",
++ .args_type = "name:s",
++ .mhandler.cmd_new = qmp_marshal_get_link_status,
++ },
++
++SQMP
++get_link_status
++--------
++
++Get the link status of a network adapter.
++
++Arguments:
++
++- "name": network device name (json-string)
++
++Example:
++
++-> { "execute": "get_link_status", "arguments": { "name": "e1000.0" } }
++<- { "return": {1} }
++
++EQMP
++
++ {
+ .name = "getfd",
+ .args_type = "fdname:s",
+ .params = "getfd name",
+diff --git a/scripts/qapi.py b/scripts/qapi.py
+index 21bc32f..f900659 100644
+--- a/scripts/qapi.py
++++ b/scripts/qapi.py
+@@ -39,6 +39,8 @@ builtin_types = {
+
+ # Whitelist of commands allowed to return a non-dictionary
+ returns_whitelist = [
++ 'get_link_status',
++
+ # From QMP:
+ 'human-monitor-command',
+ 'qom-get',
+--
+2.1.4
+
+++ /dev/null
-From a88a0bf330a1ede68e867d7ae932b3ffd4d4911a Mon Sep 17 00:00:00 2001
-From: Wolfgang Bumiller <w.bumiller@proxmox.com>
-Date: Wed, 9 Dec 2015 16:32:39 +0100
-Subject: [PATCH 25/55] backup: vma: add BlockDriver to bdrv_open in
- extract_content
-
----
- vma.c | 8 +++++++-
- 1 file changed, 7 insertions(+), 1 deletion(-)
-
-diff --git a/vma.c b/vma.c
-index 00f20b0..543e7d8 100644
---- a/vma.c
-+++ b/vma.c
-@@ -299,7 +299,13 @@ static int extract_content(int argc, char **argv)
- }
-
- BlockDriverState *bs = bdrv_new();
-- if (errp || bdrv_open(&bs, devfn, NULL, NULL, flags, NULL, &errp)) {
-+
-+ const char *tmp = g_strrstr(devfn, ".");
-+ const char *format = (tmp == NULL) ? "raw" : ++tmp;
-+
-+ BlockDriver *drv = bdrv_find_format(format);
-+
-+ if (errp || bdrv_open(&bs, devfn, NULL, NULL, flags, drv, &errp)) {
- g_error("can't open file %s - %s", devfn,
- error_get_pretty(errp));
- }
---
-2.1.4
-
--- /dev/null
+From 38b2b042f9943786c9d3728d9c24cb110170df88 Mon Sep 17 00:00:00 2001
+From: Alexandre Derumier <aderumier@odiso.com>
+Date: Tue, 29 Sep 2015 15:37:44 +0200
+Subject: [PATCH 25/41] smm_available = false
+
+Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
+---
+ hw/i386/pc.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/hw/i386/pc.c b/hw/i386/pc.c
+index ba8a5a1..9c206fc 100644
+--- a/hw/i386/pc.c
++++ b/hw/i386/pc.c
+@@ -2084,7 +2084,7 @@ bool pc_machine_is_smm_enabled(PCMachineState *pcms)
+ if (tcg_enabled() || qtest_enabled()) {
+ smm_available = true;
+ } else if (kvm_enabled()) {
+- smm_available = kvm_has_smm();
++ smm_available = false;
+ }
+
+ if (smm_available) {
+--
+2.1.4
+
+++ /dev/null
-From 577243992461100d840e90b6018df958ee165a34 Mon Sep 17 00:00:00 2001
-From: Wolfgang Bumiller <w.bumiller@proxmox.com>
-Date: Wed, 9 Dec 2015 16:33:10 +0100
-Subject: [PATCH 26/55] glusterfs: daemonize
-
----
- block/gluster.c | 8 +++++---
- 1 file changed, 5 insertions(+), 3 deletions(-)
-
-diff --git a/block/gluster.c b/block/gluster.c
-index a8aaacf..9cf33e9 100644
---- a/block/gluster.c
-+++ b/block/gluster.c
-@@ -198,9 +198,11 @@ static struct glfs *qemu_gluster_init(GlusterConf *gconf, const char *filename,
- * TODO: Use GF_LOG_ERROR instead of hard code value of 4 here when
- * GlusterFS makes GF_LOG_* macros available to libgfapi users.
- */
-- ret = glfs_set_logging(glfs, "-", 4);
-- if (ret < 0) {
-- goto out;
-+ if (!is_daemonized()) {
-+ ret = glfs_set_logging(glfs, "-", 4);
-+ if (ret < 0) {
-+ goto out;
-+ }
- }
-
- ret = glfs_init(glfs);
---
-2.1.4
-
--- /dev/null
+From 6c2e8cae485cb72873084b36d4b701606cd28f3a Mon Sep 17 00:00:00 2001
+From: Wolfgang Bumiller <w.bumiller@proxmox.com>
+Date: Wed, 9 Dec 2015 16:50:05 +0100
+Subject: [PATCH 26/41] use whitespace between VERSION and PKGVERSION
+
+Our kvm version parser expects a white space or comma after
+the version string, see PVE::QemuServer::kvm_user_version()
+---
+ vl.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/vl.c b/vl.c
+index 85033e3..fefc0bd 100644
+--- a/vl.c
++++ b/vl.c
+@@ -1919,7 +1919,7 @@ static void main_loop(void)
+
+ static void version(void)
+ {
+- printf("QEMU emulator version " QEMU_VERSION QEMU_PKGVERSION ", "
++ printf("QEMU emulator version " QEMU_VERSION " " QEMU_PKGVERSION ", "
+ QEMU_COPYRIGHT "\n");
+ }
+
+--
+2.1.4
+
+++ /dev/null
-From 15c3d8e19c9d7534441226229dd509e3a61ce82a Mon Sep 17 00:00:00 2001
-From: Wolfgang Bumiller <w.bumiller@proxmox.com>
-Date: Wed, 9 Dec 2015 16:33:25 +0100
-Subject: [PATCH 27/55] gluster: possiblity to specify a secondary server
-
----
- block/gluster.c | 40 +++++++++++++++++++++++++++++++---------
- 1 file changed, 31 insertions(+), 9 deletions(-)
-
-diff --git a/block/gluster.c b/block/gluster.c
-index 9cf33e9..0377725 100644
---- a/block/gluster.c
-+++ b/block/gluster.c
-@@ -28,6 +28,7 @@ typedef struct BDRVGlusterState {
-
- typedef struct GlusterConf {
- char *server;
-+ char *backupserver;
- int port;
- char *volname;
- char *image;
-@@ -38,6 +39,7 @@ static void qemu_gluster_gconf_free(GlusterConf *gconf)
- {
- if (gconf) {
- g_free(gconf->server);
-+ g_free(gconf->backupserver);
- g_free(gconf->volname);
- g_free(gconf->image);
- g_free(gconf->transport);
-@@ -71,7 +73,7 @@ static int parse_volume_options(GlusterConf *gconf, char *path)
- }
-
- /*
-- * file=gluster[+transport]://[server[:port]]/volname/image[?socket=...]
-+ * file=gluster[+transport]://[server[:port]]/volname/image[?socket=...|?s2=...]
- *
- * 'gluster' is the protocol.
- *
-@@ -87,6 +89,8 @@ static int parse_volume_options(GlusterConf *gconf, char *path)
- * The 'socket' field needs to be populated with the path to unix domain
- * socket.
- *
-+ * 's2' can be used to specifies a second volfile server.
-+ *
- * 'port' is the port number on which glusterd is listening. This is optional
- * and if not specified, QEMU will send 0 which will make gluster to use the
- * default port. If the transport type is unix, then 'port' should not be
-@@ -99,6 +103,7 @@ static int parse_volume_options(GlusterConf *gconf, char *path)
- * Examples:
- *
- * file=gluster://1.2.3.4/testvol/a.img
-+ * file=gluster://1.2.3.4/testvol/a.img?s2=1.2.3.5
- * file=gluster+tcp://1.2.3.4/testvol/a.img
- * file=gluster+tcp://1.2.3.4:24007/testvol/dir/a.img
- * file=gluster+tcp://[1:2:3:4:5:6:7:8]/testvol/dir/a.img
-@@ -113,6 +118,8 @@ static int qemu_gluster_parseuri(GlusterConf *gconf, const char *filename)
- QueryParams *qp = NULL;
- bool is_unix = false;
- int ret = 0;
-+ int i;
-+ char *socket = NULL;
-
- uri = uri_parse(filename);
- if (!uri) {
-@@ -140,21 +147,28 @@ static int qemu_gluster_parseuri(GlusterConf *gconf, const char *filename)
- }
-
- qp = query_params_parse(uri->query);
-- if (qp->n > 1 || (is_unix && !qp->n) || (!is_unix && qp->n)) {
-+ for (i = 0; i < qp->n; i++) {
-+ if (!is_unix && strcmp(qp->p[i].name, "s2") == 0) {
-+ gconf->backupserver = g_strdup(qp->p[i].value);
-+ } else if (is_unix && strcmp(qp->p[i].name, "socket") == 0) {
-+ socket = qp->p[i].value;
-+ } else {
-+ ret = -EINVAL;
-+ goto out;
-+ }
-+ }
-+
-+ if (is_unix && !socket) {
- ret = -EINVAL;
- goto out;
- }
-
- if (is_unix) {
-- if (uri->server || uri->port) {
-+ if (!socket || uri->server || uri->port) {
- ret = -EINVAL;
- goto out;
- }
-- if (strcmp(qp->p[0].name, "socket")) {
-- ret = -EINVAL;
-- goto out;
-- }
-- gconf->server = g_strdup(qp->p[0].value);
-+ gconf->server = g_strdup(socket);
- } else {
- gconf->server = g_strdup(uri->server ? uri->server : "localhost");
- gconf->port = uri->port;
-@@ -178,7 +192,7 @@ static struct glfs *qemu_gluster_init(GlusterConf *gconf, const char *filename,
- ret = qemu_gluster_parseuri(gconf, filename);
- if (ret < 0) {
- error_setg(errp, "Usage: file=gluster[+transport]://[server[:port]]/"
-- "volname/image[?socket=...]");
-+ "volname/image[?socket=...|?s2=...]");
- errno = -ret;
- goto out;
- }
-@@ -194,6 +208,14 @@ static struct glfs *qemu_gluster_init(GlusterConf *gconf, const char *filename,
- goto out;
- }
-
-+ if (gconf->backupserver) {
-+ ret = glfs_set_volfile_server(glfs, gconf->transport, gconf->backupserver,
-+ gconf->port);
-+ if (ret < 0) {
-+ goto out;
-+ }
-+ }
-+
- /*
- * TODO: Use GF_LOG_ERROR instead of hard code value of 4 here when
- * GlusterFS makes GF_LOG_* macros available to libgfapi users.
---
-2.1.4
-
--- /dev/null
+From eeedbc0558580dcb28d4e82b567ea980b24c9cd3 Mon Sep 17 00:00:00 2001
+From: Wolfgang Bumiller <w.bumiller@proxmox.com>
+Date: Wed, 9 Dec 2015 16:51:23 +0100
+Subject: [PATCH 27/41] vma: add firewall
+
+---
+ blockdev.c | 78 ++++++++++++++++++++++++++++++++++----------------------
+ hmp.c | 2 +-
+ qapi-schema.json | 1 +
+ qmp-commands.hx | 2 +-
+ 4 files changed, 51 insertions(+), 32 deletions(-)
+
+diff --git a/blockdev.c b/blockdev.c
+index 2371cf3..bbb1502 100644
+--- a/blockdev.c
++++ b/blockdev.c
+@@ -3157,6 +3157,44 @@ void qmp_backup_cancel(Error **errp)
+ }
+ }
+
++static int config_to_vma(const char *file, BackupFormat format,
++ const char *backup_dir, VmaWriter *vmaw,
++ Error **errp)
++{
++ char *cdata = NULL;
++ gsize clen = 0;
++ GError *err = NULL;
++ if (!g_file_get_contents(file, &cdata, &clen, &err)) {
++ error_setg(errp, "unable to read file '%s'", file);
++ return 1;
++ }
++
++ char *basename = g_path_get_basename(file);
++
++ if (format == BACKUP_FORMAT_VMA) {
++ if (vma_writer_add_config(vmaw, basename, cdata, clen) != 0) {
++ error_setg(errp, "unable to add %s config data to vma archive", file);
++ g_free(cdata);
++ g_free(basename);
++ return 1;
++ }
++ } else if (format == BACKUP_FORMAT_DIR) {
++ char config_path[PATH_MAX];
++ snprintf(config_path, PATH_MAX, "%s/%s", backup_dir, basename);
++ if (!g_file_set_contents(config_path, cdata, clen, &err)) {
++ error_setg(errp, "unable to write config file '%s'", config_path);
++ g_free(cdata);
++ g_free(basename);
++ return 1;
++ }
++ }
++
++ g_free(basename);
++ g_free(cdata);
++
++ return 0;
++}
++
+ bool block_job_should_pause(BlockJob *job);
+ static void pvebackup_run_next_job(void)
+ {
+@@ -3184,6 +3222,7 @@ static void pvebackup_run_next_job(void)
+ UuidInfo *qmp_backup(const char *backup_file, bool has_format,
+ BackupFormat format,
+ bool has_config_file, const char *config_file,
++ bool has_firewall_file, const char *firewall_file,
+ bool has_devlist, const char *devlist,
+ bool has_speed, int64_t speed, Error **errp)
+ {
+@@ -3335,38 +3374,17 @@ UuidInfo *qmp_backup(const char *backup_file, bool has_format,
+
+ /* add configuration file to archive */
+ if (has_config_file) {
+- char *cdata = NULL;
+- gsize clen = 0;
+- GError *err = NULL;
+- if (!g_file_get_contents(config_file, &cdata, &clen, &err)) {
+- error_setg(errp, "unable to read file '%s'", config_file);
+- goto err;
+- }
+-
+- char *basename = g_path_get_basename(config_file);
+-
+- if (format == BACKUP_FORMAT_VMA) {
+- if (vma_writer_add_config(vmaw, basename, cdata, clen) != 0) {
+- error_setg(errp, "unable to add config data to vma archive");
+- g_free(cdata);
+- g_free(basename);
+- goto err;
+- }
+- } else if (format == BACKUP_FORMAT_DIR) {
+- char config_path[PATH_MAX];
+- snprintf(config_path, PATH_MAX, "%s/%s", backup_dir, basename);
+- if (!g_file_set_contents(config_path, cdata, clen, &err)) {
+- error_setg(errp, "unable to write config file '%s'", config_path);
+- g_free(cdata);
+- g_free(basename);
+- goto err;
+- }
+- }
+-
+- g_free(basename);
+- g_free(cdata);
++ if(config_to_vma(config_file, format, backup_dir, vmaw, errp) != 0) {
++ goto err;
++ }
+ }
+
++ /* add firewall file to archive */
++ if (has_firewall_file) {
++ if(config_to_vma(firewall_file, format, backup_dir, vmaw, errp) != 0) {
++ goto err;
++ }
++ }
+ /* initialize global backup_state now */
+
+ backup_state.cancel = false;
+diff --git a/hmp.c b/hmp.c
+index 7bd319f..6c6a0ac 100644
+--- a/hmp.c
++++ b/hmp.c
+@@ -1549,7 +1549,7 @@ void hmp_backup(Monitor *mon, const QDict *qdict)
+ int64_t speed = qdict_get_try_int(qdict, "speed", 0);
+
+ qmp_backup(backup_file, true, dir ? BACKUP_FORMAT_DIR : BACKUP_FORMAT_VMA,
+- false, NULL, !!devlist,
++ false, NULL, false, NULL, !!devlist,
+ devlist, qdict_haskey(qdict, "speed"), speed, &error);
+
+ hmp_handle_error(mon, &error);
+diff --git a/qapi-schema.json b/qapi-schema.json
+index d75e932..7bb0ee0 100644
+--- a/qapi-schema.json
++++ b/qapi-schema.json
+@@ -420,6 +420,7 @@
+ { 'command': 'backup', 'data': { 'backup-file': 'str',
+ '*format': 'BackupFormat',
+ '*config-file': 'str',
++ '*firewall-file': 'str',
+ '*devlist': 'str', '*speed': 'int' },
+ 'returns': 'UuidInfo' }
+
+diff --git a/qmp-commands.hx b/qmp-commands.hx
+index a84932a..94cfac2 100644
+--- a/qmp-commands.hx
++++ b/qmp-commands.hx
+@@ -1315,7 +1315,7 @@ EQMP
+
+ {
+ .name = "backup",
+- .args_type = "backup-file:s,format:s?,config-file:F?,speed:o?,devlist:s?",
++ .args_type = "backup-file:s,format:s?,config-file:F?,firewall-file:F?,speed:o?,devlist:s?",
+ .mhandler.cmd_new = qmp_marshal_backup,
+ },
+
+--
+2.1.4
+
+++ /dev/null
-From 9170833ae6e593326b1a37892282d4177ab7414e Mon Sep 17 00:00:00 2001
-From: Wolfgang Bumiller <w.bumiller@proxmox.com>
-Date: Wed, 9 Dec 2015 16:34:41 +0100
-Subject: [PATCH 28/55] qmp: add get_link_status
-
----
- net/net.c | 27 +++++++++++++++++++++++++++
- qapi-schema.json | 15 +++++++++++++++
- qmp-commands.hx | 23 +++++++++++++++++++++++
- scripts/qapi.py | 2 ++
- 4 files changed, 67 insertions(+)
-
-diff --git a/net/net.c b/net/net.c
-index 6b0b375..0e3f231 100644
---- a/net/net.c
-+++ b/net/net.c
-@@ -1325,6 +1325,33 @@ void hmp_info_network(Monitor *mon, const QDict *qdict)
- }
- }
-
-+int64_t qmp_get_link_status(const char *name, Error **errp)
-+{
-+ NetClientState *ncs[MAX_QUEUE_NUM];
-+ NetClientState *nc;
-+ int queues;
-+ bool ret;
-+
-+ queues = qemu_find_net_clients_except(name, ncs,
-+ NET_CLIENT_OPTIONS_KIND_MAX,
-+ MAX_QUEUE_NUM);
-+
-+ if (queues == 0) {
-+ error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
-+ "Device '%s' not found", name);
-+ return (int64_t) -1;
-+ }
-+
-+ nc = ncs[0];
-+ ret = ncs[0]->link_down;
-+
-+ if (nc->peer->info->type == NET_CLIENT_OPTIONS_KIND_NIC) {
-+ ret = ncs[0]->peer->link_down;
-+ }
-+
-+ return (int64_t) ret ? 0 : 1;
-+}
-+
- void qmp_set_link(const char *name, bool up, Error **errp)
- {
- NetClientState *ncs[MAX_QUEUE_NUM];
-diff --git a/qapi-schema.json b/qapi-schema.json
-index 2392139..d1985d2 100644
---- a/qapi-schema.json
-+++ b/qapi-schema.json
-@@ -1725,6 +1725,21 @@
- { 'command': 'set_link', 'data': {'name': 'str', 'up': 'bool'} }
-
- ##
-+# @get_link_status
-+#
-+# Get the current link state of the nics or nic.
-+#
-+# @name: name of the nic you get the state of
-+#
-+# Return: If link is up 1
-+# If link is down 0
-+# If an error occure an empty string.
-+#
-+# Notes: this is an Proxmox VE extension and not offical part of Qemu.
-+##
-+{ 'command': 'get_link_status', 'data': {'name': 'str'}, 'returns': 'int'}
-+
-+##
- # @balloon:
- #
- # Request the balloon driver to change its balloon size.
-diff --git a/qmp-commands.hx b/qmp-commands.hx
-index 437ddd6..71c5d53 100644
---- a/qmp-commands.hx
-+++ b/qmp-commands.hx
-@@ -1844,6 +1844,29 @@ Example:
- EQMP
-
- {
-+ .name = "get_link_status",
-+ .args_type = "name:s",
-+ .mhandler.cmd_new = qmp_marshal_input_get_link_status,
-+ },
-+
-+SQMP
-+get_link_status
-+--------
-+
-+Get the link status of a network adapter.
-+
-+Arguments:
-+
-+- "name": network device name (json-string)
-+
-+Example:
-+
-+-> { "execute": "get_link_status", "arguments": { "name": "e1000.0" } }
-+<- { "return": {1} }
-+
-+EQMP
-+
-+ {
- .name = "getfd",
- .args_type = "fdname:s",
- .params = "getfd name",
-diff --git a/scripts/qapi.py b/scripts/qapi.py
-index b13ae47..088478a 100644
---- a/scripts/qapi.py
-+++ b/scripts/qapi.py
-@@ -39,6 +39,8 @@ builtin_types = {
-
- # Whitelist of commands allowed to return a non-dictionary
- returns_whitelist = [
-+ 'get_link_status',
-+
- # From QMP:
- 'human-monitor-command',
- 'qom-get',
---
-2.1.4
-
--- /dev/null
+From f4494a796f451cbb9bb680798b73f62cac943e54 Mon Sep 17 00:00:00 2001
+From: Wolfgang Bumiller <w.bumiller@proxmox.com>
+Date: Thu, 10 Dec 2015 15:14:00 +0100
+Subject: [PATCH 28/41] savevm-async: migration and bdrv_open update
+
+---
+ savevm-async.c | 25 ++++++++++++-------------
+ 1 file changed, 12 insertions(+), 13 deletions(-)
+
+diff --git a/savevm-async.c b/savevm-async.c
+index ae7ea84..7979435 100644
+--- a/savevm-async.c
++++ b/savevm-async.c
+@@ -154,10 +154,10 @@ static int block_state_close(void *opaque)
+ return bdrv_flush(snap_state.bs);
+ }
+
+-static int block_state_put_buffer(void *opaque, const uint8_t *buf,
+- int64_t pos, int size)
++static ssize_t block_state_put_buffer(void *opaque, const uint8_t *buf,
++ int64_t pos, size_t size)
+ {
+- int ret;
++ ssize_t ret;
+
+ assert(pos == snap_state.bs_pos);
+
+@@ -201,12 +201,13 @@ static void process_savevm_co(void *opaque)
+ }
+
+ while (snap_state.state == SAVE_STATE_ACTIVE) {
+- uint64_t pending_size;
++ uint64_t pending_size, pend_post, pend_nonpost;
+
+- pending_size = qemu_savevm_state_pending(snap_state.file, 0);
++ qemu_savevm_state_pending(snap_state.file, 0, &pend_nonpost, &pend_post);
++ pending_size = pend_post + pend_nonpost;
+
+ if (pending_size) {
+- ret = qemu_savevm_state_iterate(snap_state.file);
++ ret = qemu_savevm_state_iterate(snap_state.file, false);
+ if (ret < 0) {
+ save_snapshot_error("qemu_savevm_state_iterate error %d", ret);
+ break;
+@@ -217,7 +218,7 @@ static void process_savevm_co(void *opaque)
+ if (store_and_stop())
+ break;
+ DPRINTF("savevm inerate finished\n");
+- qemu_savevm_state_complete_precopy(snap_state.file);
++ qemu_savevm_state_complete_precopy(snap_state.file, false);
+ DPRINTF("save complete\n");
+ save_snapshot_completed();
+ break;
+@@ -250,7 +251,6 @@ static const QEMUFileOps block_file_ops = {
+
+ void qmp_savevm_start(bool has_statefile, const char *statefile, Error **errp)
+ {
+- BlockDriver *drv = NULL;
+ Error *local_err = NULL;
+
+ int bdrv_oflags = BDRV_O_RDWR;
+@@ -289,7 +289,7 @@ void qmp_savevm_start(bool has_statefile, const char *statefile, Error **errp)
+ QDict *options = NULL;
+ options = qdict_new();
+ qdict_put(options, "driver", qstring_from_str("raw"));
+- ret = bdrv_open(&snap_state.bs, statefile, NULL, options, bdrv_oflags, drv, &local_err);
++ ret = bdrv_open(&snap_state.bs, statefile, NULL, options, bdrv_oflags, &local_err);
+ if (ret < 0) {
+ error_set(errp, ERROR_CLASS_GENERIC_ERROR, "failed to open '%s'", statefile);
+ goto restart;
+@@ -454,8 +454,8 @@ void qmp_delete_drive_snapshot(const char *device, const char *name,
+ }
+ }
+
+-static int loadstate_get_buffer(void *opaque, uint8_t *buf, int64_t pos,
+- int size)
++static ssize_t loadstate_get_buffer(void *opaque, uint8_t *buf, int64_t pos,
++ size_t size)
+ {
+ BlockDriverState *bs = (BlockDriverState *)opaque;
+ int64_t maxlen = bdrv_getlength(bs);
+@@ -478,7 +478,6 @@ static const QEMUFileOps loadstate_file_ops = {
+ int load_state_from_blockdev(const char *filename)
+ {
+ BlockDriverState *bs = NULL;
+- BlockDriver *drv = NULL;
+ Error *local_err = NULL;
+ Error *blocker = NULL;
+
+@@ -486,7 +485,7 @@ int load_state_from_blockdev(const char *filename)
+ int ret;
+
+ bs = bdrv_new();
+- ret = bdrv_open(&bs, filename, NULL, NULL, 0, drv, &local_err);
++ ret = bdrv_open(&bs, filename, NULL, NULL, 0, &local_err);
+ error_setg(&blocker, "block device is in use by load state");
+ bdrv_op_block_all(bs, blocker);
+
+--
+2.1.4
+
+++ /dev/null
-From 3aebb3903e61b8ceb7f0d302f8dfddfe7195c10e Mon Sep 17 00:00:00 2001
-From: Alexandre Derumier <aderumier@odiso.com>
-Date: Tue, 29 Sep 2015 15:37:44 +0200
-Subject: [PATCH 29/55] smm_available = false
-
-Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
----
- hw/i386/pc.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/hw/i386/pc.c b/hw/i386/pc.c
-index 1225a99..ceae96c 100644
---- a/hw/i386/pc.c
-+++ b/hw/i386/pc.c
-@@ -1839,7 +1839,7 @@ bool pc_machine_is_smm_enabled(PCMachineState *pcms)
- if (tcg_enabled() || qtest_enabled()) {
- smm_available = true;
- } else if (kvm_enabled()) {
-- smm_available = kvm_has_smm();
-+ smm_available = false;
- }
-
- if (smm_available) {
---
-2.1.4
-
--- /dev/null
+From a826a6ead084ea0d86680c6e9901f4f51ee5beb0 Mon Sep 17 00:00:00 2001
+From: Wolfgang Bumiller <w.bumiller@proxmox.com>
+Date: Tue, 12 Jan 2016 09:09:49 +0100
+Subject: [PATCH 29/41] vnc: make x509 imply tls again
+
+---
+ ui/vnc.c | 5 ++---
+ 1 file changed, 2 insertions(+), 3 deletions(-)
+
+diff --git a/ui/vnc.c b/ui/vnc.c
+index 8d7d435..a7f5f1e 100644
+--- a/ui/vnc.c
++++ b/ui/vnc.c
+@@ -3725,9 +3725,8 @@ void vnc_display_open(const char *id, Error **errp)
+ const char *path;
+ bool tls = false, x509 = false, x509verify = false;
+ tls = qemu_opt_get_bool(opts, "tls", false);
+- if (tls) {
+- path = qemu_opt_get(opts, "x509");
+-
++ path = qemu_opt_get(opts, "x509");
++ if (tls || path) {
+ if (path) {
+ x509 = true;
+ } else {
+--
+2.1.4
+
--- /dev/null
+From 09e462b87b17f37fa98a2745dcf2d14c981b7e95 Mon Sep 17 00:00:00 2001
+From: Wolfgang Bumiller <w.bumiller@proxmox.com>
+Date: Mon, 11 Jan 2016 10:40:31 +0100
+Subject: [PATCH 30/41] PVE VNC authentication
+
+---
+ crypto/tlscreds.c | 47 +++++++++++
+ crypto/tlscredspriv.h | 2 +
+ crypto/tlscredsx509.c | 13 ++--
+ crypto/tlssession.c | 1 +
+ include/crypto/tlscreds.h | 1 +
+ include/ui/console.h | 1 +
+ qemu-options.hx | 3 +
+ ui/vnc-auth-vencrypt.c | 194 ++++++++++++++++++++++++++++++++++++++--------
+ ui/vnc.c | 140 ++++++++++++++++++++++++++++++++-
+ ui/vnc.h | 4 +
+ vl.c | 9 +++
+ 11 files changed, 375 insertions(+), 40 deletions(-)
+
+diff --git a/crypto/tlscreds.c b/crypto/tlscreds.c
+index a896553..e9ae13c 100644
+--- a/crypto/tlscreds.c
++++ b/crypto/tlscreds.c
+@@ -158,6 +158,33 @@ qcrypto_tls_creds_prop_get_verify(Object *obj,
+
+
+ static void
++qcrypto_tls_creds_prop_set_pve(Object *obj,
++ bool value,
++ Error **errp G_GNUC_UNUSED)
++{
++ QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj);
++
++ creds->pve = value;
++}
++
++
++static bool
++qcrypto_tls_creds_prop_get_pve(Object *obj,
++ Error **errp G_GNUC_UNUSED)
++{
++ QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj);
++
++ return creds->pve;
++}
++
++bool qcrypto_tls_creds_is_pve(QCryptoTLSCreds *creds)
++{
++ Error *errp = NULL;
++ return qcrypto_tls_creds_prop_get_pve((Object*)creds, &errp);
++}
++
++
++static void
+ qcrypto_tls_creds_prop_set_dir(Object *obj,
+ const char *value,
+ Error **errp G_GNUC_UNUSED)
+@@ -250,6 +277,26 @@ qcrypto_tls_creds_init(Object *obj)
+ QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj);
+
+ creds->verifyPeer = true;
++ creds->pve = false;
++
++ object_property_add_bool(obj, "verify-peer",
++ qcrypto_tls_creds_prop_get_verify,
++ qcrypto_tls_creds_prop_set_verify,
++ NULL);
++ object_property_add_bool(obj, "pve",
++ qcrypto_tls_creds_prop_get_pve,
++ qcrypto_tls_creds_prop_set_pve,
++ NULL);
++ object_property_add_str(obj, "dir",
++ qcrypto_tls_creds_prop_get_dir,
++ qcrypto_tls_creds_prop_set_dir,
++ NULL);
++ object_property_add_enum(obj, "endpoint",
++ "QCryptoTLSCredsEndpoint",
++ QCryptoTLSCredsEndpoint_lookup,
++ qcrypto_tls_creds_prop_get_endpoint,
++ qcrypto_tls_creds_prop_set_endpoint,
++ NULL);
+ }
+
+
+diff --git a/crypto/tlscredspriv.h b/crypto/tlscredspriv.h
+index 13e9b6c..0356acc 100644
+--- a/crypto/tlscredspriv.h
++++ b/crypto/tlscredspriv.h
+@@ -36,6 +36,8 @@ int qcrypto_tls_creds_get_dh_params_file(QCryptoTLSCreds *creds,
+ gnutls_dh_params_t *dh_params,
+ Error **errp);
+
++bool qcrypto_tls_creds_is_pve(QCryptoTLSCreds *creds);
++
+ #endif
+
+ #endif /* QCRYPTO_TLSCREDSPRIV_H */
+diff --git a/crypto/tlscredsx509.c b/crypto/tlscredsx509.c
+index 520d34d..1ba971c 100644
+--- a/crypto/tlscredsx509.c
++++ b/crypto/tlscredsx509.c
+@@ -555,22 +555,23 @@ qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds,
+ *key = NULL, *dhparams = NULL;
+ int ret;
+ int rv = -1;
++ bool pve = qcrypto_tls_creds_is_pve(&creds->parent_obj);
+
+ trace_qcrypto_tls_creds_x509_load(creds,
+ creds->parent_obj.dir ? creds->parent_obj.dir : "<nodir>");
+
+ if (creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
+ if (qcrypto_tls_creds_get_path(&creds->parent_obj,
+- QCRYPTO_TLS_CREDS_X509_CA_CERT,
++ pve ? "pve-root-ca.pem" : QCRYPTO_TLS_CREDS_X509_CA_CERT,
+ true, &cacert, errp) < 0 ||
+ qcrypto_tls_creds_get_path(&creds->parent_obj,
+ QCRYPTO_TLS_CREDS_X509_CA_CRL,
+ false, &cacrl, errp) < 0 ||
+ qcrypto_tls_creds_get_path(&creds->parent_obj,
+- QCRYPTO_TLS_CREDS_X509_SERVER_CERT,
++ pve ? "local/pve-ssl.pem" : QCRYPTO_TLS_CREDS_X509_SERVER_CERT,
+ true, &cert, errp) < 0 ||
+ qcrypto_tls_creds_get_path(&creds->parent_obj,
+- QCRYPTO_TLS_CREDS_X509_SERVER_KEY,
++ pve ? "local/pve-ssl.key" : QCRYPTO_TLS_CREDS_X509_SERVER_KEY,
+ true, &key, errp) < 0 ||
+ qcrypto_tls_creds_get_path(&creds->parent_obj,
+ QCRYPTO_TLS_CREDS_DH_PARAMS,
+@@ -579,13 +580,13 @@ qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds,
+ }
+ } else {
+ if (qcrypto_tls_creds_get_path(&creds->parent_obj,
+- QCRYPTO_TLS_CREDS_X509_CA_CERT,
++ pve ? "pve-root-ca.pem" : QCRYPTO_TLS_CREDS_X509_CA_CERT,
+ true, &cacert, errp) < 0 ||
+ qcrypto_tls_creds_get_path(&creds->parent_obj,
+- QCRYPTO_TLS_CREDS_X509_CLIENT_CERT,
++ pve ? "local/pve-ssl.pem" : QCRYPTO_TLS_CREDS_X509_CLIENT_CERT,
+ false, &cert, errp) < 0 ||
+ qcrypto_tls_creds_get_path(&creds->parent_obj,
+- QCRYPTO_TLS_CREDS_X509_CLIENT_KEY,
++ pve ? "local/pve-ssl.key" : QCRYPTO_TLS_CREDS_X509_CLIENT_KEY,
+ false, &key, errp) < 0) {
+ goto cleanup;
+ }
+diff --git a/crypto/tlssession.c b/crypto/tlssession.c
+index 2de42c6..768466a 100644
+--- a/crypto/tlssession.c
++++ b/crypto/tlssession.c
+@@ -23,6 +23,7 @@
+ #include "crypto/tlscredsanon.h"
+ #include "crypto/tlscredsx509.h"
+ #include "qapi/error.h"
++#include "crypto/tlscredspriv.h"
+ #include "qemu/acl.h"
+ #include "trace.h"
+
+diff --git a/include/crypto/tlscreds.h b/include/crypto/tlscreds.h
+index ad47d88..f86d379 100644
+--- a/include/crypto/tlscreds.h
++++ b/include/crypto/tlscreds.h
+@@ -55,6 +55,7 @@ struct QCryptoTLSCreds {
+ #endif
+ bool verifyPeer;
+ char *priority;
++ bool pve;
+ };
+
+
+diff --git a/include/ui/console.h b/include/ui/console.h
+index 2703a3a..db6dd22 100644
+--- a/include/ui/console.h
++++ b/include/ui/console.h
+@@ -456,6 +456,7 @@ static inline void cocoa_display_init(DisplayState *ds, int full_screen)
+ #endif
+
+ /* vnc.c */
++void pve_auth_setup(int vmid);
+ void vnc_display_init(const char *id);
+ void vnc_display_open(const char *id, Error **errp);
+ void vnc_display_add_client(const char *id, int csock, bool skipauth);
+diff --git a/qemu-options.hx b/qemu-options.hx
+index 37fad3b..f943ae6 100644
+--- a/qemu-options.hx
++++ b/qemu-options.hx
+@@ -473,6 +473,9 @@ STEXI
+ @table @option
+ ETEXI
+
++DEF("id", HAS_ARG, QEMU_OPTION_id,
++ "-id n set the VMID\n", QEMU_ARCH_ALL)
++
+ DEF("fda", HAS_ARG, QEMU_OPTION_fda,
+ "-fda/-fdb file use 'file' as floppy disk 0/1 image\n", QEMU_ARCH_ALL)
+ DEF("fdb", HAS_ARG, QEMU_OPTION_fdb, "", QEMU_ARCH_ALL)
+diff --git a/ui/vnc-auth-vencrypt.c b/ui/vnc-auth-vencrypt.c
+index 11c8c9a..d11f1df 100644
+--- a/ui/vnc-auth-vencrypt.c
++++ b/ui/vnc-auth-vencrypt.c
+@@ -28,6 +28,107 @@
+ #include "vnc.h"
+ #include "qapi/error.h"
+ #include "qemu/main-loop.h"
++#include "qemu/sockets.h"
++
++static int protocol_client_auth_plain(VncState *vs, uint8_t *data, size_t len)
++{
++ const char *err = NULL;
++ char username[256];
++ char passwd[512];
++
++ char clientip[256];
++ clientip[0] = 0;
++ struct sockaddr_in client;
++ socklen_t addrlen = sizeof(client);
++ if (getpeername(vs->csock, &client, &addrlen) == 0) {
++ inet_ntop(client.sin_family, &client.sin_addr,
++ clientip, sizeof(clientip));
++ }
++
++ if ((len != (vs->username_len + vs->password_len)) ||
++ (vs->username_len >= (sizeof(username)-1)) ||
++ (vs->password_len >= (sizeof(passwd)-1)) ) {
++ err = "Got unexpected data length";
++ goto err;
++ }
++
++ strncpy(username, (char *)data, vs->username_len);
++ username[vs->username_len] = 0;
++ strncpy(passwd, (char *)data + vs->username_len, vs->password_len);
++ passwd[vs->password_len] = 0;
++
++ VNC_DEBUG("AUTH PLAIN username: %s pw: %s\n", username, passwd);
++
++ if (pve_auth_verify(clientip, username, passwd) == 0) {
++ vnc_write_u32(vs, 0); /* Accept auth completion */
++ start_client_init(vs);
++ return 0;
++ }
++
++ err = "Authentication failed";
++err:
++ if (err) {
++ VNC_DEBUG("AUTH PLAIN ERROR: %s\n", err);
++ vnc_write_u32(vs, 1); /* Reject auth */
++ if (vs->minor >= 8) {
++ int elen = strlen(err);
++ vnc_write_u32(vs, elen);
++ vnc_write(vs, err, elen);
++ }
++ }
++ vnc_flush(vs);
++ vnc_client_error(vs);
++
++ return 0;
++
++}
++
++static int protocol_client_auth_plain_start(VncState *vs, uint8_t *data, size_t len)
++{
++ uint32_t ulen = read_u32(data, 0);
++ uint32_t pwlen = read_u32(data, 4);
++ const char *err = NULL;
++
++ VNC_DEBUG("AUTH PLAIN START %u %u\n", ulen, pwlen);
++
++ if (!ulen) {
++ err = "No User name.";
++ goto err;
++ }
++ if (ulen >= 255) {
++ err = "User name too long.";
++ goto err;
++ }
++ if (!pwlen) {
++ err = "Password too short";
++ goto err;
++ }
++ if (pwlen >= 511) {
++ err = "Password too long.";
++ goto err;
++ }
++
++ vs->username_len = ulen;
++ vs->password_len = pwlen;
++
++ vnc_read_when(vs, protocol_client_auth_plain, ulen + pwlen);
++
++ return 0;
++err:
++ if (err) {
++ VNC_DEBUG("AUTH PLAIN ERROR: %s\n", err);
++ vnc_write_u32(vs, 1); /* Reject auth */
++ if (vs->minor >= 8) {
++ int elen = strlen(err);
++ vnc_write_u32(vs, elen);
++ vnc_write(vs, err, elen);
++ }
++ }
++ vnc_flush(vs);
++ vnc_client_error(vs);
++
++ return 0;
++}
+
+ static void start_auth_vencrypt_subauth(VncState *vs)
+ {
+@@ -39,6 +140,17 @@ static void start_auth_vencrypt_subauth(VncState *vs)
+ start_client_init(vs);
+ break;
+
++ case VNC_AUTH_VENCRYPT_TLSPLAIN:
++ case VNC_AUTH_VENCRYPT_X509PLAIN:
++ VNC_DEBUG("Start TLS auth PLAIN\n");
++ vnc_read_when(vs, protocol_client_auth_plain_start, 8);
++ break;
++
++ case VNC_AUTH_VENCRYPT_PLAIN:
++ VNC_DEBUG("Start auth PLAIN\n");
++ vnc_read_when(vs, protocol_client_auth_plain_start, 8);
++ break;
++
+ case VNC_AUTH_VENCRYPT_TLSVNC:
+ case VNC_AUTH_VENCRYPT_X509VNC:
+ VNC_DEBUG("Start TLS auth VNC\n");
+@@ -87,44 +199,63 @@ static int protocol_client_vencrypt_auth(VncState *vs, uint8_t *data, size_t len
+ {
+ int auth = read_u32(data, 0);
+
+- if (auth != vs->subauth) {
++ if (auth != vs->subauth && auth != VNC_AUTH_VENCRYPT_PLAIN) {
+ VNC_DEBUG("Rejecting auth %d\n", auth);
+ vnc_write_u8(vs, 0); /* Reject auth */
+ vnc_flush(vs);
+ vnc_client_error(vs);
+ } else {
+- Error *err = NULL;
+- QIOChannelTLS *tls;
+- VNC_DEBUG("Accepting auth %d, setting up TLS for handshake\n", auth);
+- vnc_write_u8(vs, 1); /* Accept auth */
+- vnc_flush(vs);
+-
+- if (vs->ioc_tag) {
+- g_source_remove(vs->ioc_tag);
+- vs->ioc_tag = 0;
++ if (auth == VNC_AUTH_VENCRYPT_PLAIN) {
++ vs->subauth = auth;
++ start_auth_vencrypt_subauth(vs);
+ }
++ else
++ {
++ Error *err = NULL;
++ QIOChannelTLS *tls;
++ VNC_DEBUG("Accepting auth %d, setting up TLS for handshake\n", auth);
++ vnc_write_u8(vs, 1); /* Accept auth */
++ vnc_flush(vs);
+
+- tls = qio_channel_tls_new_server(
+- vs->ioc,
+- vs->vd->tlscreds,
+- vs->vd->tlsaclname,
+- &err);
+- if (!tls) {
+- VNC_DEBUG("Failed to setup TLS %s\n", error_get_pretty(err));
+- error_free(err);
+- vnc_client_error(vs);
+- return 0;
+- }
++ if (vs->ioc_tag) {
++ g_source_remove(vs->ioc_tag);
++ vs->ioc_tag = 0;
++ }
+
+- VNC_DEBUG("Start TLS VeNCrypt handshake process\n");
+- object_unref(OBJECT(vs->ioc));
+- vs->ioc = QIO_CHANNEL(tls);
+- vs->tls = qio_channel_tls_get_session(tls);
++ tls = qio_channel_tls_new_server(
++ vs->ioc,
++ vs->vd->tlscreds,
++ vs->vd->tlsaclname,
++ &err);
++ if (!tls) {
++ VNC_DEBUG("Failed to setup TLS %s\n", error_get_pretty(err));
++ error_free(err);
++ vnc_client_error(vs);
++ return 0;
++ vs->tls = qcrypto_tls_session_new(vs->vd->tlscreds,
++ NULL,
++ vs->vd->tlsaclname,
++ QCRYPTO_TLS_CREDS_ENDPOINT_SERVER,
++ &err);
++ if (!vs->tls) {
++ VNC_DEBUG("Failed to setup TLS %s\n",
++ error_get_pretty(err));
++ error_free(err);
++ vnc_client_error(vs);
++ return 0;
++ }
++ }
+
+- qio_channel_tls_handshake(tls,
+- vnc_tls_handshake_done,
+- vs,
+- NULL);
++ VNC_DEBUG("Start TLS VeNCrypt handshake process\n");
++ object_unref(OBJECT(vs->ioc));
++ vs->ioc = QIO_CHANNEL(tls);
++ vs->tls = qio_channel_tls_get_session(tls);
++
++ qio_channel_tls_handshake(tls,
++ vnc_tls_handshake_done,
++ vs,
++ NULL);
++ }
+ }
+ return 0;
+ }
+@@ -138,10 +269,11 @@ static int protocol_client_vencrypt_init(VncState *vs, uint8_t *data, size_t len
+ vnc_flush(vs);
+ vnc_client_error(vs);
+ } else {
+- VNC_DEBUG("Sending allowed auth %d\n", vs->subauth);
++ VNC_DEBUG("Sending allowed auths %d %d\n", vs->subauth, VNC_AUTH_VENCRYPT_PLAIN);
+ vnc_write_u8(vs, 0); /* Accept version */
+- vnc_write_u8(vs, 1); /* Number of sub-auths */
++ vnc_write_u8(vs, 2); /* Number of sub-auths */
+ vnc_write_u32(vs, vs->subauth); /* The supported auth */
++ vnc_write_u32(vs, VNC_AUTH_VENCRYPT_PLAIN); /* Alternative supported auth */
+ vnc_flush(vs);
+ vnc_read_when(vs, protocol_client_vencrypt_auth, 4);
+ }
+diff --git a/ui/vnc.c b/ui/vnc.c
+index a7f5f1e..6b73667 100644
+--- a/ui/vnc.c
++++ b/ui/vnc.c
+@@ -55,6 +55,125 @@ static const struct timeval VNC_REFRESH_LOSSY = { 2, 0 };
+ #include "vnc_keysym.h"
+ #include "crypto/cipher.h"
+
++static int pve_vmid = 0;
++
++void pve_auth_setup(int vmid) {
++ pve_vmid = vmid;
++}
++
++static char *
++urlencode(char *buf, const char *value)
++{
++ static const char *hexchar = "0123456789abcdef";
++ char *p = buf;
++ int i;
++ int l = strlen(value);
++ for (i = 0; i < l; i++) {
++ char c = value[i];
++ if (('a' <= c && c <= 'z') ||
++ ('A' <= c && c <= 'Z') ||
++ ('0' <= c && c <= '9')) {
++ *p++ = c;
++ } else if (c == 32) {
++ *p++ = '+';
++ } else {
++ *p++ = '%';
++ *p++ = hexchar[c >> 4];
++ *p++ = hexchar[c & 15];
++ }
++ }
++ *p = 0;
++
++ return p;
++}
++
++int
++pve_auth_verify(const char *clientip, const char *username, const char *passwd)
++{
++ struct sockaddr_in server;
++
++ int sfd = socket(AF_INET, SOCK_STREAM, 0);
++ if (sfd == -1) {
++ perror("pve_auth_verify: socket failed");
++ return -1;
++ }
++
++ struct hostent *he;
++ if ((he = gethostbyname("localhost")) == NULL) {
++ fprintf(stderr, "pve_auth_verify: error resolving hostname\n");
++ goto err;
++ }
++
++ memcpy(&server.sin_addr, he->h_addr_list[0], he->h_length);
++ server.sin_family = AF_INET;
++ server.sin_port = htons(85);
++
++ if (connect(sfd, (struct sockaddr *)&server, sizeof(server))) {
++ perror("pve_auth_verify: error connecting to server");
++ goto err;
++ }
++
++ char buf[8192];
++ char form[8192];
++
++ char *p = form;
++ p = urlencode(p, "username");
++ *p++ = '=';
++ p = urlencode(p, username);
++
++ *p++ = '&';
++ p = urlencode(p, "password");
++ *p++ = '=';
++ p = urlencode(p, passwd);
++
++ *p++ = '&';
++ p = urlencode(p, "path");
++ *p++ = '=';
++ char authpath[256];
++ sprintf(authpath, "/vms/%d", pve_vmid);
++ p = urlencode(p, authpath);
++
++ *p++ = '&';
++ p = urlencode(p, "privs");
++ *p++ = '=';
++ p = urlencode(p, "VM.Console");
++
++ sprintf(buf, "POST /api2/json/access/ticket HTTP/1.1\n"
++ "Host: localhost:85\n"
++ "Connection: close\n"
++ "PVEClientIP: %s\n"
++ "Content-Type: application/x-www-form-urlencoded\n"
++ "Content-Length: %zd\n\n%s\n", clientip, strlen(form), form);
++ ssize_t len = strlen(buf);
++ ssize_t sb = send(sfd, buf, len, 0);
++ if (sb < 0) {
++ perror("pve_auth_verify: send failed");
++ goto err;
++ }
++ if (sb != len) {
++ fprintf(stderr, "pve_auth_verify: partial send error\n");
++ goto err;
++ }
++
++ len = recv(sfd, buf, sizeof(buf) - 1, 0);
++ if (len < 0) {
++ perror("pve_auth_verify: recv failed");
++ goto err;
++ }
++
++ buf[len] = 0;
++
++ //printf("DATA:%s\n", buf);
++
++ shutdown(sfd, SHUT_RDWR);
++
++ return strncmp(buf, "HTTP/1.1 200 OK", 15);
++
++err:
++ shutdown(sfd, SHUT_RDWR);
++ return -1;
++}
++
+ static QTAILQ_HEAD(, VncDisplay) vnc_displays =
+ QTAILQ_HEAD_INITIALIZER(vnc_displays);
+
+@@ -3409,11 +3528,17 @@ vnc_display_setup_auth(VncDisplay *vs,
+ if (object_dynamic_cast(OBJECT(vs->tlscreds),
+ TYPE_QCRYPTO_TLS_CREDS_X509)) {
+ VNC_DEBUG("Initializing VNC server with x509 password auth\n");
+- vs->subauth = VNC_AUTH_VENCRYPT_X509VNC;
++ if (vs->tlscreds->pve)
++ vs->subauth = VNC_AUTH_VENCRYPT_X509PLAIN;
++ else
++ vs->subauth = VNC_AUTH_VENCRYPT_X509VNC;
+ } else if (object_dynamic_cast(OBJECT(vs->tlscreds),
+ TYPE_QCRYPTO_TLS_CREDS_ANON)) {
+ VNC_DEBUG("Initializing VNC server with TLS password auth\n");
+- vs->subauth = VNC_AUTH_VENCRYPT_TLSVNC;
++ if (vs->tlscreds->pve)
++ vs->subauth = VNC_AUTH_VENCRYPT_TLSPLAIN;
++ else
++ vs->subauth = VNC_AUTH_VENCRYPT_TLSVNC;
+ } else {
+ error_setg(errp,
+ "Unsupported TLS cred type %s",
+@@ -3504,6 +3629,7 @@ vnc_display_create_creds(bool x509,
+ bool x509verify,
+ const char *dir,
+ const char *id,
++ bool pve,
+ Error **errp)
+ {
+ gchar *credsid = g_strdup_printf("tlsvnc%s", id);
+@@ -3519,6 +3645,7 @@ vnc_display_create_creds(bool x509,
+ "endpoint", "server",
+ "dir", dir,
+ "verify-peer", x509verify ? "yes" : "no",
++ "pve", pve ? "yes" : "no",
+ NULL);
+ } else {
+ creds = object_new_with_props(TYPE_QCRYPTO_TLS_CREDS_ANON,
+@@ -3526,6 +3653,7 @@ vnc_display_create_creds(bool x509,
+ credsid,
+ &err,
+ "endpoint", "server",
++ "pve", pve ? "yes" : "no",
+ NULL);
+ }
+
+@@ -3723,12 +3851,17 @@ void vnc_display_open(const char *id, Error **errp)
+ }
+ } else {
+ const char *path;
+- bool tls = false, x509 = false, x509verify = false;
++ bool tls = false, x509 = false, x509verify = false, pve = false;
+ tls = qemu_opt_get_bool(opts, "tls", false);
+ path = qemu_opt_get(opts, "x509");
+ if (tls || path) {
+ if (path) {
+ x509 = true;
++ if (!strcmp(path, "on")) {
++ /* magic to default to /etc/pve */
++ path = "/etc/pve";
++ pve = true;
++ }
+ } else {
+ path = qemu_opt_get(opts, "x509verify");
+ if (path) {
+@@ -3740,6 +3873,7 @@ void vnc_display_open(const char *id, Error **errp)
+ x509verify,
+ path,
+ vs->id,
++ pve,
+ errp);
+ if (!vs->tlscreds) {
+ goto fail;
+diff --git a/ui/vnc.h b/ui/vnc.h
+index ab5f244..2fde9d3 100644
+--- a/ui/vnc.h
++++ b/ui/vnc.h
+@@ -282,6 +282,8 @@ struct VncState
+ int auth;
+ int subauth; /* Used by VeNCrypt */
+ char challenge[VNC_AUTH_CHALLENGE_SIZE];
++ int username_len;
++ int password_len;
+ QCryptoTLSSession *tls; /* Borrowed pointer from channel, don't free */
+ #ifdef CONFIG_VNC_SASL
+ VncStateSASL sasl;
+@@ -577,4 +579,6 @@ int vnc_zrle_send_framebuffer_update(VncState *vs, int x, int y, int w, int h);
+ int vnc_zywrle_send_framebuffer_update(VncState *vs, int x, int y, int w, int h);
+ void vnc_zrle_clear(VncState *vs);
+
++int pve_auth_verify(const char *clientip, const char *username, const char *passwd);
++
+ #endif /* QEMU_VNC_H */
+diff --git a/vl.c b/vl.c
+index fefc0bd..7813b78 100644
+--- a/vl.c
++++ b/vl.c
+@@ -2936,6 +2936,7 @@ static int global_init_func(void *opaque, QemuOpts *opts, Error **errp)
+ int main(int argc, char **argv, char **envp)
+ {
+ int i;
++ long int vm_id_long = 0;
+ int snapshot, linux_boot;
+ const char *initrd_filename;
+ const char *kernel_filename, *kernel_cmdline;
+@@ -3708,6 +3709,14 @@ int main(int argc, char **argv, char **envp)
+ exit(1);
+ }
+ break;
++ case QEMU_OPTION_id:
++ vm_id_long = strtol(optarg, (char **) &optarg, 10);
++ if (*optarg != 0 || vm_id_long < 100 || vm_id_long > INT_MAX) {
++ fprintf(stderr, "Invalid ID\n");
++ exit(1);
++ }
++ pve_auth_setup(vm_id_long);
++ break;
+ case QEMU_OPTION_vnc:
+ vnc_parse(optarg, &error_fatal);
+ break;
+--
+2.1.4
+
+++ /dev/null
-From 011e629353ed4aeac6eeb7f1c9021b4906ae2946 Mon Sep 17 00:00:00 2001
-From: Wolfgang Bumiller <w.bumiller@proxmox.com>
-Date: Wed, 9 Dec 2015 16:50:05 +0100
-Subject: [PATCH 30/55] use whitespace between VERSION and PKGVERSION
-
-Our kvm version parser expects a white space or comma after
-the version string, see PVE::QemuServer::kvm_user_version()
----
- vl.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/vl.c b/vl.c
-index ba29593..f393767 100644
---- a/vl.c
-+++ b/vl.c
-@@ -1949,7 +1949,7 @@ static void main_loop(void)
-
- static void version(void)
- {
-- printf("QEMU emulator version " QEMU_VERSION QEMU_PKGVERSION ", Copyright (c) 2003-2008 Fabrice Bellard\n");
-+ printf("QEMU emulator version " QEMU_VERSION " " QEMU_PKGVERSION ", Copyright (c) 2003-2008 Fabrice Bellard\n");
- }
-
- static void help(int exitcode)
---
-2.1.4
-
+++ /dev/null
-From a89fc66658089da88eae095b3faa377c25db0198 Mon Sep 17 00:00:00 2001
-From: Wolfgang Bumiller <w.bumiller@proxmox.com>
-Date: Wed, 9 Dec 2015 16:51:23 +0100
-Subject: [PATCH 31/55] vma: add firewall
-
----
- blockdev.c | 78 ++++++++++++++++++++++++++++++++++----------------------
- hmp.c | 2 +-
- qapi-schema.json | 1 +
- qmp-commands.hx | 2 +-
- 4 files changed, 51 insertions(+), 32 deletions(-)
-
-diff --git a/blockdev.c b/blockdev.c
-index 0131e92..dd4e4bd 100644
---- a/blockdev.c
-+++ b/blockdev.c
-@@ -3170,6 +3170,44 @@ void qmp_backup_cancel(Error **errp)
- }
- }
-
-+static int config_to_vma(const char *file, BackupFormat format,
-+ const char *backup_dir, VmaWriter *vmaw,
-+ Error **errp)
-+{
-+ char *cdata = NULL;
-+ gsize clen = 0;
-+ GError *err = NULL;
-+ if (!g_file_get_contents(file, &cdata, &clen, &err)) {
-+ error_setg(errp, "unable to read file '%s'", file);
-+ return 1;
-+ }
-+
-+ char *basename = g_path_get_basename(file);
-+
-+ if (format == BACKUP_FORMAT_VMA) {
-+ if (vma_writer_add_config(vmaw, basename, cdata, clen) != 0) {
-+ error_setg(errp, "unable to add %s config data to vma archive", file);
-+ g_free(cdata);
-+ g_free(basename);
-+ return 1;
-+ }
-+ } else if (format == BACKUP_FORMAT_DIR) {
-+ char config_path[PATH_MAX];
-+ snprintf(config_path, PATH_MAX, "%s/%s", backup_dir, basename);
-+ if (!g_file_set_contents(config_path, cdata, clen, &err)) {
-+ error_setg(errp, "unable to write config file '%s'", config_path);
-+ g_free(cdata);
-+ g_free(basename);
-+ return 1;
-+ }
-+ }
-+
-+ g_free(basename);
-+ g_free(cdata);
-+
-+ return 0;
-+}
-+
- static void pvebackup_run_next_job(void)
- {
- GList *l = backup_state.di_list;
-@@ -3196,6 +3234,7 @@ static void pvebackup_run_next_job(void)
- UuidInfo *qmp_backup(const char *backup_file, bool has_format,
- BackupFormat format,
- bool has_config_file, const char *config_file,
-+ bool has_firewall_file, const char *firewall_file,
- bool has_devlist, const char *devlist,
- bool has_speed, int64_t speed, Error **errp)
- {
-@@ -3348,38 +3387,17 @@ UuidInfo *qmp_backup(const char *backup_file, bool has_format,
-
- /* add configuration file to archive */
- if (has_config_file) {
-- char *cdata = NULL;
-- gsize clen = 0;
-- GError *err = NULL;
-- if (!g_file_get_contents(config_file, &cdata, &clen, &err)) {
-- error_setg(errp, "unable to read file '%s'", config_file);
-- goto err;
-- }
--
-- char *basename = g_path_get_basename(config_file);
--
-- if (format == BACKUP_FORMAT_VMA) {
-- if (vma_writer_add_config(vmaw, basename, cdata, clen) != 0) {
-- error_setg(errp, "unable to add config data to vma archive");
-- g_free(cdata);
-- g_free(basename);
-- goto err;
-- }
-- } else if (format == BACKUP_FORMAT_DIR) {
-- char config_path[PATH_MAX];
-- snprintf(config_path, PATH_MAX, "%s/%s", backup_dir, basename);
-- if (!g_file_set_contents(config_path, cdata, clen, &err)) {
-- error_setg(errp, "unable to write config file '%s'", config_path);
-- g_free(cdata);
-- g_free(basename);
-- goto err;
-- }
-- }
--
-- g_free(basename);
-- g_free(cdata);
-+ if(config_to_vma(config_file, format, backup_dir, vmaw, errp) != 0) {
-+ goto err;
-+ }
- }
-
-+ /* add firewall file to archive */
-+ if (has_firewall_file) {
-+ if(config_to_vma(firewall_file, format, backup_dir, vmaw, errp) != 0) {
-+ goto err;
-+ }
-+ }
- /* initialize global backup_state now */
-
- backup_state.cancel = false;
-diff --git a/hmp.c b/hmp.c
-index 78644a7..f989f01 100644
---- a/hmp.c
-+++ b/hmp.c
-@@ -1533,7 +1533,7 @@ void hmp_backup(Monitor *mon, const QDict *qdict)
- int64_t speed = qdict_get_try_int(qdict, "speed", 0);
-
- qmp_backup(backup_file, true, dir ? BACKUP_FORMAT_DIR : BACKUP_FORMAT_VMA,
-- false, NULL, !!devlist,
-+ false, NULL, false, NULL, !!devlist,
- devlist, qdict_haskey(qdict, "speed"), speed, &error);
-
- hmp_handle_error(mon, &error);
-diff --git a/qapi-schema.json b/qapi-schema.json
-index d1985d2..2c31eb1 100644
---- a/qapi-schema.json
-+++ b/qapi-schema.json
-@@ -420,6 +420,7 @@
- { 'command': 'backup', 'data': { 'backup-file': 'str',
- '*format': 'BackupFormat',
- '*config-file': 'str',
-+ '*firewall-file': 'str',
- '*devlist': 'str', '*speed': 'int' },
- 'returns': 'UuidInfo' }
-
-diff --git a/qmp-commands.hx b/qmp-commands.hx
-index 71c5d53..964563e 100644
---- a/qmp-commands.hx
-+++ b/qmp-commands.hx
-@@ -1280,7 +1280,7 @@ EQMP
-
- {
- .name = "backup",
-- .args_type = "backup-file:s,format:s?,config-file:F?,speed:o?,devlist:s?",
-+ .args_type = "backup-file:s,format:s?,config-file:F?,firewall-file:F?,speed:o?,devlist:s?",
- .mhandler.cmd_new = qmp_marshal_input_backup,
- },
-
---
-2.1.4
-
--- /dev/null
+From 9625add5f8199243a7b3a4fa045adf25f99ed52d Mon Sep 17 00:00:00 2001
+From: Wolfgang Bumiller <w.bumiller@proxmox.com>
+Date: Mon, 8 Feb 2016 08:23:34 +0100
+Subject: [PATCH 31/41] vma-writer: don't bail out on zero-length files
+
+---
+ vma-writer.c | 1 -
+ 1 file changed, 1 deletion(-)
+
+diff --git a/vma-writer.c b/vma-writer.c
+index 0d26fc6..a378762 100644
+--- a/vma-writer.c
++++ b/vma-writer.c
+@@ -130,7 +130,6 @@ int vma_writer_add_config(VmaWriter *vmaw, const char *name, gpointer data,
+ assert(vmaw->config_count < VMA_MAX_CONFIGS);
+ assert(name);
+ assert(data);
+- assert(len);
+
+ gchar *basename = g_path_get_basename(name);
+ uint32_t name_ptr = allocate_header_string(vmaw, basename);
+--
+2.1.4
+
--- /dev/null
+From 88f2fbcb13778b146482f034d88968ed3ea57a6b Mon Sep 17 00:00:00 2001
+From: Wolfgang Bumiller <w.bumiller@proxmox.com>
+Date: Tue, 23 Feb 2016 15:48:41 +0100
+Subject: [PATCH 32/41] vma: better driver guessing for bdrv_open
+
+Only use 'raw' when the file actually ends with .raw and
+no protocol has been specified. With protocol pass the
+BDRV_O_PROTOCOL flag to tell bdrv_fill_options() to take it
+into account.
+---
+ vma.c | 15 ++++++++++++++-
+ 1 file changed, 14 insertions(+), 1 deletion(-)
+
+diff --git a/vma.c b/vma.c
+index 08e4725..8a27704 100644
+--- a/vma.c
++++ b/vma.c
+@@ -293,7 +293,20 @@ static int extract_content(int argc, char **argv)
+ }
+
+ BlockDriverState *bs = bdrv_new();
+- if (errp || bdrv_open(&bs, devfn, NULL, NULL, flags, &errp)) {
++
++ size_t devlen = strlen(devfn);
++ bool protocol = path_has_protocol(devfn);
++ QDict *options = NULL;
++ if (devlen > 4 && strcmp(devfn+devlen-4, ".raw") == 0 && !protocol) {
++ /* explicit raw format */
++ options = qdict_new();
++ qdict_put(options, "driver", qstring_from_str("raw"));
++ } else if (protocol) {
++ /* tell bdrv_open to honor the protocol */
++ flags |= BDRV_O_PROTOCOL;
++ }
++
++ if (errp || bdrv_open(&bs, devfn, NULL, options, flags, &errp)) {
+ g_error("can't open file %s - %s", devfn,
+ error_get_pretty(errp));
+ }
+--
+2.1.4
+
+++ /dev/null
-From 2d152da7999e912f6fc104f81a1616c3c6d96a89 Mon Sep 17 00:00:00 2001
-From: Wolfgang Bumiller <w.bumiller@proxmox.com>
-Date: Thu, 10 Dec 2015 14:25:01 +0100
-Subject: [PATCH 32/55] vma-writer: aio_set_fd_handler update
-
-passing 'false' as new is_external parameter to
-aio_set_fd_handler as per commit
-dca21e23ba: aio: Add "is_external" flag for event handlers
-
-For-patch: introduce new vma archive format
----
- vma-writer.c | 4 ++--
- 1 file changed, 2 insertions(+), 2 deletions(-)
-
-diff --git a/vma-writer.c b/vma-writer.c
-index 5cc4564..f5a7abd 100644
---- a/vma-writer.c
-+++ b/vma-writer.c
-@@ -231,9 +231,9 @@ vma_queue_write(VmaWriter *vmaw, const void *buf, size_t bytes)
- vmaw->co_writer = qemu_coroutine_self();
-
- while (done < bytes) {
-- aio_set_fd_handler(qemu_get_aio_context(), vmaw->fd, NULL, vma_co_continue_write, vmaw);
-+ aio_set_fd_handler(qemu_get_aio_context(), vmaw->fd, false, NULL, vma_co_continue_write, vmaw);
- qemu_coroutine_yield();
-- aio_set_fd_handler(qemu_get_aio_context(), vmaw->fd, NULL, NULL, NULL);
-+ aio_set_fd_handler(qemu_get_aio_context(), vmaw->fd, false, NULL, NULL, NULL);
- if (vmaw->status < 0) {
- DPRINTF("vma_queue_write detected canceled backup\n");
- done = -1;
---
-2.1.4
-
--- /dev/null
+From 01bb198e6aba040636af95939fcf933383e19835 Mon Sep 17 00:00:00 2001
+From: Wolfgang Bumiller <w.bumiller@proxmox.com>
+Date: Thu, 17 Mar 2016 11:33:37 +0100
+Subject: [PATCH 33/41] block: add the zeroinit block driver filter
+
+---
+ block/Makefile.objs | 1 +
+ block/zeroinit.c | 220 ++++++++++++++++++++++++++++++++++++++++++++++++++++
+ 2 files changed, 221 insertions(+)
+ create mode 100644 block/zeroinit.c
+
+diff --git a/block/Makefile.objs b/block/Makefile.objs
+index 2593a2f..930ca33 100644
+--- a/block/Makefile.objs
++++ b/block/Makefile.objs
+@@ -4,6 +4,7 @@ block-obj-y += qed.o qed-gencb.o qed-l2-cache.o qed-table.o qed-cluster.o
+ block-obj-y += qed-check.o
+ block-obj-$(CONFIG_VHDX) += vhdx.o vhdx-endian.o vhdx-log.o
+ block-obj-y += quorum.o
++block-obj-y += zeroinit.o
+ block-obj-y += parallels.o blkdebug.o blkverify.o blkreplay.o
+ block-obj-y += block-backend.o snapshot.o qapi.o
+ block-obj-$(CONFIG_WIN32) += raw-win32.o win32-aio.o
+diff --git a/block/zeroinit.c b/block/zeroinit.c
+new file mode 100644
+index 0000000..c56a446
+--- /dev/null
++++ b/block/zeroinit.c
+@@ -0,0 +1,220 @@
++/*
++ * Filter to fake a zero-initialized block device.
++ *
++ * Copyright (c) 2016 Wolfgang Bumiller <w.bumiller@proxmox.com>
++ * Copyright (c) 2016 Proxmox Server Solutions GmbH
++ *
++ * This work is licensed under the terms of the GNU GPL, version 2 or later.
++ * See the COPYING file in the top-level directory.
++ */
++
++#include "qemu/osdep.h"
++#include "qapi/error.h"
++#include "block/block_int.h"
++#include "qapi/qmp/qdict.h"
++#include "qapi/qmp/qstring.h"
++#include "qemu/cutils.h"
++
++typedef struct {
++ bool has_zero_init;
++ int64_t extents;
++} BDRVZeroinitState;
++
++/* Valid blkverify filenames look like blkverify:path/to/raw_image:path/to/image */
++static void zeroinit_parse_filename(const char *filename, QDict *options,
++ Error **errp)
++{
++ QString *raw_path;
++
++ /* Parse the blkverify: prefix */
++ if (!strstart(filename, "zeroinit:", &filename)) {
++ /* There was no prefix; therefore, all options have to be already
++ present in the QDict (except for the filename) */
++ return;
++ }
++
++ raw_path = qstring_from_str(filename);
++ qdict_put(options, "x-next", raw_path);
++}
++
++static QemuOptsList runtime_opts = {
++ .name = "zeroinit",
++ .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
++ .desc = {
++ {
++ .name = "x-next",
++ .type = QEMU_OPT_STRING,
++ .help = "[internal use only, will be removed]",
++ },
++ {
++ .name = "x-zeroinit",
++ .type = QEMU_OPT_BOOL,
++ .help = "set has_initialized_zero flag",
++ },
++ { /* end of list */ }
++ },
++};
++
++static int zeroinit_open(BlockDriverState *bs, QDict *options, int flags,
++ Error **errp)
++{
++ BDRVZeroinitState *s = bs->opaque;
++ QemuOpts *opts;
++ Error *local_err = NULL;
++ int ret;
++
++ s->extents = 0;
++
++ opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
++ qemu_opts_absorb_qdict(opts, options, &local_err);
++ if (local_err) {
++ error_propagate(errp, local_err);
++ ret = -EINVAL;
++ goto fail;
++ }
++
++ /* Open the raw file */
++ bs->file = bdrv_open_child(qemu_opt_get(opts, "x-next"), options, "next",
++ bs, &child_file, false, &local_err);
++ if (local_err) {
++ ret = -EINVAL;
++ error_propagate(errp, local_err);
++ goto fail;
++ }
++
++ /* set the options */
++ s->has_zero_init = qemu_opt_get_bool(opts, "x-zeroinit", true);
++
++ ret = 0;
++fail:
++ if (ret < 0) {
++ bdrv_unref_child(bs, bs->file);
++ }
++ qemu_opts_del(opts);
++ return ret;
++}
++
++static void zeroinit_close(BlockDriverState *bs)
++{
++ BDRVZeroinitState *s = bs->opaque;
++ (void)s;
++}
++
++static int64_t zeroinit_getlength(BlockDriverState *bs)
++{
++ return bdrv_getlength(bs->file->bs);
++}
++
++static BlockAIOCB *zeroinit_aio_readv(BlockDriverState *bs,
++ int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
++ BlockCompletionFunc *cb, void *opaque)
++{
++ return bdrv_aio_readv(bs->file, sector_num, qiov, nb_sectors,
++ cb, opaque);
++}
++
++static int coroutine_fn zeroinit_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset,
++ int count, BdrvRequestFlags flags)
++{
++ BDRVZeroinitState *s = bs->opaque;
++ if (offset >= s->extents)
++ return 0;
++ return bdrv_pwrite_zeroes(bs->file, offset, count, flags);
++}
++
++static BlockAIOCB *zeroinit_aio_writev(BlockDriverState *bs,
++ int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
++ BlockCompletionFunc *cb, void *opaque)
++{
++ BDRVZeroinitState *s = bs->opaque;
++ int64_t extents = (sector_num << BDRV_SECTOR_BITS) + ((nb_sectors + 1) << BDRV_SECTOR_BITS);
++ if (extents > s->extents)
++ s->extents = extents;
++ return bdrv_aio_writev(bs->file, sector_num, qiov, nb_sectors,
++ cb, opaque);
++}
++
++static BlockAIOCB *zeroinit_aio_flush(BlockDriverState *bs,
++ BlockCompletionFunc *cb,
++ void *opaque)
++{
++ return bdrv_aio_flush(bs->file->bs, cb, opaque);
++}
++
++static bool zeroinit_recurse_is_first_non_filter(BlockDriverState *bs,
++ BlockDriverState *candidate)
++{
++ return bdrv_recurse_is_first_non_filter(bs->file->bs, candidate);
++}
++
++static coroutine_fn int zeroinit_co_flush(BlockDriverState *bs)
++{
++ return bdrv_co_flush(bs->file->bs);
++}
++
++static int zeroinit_has_zero_init(BlockDriverState *bs)
++{
++ BDRVZeroinitState *s = bs->opaque;
++ return s->has_zero_init;
++}
++
++static int64_t coroutine_fn zeroinit_co_get_block_status(BlockDriverState *bs,
++ int64_t sector_num,
++ int nb_sectors, int *pnum,
++ BlockDriverState **file)
++{
++ return bdrv_get_block_status(bs->file->bs, sector_num, nb_sectors, pnum, file);
++}
++
++static coroutine_fn BlockAIOCB *zeroinit_aio_pdiscard(BlockDriverState *bs,
++ int64_t offset, int count,
++ BlockCompletionFunc *cb, void *opaque)
++{
++ return bdrv_aio_pdiscard(bs->file->bs, offset, count, cb, opaque);
++}
++
++static int zeroinit_truncate(BlockDriverState *bs, int64_t offset)
++{
++ return bdrv_truncate(bs->file->bs, offset);
++}
++
++static int zeroinit_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
++{
++ return bdrv_get_info(bs->file->bs, bdi);
++}
++
++static BlockDriver bdrv_zeroinit = {
++ .format_name = "zeroinit",
++ .protocol_name = "zeroinit",
++ .instance_size = sizeof(BDRVZeroinitState),
++
++ .bdrv_parse_filename = zeroinit_parse_filename,
++ .bdrv_file_open = zeroinit_open,
++ .bdrv_close = zeroinit_close,
++ .bdrv_getlength = zeroinit_getlength,
++ .bdrv_co_flush_to_disk = zeroinit_co_flush,
++
++ .bdrv_co_pwrite_zeroes = zeroinit_co_pwrite_zeroes,
++ .bdrv_aio_writev = zeroinit_aio_writev,
++ .bdrv_aio_readv = zeroinit_aio_readv,
++ .bdrv_aio_flush = zeroinit_aio_flush,
++
++ .is_filter = true,
++ .bdrv_recurse_is_first_non_filter = zeroinit_recurse_is_first_non_filter,
++
++ .bdrv_has_zero_init = zeroinit_has_zero_init,
++
++ .bdrv_co_get_block_status = zeroinit_co_get_block_status,
++
++ .bdrv_aio_pdiscard = zeroinit_aio_pdiscard,
++
++ .bdrv_truncate = zeroinit_truncate,
++ .bdrv_get_info = zeroinit_get_info,
++};
++
++static void bdrv_zeroinit_init(void)
++{
++ bdrv_register(&bdrv_zeroinit);
++}
++
++block_init(bdrv_zeroinit_init);
+--
+2.1.4
+
+++ /dev/null
-From c0ca2a0437b4d648df1b54513c9f22a7519a328a Mon Sep 17 00:00:00 2001
-From: Wolfgang Bumiller <w.bumiller@proxmox.com>
-Date: Thu, 10 Dec 2015 14:43:07 +0100
-Subject: [PATCH 33/55] vma: bdrv_open dropped the drv parameter
-
-we now pass the 'drive' option via a qdict.
-As per commit:
-6ebf9aa2ef: block: Drop drv parameter from bdrv_open()
-
-For-patch: introduce new vma archive format
----
- vma.c | 10 +++++-----
- 1 file changed, 5 insertions(+), 5 deletions(-)
-
-diff --git a/vma.c b/vma.c
-index 543e7d8..cc48013 100644
---- a/vma.c
-+++ b/vma.c
-@@ -25,6 +25,7 @@
- #include "qemu-common.h"
- #include "qemu/error-report.h"
- #include "qemu/main-loop.h"
-+#include "sysemu/char.h" /* qstring_from_str */
-
- static void help(void)
- {
-@@ -302,10 +303,10 @@ static int extract_content(int argc, char **argv)
-
- const char *tmp = g_strrstr(devfn, ".");
- const char *format = (tmp == NULL) ? "raw" : ++tmp;
-+ QDict *options = qdict_new();
-+ qdict_put(options, "driver", qstring_from_str(format));
-
-- BlockDriver *drv = bdrv_find_format(format);
--
-- if (errp || bdrv_open(&bs, devfn, NULL, NULL, flags, drv, &errp)) {
-+ if (errp || bdrv_open(&bs, devfn, NULL, options, flags, &errp)) {
- g_error("can't open file %s - %s", devfn,
- error_get_pretty(errp));
- }
-@@ -534,11 +535,10 @@ static int create_archive(int argc, char **argv)
- char *devname = NULL;
- path = extract_devname(path, &devname, devcount++);
-
-- BlockDriver *drv = NULL;
- Error *errp = NULL;
- BlockDriverState *bs = bdrv_new();
-
-- res = bdrv_open(&bs, path, NULL, NULL, BDRV_O_CACHE_WB , drv, &errp);
-+ res = bdrv_open(&bs, path, NULL, NULL, BDRV_O_CACHE_WB, &errp);
- if (res < 0) {
- unlink(archivename);
- g_error("bdrv_open '%s' failed - %s", path, error_get_pretty(errp));
---
-2.1.4
-
+++ /dev/null
-From 548b3b099d230b1221edf8062341900679d83f3e Mon Sep 17 00:00:00 2001
-From: Wolfgang Bumiller <w.bumiller@proxmox.com>
-Date: Thu, 10 Dec 2015 14:46:59 +0100
-Subject: [PATCH 34/55] blockdev: bdrv_open dropped the drv parameter
-
-As per commit:
-6ebf9aa2ef: block: Drop drv parameter from bdrv_open()
-
-For-patch: backup: vma: add dir format
----
- blockdev.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/blockdev.c b/blockdev.c
-index dd4e4bd..bf61a74 100644
---- a/blockdev.c
-+++ b/blockdev.c
-@@ -3374,7 +3374,7 @@ UuidInfo *qmp_backup(const char *backup_file, bool has_format,
- }
-
- di->target = bdrv_new();
-- if (bdrv_open(&di->target, di->targetfile, NULL, NULL, flags, NULL, &local_err) < 0) {
-+ if (bdrv_open(&di->target, di->targetfile, NULL, NULL, flags, &local_err) < 0) {
- bdrv_unref(di->target);
- error_propagate(errp, local_err);
- goto err;
---
-2.1.4
-
--- /dev/null
+From 20cd4944b9581a7197d5544077117e5d704d9f78 Mon Sep 17 00:00:00 2001
+From: Wolfgang Bumiller <w.bumiller@proxmox.com>
+Date: Tue, 12 Apr 2016 13:49:44 +0200
+Subject: [PATCH 34/41] vma: add format option to device mapping
+
+The BDRV_O_PROTOCOL option breaks non-raw protocol devices,
+so we instead now allow the format to be explicitly
+specified from the outside.
+
+In other words we now too deprecate the automatic guessing
+of raw formats, just like qemu already does, and have to
+silence the warnings by passing the drive mapping.
+---
+ vma.c | 34 +++++++++++++++++++++++++++-------
+ 1 file changed, 27 insertions(+), 7 deletions(-)
+
+diff --git a/vma.c b/vma.c
+index 8a27704..c8ad6c0 100644
+--- a/vma.c
++++ b/vma.c
+@@ -130,6 +130,7 @@ static int list_content(int argc, char **argv)
+ typedef struct RestoreMap {
+ char *devname;
+ char *path;
++ char *format;
+ bool write_zero;
+ } RestoreMap;
+
+@@ -217,13 +218,24 @@ static int extract_content(int argc, char **argv)
+ }
+ }
+
++ char *format = NULL;
++ if (strncmp(line, "format=", sizeof("format=")-1) == 0) {
++ format = line + sizeof("format=")-1;
++ char *colon = strchr(format, ':');
++ if (!colon) {
++ g_error("read map failed - found only a format ('%s')", inbuf);
++ }
++ format = g_strndup(format, colon - format);
++ line = colon+1;
++ }
++
+ const char *path;
+ bool write_zero;
+ if (line[0] == '0' && line[1] == ':') {
+- path = inbuf + 2;
++ path = line + 2;
+ write_zero = false;
+ } else if (line[0] == '1' && line[1] == ':') {
+- path = inbuf + 2;
++ path = line + 2;
+ write_zero = true;
+ } else {
+ g_error("read map failed - parse error ('%s')", inbuf);
+@@ -239,6 +251,7 @@ static int extract_content(int argc, char **argv)
+ RestoreMap *map = g_new0(RestoreMap, 1);
+ map->devname = g_strdup(devname);
+ map->path = g_strdup(path);
++ map->format = format;
+ map->write_zero = write_zero;
+
+ g_hash_table_insert(devmap, map->devname, map);
+@@ -263,6 +276,7 @@ static int extract_content(int argc, char **argv)
+ g_free(statefn);
+ } else if (di) {
+ char *devfn = NULL;
++ const char *format = NULL;
+ int flags = BDRV_O_RDWR;
+ bool write_zero = true;
+
+@@ -273,6 +287,7 @@ static int extract_content(int argc, char **argv)
+ g_error("no device name mapping for %s", di->devname);
+ }
+ devfn = map->path;
++ format = map->format;
+ write_zero = map->write_zero;
+ } else {
+ devfn = g_strdup_printf("%s/tmp-disk-%s.raw",
+@@ -295,15 +310,20 @@ static int extract_content(int argc, char **argv)
+ BlockDriverState *bs = bdrv_new();
+
+ size_t devlen = strlen(devfn);
+- bool protocol = path_has_protocol(devfn);
+ QDict *options = NULL;
+- if (devlen > 4 && strcmp(devfn+devlen-4, ".raw") == 0 && !protocol) {
++ if (format) {
++ /* explicit format from commandline */
++ options = qdict_new();
++ qdict_put(options, "driver", qstring_from_str(format));
++ } else if ((devlen > 4 && strcmp(devfn+devlen-4, ".raw") == 0) ||
++ strncmp(devfn, "/dev/", 5) == 0)
++ {
++ /* This part is now deprecated for PVE as well (just as qemu
++ * deprecated not specifying an explicit raw format, too.
++ */
+ /* explicit raw format */
+ options = qdict_new();
+ qdict_put(options, "driver", qstring_from_str("raw"));
+- } else if (protocol) {
+- /* tell bdrv_open to honor the protocol */
+- flags |= BDRV_O_PROTOCOL;
+ }
+
+ if (errp || bdrv_open(&bs, devfn, NULL, options, flags, &errp)) {
+--
+2.1.4
+
+++ /dev/null
-From eb1226235b886a2e472ed78d5df38608eb537618 Mon Sep 17 00:00:00 2001
-From: Wolfgang Bumiller <w.bumiller@proxmox.com>
-Date: Thu, 10 Dec 2015 14:50:05 +0100
-Subject: [PATCH 35/55] blockdev: backup_start now takes a BlockJobTxn
-
-As per commit 78f51fde88d1:
-block: Add BlockJobTxn support to backup_run
-
-For-patch: backup: add pve monitor commands
----
- blockdev.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/blockdev.c b/blockdev.c
-index bf61a74..96fc30b 100644
---- a/blockdev.c
-+++ b/blockdev.c
-@@ -3437,7 +3437,7 @@ UuidInfo *qmp_backup(const char *backup_file, bool has_format,
- backup_start(di->bs, di->target, speed, MIRROR_SYNC_MODE_FULL, NULL,
- BLOCKDEV_ON_ERROR_REPORT, BLOCKDEV_ON_ERROR_REPORT,
- pvebackup_dump_cb, pvebackup_complete_cb, di,
-- 1, &local_err);
-+ 1, NULL, &local_err);
-
- if (local_err != NULL) {
- error_setg(&backup_state.error, "backup_job_create failed");
---
-2.1.4
-
--- /dev/null
+From bac1501cc8927f21ba79f986cefe5d741825f33d Mon Sep 17 00:00:00 2001
+From: Thomas Lamprecht <t.lamprecht@proxmox.com>
+Date: Wed, 6 Apr 2016 16:45:15 +0200
+Subject: [PATCH 35/41] fix possible unitialised return value
+
+---
+ migration/savevm.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/migration/savevm.c b/migration/savevm.c
+index b1bdfb6..cebba77 100644
+--- a/migration/savevm.c
++++ b/migration/savevm.c
+@@ -1020,7 +1020,7 @@ int qemu_savevm_state_complete_precopy(QEMUFile *f, bool iterable_only)
+ QJSON *vmdesc;
+ int vmdesc_len;
+ SaveStateEntry *se;
+- int ret;
++ int ret = -1;
+ bool in_postcopy = migration_in_postcopy(migrate_get_current());
+
+ trace_savevm_state_complete_precopy();
+--
+2.1.4
+
+++ /dev/null
-From 6517453d33b72863940749b1b4ee1b36b7acf24c Mon Sep 17 00:00:00 2001
-From: Wolfgang Bumiller <w.bumiller@proxmox.com>
-Date: Thu, 10 Dec 2015 15:14:00 +0100
-Subject: [PATCH 36/55] savevm-async: migration and bdrv_open update
-
----
- savevm-async.c | 25 ++++++++++++-------------
- 1 file changed, 12 insertions(+), 13 deletions(-)
-
-diff --git a/savevm-async.c b/savevm-async.c
-index 8117443..bb4372c 100644
---- a/savevm-async.c
-+++ b/savevm-async.c
-@@ -150,10 +150,10 @@ static int block_state_close(void *opaque)
- return bdrv_flush(snap_state.bs);
- }
-
--static int block_state_put_buffer(void *opaque, const uint8_t *buf,
-- int64_t pos, int size)
-+static ssize_t block_state_put_buffer(void *opaque, const uint8_t *buf,
-+ int64_t pos, size_t size)
- {
-- int ret;
-+ ssize_t ret;
-
- assert(pos == snap_state.bs_pos);
-
-@@ -197,12 +197,13 @@ static void process_savevm_co(void *opaque)
- }
-
- while (snap_state.state == SAVE_STATE_ACTIVE) {
-- uint64_t pending_size;
-+ uint64_t pending_size, pend_post, pend_nonpost;
-
-- pending_size = qemu_savevm_state_pending(snap_state.file, 0);
-+ qemu_savevm_state_pending(snap_state.file, 0, &pend_nonpost, &pend_post);
-+ pending_size = pend_post + pend_nonpost;
-
- if (pending_size) {
-- ret = qemu_savevm_state_iterate(snap_state.file);
-+ ret = qemu_savevm_state_iterate(snap_state.file, false);
- if (ret < 0) {
- save_snapshot_error("qemu_savevm_state_iterate error %d", ret);
- break;
-@@ -213,7 +214,7 @@ static void process_savevm_co(void *opaque)
- if (store_and_stop())
- break;
- DPRINTF("savevm inerate finished\n");
-- qemu_savevm_state_complete_precopy(snap_state.file);
-+ qemu_savevm_state_complete_precopy(snap_state.file, false);
- DPRINTF("save complete\n");
- save_snapshot_completed();
- break;
-@@ -246,7 +247,6 @@ static const QEMUFileOps block_file_ops = {
-
- void qmp_savevm_start(bool has_statefile, const char *statefile, Error **errp)
- {
-- BlockDriver *drv = NULL;
- Error *local_err = NULL;
-
- int bdrv_oflags = BDRV_O_CACHE_WB | BDRV_O_RDWR;
-@@ -285,7 +285,7 @@ void qmp_savevm_start(bool has_statefile, const char *statefile, Error **errp)
- QDict *options = NULL;
- options = qdict_new();
- qdict_put(options, "driver", qstring_from_str("raw"));
-- ret = bdrv_open(&snap_state.bs, statefile, NULL, options, bdrv_oflags, drv, &local_err);
-+ ret = bdrv_open(&snap_state.bs, statefile, NULL, options, bdrv_oflags, &local_err);
- if (ret < 0) {
- error_set(errp, ERROR_CLASS_GENERIC_ERROR, "failed to open '%s'", statefile);
- goto restart;
-@@ -450,8 +450,8 @@ void qmp_delete_drive_snapshot(const char *device, const char *name,
- }
- }
-
--static int loadstate_get_buffer(void *opaque, uint8_t *buf, int64_t pos,
-- int size)
-+static ssize_t loadstate_get_buffer(void *opaque, uint8_t *buf, int64_t pos,
-+ size_t size)
- {
- BlockDriverState *bs = (BlockDriverState *)opaque;
- int64_t maxlen = bdrv_getlength(bs);
-@@ -474,7 +474,6 @@ static const QEMUFileOps loadstate_file_ops = {
- int load_state_from_blockdev(const char *filename)
- {
- BlockDriverState *bs = NULL;
-- BlockDriver *drv = NULL;
- Error *local_err = NULL;
- Error *blocker = NULL;
-
-@@ -482,7 +481,7 @@ int load_state_from_blockdev(const char *filename)
- int ret;
-
- bs = bdrv_new();
-- ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_CACHE_WB, drv, &local_err);
-+ ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_CACHE_WB, &local_err);
- error_setg(&blocker, "block device is in use by load state");
- bdrv_op_block_all(bs, blocker);
-
---
-2.1.4
-
--- /dev/null
+From 2782de60b0da6b7c8a2961c366aec92164beaaaa Mon Sep 17 00:00:00 2001
+From: Thomas Lamprecht <t.lamprecht@proxmox.com>
+Date: Wed, 6 Apr 2016 16:47:54 +0200
+Subject: [PATCH 36/41] vnc: refactor to QIOChannelSocket
+
+---
+ ui/vnc-auth-vencrypt.c | 31 ++++++++++++++++---------------
+ 1 file changed, 16 insertions(+), 15 deletions(-)
+
+diff --git a/ui/vnc-auth-vencrypt.c b/ui/vnc-auth-vencrypt.c
+index d11f1df..a529520 100644
+--- a/ui/vnc-auth-vencrypt.c
++++ b/ui/vnc-auth-vencrypt.c
+@@ -28,27 +28,23 @@
+ #include "vnc.h"
+ #include "qapi/error.h"
+ #include "qemu/main-loop.h"
+-#include "qemu/sockets.h"
++#include "io/channel-socket.h"
+
+ static int protocol_client_auth_plain(VncState *vs, uint8_t *data, size_t len)
+ {
+- const char *err = NULL;
++ Error *err = NULL;
+ char username[256];
+ char passwd[512];
+
+- char clientip[256];
+- clientip[0] = 0;
+- struct sockaddr_in client;
+- socklen_t addrlen = sizeof(client);
+- if (getpeername(vs->csock, &client, &addrlen) == 0) {
+- inet_ntop(client.sin_family, &client.sin_addr,
+- clientip, sizeof(clientip));
++ SocketAddress *clientip = qio_channel_socket_get_remote_address(vs->sioc, &err);
++ if (err) {
++ goto err;
+ }
+
+ if ((len != (vs->username_len + vs->password_len)) ||
+ (vs->username_len >= (sizeof(username)-1)) ||
+ (vs->password_len >= (sizeof(passwd)-1)) ) {
+- err = "Got unexpected data length";
++ error_setg(&err, "Got unexpected data length");
+ goto err;
+ }
+
+@@ -59,26 +55,31 @@ static int protocol_client_auth_plain(VncState *vs, uint8_t *data, size_t len)
+
+ VNC_DEBUG("AUTH PLAIN username: %s pw: %s\n", username, passwd);
+
+- if (pve_auth_verify(clientip, username, passwd) == 0) {
++ if (pve_auth_verify(clientip->u.inet.data->host, username, passwd) == 0) {
+ vnc_write_u32(vs, 0); /* Accept auth completion */
+ start_client_init(vs);
++ qapi_free_SocketAddress(clientip);
+ return 0;
+ }
+
+- err = "Authentication failed";
++ error_setg(&err, "Authentication failed");
+ err:
+ if (err) {
+- VNC_DEBUG("AUTH PLAIN ERROR: %s\n", err);
++ const char *err_msg = error_get_pretty(err);
++ VNC_DEBUG("AUTH PLAIN ERROR: %s\n", err_msg);
+ vnc_write_u32(vs, 1); /* Reject auth */
+ if (vs->minor >= 8) {
+- int elen = strlen(err);
++ int elen = strlen(err_msg);
+ vnc_write_u32(vs, elen);
+- vnc_write(vs, err, elen);
++ vnc_write(vs, err_msg, elen);
+ }
++ error_free(err);
+ }
+ vnc_flush(vs);
+ vnc_client_error(vs);
+
++ qapi_free_SocketAddress(clientip);
++
+ return 0;
+
+ }
+--
+2.1.4
+
+++ /dev/null
-From 48d52946667b9814ff390405e649d8a8705ee584 Mon Sep 17 00:00:00 2001
-From: Wolfgang Bumiller <w.bumiller@proxmox.com>
-Date: Thu, 10 Dec 2015 15:19:42 +0100
-Subject: [PATCH 37/55] qapi: qmp_marshal_* renames for pve monitor commands
-
-As per 7fad30f0:
-qapi: Rename qmp_marshal_input_FOO() to qmp_marshal_FOO()
-
-For-patch: backup: add pve monitor commands
----
- qmp-commands.hx | 6 +++---
- 1 file changed, 3 insertions(+), 3 deletions(-)
-
-diff --git a/qmp-commands.hx b/qmp-commands.hx
-index 964563e..0eebb75 100644
---- a/qmp-commands.hx
-+++ b/qmp-commands.hx
-@@ -1281,19 +1281,19 @@ EQMP
- {
- .name = "backup",
- .args_type = "backup-file:s,format:s?,config-file:F?,firewall-file:F?,speed:o?,devlist:s?",
-- .mhandler.cmd_new = qmp_marshal_input_backup,
-+ .mhandler.cmd_new = qmp_marshal_backup,
- },
-
- {
- .name = "backup-cancel",
- .args_type = "",
-- .mhandler.cmd_new = qmp_marshal_input_backup_cancel,
-+ .mhandler.cmd_new = qmp_marshal_backup_cancel,
- },
-
- {
- .name = "query-backup",
- .args_type = "",
-- .mhandler.cmd_new = qmp_marshal_input_query_backup,
-+ .mhandler.cmd_new = qmp_marshal_query_backup,
- },
-
- {
---
-2.1.4
-
--- /dev/null
+From e743956b35723c39dba917ee92bb3cda7c634326 Mon Sep 17 00:00:00 2001
+From: Thomas Lamprecht <t.lamprecht@proxmox.com>
+Date: Fri, 1 Jul 2016 15:47:29 +0200
+Subject: [PATCH 37/41] vma: use BlockBackend on extract
+
+As we else rely on bdrv_close_all() do clean up, which was rewritten
+in ca9bd24cf1d53775169ba9adc17e265554d1afed and fails on "dangling"
+BDS pointers, such a pointer exists with *bs.
+Use the BlockBackend to get our BDS and just unref the BlockBackend
+when done, it handles the rest for us.
+
+The other two calls to bdrv_close_all() happen in verify_content()
+and dump_config(), both do not have a BDS so no need to change here.
+---
+ vma.c | 13 ++++++++++---
+ 1 file changed, 10 insertions(+), 3 deletions(-)
+
+diff --git a/vma.c b/vma.c
+index c8ad6c0..a2ddd32 100644
+--- a/vma.c
++++ b/vma.c
+@@ -19,6 +19,7 @@
+ #include "qemu/error-report.h"
+ #include "qemu/main-loop.h"
+ #include "sysemu/char.h" /* qstring_from_str */
++#include "sysemu/block-backend.h"
+
+ static void help(void)
+ {
+@@ -263,6 +264,8 @@ static int extract_content(int argc, char **argv)
+ int vmstate_fd = -1;
+ guint8 vmstate_stream = 0;
+
++ BlockBackend *blk = NULL;
++
+ for (i = 1; i < 255; i++) {
+ VmaDeviceInfo *di = vma_reader_get_device_info(vmar, i);
+ if (di && (strcmp(di->devname, "vmstate") == 0)) {
+@@ -307,8 +310,6 @@ static int extract_content(int argc, char **argv)
+ write_zero = false;
+ }
+
+- BlockDriverState *bs = bdrv_new();
+-
+ size_t devlen = strlen(devfn);
+ QDict *options = NULL;
+ if (format) {
+@@ -326,10 +327,14 @@ static int extract_content(int argc, char **argv)
+ qdict_put(options, "driver", qstring_from_str("raw"));
+ }
+
+- if (errp || bdrv_open(&bs, devfn, NULL, options, flags, &errp)) {
++
++ if (errp || !(blk = blk_new_open(devfn, NULL, options, flags, &errp))) {
+ g_error("can't open file %s - %s", devfn,
+ error_get_pretty(errp));
+ }
++
++ BlockDriverState *bs = blk_bs(blk);
++
+ if (vma_reader_register_bs(vmar, i, bs, write_zero, &errp) < 0) {
+ g_error("%s", error_get_pretty(errp));
+ }
+@@ -362,6 +367,8 @@ static int extract_content(int argc, char **argv)
+
+ vma_reader_destroy(vmar);
+
++ blk_unref(blk);
++
+ bdrv_close_all();
+
+ return ret;
+--
+2.1.4
+
+++ /dev/null
-From 6eeadc56b6dac81222e595fd4d95daac4cac3333 Mon Sep 17 00:00:00 2001
-From: Wolfgang Bumiller <w.bumiller@proxmox.com>
-Date: Thu, 10 Dec 2015 15:21:10 +0100
-Subject: [PATCH 38/55] qapi: qmp_mashal_* renames for async snapshot
-
-As per 7fad30f0:
-qapi: Rename qmp_marshal_input_FOO() to qmp_marshal_FOO()
-
-For-patch: internal snapshot async
----
- qmp-commands.hx | 10 +++++-----
- 1 file changed, 5 insertions(+), 5 deletions(-)
-
-diff --git a/qmp-commands.hx b/qmp-commands.hx
-index 0eebb75..6abe9df 100644
---- a/qmp-commands.hx
-+++ b/qmp-commands.hx
-@@ -4808,31 +4808,31 @@ EQMP
- {
- .name = "savevm-start",
- .args_type = "statefile:s?",
-- .mhandler.cmd_new = qmp_marshal_input_savevm_start,
-+ .mhandler.cmd_new = qmp_marshal_savevm_start,
- },
-
- {
- .name = "snapshot-drive",
- .args_type = "device:s,name:s",
-- .mhandler.cmd_new = qmp_marshal_input_snapshot_drive,
-+ .mhandler.cmd_new = qmp_marshal_snapshot_drive,
- },
-
- {
- .name = "delete-drive-snapshot",
- .args_type = "device:s,name:s",
-- .mhandler.cmd_new = qmp_marshal_input_delete_drive_snapshot,
-+ .mhandler.cmd_new = qmp_marshal_delete_drive_snapshot,
- },
-
- {
- .name = "savevm-end",
- .args_type = "",
-- .mhandler.cmd_new = qmp_marshal_input_savevm_end,
-+ .mhandler.cmd_new = qmp_marshal_savevm_end,
- },
-
- {
- .name = "query-savevm",
- .args_type = "",
-- .mhandler.cmd_new = qmp_marshal_input_query_savevm,
-+ .mhandler.cmd_new = qmp_marshal_query_savevm,
- },
-
- {
---
-2.1.4
-
--- /dev/null
+From 80c3adfb9b7da34e8e63f4d3b404bfc93b35ab64 Mon Sep 17 00:00:00 2001
+From: Wolfgang Bumiller <w.bumiller@proxmox.com>
+Date: Fri, 9 Sep 2016 14:51:28 +0200
+Subject: [PATCH 38/41] vma: byte based write calls
+
+---
+ vma-reader.c | 42 +++++++++++++++++++++---------------------
+ vma.c | 22 ++++++++++------------
+ vma.h | 2 +-
+ 3 files changed, 32 insertions(+), 34 deletions(-)
+
+diff --git a/vma-reader.c b/vma-reader.c
+index 78f1de9..2000889 100644
+--- a/vma-reader.c
++++ b/vma-reader.c
+@@ -25,7 +25,7 @@
+ static unsigned char zero_vma_block[VMA_BLOCK_SIZE];
+
+ typedef struct VmaRestoreState {
+- BlockDriverState *bs;
++ BlockBackend *target;
+ bool write_zeroes;
+ unsigned long *bitmap;
+ int bitmap_size;
+@@ -423,12 +423,12 @@ VmaDeviceInfo *vma_reader_get_device_info(VmaReader *vmar, guint8 dev_id)
+ }
+
+ static void allocate_rstate(VmaReader *vmar, guint8 dev_id,
+- BlockDriverState *bs, bool write_zeroes)
++ BlockBackend *target, bool write_zeroes)
+ {
+ assert(vmar);
+ assert(dev_id);
+
+- vmar->rstate[dev_id].bs = bs;
++ vmar->rstate[dev_id].target = target;
+ vmar->rstate[dev_id].write_zeroes = write_zeroes;
+
+ int64_t size = vmar->devinfo[dev_id].size;
+@@ -443,15 +443,15 @@ static void allocate_rstate(VmaReader *vmar, guint8 dev_id,
+ vmar->cluster_count += size/VMA_CLUSTER_SIZE;
+ }
+
+-int vma_reader_register_bs(VmaReader *vmar, guint8 dev_id, BlockDriverState *bs,
++int vma_reader_register_bs(VmaReader *vmar, guint8 dev_id, BlockBackend *target,
+ bool write_zeroes, Error **errp)
+ {
+ assert(vmar);
+- assert(bs != NULL);
++ assert(target != NULL);
+ assert(dev_id);
+- assert(vmar->rstate[dev_id].bs == NULL);
++ assert(vmar->rstate[dev_id].target == NULL);
+
+- int64_t size = bdrv_getlength(bs);
++ int64_t size = blk_getlength(target);
+ int64_t size_diff = size - vmar->devinfo[dev_id].size;
+
+ /* storage types can have different size restrictions, so it
+@@ -465,7 +465,7 @@ int vma_reader_register_bs(VmaReader *vmar, guint8 dev_id, BlockDriverState *bs,
+ return -1;
+ }
+
+- allocate_rstate(vmar, dev_id, bs, write_zeroes);
++ allocate_rstate(vmar, dev_id, target, write_zeroes);
+
+ return 0;
+ }
+@@ -507,7 +507,7 @@ static size_t full_write(int fd, void *buf, size_t len)
+ }
+
+ static int restore_write_data(VmaReader *vmar, guint8 dev_id,
+- BlockDriverState *bs, int vmstate_fd,
++ BlockBackend *target, int vmstate_fd,
+ unsigned char *buf, int64_t sector_num,
+ int nb_sectors, Error **errp)
+ {
+@@ -523,10 +523,10 @@ static int restore_write_data(VmaReader *vmar, guint8 dev_id,
+ }
+ }
+ } else {
+- int res = bdrv_write(bs, sector_num, buf, nb_sectors);
++ int res = blk_pwrite(target, sector_num * BDRV_SECTOR_SIZE, buf, nb_sectors * BDRV_SECTOR_SIZE, 0);
+ if (res < 0) {
+- error_setg(errp, "bdrv_write to %s failed (%d)",
+- bdrv_get_device_name(bs), res);
++ error_setg(errp, "blk_pwrite to %s failed (%d)",
++ bdrv_get_device_name(blk_bs(target)), res);
+ return -1;
+ }
+ }
+@@ -556,11 +556,11 @@ static int restore_extent(VmaReader *vmar, unsigned char *buf,
+ }
+
+ VmaRestoreState *rstate = &vmar->rstate[dev_id];
+- BlockDriverState *bs = NULL;
++ BlockBackend *target = NULL;
+
+ if (dev_id != vmar->vmstate_stream) {
+- bs = rstate->bs;
+- if (!verify && !bs) {
++ target = rstate->target;
++ if (!verify && !target) {
+ error_setg(errp, "got wrong dev id %d", dev_id);
+ return -1;
+ }
+@@ -618,7 +618,7 @@ static int restore_extent(VmaReader *vmar, unsigned char *buf,
+
+ if (!verify) {
+ int nb_sectors = end_sector - sector_num;
+- if (restore_write_data(vmar, dev_id, bs, vmstate_fd,
++ if (restore_write_data(vmar, dev_id, target, vmstate_fd,
+ buf + start, sector_num, nb_sectors,
+ errp) < 0) {
+ return -1;
+@@ -654,7 +654,7 @@ static int restore_extent(VmaReader *vmar, unsigned char *buf,
+
+ if (!verify) {
+ int nb_sectors = end_sector - sector_num;
+- if (restore_write_data(vmar, dev_id, bs, vmstate_fd,
++ if (restore_write_data(vmar, dev_id, target, vmstate_fd,
+ buf + start, sector_num,
+ nb_sectors, errp) < 0) {
+ return -1;
+@@ -678,7 +678,7 @@ static int restore_extent(VmaReader *vmar, unsigned char *buf,
+ }
+
+ if (rstate->write_zeroes && !verify) {
+- if (restore_write_data(vmar, dev_id, bs, vmstate_fd,
++ if (restore_write_data(vmar, dev_id, target, vmstate_fd,
+ zero_vma_block, sector_num,
+ nb_sectors, errp) < 0) {
+ return -1;
+@@ -786,12 +786,12 @@ static int vma_reader_restore_full(VmaReader *vmar, int vmstate_fd,
+ int i;
+ for (i = 1; i < 256; i++) {
+ VmaRestoreState *rstate = &vmar->rstate[i];
+- if (!rstate->bs) {
++ if (!rstate->target) {
+ continue;
+ }
+
+- if (bdrv_flush(rstate->bs) < 0) {
+- error_setg(errp, "vma bdrv_flush %s failed",
++ if (blk_flush(rstate->target) < 0) {
++ error_setg(errp, "vma blk_flush %s failed",
+ vmar->devinfo[i].devname);
+ return -1;
+ }
+diff --git a/vma.c b/vma.c
+index a2ddd32..ff974bd 100644
+--- a/vma.c
++++ b/vma.c
+@@ -333,9 +333,7 @@ static int extract_content(int argc, char **argv)
+ error_get_pretty(errp));
+ }
+
+- BlockDriverState *bs = blk_bs(blk);
+-
+- if (vma_reader_register_bs(vmar, i, bs, write_zero, &errp) < 0) {
++ if (vma_reader_register_bs(vmar, i, blk, write_zero, &errp) < 0) {
+ g_error("%s", error_get_pretty(errp));
+ }
+
+@@ -427,7 +425,7 @@ static int verify_content(int argc, char **argv)
+ }
+
+ typedef struct BackupJob {
+- BlockDriverState *bs;
++ BlockBackend *target;
+ int64_t len;
+ VmaWriter *vmaw;
+ uint8_t dev_id;
+@@ -456,7 +454,7 @@ static void coroutine_fn backup_run(void *opaque)
+ int64_t start, end;
+ int ret = 0;
+
+- unsigned char *buf = qemu_blockalign(job->bs, VMA_CLUSTER_SIZE);
++ unsigned char *buf = blk_blockalign(job->target, VMA_CLUSTER_SIZE);
+
+ start = 0;
+ end = DIV_ROUND_UP(job->len / BDRV_SECTOR_SIZE,
+@@ -467,8 +465,8 @@ static void coroutine_fn backup_run(void *opaque)
+ iov.iov_len = VMA_CLUSTER_SIZE;
+ qemu_iovec_init_external(&qiov, &iov, 1);
+
+- ret = bdrv_co_readv(job->bs, start * BACKUP_SECTORS_PER_CLUSTER,
+- BACKUP_SECTORS_PER_CLUSTER, &qiov);
++ ret = blk_co_preadv(job->target, start * BACKUP_SECTORS_PER_CLUSTER,
++ BACKUP_SECTORS_PER_CLUSTER, &qiov, 0);
+ if (ret < 0) {
+ vma_writer_set_error(job->vmaw, "read error", -1);
+ goto out;
+@@ -563,14 +561,14 @@ static int create_archive(int argc, char **argv)
+ path = extract_devname(path, &devname, devcount++);
+
+ Error *errp = NULL;
+- BlockDriverState *bs;
++ BlockBackend *target;
+
+- bs = bdrv_open(path, NULL, NULL, 0, &errp);
+- if (!bs) {
++ target = blk_new_open(path, NULL, NULL, 0, &errp);
++ if (!target) {
+ unlink(archivename);
+ g_error("bdrv_open '%s' failed - %s", path, error_get_pretty(errp));
+ }
+- int64_t size = bdrv_getlength(bs);
++ int64_t size = blk_getlength(target);
+ int dev_id = vma_writer_register_stream(vmaw, devname, size);
+ if (dev_id <= 0) {
+ unlink(archivename);
+@@ -579,7 +577,7 @@ static int create_archive(int argc, char **argv)
+
+ BackupJob *job = g_new0(BackupJob, 1);
+ job->len = size;
+- job->bs = bs;
++ job->target = target;
+ job->vmaw = vmaw;
+ job->dev_id = dev_id;
+
+diff --git a/vma.h b/vma.h
+index 365ceb2..fa6f4df 100644
+--- a/vma.h
++++ b/vma.h
+@@ -140,7 +140,7 @@ VmaHeader *vma_reader_get_header(VmaReader *vmar);
+ GList *vma_reader_get_config_data(VmaReader *vmar);
+ VmaDeviceInfo *vma_reader_get_device_info(VmaReader *vmar, guint8 dev_id);
+ int vma_reader_register_bs(VmaReader *vmar, guint8 dev_id,
+- BlockDriverState *bs, bool write_zeroes,
++ BlockBackend *target, bool write_zeroes,
+ Error **errp);
+ int vma_reader_restore(VmaReader *vmar, int vmstate_fd, bool verbose,
+ Error **errp);
+--
+2.1.4
+
+++ /dev/null
-From 0c87d830e293ad329e1ac175475cd149ce189177 Mon Sep 17 00:00:00 2001
-From: Wolfgang Bumiller <w.bumiller@proxmox.com>
-Date: Thu, 10 Dec 2015 15:21:59 +0100
-Subject: [PATCH 39/55] qapi: qmp_mashal_* renames for get_link_status
-
-As per 7fad30f0:
-qapi: Rename qmp_marshal_input_FOO() to qmp_marshal_FOO()
-
-For-patch: qmp: add get_link_status
----
- qmp-commands.hx | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/qmp-commands.hx b/qmp-commands.hx
-index 6abe9df..38ec312 100644
---- a/qmp-commands.hx
-+++ b/qmp-commands.hx
-@@ -1846,7 +1846,7 @@ EQMP
- {
- .name = "get_link_status",
- .args_type = "name:s",
-- .mhandler.cmd_new = qmp_marshal_input_get_link_status,
-+ .mhandler.cmd_new = qmp_marshal_get_link_status,
- },
-
- SQMP
---
-2.1.4
-
--- /dev/null
+From e588ad5b8eccaecfea37f4bdcedfd7cc11ba4276 Mon Sep 17 00:00:00 2001
+From: Alexandre Derumier <aderumier@odiso.com>
+Date: Tue, 26 Jul 2016 16:51:00 +0200
+Subject: [PATCH 39/41] rbd: disable rbd_cache_writethrough_until_flush with
+ cache=unsafe
+
+Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
+---
+ block/rbd.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/block/rbd.c b/block/rbd.c
+index 0106fea..66428af 100644
+--- a/block/rbd.c
++++ b/block/rbd.c
+@@ -552,6 +552,10 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
+ rados_conf_set(s->cluster, "rbd_cache", "true");
+ }
+
++ if (flags & BDRV_O_NO_FLUSH) {
++ rados_conf_set(s->cluster, "rbd_cache_writethrough_until_flush", "false");
++ }
++
+ r = rados_connect(s->cluster);
+ if (r < 0) {
+ error_setg_errno(errp, -r, "error connecting");
+--
+2.1.4
+
--- /dev/null
+From 65b2906879eed81749598de83ae8f4f362e9f992 Mon Sep 17 00:00:00 2001
+From: Wolfgang Bumiller <w.bumiller@proxmox.com>
+Date: Mon, 1 Aug 2016 10:52:46 +0200
+Subject: [PATCH 40/41] enable cache=unsafe for vma extract_content and
+ qmp_savevm_start
+
+We don't send any flush here, so we need to open with cache=unsafe.
+
+Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
+---
+ savevm-async.c | 2 +-
+ vma.c | 2 +-
+ 2 files changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/savevm-async.c b/savevm-async.c
+index 7979435..76cd8fa 100644
+--- a/savevm-async.c
++++ b/savevm-async.c
+@@ -253,7 +253,7 @@ void qmp_savevm_start(bool has_statefile, const char *statefile, Error **errp)
+ {
+ Error *local_err = NULL;
+
+- int bdrv_oflags = BDRV_O_RDWR;
++ int bdrv_oflags = BDRV_O_RDWR | BDRV_O_NO_FLUSH;
+ int ret;
+
+ if (snap_state.state != SAVE_STATE_DONE) {
+diff --git a/vma.c b/vma.c
+index ff974bd..a8fa4ff 100644
+--- a/vma.c
++++ b/vma.c
+@@ -280,7 +280,7 @@ static int extract_content(int argc, char **argv)
+ } else if (di) {
+ char *devfn = NULL;
+ const char *format = NULL;
+- int flags = BDRV_O_RDWR;
++ int flags = BDRV_O_RDWR | BDRV_O_NO_FLUSH;
+ bool write_zero = true;
+
+ if (readmap) {
+--
+2.1.4
+
+++ /dev/null
-From 6a839e5bfcd7e41815b3ba407361c8effd36b5a8 Mon Sep 17 00:00:00 2001
-From: Wolfgang Bumiller <w.bumiller@proxmox.com>
-Date: Tue, 12 Jan 2016 09:09:49 +0100
-Subject: [PATCH 40/55] vnc: make x509 imply tls again
-
----
- ui/vnc.c | 5 ++---
- 1 file changed, 2 insertions(+), 3 deletions(-)
-
-diff --git a/ui/vnc.c b/ui/vnc.c
-index 88a5824..4610ce8 100644
---- a/ui/vnc.c
-+++ b/ui/vnc.c
-@@ -3671,9 +3671,8 @@ void vnc_display_open(const char *id, Error **errp)
- const char *path;
- bool tls = false, x509 = false, x509verify = false;
- tls = qemu_opt_get_bool(opts, "tls", false);
-- if (tls) {
-- path = qemu_opt_get(opts, "x509");
--
-+ path = qemu_opt_get(opts, "x509");
-+ if (tls || path) {
- if (path) {
- x509 = true;
- } else {
---
-2.1.4
-
+++ /dev/null
-From 4f24f670a0bbbce29981d7ee54d31a960e00617c Mon Sep 17 00:00:00 2001
-From: Wolfgang Bumiller <w.bumiller@proxmox.com>
-Date: Mon, 11 Jan 2016 10:40:31 +0100
-Subject: [PATCH 41/55] PVE VNC authentication
-
----
- crypto/tlscreds.c | 47 +++++++++++
- crypto/tlscredspriv.h | 2 +
- crypto/tlscredsx509.c | 13 ++--
- crypto/tlssession.c | 1 +
- include/crypto/tlscreds.h | 1 +
- include/ui/console.h | 1 +
- qemu-options.hx | 3 +
- ui/vnc-auth-vencrypt.c | 194 ++++++++++++++++++++++++++++++++++++++--------
- ui/vnc.c | 140 ++++++++++++++++++++++++++++++++-
- ui/vnc.h | 4 +
- vl.c | 9 +++
- 11 files changed, 375 insertions(+), 40 deletions(-)
-
-diff --git a/crypto/tlscreds.c b/crypto/tlscreds.c
-index 1620e12..affe020 100644
---- a/crypto/tlscreds.c
-+++ b/crypto/tlscreds.c
-@@ -158,6 +158,33 @@ qcrypto_tls_creds_prop_get_verify(Object *obj,
-
-
- static void
-+qcrypto_tls_creds_prop_set_pve(Object *obj,
-+ bool value,
-+ Error **errp G_GNUC_UNUSED)
-+{
-+ QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj);
-+
-+ creds->pve = value;
-+}
-+
-+
-+static bool
-+qcrypto_tls_creds_prop_get_pve(Object *obj,
-+ Error **errp G_GNUC_UNUSED)
-+{
-+ QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj);
-+
-+ return creds->pve;
-+}
-+
-+bool qcrypto_tls_creds_is_pve(QCryptoTLSCreds *creds)
-+{
-+ Error *errp = NULL;
-+ return qcrypto_tls_creds_prop_get_pve((Object*)creds, &errp);
-+}
-+
-+
-+static void
- qcrypto_tls_creds_prop_set_dir(Object *obj,
- const char *value,
- Error **errp G_GNUC_UNUSED)
-@@ -225,6 +252,26 @@ qcrypto_tls_creds_init(Object *obj)
- QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj);
-
- creds->verifyPeer = true;
-+ creds->pve = false;
-+
-+ object_property_add_bool(obj, "verify-peer",
-+ qcrypto_tls_creds_prop_get_verify,
-+ qcrypto_tls_creds_prop_set_verify,
-+ NULL);
-+ object_property_add_bool(obj, "pve",
-+ qcrypto_tls_creds_prop_get_pve,
-+ qcrypto_tls_creds_prop_set_pve,
-+ NULL);
-+ object_property_add_str(obj, "dir",
-+ qcrypto_tls_creds_prop_get_dir,
-+ qcrypto_tls_creds_prop_set_dir,
-+ NULL);
-+ object_property_add_enum(obj, "endpoint",
-+ "QCryptoTLSCredsEndpoint",
-+ QCryptoTLSCredsEndpoint_lookup,
-+ qcrypto_tls_creds_prop_get_endpoint,
-+ qcrypto_tls_creds_prop_set_endpoint,
-+ NULL);
- }
-
-
-diff --git a/crypto/tlscredspriv.h b/crypto/tlscredspriv.h
-index 9222be4..3a34279 100644
---- a/crypto/tlscredspriv.h
-+++ b/crypto/tlscredspriv.h
-@@ -36,6 +36,8 @@ int qcrypto_tls_creds_get_dh_params_file(QCryptoTLSCreds *creds,
- gnutls_dh_params_t *dh_params,
- Error **errp);
-
-+bool qcrypto_tls_creds_is_pve(QCryptoTLSCreds *creds);
-+
- #endif
-
- #endif /* QCRYPTO_TLSCRED_PRIV_H__ */
-diff --git a/crypto/tlscredsx509.c b/crypto/tlscredsx509.c
-index 6a0179c..d8f3bf1 100644
---- a/crypto/tlscredsx509.c
-+++ b/crypto/tlscredsx509.c
-@@ -550,22 +550,23 @@ qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds,
- *key = NULL, *dhparams = NULL;
- int ret;
- int rv = -1;
-+ bool pve = qcrypto_tls_creds_is_pve(&creds->parent_obj);
-
- trace_qcrypto_tls_creds_x509_load(creds,
- creds->parent_obj.dir ? creds->parent_obj.dir : "<nodir>");
-
- if (creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
- if (qcrypto_tls_creds_get_path(&creds->parent_obj,
-- QCRYPTO_TLS_CREDS_X509_CA_CERT,
-+ pve ? "pve-root-ca.pem" : QCRYPTO_TLS_CREDS_X509_CA_CERT,
- true, &cacert, errp) < 0 ||
- qcrypto_tls_creds_get_path(&creds->parent_obj,
- QCRYPTO_TLS_CREDS_X509_CA_CRL,
- false, &cacrl, errp) < 0 ||
- qcrypto_tls_creds_get_path(&creds->parent_obj,
-- QCRYPTO_TLS_CREDS_X509_SERVER_CERT,
-+ pve ? "local/pve-ssl.pem" : QCRYPTO_TLS_CREDS_X509_SERVER_CERT,
- true, &cert, errp) < 0 ||
- qcrypto_tls_creds_get_path(&creds->parent_obj,
-- QCRYPTO_TLS_CREDS_X509_SERVER_KEY,
-+ pve ? "local/pve-ssl.key" : QCRYPTO_TLS_CREDS_X509_SERVER_KEY,
- true, &key, errp) < 0 ||
- qcrypto_tls_creds_get_path(&creds->parent_obj,
- QCRYPTO_TLS_CREDS_DH_PARAMS,
-@@ -574,13 +575,13 @@ qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds,
- }
- } else {
- if (qcrypto_tls_creds_get_path(&creds->parent_obj,
-- QCRYPTO_TLS_CREDS_X509_CA_CERT,
-+ pve ? "pve-root-ca.pem" : QCRYPTO_TLS_CREDS_X509_CA_CERT,
- true, &cacert, errp) < 0 ||
- qcrypto_tls_creds_get_path(&creds->parent_obj,
-- QCRYPTO_TLS_CREDS_X509_CLIENT_CERT,
-+ pve ? "local/pve-ssl.pem" : QCRYPTO_TLS_CREDS_X509_CLIENT_CERT,
- false, &cert, errp) < 0 ||
- qcrypto_tls_creds_get_path(&creds->parent_obj,
-- QCRYPTO_TLS_CREDS_X509_CLIENT_KEY,
-+ pve ? "local/pve-ssl.key" : QCRYPTO_TLS_CREDS_X509_CLIENT_KEY,
- false, &key, errp) < 0) {
- goto cleanup;
- }
-diff --git a/crypto/tlssession.c b/crypto/tlssession.c
-index a543e5a..564ad5d 100644
---- a/crypto/tlssession.c
-+++ b/crypto/tlssession.c
-@@ -23,6 +23,7 @@
- #include "crypto/tlscredsanon.h"
- #include "crypto/tlscredsx509.h"
- #include "qapi/error.h"
-+#include "crypto/tlscredspriv.h"
- #include "qemu/acl.h"
- #include "trace.h"
-
-diff --git a/include/crypto/tlscreds.h b/include/crypto/tlscreds.h
-index 8e2babd..8969e66 100644
---- a/include/crypto/tlscreds.h
-+++ b/include/crypto/tlscreds.h
-@@ -54,6 +54,7 @@ struct QCryptoTLSCreds {
- gnutls_dh_params_t dh_params;
- #endif
- bool verifyPeer;
-+ bool pve;
- };
-
-
-diff --git a/include/ui/console.h b/include/ui/console.h
-index d5a88d9..c17bee0 100644
---- a/include/ui/console.h
-+++ b/include/ui/console.h
-@@ -427,6 +427,7 @@ void sdl_display_init(DisplayState *ds, int full_screen, int no_frame);
- void cocoa_display_init(DisplayState *ds, int full_screen);
-
- /* vnc.c */
-+void pve_auth_setup(int vmid);
- void vnc_display_init(const char *id);
- void vnc_display_open(const char *id, Error **errp);
- void vnc_display_add_client(const char *id, int csock, bool skipauth);
-diff --git a/qemu-options.hx b/qemu-options.hx
-index 20ab9ac..30b90f5 100644
---- a/qemu-options.hx
-+++ b/qemu-options.hx
-@@ -476,6 +476,9 @@ STEXI
- @table @option
- ETEXI
-
-+DEF("id", HAS_ARG, QEMU_OPTION_id,
-+ "-id n set the VMID\n", QEMU_ARCH_ALL)
-+
- DEF("fda", HAS_ARG, QEMU_OPTION_fda,
- "-fda/-fdb file use 'file' as floppy disk 0/1 image\n", QEMU_ARCH_ALL)
- DEF("fdb", HAS_ARG, QEMU_OPTION_fdb, "", QEMU_ARCH_ALL)
-diff --git a/ui/vnc-auth-vencrypt.c b/ui/vnc-auth-vencrypt.c
-index 11c8c9a..d11f1df 100644
---- a/ui/vnc-auth-vencrypt.c
-+++ b/ui/vnc-auth-vencrypt.c
-@@ -28,6 +28,107 @@
- #include "vnc.h"
- #include "qapi/error.h"
- #include "qemu/main-loop.h"
-+#include "qemu/sockets.h"
-+
-+static int protocol_client_auth_plain(VncState *vs, uint8_t *data, size_t len)
-+{
-+ const char *err = NULL;
-+ char username[256];
-+ char passwd[512];
-+
-+ char clientip[256];
-+ clientip[0] = 0;
-+ struct sockaddr_in client;
-+ socklen_t addrlen = sizeof(client);
-+ if (getpeername(vs->csock, &client, &addrlen) == 0) {
-+ inet_ntop(client.sin_family, &client.sin_addr,
-+ clientip, sizeof(clientip));
-+ }
-+
-+ if ((len != (vs->username_len + vs->password_len)) ||
-+ (vs->username_len >= (sizeof(username)-1)) ||
-+ (vs->password_len >= (sizeof(passwd)-1)) ) {
-+ err = "Got unexpected data length";
-+ goto err;
-+ }
-+
-+ strncpy(username, (char *)data, vs->username_len);
-+ username[vs->username_len] = 0;
-+ strncpy(passwd, (char *)data + vs->username_len, vs->password_len);
-+ passwd[vs->password_len] = 0;
-+
-+ VNC_DEBUG("AUTH PLAIN username: %s pw: %s\n", username, passwd);
-+
-+ if (pve_auth_verify(clientip, username, passwd) == 0) {
-+ vnc_write_u32(vs, 0); /* Accept auth completion */
-+ start_client_init(vs);
-+ return 0;
-+ }
-+
-+ err = "Authentication failed";
-+err:
-+ if (err) {
-+ VNC_DEBUG("AUTH PLAIN ERROR: %s\n", err);
-+ vnc_write_u32(vs, 1); /* Reject auth */
-+ if (vs->minor >= 8) {
-+ int elen = strlen(err);
-+ vnc_write_u32(vs, elen);
-+ vnc_write(vs, err, elen);
-+ }
-+ }
-+ vnc_flush(vs);
-+ vnc_client_error(vs);
-+
-+ return 0;
-+
-+}
-+
-+static int protocol_client_auth_plain_start(VncState *vs, uint8_t *data, size_t len)
-+{
-+ uint32_t ulen = read_u32(data, 0);
-+ uint32_t pwlen = read_u32(data, 4);
-+ const char *err = NULL;
-+
-+ VNC_DEBUG("AUTH PLAIN START %u %u\n", ulen, pwlen);
-+
-+ if (!ulen) {
-+ err = "No User name.";
-+ goto err;
-+ }
-+ if (ulen >= 255) {
-+ err = "User name too long.";
-+ goto err;
-+ }
-+ if (!pwlen) {
-+ err = "Password too short";
-+ goto err;
-+ }
-+ if (pwlen >= 511) {
-+ err = "Password too long.";
-+ goto err;
-+ }
-+
-+ vs->username_len = ulen;
-+ vs->password_len = pwlen;
-+
-+ vnc_read_when(vs, protocol_client_auth_plain, ulen + pwlen);
-+
-+ return 0;
-+err:
-+ if (err) {
-+ VNC_DEBUG("AUTH PLAIN ERROR: %s\n", err);
-+ vnc_write_u32(vs, 1); /* Reject auth */
-+ if (vs->minor >= 8) {
-+ int elen = strlen(err);
-+ vnc_write_u32(vs, elen);
-+ vnc_write(vs, err, elen);
-+ }
-+ }
-+ vnc_flush(vs);
-+ vnc_client_error(vs);
-+
-+ return 0;
-+}
-
- static void start_auth_vencrypt_subauth(VncState *vs)
- {
-@@ -39,6 +140,17 @@ static void start_auth_vencrypt_subauth(VncState *vs)
- start_client_init(vs);
- break;
-
-+ case VNC_AUTH_VENCRYPT_TLSPLAIN:
-+ case VNC_AUTH_VENCRYPT_X509PLAIN:
-+ VNC_DEBUG("Start TLS auth PLAIN\n");
-+ vnc_read_when(vs, protocol_client_auth_plain_start, 8);
-+ break;
-+
-+ case VNC_AUTH_VENCRYPT_PLAIN:
-+ VNC_DEBUG("Start auth PLAIN\n");
-+ vnc_read_when(vs, protocol_client_auth_plain_start, 8);
-+ break;
-+
- case VNC_AUTH_VENCRYPT_TLSVNC:
- case VNC_AUTH_VENCRYPT_X509VNC:
- VNC_DEBUG("Start TLS auth VNC\n");
-@@ -87,44 +199,63 @@ static int protocol_client_vencrypt_auth(VncState *vs, uint8_t *data, size_t len
- {
- int auth = read_u32(data, 0);
-
-- if (auth != vs->subauth) {
-+ if (auth != vs->subauth && auth != VNC_AUTH_VENCRYPT_PLAIN) {
- VNC_DEBUG("Rejecting auth %d\n", auth);
- vnc_write_u8(vs, 0); /* Reject auth */
- vnc_flush(vs);
- vnc_client_error(vs);
- } else {
-- Error *err = NULL;
-- QIOChannelTLS *tls;
-- VNC_DEBUG("Accepting auth %d, setting up TLS for handshake\n", auth);
-- vnc_write_u8(vs, 1); /* Accept auth */
-- vnc_flush(vs);
--
-- if (vs->ioc_tag) {
-- g_source_remove(vs->ioc_tag);
-- vs->ioc_tag = 0;
-+ if (auth == VNC_AUTH_VENCRYPT_PLAIN) {
-+ vs->subauth = auth;
-+ start_auth_vencrypt_subauth(vs);
- }
-+ else
-+ {
-+ Error *err = NULL;
-+ QIOChannelTLS *tls;
-+ VNC_DEBUG("Accepting auth %d, setting up TLS for handshake\n", auth);
-+ vnc_write_u8(vs, 1); /* Accept auth */
-+ vnc_flush(vs);
-
-- tls = qio_channel_tls_new_server(
-- vs->ioc,
-- vs->vd->tlscreds,
-- vs->vd->tlsaclname,
-- &err);
-- if (!tls) {
-- VNC_DEBUG("Failed to setup TLS %s\n", error_get_pretty(err));
-- error_free(err);
-- vnc_client_error(vs);
-- return 0;
-- }
-+ if (vs->ioc_tag) {
-+ g_source_remove(vs->ioc_tag);
-+ vs->ioc_tag = 0;
-+ }
-
-- VNC_DEBUG("Start TLS VeNCrypt handshake process\n");
-- object_unref(OBJECT(vs->ioc));
-- vs->ioc = QIO_CHANNEL(tls);
-- vs->tls = qio_channel_tls_get_session(tls);
-+ tls = qio_channel_tls_new_server(
-+ vs->ioc,
-+ vs->vd->tlscreds,
-+ vs->vd->tlsaclname,
-+ &err);
-+ if (!tls) {
-+ VNC_DEBUG("Failed to setup TLS %s\n", error_get_pretty(err));
-+ error_free(err);
-+ vnc_client_error(vs);
-+ return 0;
-+ vs->tls = qcrypto_tls_session_new(vs->vd->tlscreds,
-+ NULL,
-+ vs->vd->tlsaclname,
-+ QCRYPTO_TLS_CREDS_ENDPOINT_SERVER,
-+ &err);
-+ if (!vs->tls) {
-+ VNC_DEBUG("Failed to setup TLS %s\n",
-+ error_get_pretty(err));
-+ error_free(err);
-+ vnc_client_error(vs);
-+ return 0;
-+ }
-+ }
-
-- qio_channel_tls_handshake(tls,
-- vnc_tls_handshake_done,
-- vs,
-- NULL);
-+ VNC_DEBUG("Start TLS VeNCrypt handshake process\n");
-+ object_unref(OBJECT(vs->ioc));
-+ vs->ioc = QIO_CHANNEL(tls);
-+ vs->tls = qio_channel_tls_get_session(tls);
-+
-+ qio_channel_tls_handshake(tls,
-+ vnc_tls_handshake_done,
-+ vs,
-+ NULL);
-+ }
- }
- return 0;
- }
-@@ -138,10 +269,11 @@ static int protocol_client_vencrypt_init(VncState *vs, uint8_t *data, size_t len
- vnc_flush(vs);
- vnc_client_error(vs);
- } else {
-- VNC_DEBUG("Sending allowed auth %d\n", vs->subauth);
-+ VNC_DEBUG("Sending allowed auths %d %d\n", vs->subauth, VNC_AUTH_VENCRYPT_PLAIN);
- vnc_write_u8(vs, 0); /* Accept version */
-- vnc_write_u8(vs, 1); /* Number of sub-auths */
-+ vnc_write_u8(vs, 2); /* Number of sub-auths */
- vnc_write_u32(vs, vs->subauth); /* The supported auth */
-+ vnc_write_u32(vs, VNC_AUTH_VENCRYPT_PLAIN); /* Alternative supported auth */
- vnc_flush(vs);
- vnc_read_when(vs, protocol_client_vencrypt_auth, 4);
- }
-diff --git a/ui/vnc.c b/ui/vnc.c
-index 4610ce8..66e2163 100644
---- a/ui/vnc.c
-+++ b/ui/vnc.c
-@@ -55,6 +55,125 @@ static const struct timeval VNC_REFRESH_LOSSY = { 2, 0 };
- #include "vnc_keysym.h"
- #include "crypto/cipher.h"
-
-+static int pve_vmid = 0;
-+
-+void pve_auth_setup(int vmid) {
-+ pve_vmid = vmid;
-+}
-+
-+static char *
-+urlencode(char *buf, const char *value)
-+{
-+ static const char *hexchar = "0123456789abcdef";
-+ char *p = buf;
-+ int i;
-+ int l = strlen(value);
-+ for (i = 0; i < l; i++) {
-+ char c = value[i];
-+ if (('a' <= c && c <= 'z') ||
-+ ('A' <= c && c <= 'Z') ||
-+ ('0' <= c && c <= '9')) {
-+ *p++ = c;
-+ } else if (c == 32) {
-+ *p++ = '+';
-+ } else {
-+ *p++ = '%';
-+ *p++ = hexchar[c >> 4];
-+ *p++ = hexchar[c & 15];
-+ }
-+ }
-+ *p = 0;
-+
-+ return p;
-+}
-+
-+int
-+pve_auth_verify(const char *clientip, const char *username, const char *passwd)
-+{
-+ struct sockaddr_in server;
-+
-+ int sfd = socket(AF_INET, SOCK_STREAM, 0);
-+ if (sfd == -1) {
-+ perror("pve_auth_verify: socket failed");
-+ return -1;
-+ }
-+
-+ struct hostent *he;
-+ if ((he = gethostbyname("localhost")) == NULL) {
-+ fprintf(stderr, "pve_auth_verify: error resolving hostname\n");
-+ goto err;
-+ }
-+
-+ memcpy(&server.sin_addr, he->h_addr_list[0], he->h_length);
-+ server.sin_family = AF_INET;
-+ server.sin_port = htons(85);
-+
-+ if (connect(sfd, (struct sockaddr *)&server, sizeof(server))) {
-+ perror("pve_auth_verify: error connecting to server");
-+ goto err;
-+ }
-+
-+ char buf[8192];
-+ char form[8192];
-+
-+ char *p = form;
-+ p = urlencode(p, "username");
-+ *p++ = '=';
-+ p = urlencode(p, username);
-+
-+ *p++ = '&';
-+ p = urlencode(p, "password");
-+ *p++ = '=';
-+ p = urlencode(p, passwd);
-+
-+ *p++ = '&';
-+ p = urlencode(p, "path");
-+ *p++ = '=';
-+ char authpath[256];
-+ sprintf(authpath, "/vms/%d", pve_vmid);
-+ p = urlencode(p, authpath);
-+
-+ *p++ = '&';
-+ p = urlencode(p, "privs");
-+ *p++ = '=';
-+ p = urlencode(p, "VM.Console");
-+
-+ sprintf(buf, "POST /api2/json/access/ticket HTTP/1.1\n"
-+ "Host: localhost:85\n"
-+ "Connection: close\n"
-+ "PVEClientIP: %s\n"
-+ "Content-Type: application/x-www-form-urlencoded\n"
-+ "Content-Length: %zd\n\n%s\n", clientip, strlen(form), form);
-+ ssize_t len = strlen(buf);
-+ ssize_t sb = send(sfd, buf, len, 0);
-+ if (sb < 0) {
-+ perror("pve_auth_verify: send failed");
-+ goto err;
-+ }
-+ if (sb != len) {
-+ fprintf(stderr, "pve_auth_verify: partial send error\n");
-+ goto err;
-+ }
-+
-+ len = recv(sfd, buf, sizeof(buf) - 1, 0);
-+ if (len < 0) {
-+ perror("pve_auth_verify: recv failed");
-+ goto err;
-+ }
-+
-+ buf[len] = 0;
-+
-+ //printf("DATA:%s\n", buf);
-+
-+ shutdown(sfd, SHUT_RDWR);
-+
-+ return strncmp(buf, "HTTP/1.1 200 OK", 15);
-+
-+err:
-+ shutdown(sfd, SHUT_RDWR);
-+ return -1;
-+}
-+
- static QTAILQ_HEAD(, VncDisplay) vnc_displays =
- QTAILQ_HEAD_INITIALIZER(vnc_displays);
-
-@@ -3359,11 +3478,17 @@ vnc_display_setup_auth(VncDisplay *vs,
- if (object_dynamic_cast(OBJECT(vs->tlscreds),
- TYPE_QCRYPTO_TLS_CREDS_X509)) {
- VNC_DEBUG("Initializing VNC server with x509 password auth\n");
-- vs->subauth = VNC_AUTH_VENCRYPT_X509VNC;
-+ if (vs->tlscreds->pve)
-+ vs->subauth = VNC_AUTH_VENCRYPT_X509PLAIN;
-+ else
-+ vs->subauth = VNC_AUTH_VENCRYPT_X509VNC;
- } else if (object_dynamic_cast(OBJECT(vs->tlscreds),
- TYPE_QCRYPTO_TLS_CREDS_ANON)) {
- VNC_DEBUG("Initializing VNC server with TLS password auth\n");
-- vs->subauth = VNC_AUTH_VENCRYPT_TLSVNC;
-+ if (vs->tlscreds->pve)
-+ vs->subauth = VNC_AUTH_VENCRYPT_TLSPLAIN;
-+ else
-+ vs->subauth = VNC_AUTH_VENCRYPT_TLSVNC;
- } else {
- error_setg(errp,
- "Unsupported TLS cred type %s",
-@@ -3454,6 +3579,7 @@ vnc_display_create_creds(bool x509,
- bool x509verify,
- const char *dir,
- const char *id,
-+ bool pve,
- Error **errp)
- {
- gchar *credsid = g_strdup_printf("tlsvnc%s", id);
-@@ -3469,6 +3595,7 @@ vnc_display_create_creds(bool x509,
- "endpoint", "server",
- "dir", dir,
- "verify-peer", x509verify ? "yes" : "no",
-+ "pve", pve ? "yes" : "no",
- NULL);
- } else {
- creds = object_new_with_props(TYPE_QCRYPTO_TLS_CREDS_ANON,
-@@ -3476,6 +3603,7 @@ vnc_display_create_creds(bool x509,
- credsid,
- &err,
- "endpoint", "server",
-+ "pve", pve ? "yes" : "no",
- NULL);
- }
-
-@@ -3669,12 +3797,17 @@ void vnc_display_open(const char *id, Error **errp)
- }
- } else {
- const char *path;
-- bool tls = false, x509 = false, x509verify = false;
-+ bool tls = false, x509 = false, x509verify = false, pve = false;
- tls = qemu_opt_get_bool(opts, "tls", false);
- path = qemu_opt_get(opts, "x509");
- if (tls || path) {
- if (path) {
- x509 = true;
-+ if (!strcmp(path, "on")) {
-+ /* magic to default to /etc/pve */
-+ path = "/etc/pve";
-+ pve = true;
-+ }
- } else {
- path = qemu_opt_get(opts, "x509verify");
- if (path) {
-@@ -3686,6 +3819,7 @@ void vnc_display_open(const char *id, Error **errp)
- x509verify,
- path,
- vs->id,
-+ pve,
- errp);
- if (!vs->tlscreds) {
- goto fail;
-diff --git a/ui/vnc.h b/ui/vnc.h
-index 81a3261..40e2780 100644
---- a/ui/vnc.h
-+++ b/ui/vnc.h
-@@ -281,6 +281,8 @@ struct VncState
- int auth;
- int subauth; /* Used by VeNCrypt */
- char challenge[VNC_AUTH_CHALLENGE_SIZE];
-+ int username_len;
-+ int password_len;
- QCryptoTLSSession *tls; /* Borrowed pointer from channel, don't free */
- #ifdef CONFIG_VNC_SASL
- VncStateSASL sasl;
-@@ -576,4 +578,6 @@ int vnc_zrle_send_framebuffer_update(VncState *vs, int x, int y, int w, int h);
- int vnc_zywrle_send_framebuffer_update(VncState *vs, int x, int y, int w, int h);
- void vnc_zrle_clear(VncState *vs);
-
-+int pve_auth_verify(const char *clientip, const char *username, const char *passwd);
-+
- #endif /* __QEMU_VNC_H */
-diff --git a/vl.c b/vl.c
-index f393767..f5fc86f 100644
---- a/vl.c
-+++ b/vl.c
-@@ -2967,6 +2967,7 @@ static void set_memory_options(uint64_t *ram_slots, ram_addr_t *maxram_size,
- int main(int argc, char **argv, char **envp)
- {
- int i;
-+ long int vm_id_long = 0;
- int snapshot, linux_boot;
- const char *initrd_filename;
- const char *kernel_filename, *kernel_cmdline;
-@@ -3737,6 +3738,14 @@ int main(int argc, char **argv, char **envp)
- exit(1);
- }
- break;
-+ case QEMU_OPTION_id:
-+ vm_id_long = strtol(optarg, (char **) &optarg, 10);
-+ if (*optarg != 0 || vm_id_long < 100 || vm_id_long > INT_MAX) {
-+ fprintf(stderr, "Invalid ID\n");
-+ exit(1);
-+ }
-+ pve_auth_setup(vm_id_long);
-+ break;
- case QEMU_OPTION_vnc:
- {
- #ifdef CONFIG_VNC
---
-2.1.4
-
--- /dev/null
+From 8ba1bc13200b72a287554224b5aeb8cbf1bba156 Mon Sep 17 00:00:00 2001
+From: Wolfgang Bumiller <w.bumiller@proxmox.com>
+Date: Fri, 9 Sep 2016 15:21:19 +0200
+Subject: [PATCH 41/41] savevm-async updates
+
+---
+ savevm-async.c | 99 +++++++++++++++++++++++++++++++++++++---------------------
+ 1 file changed, 63 insertions(+), 36 deletions(-)
+
+diff --git a/savevm-async.c b/savevm-async.c
+index 76cd8fa..fddb18c 100644
+--- a/savevm-async.c
++++ b/savevm-async.c
+@@ -20,6 +20,8 @@
+
+ /* #define DEBUG_SAVEVM_STATE */
+
++#define NOT_DONE 0x7fffffff /* used while emulated sync operation in progress */
++
+ #ifdef DEBUG_SAVEVM_STATE
+ #define DPRINTF(fmt, ...) \
+ do { printf("savevm-async: " fmt, ## __VA_ARGS__); } while (0)
+@@ -38,7 +40,7 @@ enum {
+
+
+ static struct SnapshotState {
+- BlockDriverState *bs;
++ BlockBackend *target;
+ size_t bs_pos;
+ int state;
+ Error *error;
+@@ -99,17 +101,17 @@ static int save_snapshot_cleanup(void)
+ ret = qemu_fclose(snap_state.file);
+ }
+
+- if (snap_state.bs) {
++ if (snap_state.target) {
+ /* try to truncate, but ignore errors (will fail on block devices).
+ * note: bdrv_read() need whole blocks, so we round up
+ */
+ size_t size = (snap_state.bs_pos + BDRV_SECTOR_SIZE) & BDRV_SECTOR_MASK;
+- bdrv_truncate(snap_state.bs, size);
+- bdrv_op_unblock_all(snap_state.bs, snap_state.blocker);
++ blk_truncate(snap_state.target, size);
++ blk_op_unblock_all(snap_state.target, snap_state.blocker);
+ error_free(snap_state.blocker);
+ snap_state.blocker = NULL;
+- bdrv_unref(snap_state.bs);
+- snap_state.bs = NULL;
++ blk_unref(snap_state.target);
++ snap_state.target = NULL;
+ }
+
+ return ret;
+@@ -151,21 +153,49 @@ static void save_snapshot_completed(void)
+ static int block_state_close(void *opaque)
+ {
+ snap_state.file = NULL;
+- return bdrv_flush(snap_state.bs);
++ return blk_flush(snap_state.target);
+ }
+
+-static ssize_t block_state_put_buffer(void *opaque, const uint8_t *buf,
+- int64_t pos, size_t size)
++typedef struct BlkRwCo {
++ int64_t offset;
++ QEMUIOVector *qiov;
++ int ret;
++} BlkRwCo;
++
++static void block_state_write_entry(void *opaque) {
++ BlkRwCo *rwco = opaque;
++ rwco->ret = blk_co_pwritev(snap_state.target, rwco->offset, rwco->qiov->size,
++ rwco->qiov, 0);
++}
++
++static ssize_t block_state_writev_buffer(void *opaque, struct iovec *iov,
++ int iovcnt, int64_t pos)
+ {
+- ssize_t ret;
++ AioContext *aio_context;
++ QEMUIOVector qiov;
++ Coroutine *co;
++ BlkRwCo rwco;
++
++ qemu_iovec_init_external(&qiov, iov, iovcnt);
++
++ rwco = (BlkRwCo) {
++ .offset = pos,
++ .qiov = &qiov,
++ .ret = NOT_DONE,
++ };
+
+- assert(pos == snap_state.bs_pos);
++ co = qemu_coroutine_create(&block_state_write_entry, &rwco);
++ qemu_coroutine_enter(co);
+
+- if ((ret = bdrv_pwrite(snap_state.bs, snap_state.bs_pos, buf, size)) > 0) {
+- snap_state.bs_pos += ret;
++ aio_context = blk_get_aio_context(snap_state.target);
++ while (rwco.ret == NOT_DONE) {
++ aio_poll(aio_context, true);
+ }
+
+- return ret;
++ if (rwco.ret < 0) {
++ return rwco.ret;
++ }
++ return qiov.size;
+ }
+
+ static int store_and_stop(void) {
+@@ -227,7 +257,7 @@ static void process_savevm_co(void *opaque)
+ /* stop the VM if we get to the end of available space,
+ * or if pending_size is just a few MB
+ */
+- maxlen = bdrv_getlength(snap_state.bs) - 30*1024*1024;
++ maxlen = blk_getlength(snap_state.target) - 30*1024*1024;
+ if ((pending_size < 100000) ||
+ ((snap_state.bs_pos + pending_size) >= maxlen)) {
+ if (store_and_stop())
+@@ -244,7 +274,7 @@ static void process_savevm_co(void *opaque)
+ }
+
+ static const QEMUFileOps block_file_ops = {
+- .put_buffer = block_state_put_buffer,
++ .writev_buffer = block_state_writev_buffer,
+ .close = block_state_close,
+ };
+
+@@ -254,7 +284,6 @@ void qmp_savevm_start(bool has_statefile, const char *statefile, Error **errp)
+ Error *local_err = NULL;
+
+ int bdrv_oflags = BDRV_O_RDWR | BDRV_O_NO_FLUSH;
+- int ret;
+
+ if (snap_state.state != SAVE_STATE_DONE) {
+ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
+@@ -284,13 +313,11 @@ void qmp_savevm_start(bool has_statefile, const char *statefile, Error **errp)
+ }
+
+ /* Open the image */
+- snap_state.bs = bdrv_new();
+-
+ QDict *options = NULL;
+ options = qdict_new();
+ qdict_put(options, "driver", qstring_from_str("raw"));
+- ret = bdrv_open(&snap_state.bs, statefile, NULL, options, bdrv_oflags, &local_err);
+- if (ret < 0) {
++ snap_state.target = blk_new_open(statefile, NULL, options, bdrv_oflags, &local_err);
++ if (!snap_state.target) {
+ error_set(errp, ERROR_CLASS_GENERIC_ERROR, "failed to open '%s'", statefile);
+ goto restart;
+ }
+@@ -304,9 +331,9 @@ void qmp_savevm_start(bool has_statefile, const char *statefile, Error **errp)
+
+
+ error_setg(&snap_state.blocker, "block device is in use by savevm");
+- bdrv_op_block_all(snap_state.bs, snap_state.blocker);
++ blk_op_block_all(snap_state.target, snap_state.blocker);
+
+- Coroutine *co = qemu_coroutine_create(process_savevm_co);
++ Coroutine *co = qemu_coroutine_create(process_savevm_co, NULL);
+ qemu_coroutine_enter(co);
+
+ return;
+@@ -457,8 +484,8 @@ void qmp_delete_drive_snapshot(const char *device, const char *name,
+ static ssize_t loadstate_get_buffer(void *opaque, uint8_t *buf, int64_t pos,
+ size_t size)
+ {
+- BlockDriverState *bs = (BlockDriverState *)opaque;
+- int64_t maxlen = bdrv_getlength(bs);
++ BlockBackend *be = opaque;
++ int64_t maxlen = blk_getlength(be);
+ if (pos > maxlen) {
+ return -EIO;
+ }
+@@ -468,7 +495,7 @@ static ssize_t loadstate_get_buffer(void *opaque, uint8_t *buf, int64_t pos,
+ if (size == 0) {
+ return 0;
+ }
+- return bdrv_pread(bs, pos, buf, size);
++ return blk_pread(be, pos, buf, size);
+ }
+
+ static const QEMUFileOps loadstate_file_ops = {
+@@ -477,25 +504,25 @@ static const QEMUFileOps loadstate_file_ops = {
+
+ int load_state_from_blockdev(const char *filename)
+ {
+- BlockDriverState *bs = NULL;
++ BlockBackend *be;
+ Error *local_err = NULL;
+ Error *blocker = NULL;
+
+ QEMUFile *f;
+ int ret;
+
+- bs = bdrv_new();
+- ret = bdrv_open(&bs, filename, NULL, NULL, 0, &local_err);
+- error_setg(&blocker, "block device is in use by load state");
+- bdrv_op_block_all(bs, blocker);
++ be = blk_new_open(filename, NULL, NULL, 0, &local_err);
+
+- if (ret < 0) {
++ if (!be) {
+ error_report("Could not open VM state file");
+ goto the_end;
+ }
+
++ error_setg(&blocker, "block device is in use by load state");
++ blk_op_block_all(be, blocker);
++
+ /* restore the VM state */
+- f = qemu_fopen_ops(bs, &loadstate_file_ops);
++ f = qemu_fopen_ops(be, &loadstate_file_ops);
+ if (!f) {
+ error_report("Could not open VM state file");
+ ret = -EINVAL;
+@@ -516,10 +543,10 @@ int load_state_from_blockdev(const char *filename)
+ ret = 0;
+
+ the_end:
+- if (bs) {
+- bdrv_op_unblock_all(bs, blocker);
++ if (be) {
++ blk_op_unblock_all(be, blocker);
+ error_free(blocker);
+- bdrv_unref(bs);
++ blk_unref(be);
+ }
+ return ret;
+ }
+--
+2.1.4
+
--- /dev/null
+From fa4f5325137a86bbbdc3f477a2a171a43ea80a7a Mon Sep 17 00:00:00 2001
+From: Alexandre Derumier <aderumier@odiso.com>
+Date: Tue, 13 Sep 2016 01:57:56 +0200
+Subject: [PATCH 42/42] qmp_snapshot_drive: add aiocontext
+
+Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
+---
+ savevm-async.c | 15 +++++++++++----
+ 1 file changed, 11 insertions(+), 4 deletions(-)
+
+diff --git a/savevm-async.c b/savevm-async.c
+index fddb18c..a158249 100644
+--- a/savevm-async.c
++++ b/savevm-async.c
+@@ -372,6 +372,7 @@ void qmp_snapshot_drive(const char *device, const char *name, Error **errp)
+ BlockBackend *blk;
+ BlockDriverState *bs;
+ QEMUSnapshotInfo sn1, *sn = &sn1;
++ AioContext *aio_context;
+ int ret;
+ #ifdef _WIN32
+ struct _timeb tb;
+@@ -398,20 +399,23 @@ void qmp_snapshot_drive(const char *device, const char *name, Error **errp)
+ return;
+ }
+
++ aio_context = bdrv_get_aio_context(bs);
++ aio_context_acquire(aio_context);
++
+ if (bdrv_is_read_only(bs)) {
+ error_setg(errp, "Node '%s' is read only", device);
+- return;
++ goto out;
+ }
+
+ if (!bdrv_can_snapshot(bs)) {
+ error_setg(errp, QERR_UNSUPPORTED);
+- return;
++ goto out;
+ }
+
+ if (bdrv_snapshot_find(bs, sn, name) >= 0) {
+ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
+ "snapshot '%s' already exists", name);
+- return;
++ goto out;
+ }
+
+ sn = &sn1;
+@@ -436,8 +440,11 @@ void qmp_snapshot_drive(const char *device, const char *name, Error **errp)
+ if (ret < 0) {
+ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
+ "Error while creating snapshot on '%s'\n", device);
+- return;
++ goto out;
+ }
++
++out:
++ aio_context_release(aio_context);
+ }
+
+ void qmp_delete_drive_snapshot(const char *device, const char *name,
+--
+2.1.4
+
+++ /dev/null
-From f8d5f70f8dbe379e442b182c89df168e38ba999d Mon Sep 17 00:00:00 2001
-From: Wolfgang Bumiller <w.bumiller@proxmox.com>
-Date: Mon, 8 Feb 2016 08:23:34 +0100
-Subject: [PATCH 42/55] vma-writer: don't bail out on zero-length files
-
----
- vma-writer.c | 1 -
- 1 file changed, 1 deletion(-)
-
-diff --git a/vma-writer.c b/vma-writer.c
-index f5a7abd..3cde204 100644
---- a/vma-writer.c
-+++ b/vma-writer.c
-@@ -136,7 +136,6 @@ int vma_writer_add_config(VmaWriter *vmaw, const char *name, gpointer data,
- assert(vmaw->config_count < VMA_MAX_CONFIGS);
- assert(name);
- assert(data);
-- assert(len);
-
- gchar *basename = g_path_get_basename(name);
- uint32_t name_ptr = allocate_header_string(vmaw, basename);
---
-2.1.4
-
+++ /dev/null
-From d0cbfa6899c33f2a93696666b03d9a343eb81d15 Mon Sep 17 00:00:00 2001
-From: Wolfgang Bumiller <w.bumiller@proxmox.com>
-Date: Tue, 23 Feb 2016 15:48:41 +0100
-Subject: [PATCH 43/55] vma: better driver guessing for bdrv_open
-
-Only use 'raw' when the file actually ends with .raw and
-no protocol has been specified. With protocol pass the
-BDRV_O_PROTOCOL flag to tell bdrv_fill_options() to take it
-into account.
----
- vma.c | 15 +++++++++++----
- 1 file changed, 11 insertions(+), 4 deletions(-)
-
-diff --git a/vma.c b/vma.c
-index cc48013..1c4103f 100644
---- a/vma.c
-+++ b/vma.c
-@@ -301,10 +301,17 @@ static int extract_content(int argc, char **argv)
-
- BlockDriverState *bs = bdrv_new();
-
-- const char *tmp = g_strrstr(devfn, ".");
-- const char *format = (tmp == NULL) ? "raw" : ++tmp;
-- QDict *options = qdict_new();
-- qdict_put(options, "driver", qstring_from_str(format));
-+ size_t devlen = strlen(devfn);
-+ bool protocol = path_has_protocol(devfn);
-+ QDict *options = NULL;
-+ if (devlen > 4 && strcmp(devfn+devlen-4, ".raw") == 0 && !protocol) {
-+ /* explicit raw format */
-+ options = qdict_new();
-+ qdict_put(options, "driver", qstring_from_str("raw"));
-+ } else if (protocol) {
-+ /* tell bdrv_open to honor the protocol */
-+ flags |= BDRV_O_PROTOCOL;
-+ }
-
- if (errp || bdrv_open(&bs, devfn, NULL, options, flags, &errp)) {
- g_error("can't open file %s - %s", devfn,
---
-2.1.4
-
+++ /dev/null
-From b3bf300710e38fd4692f0d761c75f4ba3a7edd7e Mon Sep 17 00:00:00 2001
-From: Wolfgang Bumiller <w.bumiller@proxmox.com>
-Date: Thu, 17 Mar 2016 11:33:37 +0100
-Subject: [PATCH 44/55] block: add the zeroinit block driver filter
-
----
- block/Makefile.objs | 1 +
- block/zeroinit.c | 216 ++++++++++++++++++++++++++++++++++++++++++++++++++++
- 2 files changed, 217 insertions(+)
- create mode 100644 block/zeroinit.c
-
-diff --git a/block/Makefile.objs b/block/Makefile.objs
-index 44a5416..9e2cdbc 100644
---- a/block/Makefile.objs
-+++ b/block/Makefile.objs
-@@ -4,6 +4,7 @@ block-obj-y += qed.o qed-gencb.o qed-l2-cache.o qed-table.o qed-cluster.o
- block-obj-y += qed-check.o
- block-obj-$(CONFIG_VHDX) += vhdx.o vhdx-endian.o vhdx-log.o
- block-obj-y += quorum.o
-+block-obj-y += zeroinit.o
- block-obj-y += parallels.o blkdebug.o blkverify.o blkreplay.o
- block-obj-y += block-backend.o snapshot.o qapi.o
- block-obj-$(CONFIG_WIN32) += raw-win32.o win32-aio.o
-diff --git a/block/zeroinit.c b/block/zeroinit.c
-new file mode 100644
-index 0000000..04a9bd0
---- /dev/null
-+++ b/block/zeroinit.c
-@@ -0,0 +1,216 @@
-+/*
-+ * Filter to fake a zero-initialized block device.
-+ *
-+ * Copyright (c) 2016 Wolfgang Bumiller <w.bumiller@proxmox.com>
-+ * Copyright (c) 2016 Proxmox Server Solutions GmbH
-+ *
-+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
-+ * See the COPYING file in the top-level directory.
-+ */
-+
-+#include "block/block_int.h"
-+#include "qapi/qmp/qdict.h"
-+#include "qapi/qmp/qstring.h"
-+
-+typedef struct {
-+ bool has_zero_init;
-+ int64_t extents;
-+} BDRVZeroinitState;
-+
-+/* Valid blkverify filenames look like blkverify:path/to/raw_image:path/to/image */
-+static void zeroinit_parse_filename(const char *filename, QDict *options,
-+ Error **errp)
-+{
-+ QString *raw_path;
-+
-+ /* Parse the blkverify: prefix */
-+ if (!strstart(filename, "zeroinit:", &filename)) {
-+ /* There was no prefix; therefore, all options have to be already
-+ present in the QDict (except for the filename) */
-+ return;
-+ }
-+
-+ raw_path = qstring_from_str(filename);
-+ qdict_put(options, "x-next", raw_path);
-+}
-+
-+static QemuOptsList runtime_opts = {
-+ .name = "zeroinit",
-+ .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
-+ .desc = {
-+ {
-+ .name = "x-next",
-+ .type = QEMU_OPT_STRING,
-+ .help = "[internal use only, will be removed]",
-+ },
-+ {
-+ .name = "x-zeroinit",
-+ .type = QEMU_OPT_BOOL,
-+ .help = "set has_initialized_zero flag",
-+ },
-+ { /* end of list */ }
-+ },
-+};
-+
-+static int zeroinit_open(BlockDriverState *bs, QDict *options, int flags,
-+ Error **errp)
-+{
-+ BDRVZeroinitState *s = bs->opaque;
-+ QemuOpts *opts;
-+ Error *local_err = NULL;
-+ int ret;
-+
-+ s->extents = 0;
-+
-+ opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
-+ qemu_opts_absorb_qdict(opts, options, &local_err);
-+ if (local_err) {
-+ error_propagate(errp, local_err);
-+ ret = -EINVAL;
-+ goto fail;
-+ }
-+
-+ /* Open the raw file */
-+ bs->file = bdrv_open_child(qemu_opt_get(opts, "x-next"), options, "next",
-+ bs, &child_file, false, &local_err);
-+ if (local_err) {
-+ ret = -EINVAL;
-+ error_propagate(errp, local_err);
-+ goto fail;
-+ }
-+
-+ /* set the options */
-+ s->has_zero_init = qemu_opt_get_bool(opts, "x-zeroinit", true);
-+
-+ ret = 0;
-+fail:
-+ if (ret < 0) {
-+ bdrv_unref_child(bs, bs->file);
-+ }
-+ qemu_opts_del(opts);
-+ return ret;
-+}
-+
-+static void zeroinit_close(BlockDriverState *bs)
-+{
-+ BDRVZeroinitState *s = bs->opaque;
-+ (void)s;
-+}
-+
-+static int64_t zeroinit_getlength(BlockDriverState *bs)
-+{
-+ return bdrv_getlength(bs->file->bs);
-+}
-+
-+static BlockAIOCB *zeroinit_aio_readv(BlockDriverState *bs,
-+ int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
-+ BlockCompletionFunc *cb, void *opaque)
-+{
-+ return bdrv_aio_readv(bs->file->bs, sector_num, qiov, nb_sectors,
-+ cb, opaque);
-+}
-+
-+static int coroutine_fn zeroinit_co_write_zeroes(BlockDriverState *bs, int64_t sector_num,
-+ int nb_sectors, BdrvRequestFlags flags)
-+{
-+ BDRVZeroinitState *s = bs->opaque;
-+ if (sector_num >= s->extents)
-+ return 0;
-+ return bdrv_write_zeroes(bs->file->bs, sector_num, nb_sectors, flags);
-+}
-+
-+static BlockAIOCB *zeroinit_aio_writev(BlockDriverState *bs,
-+ int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
-+ BlockCompletionFunc *cb, void *opaque)
-+{
-+ BDRVZeroinitState *s = bs->opaque;
-+ int64_t extents = sector_num + nb_sectors + 1;
-+ if (extents > s->extents)
-+ s->extents = extents;
-+ return bdrv_aio_writev(bs->file->bs, sector_num, qiov, nb_sectors,
-+ cb, opaque);
-+}
-+
-+static BlockAIOCB *zeroinit_aio_flush(BlockDriverState *bs,
-+ BlockCompletionFunc *cb,
-+ void *opaque)
-+{
-+ return bdrv_aio_flush(bs->file->bs, cb, opaque);
-+}
-+
-+static bool zeroinit_recurse_is_first_non_filter(BlockDriverState *bs,
-+ BlockDriverState *candidate)
-+{
-+ return bdrv_recurse_is_first_non_filter(bs->file->bs, candidate);
-+}
-+
-+static coroutine_fn int zeroinit_co_flush(BlockDriverState *bs)
-+{
-+ return bdrv_co_flush(bs->file->bs);
-+}
-+
-+static int zeroinit_has_zero_init(BlockDriverState *bs)
-+{
-+ BDRVZeroinitState *s = bs->opaque;
-+ return s->has_zero_init;
-+}
-+
-+static int64_t coroutine_fn zeroinit_co_get_block_status(BlockDriverState *bs,
-+ int64_t sector_num,
-+ int nb_sectors, int *pnum)
-+{
-+ return bdrv_get_block_status(bs->file->bs, sector_num, nb_sectors, pnum);
-+}
-+
-+static coroutine_fn BlockAIOCB *zeroinit_aio_discard(BlockDriverState *bs,
-+ int64_t sector_num, int nb_sectors,
-+ BlockCompletionFunc *cb, void *opaque)
-+{
-+ return bdrv_aio_discard(bs->file->bs, sector_num, nb_sectors, cb, opaque);
-+}
-+
-+static int zeroinit_truncate(BlockDriverState *bs, int64_t offset)
-+{
-+ return bdrv_truncate(bs->file->bs, offset);
-+}
-+
-+static int zeroinit_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
-+{
-+ return bdrv_get_info(bs->file->bs, bdi);
-+}
-+
-+static BlockDriver bdrv_zeroinit = {
-+ .format_name = "zeroinit",
-+ .protocol_name = "zeroinit",
-+ .instance_size = sizeof(BDRVZeroinitState),
-+
-+ .bdrv_parse_filename = zeroinit_parse_filename,
-+ .bdrv_file_open = zeroinit_open,
-+ .bdrv_close = zeroinit_close,
-+ .bdrv_getlength = zeroinit_getlength,
-+ .bdrv_co_flush_to_disk = zeroinit_co_flush,
-+
-+ .bdrv_co_write_zeroes = zeroinit_co_write_zeroes,
-+ .bdrv_aio_writev = zeroinit_aio_writev,
-+ .bdrv_aio_readv = zeroinit_aio_readv,
-+ .bdrv_aio_flush = zeroinit_aio_flush,
-+
-+ .is_filter = true,
-+ .bdrv_recurse_is_first_non_filter = zeroinit_recurse_is_first_non_filter,
-+
-+ .bdrv_has_zero_init = zeroinit_has_zero_init,
-+
-+ .bdrv_co_get_block_status = zeroinit_co_get_block_status,
-+
-+ .bdrv_aio_discard = zeroinit_aio_discard,
-+
-+ .bdrv_truncate = zeroinit_truncate,
-+ .bdrv_get_info = zeroinit_get_info,
-+};
-+
-+static void bdrv_zeroinit_init(void)
-+{
-+ bdrv_register(&bdrv_zeroinit);
-+}
-+
-+block_init(bdrv_zeroinit_init);
---
-2.1.4
-
+++ /dev/null
-From 52002e4bb7572662d937de2c20b2110309095f29 Mon Sep 17 00:00:00 2001
-From: Wolfgang Bumiller <w.bumiller@proxmox.com>
-Date: Tue, 12 Apr 2016 13:49:44 +0200
-Subject: [PATCH 45/55] vma: add format option to device mapping
-
-The BDRV_O_PROTOCOL option breaks non-raw protocol devices,
-so we instead now allow the format to be explicitly
-specified from the outside.
-
-In other words we now too deprecate the automatic guessing
-of raw formats, just like qemu already does, and have to
-silence the warnings by passing the drive mapping.
----
- vma.c | 34 +++++++++++++++++++++++++++-------
- 1 file changed, 27 insertions(+), 7 deletions(-)
-
-diff --git a/vma.c b/vma.c
-index 1c4103f..46ae14b 100644
---- a/vma.c
-+++ b/vma.c
-@@ -137,6 +137,7 @@ static int list_content(int argc, char **argv)
- typedef struct RestoreMap {
- char *devname;
- char *path;
-+ char *format;
- bool write_zero;
- } RestoreMap;
-
-@@ -224,13 +225,24 @@ static int extract_content(int argc, char **argv)
- }
- }
-
-+ char *format = NULL;
-+ if (strncmp(line, "format=", sizeof("format=")-1) == 0) {
-+ format = line + sizeof("format=")-1;
-+ char *colon = strchr(format, ':');
-+ if (!colon) {
-+ g_error("read map failed - found only a format ('%s')", inbuf);
-+ }
-+ format = g_strndup(format, colon - format);
-+ line = colon+1;
-+ }
-+
- const char *path;
- bool write_zero;
- if (line[0] == '0' && line[1] == ':') {
-- path = inbuf + 2;
-+ path = line + 2;
- write_zero = false;
- } else if (line[0] == '1' && line[1] == ':') {
-- path = inbuf + 2;
-+ path = line + 2;
- write_zero = true;
- } else {
- g_error("read map failed - parse error ('%s')", inbuf);
-@@ -246,6 +258,7 @@ static int extract_content(int argc, char **argv)
- RestoreMap *map = g_new0(RestoreMap, 1);
- map->devname = g_strdup(devname);
- map->path = g_strdup(path);
-+ map->format = format;
- map->write_zero = write_zero;
-
- g_hash_table_insert(devmap, map->devname, map);
-@@ -270,6 +283,7 @@ static int extract_content(int argc, char **argv)
- g_free(statefn);
- } else if (di) {
- char *devfn = NULL;
-+ const char *format = NULL;
- int flags = BDRV_O_RDWR|BDRV_O_CACHE_WB;
- bool write_zero = true;
-
-@@ -280,6 +294,7 @@ static int extract_content(int argc, char **argv)
- g_error("no device name mapping for %s", di->devname);
- }
- devfn = map->path;
-+ format = map->format;
- write_zero = map->write_zero;
- } else {
- devfn = g_strdup_printf("%s/tmp-disk-%s.raw",
-@@ -302,15 +317,20 @@ static int extract_content(int argc, char **argv)
- BlockDriverState *bs = bdrv_new();
-
- size_t devlen = strlen(devfn);
-- bool protocol = path_has_protocol(devfn);
- QDict *options = NULL;
-- if (devlen > 4 && strcmp(devfn+devlen-4, ".raw") == 0 && !protocol) {
-+ if (format) {
-+ /* explicit format from commandline */
-+ options = qdict_new();
-+ qdict_put(options, "driver", qstring_from_str(format));
-+ } else if ((devlen > 4 && strcmp(devfn+devlen-4, ".raw") == 0) ||
-+ strncmp(devfn, "/dev/", 5) == 0)
-+ {
-+ /* This part is now deprecated for PVE as well (just as qemu
-+ * deprecated not specifying an explicit raw format, too.
-+ */
- /* explicit raw format */
- options = qdict_new();
- qdict_put(options, "driver", qstring_from_str("raw"));
-- } else if (protocol) {
-- /* tell bdrv_open to honor the protocol */
-- flags |= BDRV_O_PROTOCOL;
- }
-
- if (errp || bdrv_open(&bs, devfn, NULL, options, flags, &errp)) {
---
-2.1.4
-
+++ /dev/null
-From 17751f15c2d15782ee24ddcfe991de940dbdc3b0 Mon Sep 17 00:00:00 2001
-From: Thomas Lamprecht <t.lamprecht@proxmox.com>
-Date: Wed, 6 Apr 2016 13:28:16 +0200
-Subject: [PATCH 46/55] pve: cleanup includes all over the place
-
-QEMU wants to get a cleaner include system, adapt to it.
----
- block/zeroinit.c | 3 +++
- savevm-async.c | 4 ++++
- vma-reader.c | 11 +----------
- vma-writer.c | 12 +++---------
- vma.c | 9 +--------
- 5 files changed, 12 insertions(+), 27 deletions(-)
-
-diff --git a/block/zeroinit.c b/block/zeroinit.c
-index 04a9bd0..eb77657 100644
---- a/block/zeroinit.c
-+++ b/block/zeroinit.c
-@@ -8,9 +8,12 @@
- * See the COPYING file in the top-level directory.
- */
-
-+#include "qemu/osdep.h"
-+#include "qapi/error.h"
- #include "block/block_int.h"
- #include "qapi/qmp/qdict.h"
- #include "qapi/qmp/qstring.h"
-+#include "qemu/cutils.h"
-
- typedef struct {
- bool has_zero_init;
-diff --git a/savevm-async.c b/savevm-async.c
-index bb4372c..4d73e66 100644
---- a/savevm-async.c
-+++ b/savevm-async.c
-@@ -1,3 +1,4 @@
-+#include "qemu/osdep.h"
- #include "qemu-common.h"
- #include "qapi/qmp/qerror.h"
- #include "qemu/error-report.h"
-@@ -13,6 +14,9 @@
- #include "qemu/timer.h"
- #include "sysemu/block-backend.h"
- #include "qapi/qmp/qstring.h"
-+#include "qemu/rcu.h"
-+#include "qemu/thread.h"
-+#include "qemu/cutils.h"
-
- /* #define DEBUG_SAVEVM_STATE */
-
-diff --git a/vma-reader.c b/vma-reader.c
-index 5d0d3ea..cb58360 100644
---- a/vma-reader.c
-+++ b/vma-reader.c
-@@ -11,14 +11,7 @@
- *
- */
-
--#include <stdio.h>
--#include <errno.h>
--#include <unistd.h>
--#include <stdio.h>
--#include <string.h>
--#include <sys/types.h>
--#include <sys/stat.h>
--#include <fcntl.h>
-+#include "qemu/osdep.h"
- #include <glib.h>
- #include <uuid/uuid.h>
-
-@@ -28,8 +21,6 @@
- #include "vma.h"
- #include "block/block.h"
-
--#define BITS_PER_LONG (sizeof(unsigned long) * CHAR_BIT)
--
- static unsigned char zero_vma_block[VMA_BLOCK_SIZE];
-
- typedef struct VmaRestoreState {
-diff --git a/vma-writer.c b/vma-writer.c
-index 3cde204..e96fb55 100644
---- a/vma-writer.c
-+++ b/vma-writer.c
-@@ -11,22 +11,16 @@
- *
- */
-
--#include <stdio.h>
--#include <errno.h>
--#include <unistd.h>
--#include <stdio.h>
--#include <string.h>
--#include <sys/types.h>
--#include <sys/stat.h>
--#include <fcntl.h>
-+#include "qemu/osdep.h"
- #include <glib.h>
- #include <uuid/uuid.h>
-
--#include "qemu-common.h"
- #include "vma.h"
- #include "block/block.h"
- #include "monitor/monitor.h"
- #include "qemu/main-loop.h"
-+#include "qemu/coroutine.h"
-+#include "qemu/cutils.h"
-
- #define DEBUG_VMA 0
-
-diff --git a/vma.c b/vma.c
-index 46ae14b..c3db633 100644
---- a/vma.c
-+++ b/vma.c
-@@ -11,14 +11,7 @@
- *
- */
-
--#include <stdio.h>
--#include <errno.h>
--#include <unistd.h>
--#include <stdio.h>
--#include <string.h>
--#include <sys/types.h>
--#include <sys/stat.h>
--#include <fcntl.h>
-+#include "qemu/osdep.h"
- #include <glib.h>
-
- #include "vma.h"
---
-2.1.4
-
+++ /dev/null
-From e698a23ad095fcaf0d72734a480d5bc0202a6f75 Mon Sep 17 00:00:00 2001
-From: Thomas Lamprecht <t.lamprecht@proxmox.com>
-Date: Wed, 6 Apr 2016 14:15:58 +0200
-Subject: [PATCH 47/55] zeroinit: bdrv_get_block_status got a new param
-
-see: 67a0fd2a9bca204d2b39f910a97c7137636a0715
----
- block/zeroinit.c | 5 +++--
- 1 file changed, 3 insertions(+), 2 deletions(-)
-
-diff --git a/block/zeroinit.c b/block/zeroinit.c
-index eb77657..ad52847 100644
---- a/block/zeroinit.c
-+++ b/block/zeroinit.c
-@@ -160,9 +160,10 @@ static int zeroinit_has_zero_init(BlockDriverState *bs)
-
- static int64_t coroutine_fn zeroinit_co_get_block_status(BlockDriverState *bs,
- int64_t sector_num,
-- int nb_sectors, int *pnum)
-+ int nb_sectors, int *pnum,
-+ BlockDriverState **file)
- {
-- return bdrv_get_block_status(bs->file->bs, sector_num, nb_sectors, pnum);
-+ return bdrv_get_block_status(bs->file->bs, sector_num, nb_sectors, pnum, file);
- }
-
- static coroutine_fn BlockAIOCB *zeroinit_aio_discard(BlockDriverState *bs,
---
-2.1.4
-
+++ /dev/null
-From 9d30e60ad2a6a450c282c6f2df4432d8b9d84b05 Mon Sep 17 00:00:00 2001
-From: Thomas Lamprecht <t.lamprecht@proxmox.com>
-Date: Wed, 6 Apr 2016 14:24:28 +0200
-Subject: [PATCH 48/55] BDRV_O_CACHE_WB was removed
-
-see: 61de4c680846167e01d7ba42bf787f8d1d80bf5e
----
- blockdev.c | 2 +-
- savevm-async.c | 4 ++--
- vma.c | 4 ++--
- 3 files changed, 5 insertions(+), 5 deletions(-)
-
-diff --git a/blockdev.c b/blockdev.c
-index 96fc30b..9330836 100644
---- a/blockdev.c
-+++ b/blockdev.c
-@@ -3365,7 +3365,7 @@ UuidInfo *qmp_backup(const char *backup_file, bool has_format,
- const char *devname = bdrv_get_device_name(di->bs);
- snprintf(di->targetfile, PATH_MAX, "%s/%s.raw", backup_dir, devname);
-
-- int flags = BDRV_O_RDWR|BDRV_O_CACHE_WB;
-+ int flags = BDRV_O_RDWR;
- bdrv_img_create(di->targetfile, "raw", NULL, NULL, NULL,
- di->size, flags, &local_err, false);
- if (local_err) {
-diff --git a/savevm-async.c b/savevm-async.c
-index 4d73e66..2c41b17 100644
---- a/savevm-async.c
-+++ b/savevm-async.c
-@@ -253,7 +253,7 @@ void qmp_savevm_start(bool has_statefile, const char *statefile, Error **errp)
- {
- Error *local_err = NULL;
-
-- int bdrv_oflags = BDRV_O_CACHE_WB | BDRV_O_RDWR;
-+ int bdrv_oflags = BDRV_O_RDWR;
- int ret;
-
- if (snap_state.state != SAVE_STATE_DONE) {
-@@ -485,7 +485,7 @@ int load_state_from_blockdev(const char *filename)
- int ret;
-
- bs = bdrv_new();
-- ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_CACHE_WB, &local_err);
-+ ret = bdrv_open(&bs, filename, NULL, NULL, 0, &local_err);
- error_setg(&blocker, "block device is in use by load state");
- bdrv_op_block_all(bs, blocker);
-
-diff --git a/vma.c b/vma.c
-index c3db633..c1407d3 100644
---- a/vma.c
-+++ b/vma.c
-@@ -277,7 +277,7 @@ static int extract_content(int argc, char **argv)
- } else if (di) {
- char *devfn = NULL;
- const char *format = NULL;
-- int flags = BDRV_O_RDWR|BDRV_O_CACHE_WB;
-+ int flags = BDRV_O_RDWR;
- bool write_zero = true;
-
- if (readmap) {
-@@ -558,7 +558,7 @@ static int create_archive(int argc, char **argv)
- Error *errp = NULL;
- BlockDriverState *bs = bdrv_new();
-
-- res = bdrv_open(&bs, path, NULL, NULL, BDRV_O_CACHE_WB, &errp);
-+ res = bdrv_open(&bs, path, NULL, NULL, 0, &errp);
- if (res < 0) {
- unlink(archivename);
- g_error("bdrv_open '%s' failed - %s", path, error_get_pretty(errp));
---
-2.1.4
-
+++ /dev/null
-From b194d9117c084d148a3e4fee5c2b113faf799968 Mon Sep 17 00:00:00 2001
-From: Thomas Lamprecht <t.lamprecht@proxmox.com>
-Date: Wed, 6 Apr 2016 16:43:38 +0200
-Subject: [PATCH 49/55] backup: bdrv_set_enable_write_cache is no more
-
-see: 09cf9db1bcd60d9889b774925ba7058286d35412
-
-this reverts a hunk of our commit: backup: modify job api
----
- block/backup.c | 9 +++------
- 1 file changed, 3 insertions(+), 6 deletions(-)
-
-diff --git a/block/backup.c b/block/backup.c
-index c38b520..f0e647d 100644
---- a/block/backup.c
-+++ b/block/backup.c
-@@ -414,12 +414,9 @@ static void coroutine_fn backup_run(void *opaque)
-
- job->done_bitmap = bitmap_new(end);
-
-- if (target) {
-- bdrv_set_enable_write_cache(target, true);
-- if (target->blk) {
-- blk_set_on_error(target->blk, on_target_error, on_target_error);
-- blk_iostatus_enable(target->blk);
-- }
-+ if (target && target->blk) {
-+ blk_set_on_error(target->blk, on_target_error, on_target_error);
-+ blk_iostatus_enable(target->blk);
- }
-
- bdrv_add_before_write_notifier(bs, &before_write);
---
-2.1.4
-
+++ /dev/null
-From 9be0512acac31822307c8ff8bc336fee659eeeb0 Mon Sep 17 00:00:00 2001
-From: Thomas Lamprecht <t.lamprecht@proxmox.com>
-Date: Wed, 6 Apr 2016 16:45:15 +0200
-Subject: [PATCH 50/55] fix possible unitialised return value
-
----
- migration/savevm.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/migration/savevm.c b/migration/savevm.c
-index d6560f5..7b2d654 100644
---- a/migration/savevm.c
-+++ b/migration/savevm.c
-@@ -1037,7 +1037,7 @@ int qemu_savevm_state_complete_precopy(QEMUFile *f, bool iterable_only)
- QJSON *vmdesc;
- int vmdesc_len;
- SaveStateEntry *se;
-- int ret;
-+ int ret = -1;
- bool in_postcopy = migration_in_postcopy(migrate_get_current());
-
- trace_savevm_state_complete_precopy();
---
-2.1.4
-
+++ /dev/null
-From 23a15e3819093d32fbf15709bbe1fe9e3869b171 Mon Sep 17 00:00:00 2001
-From: Thomas Lamprecht <t.lamprecht@proxmox.com>
-Date: Wed, 6 Apr 2016 16:46:11 +0200
-Subject: [PATCH 51/55] net: NET_CLIENT_OPTIONS_KIND_MAX changed
-
-s/NET_CLIENT_OPTIONS_KIND_MAX/NET_CLIENT_OPTIONS_KIND__MAX/
----
- net/net.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/net/net.c b/net/net.c
-index 0e3f231..2b9de86 100644
---- a/net/net.c
-+++ b/net/net.c
-@@ -1333,7 +1333,7 @@ int64_t qmp_get_link_status(const char *name, Error **errp)
- bool ret;
-
- queues = qemu_find_net_clients_except(name, ncs,
-- NET_CLIENT_OPTIONS_KIND_MAX,
-+ NET_CLIENT_OPTIONS_KIND__MAX,
- MAX_QUEUE_NUM);
-
- if (queues == 0) {
---
-2.1.4
-
+++ /dev/null
-From 24023a5773869ccac7c4f6d5065c7937396e2df9 Mon Sep 17 00:00:00 2001
-From: Thomas Lamprecht <t.lamprecht@proxmox.com>
-Date: Wed, 6 Apr 2016 16:47:54 +0200
-Subject: [PATCH 52/55] vnc: refactor to QIOChannelSocket
-
----
- ui/vnc-auth-vencrypt.c | 31 ++++++++++++++++---------------
- 1 file changed, 16 insertions(+), 15 deletions(-)
-
-diff --git a/ui/vnc-auth-vencrypt.c b/ui/vnc-auth-vencrypt.c
-index d11f1df..a529520 100644
---- a/ui/vnc-auth-vencrypt.c
-+++ b/ui/vnc-auth-vencrypt.c
-@@ -28,27 +28,23 @@
- #include "vnc.h"
- #include "qapi/error.h"
- #include "qemu/main-loop.h"
--#include "qemu/sockets.h"
-+#include "io/channel-socket.h"
-
- static int protocol_client_auth_plain(VncState *vs, uint8_t *data, size_t len)
- {
-- const char *err = NULL;
-+ Error *err = NULL;
- char username[256];
- char passwd[512];
-
-- char clientip[256];
-- clientip[0] = 0;
-- struct sockaddr_in client;
-- socklen_t addrlen = sizeof(client);
-- if (getpeername(vs->csock, &client, &addrlen) == 0) {
-- inet_ntop(client.sin_family, &client.sin_addr,
-- clientip, sizeof(clientip));
-+ SocketAddress *clientip = qio_channel_socket_get_remote_address(vs->sioc, &err);
-+ if (err) {
-+ goto err;
- }
-
- if ((len != (vs->username_len + vs->password_len)) ||
- (vs->username_len >= (sizeof(username)-1)) ||
- (vs->password_len >= (sizeof(passwd)-1)) ) {
-- err = "Got unexpected data length";
-+ error_setg(&err, "Got unexpected data length");
- goto err;
- }
-
-@@ -59,26 +55,31 @@ static int protocol_client_auth_plain(VncState *vs, uint8_t *data, size_t len)
-
- VNC_DEBUG("AUTH PLAIN username: %s pw: %s\n", username, passwd);
-
-- if (pve_auth_verify(clientip, username, passwd) == 0) {
-+ if (pve_auth_verify(clientip->u.inet.data->host, username, passwd) == 0) {
- vnc_write_u32(vs, 0); /* Accept auth completion */
- start_client_init(vs);
-+ qapi_free_SocketAddress(clientip);
- return 0;
- }
-
-- err = "Authentication failed";
-+ error_setg(&err, "Authentication failed");
- err:
- if (err) {
-- VNC_DEBUG("AUTH PLAIN ERROR: %s\n", err);
-+ const char *err_msg = error_get_pretty(err);
-+ VNC_DEBUG("AUTH PLAIN ERROR: %s\n", err_msg);
- vnc_write_u32(vs, 1); /* Reject auth */
- if (vs->minor >= 8) {
-- int elen = strlen(err);
-+ int elen = strlen(err_msg);
- vnc_write_u32(vs, elen);
-- vnc_write(vs, err, elen);
-+ vnc_write(vs, err_msg, elen);
- }
-+ error_free(err);
- }
- vnc_flush(vs);
- vnc_client_error(vs);
-
-+ qapi_free_SocketAddress(clientip);
-+
- return 0;
-
- }
---
-2.1.4
-
+++ /dev/null
-From 817ed44ab0ada0b1fcfcc27763b6e2db546687d6 Mon Sep 17 00:00:00 2001
-From: Thomas Lamprecht <t.lamprecht@proxmox.com>
-Date: Fri, 1 Jul 2016 15:47:29 +0200
-Subject: [PATCH 53/55] vma: use BlockBackend on extract
-
-As we else rely on bdrv_close_all() do clean up, which was rewritten
-in ca9bd24cf1d53775169ba9adc17e265554d1afed and fails on "dangling"
-BDS pointers, such a pointer exists with *bs.
-Use the BlockBackend to get our BDS and just unref the BlockBackend
-when done, it handles the rest for us.
-
-The other two calls to bdrv_close_all() happen in verify_content()
-and dump_config(), both do not have a BDS so no need to change here.
----
- vma.c | 13 ++++++++++---
- 1 file changed, 10 insertions(+), 3 deletions(-)
-
-diff --git a/vma.c b/vma.c
-index c1407d3..0716a45 100644
---- a/vma.c
-+++ b/vma.c
-@@ -19,6 +19,7 @@
- #include "qemu/error-report.h"
- #include "qemu/main-loop.h"
- #include "sysemu/char.h" /* qstring_from_str */
-+#include "sysemu/block-backend.h"
-
- static void help(void)
- {
-@@ -263,6 +264,8 @@ static int extract_content(int argc, char **argv)
- int vmstate_fd = -1;
- guint8 vmstate_stream = 0;
-
-+ BlockBackend *blk = NULL;
-+
- for (i = 1; i < 255; i++) {
- VmaDeviceInfo *di = vma_reader_get_device_info(vmar, i);
- if (di && (strcmp(di->devname, "vmstate") == 0)) {
-@@ -307,8 +310,6 @@ static int extract_content(int argc, char **argv)
- write_zero = false;
- }
-
-- BlockDriverState *bs = bdrv_new();
--
- size_t devlen = strlen(devfn);
- QDict *options = NULL;
- if (format) {
-@@ -326,10 +327,14 @@ static int extract_content(int argc, char **argv)
- qdict_put(options, "driver", qstring_from_str("raw"));
- }
-
-- if (errp || bdrv_open(&bs, devfn, NULL, options, flags, &errp)) {
-+
-+ if (errp || !(blk = blk_new_open(devfn, NULL, options, flags, &errp))) {
- g_error("can't open file %s - %s", devfn,
- error_get_pretty(errp));
- }
-+
-+ BlockDriverState *bs = blk_bs(blk);
-+
- if (vma_reader_register_bs(vmar, i, bs, write_zero, &errp) < 0) {
- g_error("%s", error_get_pretty(errp));
- }
-@@ -362,6 +367,8 @@ static int extract_content(int argc, char **argv)
-
- vma_reader_destroy(vmar);
-
-+ blk_unref(blk);
-+
- bdrv_close_all();
-
- return ret;
---
-2.1.4
-
+++ /dev/null
-From 75eead3024d641e0ff2fa7a901a41781101cf87c Mon Sep 17 00:00:00 2001
-From: Alexandre Derumier <aderumier@odiso.com>
-Date: Tue, 26 Jul 2016 16:51:00 +0200
-Subject: [PATCH 54/55] rbd: disable rbd_cache_writethrough_until_flush with
- cache=unsafe
-
-Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
----
- block/rbd.c | 4 ++++
- 1 file changed, 4 insertions(+)
-
-diff --git a/block/rbd.c b/block/rbd.c
-index 5bc5b32..5656028 100644
---- a/block/rbd.c
-+++ b/block/rbd.c
-@@ -544,6 +544,10 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
- rados_conf_set(s->cluster, "rbd_cache", "true");
- }
-
-+ if (flags & BDRV_O_NO_FLUSH) {
-+ rados_conf_set(s->cluster, "rbd_cache_writethrough_until_flush", "false");
-+ }
-+
- r = rados_connect(s->cluster);
- if (r < 0) {
- error_setg(errp, "error connecting");
---
-2.1.4
-
+++ /dev/null
-From 6775cc94517696abe12bbae601395b7a16e06ba0 Mon Sep 17 00:00:00 2001
-From: Wolfgang Bumiller <w.bumiller@proxmox.com>
-Date: Mon, 1 Aug 2016 10:52:46 +0200
-Subject: [PATCH 55/55] enable cache=unsafe for vma extract_content and
- qmp_savevm_start
-
-We don't send any flush here, so we need to open with cache=unsafe.
-
-Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
-For-patch: BDRV_O_CACHE_WB was removed
----
- savevm-async.c | 2 +-
- vma.c | 2 +-
- 2 files changed, 2 insertions(+), 2 deletions(-)
-
-diff --git a/savevm-async.c b/savevm-async.c
-index 2c41b17..6a2266c 100644
---- a/savevm-async.c
-+++ b/savevm-async.c
-@@ -253,7 +253,7 @@ void qmp_savevm_start(bool has_statefile, const char *statefile, Error **errp)
- {
- Error *local_err = NULL;
-
-- int bdrv_oflags = BDRV_O_RDWR;
-+ int bdrv_oflags = BDRV_O_RDWR | BDRV_O_NO_FLUSH;
- int ret;
-
- if (snap_state.state != SAVE_STATE_DONE) {
-diff --git a/vma.c b/vma.c
-index 0716a45..4d2d5ad 100644
---- a/vma.c
-+++ b/vma.c
-@@ -280,7 +280,7 @@ static int extract_content(int argc, char **argv)
- } else if (di) {
- char *devfn = NULL;
- const char *format = NULL;
-- int flags = BDRV_O_RDWR;
-+ int flags = BDRV_O_RDWR | BDRV_O_NO_FLUSH;
- bool write_zero = true;
-
- if (readmap) {
---
-2.1.4
-
+++ /dev/null
-From 61164c3693415d6dce39a7b0cbde43b184081243 Mon Sep 17 00:00:00 2001
-From: Alexandre Derumier <aderumier@odiso.com>
-Date: Tue, 13 Sep 2016 01:57:56 +0200
-Subject: [PATCH] qmp_snapshot_drive: add aiocontext
-
-Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
----
- savevm-async.c | 15 +++++++++++----
- 1 file changed, 11 insertions(+), 4 deletions(-)
-
-diff --git a/savevm-async.c b/savevm-async.c
-index 6a2266c..308ac61 100644
---- a/savevm-async.c
-+++ b/savevm-async.c
-@@ -345,6 +345,7 @@ void qmp_snapshot_drive(const char *device, const char *name, Error **errp)
- BlockBackend *blk;
- BlockDriverState *bs;
- QEMUSnapshotInfo sn1, *sn = &sn1;
-+ AioContext *aio_context;
- int ret;
- #ifdef _WIN32
- struct _timeb tb;
-@@ -371,20 +372,23 @@ void qmp_snapshot_drive(const char *device, const char *name, Error **errp)
- return;
- }
-
-+ aio_context = bdrv_get_aio_context(bs);
-+ aio_context_acquire(aio_context);
-+
- if (bdrv_is_read_only(bs)) {
- error_setg(errp, "Node '%s' is read only", device);
-- return;
-+ goto out;
- }
-
- if (!bdrv_can_snapshot(bs)) {
- error_setg(errp, QERR_UNSUPPORTED);
-- return;
-+ goto out;
- }
-
- if (bdrv_snapshot_find(bs, sn, name) >= 0) {
- error_set(errp, ERROR_CLASS_GENERIC_ERROR,
- "snapshot '%s' already exists", name);
-- return;
-+ goto out;
- }
-
- sn = &sn1;
-@@ -409,8 +413,11 @@ void qmp_snapshot_drive(const char *device, const char *name, Error **errp)
- if (ret < 0) {
- error_set(errp, ERROR_CLASS_GENERIC_ERROR,
- "Error while creating snapshot on '%s'\n", device);
-- return;
-+ goto out;
- }
-+
-+out:
-+ aio_context_release(aio_context);
- }
-
- void qmp_delete_drive_snapshot(const char *device, const char *name,
---
-2.1.4
-
pve/0011-introduce-new-vma-archive-format.patch
pve/0012-vma-add-verify-command.patch
pve/0013-vma-add-config-command-to-dump-the-config.patch
-pve/0014-vma-restore-tolerate-a-size-difference-up-to-4M.patch
-pve/0015-backup-modify-job-api.patch
-pve/0016-backup-add-pve-monitor-commands.patch
-pve/0017-backup-vma-add-dir-format.patch
-pve/0018-backup-do-not-return-errors-in-dump-callback.patch
-pve/0019-backup-vma-correctly-propagate-error.patch
-pve/0020-backup-vma-remove-async-queue.patch
-pve/0021-backup-vma-run-flush-inside-coroutine.patch
-pve/0022-backup-do-not-use-bdrv_drain_all.patch
-pve/0023-internal-snapshot-async.patch
-pve/0024-backup-vma-allow-empty-backups.patch
-pve/0025-backup-vma-add-BlockDriver-to-bdrv_open-in-extract_c.patch
-pve/0026-glusterfs-daemonize.patch
-pve/0027-gluster-possiblity-to-specify-a-secondary-server.patch
-pve/0028-qmp-add-get_link_status.patch
-pve/0029-smm_available-false.patch
-pve/0030-use-whitespace-between-VERSION-and-PKGVERSION.patch
-pve/0031-vma-add-firewall.patch
-pve/0032-vma-writer-aio_set_fd_handler-update.patch
-pve/0033-vma-bdrv_open-dropped-the-drv-parameter.patch
-pve/0034-blockdev-bdrv_open-dropped-the-drv-parameter.patch
-pve/0035-blockdev-backup_start-now-takes-a-BlockJobTxn.patch
-pve/0036-savevm-async-migration-and-bdrv_open-update.patch
-pve/0037-qapi-qmp_marshal_-renames-for-pve-monitor-commands.patch
-pve/0038-qapi-qmp_mashal_-renames-for-async-snapshot.patch
-pve/0039-qapi-qmp_mashal_-renames-for-get_link_status.patch
-pve/0040-vnc-make-x509-imply-tls-again.patch
-pve/0041-PVE-VNC-authentication.patch
-pve/0042-vma-writer-don-t-bail-out-on-zero-length-files.patch
-pve/0043-vma-better-driver-guessing-for-bdrv_open.patch
-pve/0044-block-add-the-zeroinit-block-driver-filter.patch
-pve/0045-vma-add-format-option-to-device-mapping.patch
-pve/0046-pve-cleanup-includes-all-over-the-place.patch
-pve/0047-zeroinit-bdrv_get_block_status-got-a-new-param.patch
-pve/0048-BDRV_O_CACHE_WB-was-removed.patch
-pve/0049-backup-bdrv_set_enable_write_cache-is-no-more.patch
-pve/0050-fix-possible-unitialised-return-value.patch
-pve/0051-net-NET_CLIENT_OPTIONS_KIND_MAX-changed.patch
-pve/0052-vnc-refactor-to-QIOChannelSocket.patch
-pve/0053-vma-use-BlockBackend-on-extract.patch
-pve/0054-rbd-disable-rbd_cache_writethrough_until_flush-with-.patch
-pve/0055-enable-cache-unsafe-for-vma-extract_content-and-qmp_.patch
-pve/0056-qmp_snapshot_drive-add-aiocontext.patch
+pve/0014-backup-modify-job-api.patch
+pve/0015-backup-add-pve-monitor-commands.patch
+pve/0016-backup-vma-add-dir-format.patch
+pve/0017-backup-do-not-return-errors-in-dump-callback.patch
+pve/0018-backup-vma-correctly-propagate-error.patch
+pve/0019-backup-vma-remove-async-queue.patch
+pve/0020-backup-vma-run-flush-inside-coroutine.patch
+pve/0021-backup-do-not-use-bdrv_drain_all.patch
+pve/0022-internal-snapshot-async.patch
+pve/0023-backup-vma-allow-empty-backups.patch
+pve/0024-qmp-add-get_link_status.patch
+pve/0025-smm_available-false.patch
+pve/0026-use-whitespace-between-VERSION-and-PKGVERSION.patch
+pve/0027-vma-add-firewall.patch
+pve/0028-savevm-async-migration-and-bdrv_open-update.patch
+pve/0029-vnc-make-x509-imply-tls-again.patch
+pve/0030-PVE-VNC-authentication.patch
+pve/0031-vma-writer-don-t-bail-out-on-zero-length-files.patch
+pve/0032-vma-better-driver-guessing-for-bdrv_open.patch
+pve/0033-block-add-the-zeroinit-block-driver-filter.patch
+pve/0034-vma-add-format-option-to-device-mapping.patch
+pve/0035-fix-possible-unitialised-return-value.patch
+pve/0036-vnc-refactor-to-QIOChannelSocket.patch
+pve/0037-vma-use-BlockBackend-on-extract.patch
+pve/0038-vma-byte-based-write-calls.patch
+pve/0039-rbd-disable-rbd_cache_writethrough_until_flush-with-.patch
+pve/0040-enable-cache-unsafe-for-vma-extract_content-and-qmp_.patch
+pve/0041-savevm-async-updates.patch
+pve/0042-qmp_snapshot_drive-add-aiocontext.patch
#see https://bugs.launchpad.net/qemu/+bug/1488363?comments=all
+extra/x86-lapic-Load-LAPIC-state-at-post_load.patch
extra/0001-Revert-target-i386-disable-LINT0-after-reset.patch
-extra/0002-scsi-esp-fix-migration.patch
-extra/CVE-2016-6490-virtio-check-vring-descriptor-buffer-length.patch
-extra/0002-net-vmxnet3-check-for-device_active-before-write.patch
-extra/0005-net-vmxnet-initialise-local-tx-descriptor.patch
-extra/0009-net-limit-allocation-in-nc_sendv_compat.patch
+extra/0002-net-vmxnet-initialise-local-tx-descriptor.patch
+extra/0003-net-limit-allocation-in-nc_sendv_compat.patch
+extra/CVE-2016-7155-scsi-check-page-count-while-initialising-descriptor-.patch
extra/CVE-2016-7156-scsi-pvscsi-avoid-infinite-loop-while-building-SG-li.patch
+extra/CVE-2016-7157-scsi-mptconfig-fix-an-assert-expression.patch
extra/CVE-2016-7170-vmsvga-correct-bitmap-and-pixmap-size-checks.patch
-extra/x86-lapic-Load-LAPIC-state-at-post_load.patch
-extra/CVE-2016-7161-hw-net-Fix-a-heap-overflow-in-xlnx.xps-ethernetlite.patch
+extra/CVE-2016-7421-scsi-pvscsi-limit-process-IO-loop-to-ring-size.patch
extra/CVE-2016-7422-virtio-add-check-for-descriptor-s-mapped-address.patch
+extra/CVE-2016-7423-scsi-mptsas-use-g_new0-to-allocate-MPTSASRequest-obj.patch
+extra/CVE-2016-7466-usb-xhci-fix-memory-leak-in-usb_xhci_exit.patch
extra/CVE-2016-7907-net-imx-limit-buffer-descriptor-count.patch
extra/CVE-2016-7908-net-mcf-limit-buffer-descriptor-count.patch
extra/CVE-2016-7909-net-pcnet-check-rx-tx-descriptor-ring-length.patch
-extra/CVE-2016-7466-usb-xhci-fix-memory-leak-in-usb_xhci_exit.patch
+extra/CVE-2016-7994-virtio-gpu-fix-memory-leak-in-virtio_gpu_resource_cr.patch
+extra/CVE-2016-7995-usb-ehci-fix-memory-leak-in-ehci_process_itd.patch
mv $(destdir)/usr/share/man/man1/qemu.1 $(destdir)/usr/share/man/man1/kvm.1
# Install the userspace utilities
- install -m 0755 scripts/kvm/kvm_stat $(destdir)/usr/bin/
install -s -m 0755 vma $(destdir)/usr/bin/
install -D -m 0755 $(CURDIR)/debian/kvm-ifup $(destdir)/etc/kvm/kvm-ifup