]> git.proxmox.com Git - pve-qemu.git/commitdiff
bump version to 2.11.1-1
authorWolfgang Bumiller <w.bumiller@proxmox.com>
Thu, 22 Feb 2018 11:34:57 +0000 (12:34 +0100)
committerWolfgang Bumiller <w.bumiller@proxmox.com>
Thu, 22 Feb 2018 11:40:28 +0000 (12:40 +0100)
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
112 files changed:
Makefile
debian/changelog
debian/patches/extra/0001-Revert-target-i386-disable-LINT0-after-reset.patch
debian/patches/extra/0002-ratelimit-don-t-align-wait-time-with-slices.patch [new file with mode: 0644]
debian/patches/extra/0002-virtio-serial-fix-segfault-on-disconnect.patch [deleted file]
debian/patches/extra/0003-megasas-always-store-SCSIRequest-into-MegasasCmd.patch [deleted file]
debian/patches/extra/0004-slirp-check-len-against-dhcp-options-array-end.patch [deleted file]
debian/patches/extra/0005-IDE-Do-not-flush-empty-CDROM-drives.patch [deleted file]
debian/patches/extra/0006-bitmap-add-bitmap_copy_and_clear_atomic.patch [deleted file]
debian/patches/extra/0007-memory-add-support-getting-and-using-a-dirty-bitmap-.patch [deleted file]
debian/patches/extra/0008-vga-add-vga_scanline_invalidated-helper.patch [deleted file]
debian/patches/extra/0009-vga-make-display-updates-thread-safe.patch [deleted file]
debian/patches/extra/0010-vga-fix-display-update-region-calculation.patch [deleted file]
debian/patches/extra/0011-vga-fix-display-update-region-calculation-split-scre.patch [deleted file]
debian/patches/extra/0012-vga-stop-passing-pointers-to-vga_draw_line-functions.patch [deleted file]
debian/patches/extra/0013-multiboot-validate-multiboot-header-address-values.patch [deleted file]
debian/patches/extra/0014-virtio-fix-descriptor-counting-in-virtqueue_pop.patch [deleted file]
debian/patches/extra/0015-nbd-server-CVE-2017-15119-Reject-options-larger-than.patch [deleted file]
debian/patches/extra/0016-vga-migration-Update-memory-map-in-post_load.patch [deleted file]
debian/patches/extra/0017-vga-drop-line_offset-variable.patch [deleted file]
debian/patches/extra/0018-vga-handle-cirrus-vbe-mode-wraparounds.patch [deleted file]
debian/patches/extra/0019-vga-add-ram_addr_t-cast.patch [deleted file]
debian/patches/extra/0020-vga-fix-region-checks-in-wraparound-case.patch [deleted file]
debian/patches/extra/0021-io-monitor-encoutput-buffer-size-from-websocket-GSou.patch [deleted file]
debian/patches/extra/0022-9pfs-use-g_malloc0-to-allocate-space-for-xattr.patch [deleted file]
debian/patches/extra/0023-cirrus-fix-oob-access-in-mode4and5-write-functions.patch [deleted file]
debian/patches/extra/0024-virtio-check-VirtQueue-Vring-object-is-set.patch [deleted file]
debian/patches/extra/0025-block-gluster-glfs_lseek-workaround.patch [deleted file]
debian/patches/extra/0026-gluster-add-support-for-PREALLOC_MODE_FALLOC.patch [deleted file]
debian/patches/extra/0027-target-i386-Use-host_vendor_fms-in-max_x86_cpu_initf.patch [deleted file]
debian/patches/extra/0028-target-i386-Define-CPUID_MODEL_ID_SZ-macro.patch [deleted file]
debian/patches/extra/0029-target-i386-Don-t-use-x86_cpu_load_def-on-max-CPU-mo.patch [deleted file]
debian/patches/extra/0030-i386-Change-X86CPUDefinition-model_id-to-const-char.patch [deleted file]
debian/patches/extra/0031-i386-Add-support-for-SPEC_CTRL-MSR.patch [deleted file]
debian/patches/extra/0032-i386-Add-spec-ctrl-CPUID-bit.patch [deleted file]
debian/patches/extra/0033-i386-Add-FEAT_8000_0008_EBX-CPUID-feature-word.patch [deleted file]
debian/patches/extra/0034-i386-Add-new-IBRS-versions-of-Intel-CPU-models.patch [deleted file]
debian/patches/extra/0035-ratelimit-don-t-align-wait-time-with-slices.patch [deleted file]
debian/patches/extra/0036-nbd-make-it-thread-safe-fix-qcow2-over-nbd.patch [deleted file]
debian/patches/extra/0037-nbd-strict-nbd_wr_syncv.patch [deleted file]
debian/patches/extra/0038-nbd-read_sync-and-friends-return-0-on-success.patch [deleted file]
debian/patches/extra/0039-nbd-make-nbd_drop-public.patch [deleted file]
debian/patches/extra/0040-nbd-server-get-rid-of-nbd_negotiate_read-and-friends.patch [deleted file]
debian/patches/extra/0041-nbd-client-Fix-regression-when-server-sends-garbage.patch [deleted file]
debian/patches/extra/0042-fix-build-failure-in-nbd_read_reply_entry.patch [deleted file]
debian/patches/extra/0043-nbd-client-avoid-spurious-qio_channel_yield-re-entry.patch [deleted file]
debian/patches/extra/0044-nbd-client-avoid-read_reply_co-entry-if-send-failed.patch [deleted file]
debian/patches/extra/0045-qemu-iotests-improve-nbd-fault-injector.py-startup-p.patch [deleted file]
debian/patches/extra/0046-qemu-iotests-test-NBD-over-UNIX-domain-sockets-in-08.patch [deleted file]
debian/patches/extra/0047-block-nbd-client-nbd_co_send_request-fix-return-code.patch [deleted file]
debian/patches/extra/0048-target-i386-cpu-Add-new-EPYC-CPU-model.patch [deleted file]
debian/patches/extra/0049-i386-Add-EPYC-IBPB-CPU-model.patch [deleted file]
debian/patches/pve/0001-block-file-change-locking-default-to-off.patch [new file with mode: 0644]
debian/patches/pve/0001-fr-ca-keymap-corrections.patch [deleted file]
debian/patches/pve/0002-Adjust-network-script-path-to-etc-kvm.patch
debian/patches/pve/0003-qemu-img-return-success-on-info-without-snapshots.patch
debian/patches/pve/0004-use-kvm-by-default.patch
debian/patches/pve/0005-virtio-balloon-fix-query.patch
debian/patches/pve/0006-set-the-CPU-model-to-kvm64-32-instead-of-qemu64-32.patch
debian/patches/pve/0007-qapi-modify-query-machines.patch
debian/patches/pve/0008-qapi-modify-spice-query.patch
debian/patches/pve/0009-ui-spice-default-to-pve-certs-unless-otherwise-speci.patch
debian/patches/pve/0010-internal-snapshot-async.patch
debian/patches/pve/0011-convert-savevm-async-to-threads.patch
debian/patches/pve/0012-qmp-add-get_link_status.patch
debian/patches/pve/0013-smm_available-false.patch
debian/patches/pve/0014-use-whitespace-between-VERSION-and-PKGVERSION.patch
debian/patches/pve/0015-vnc-altgr-emulation.patch
debian/patches/pve/0016-vnc-make-x509-imply-tls-again.patch
debian/patches/pve/0017-vnc-PVE-VNC-authentication.patch
debian/patches/pve/0018-block-rbd-disable-rbd_cache_writethrough_until_flush.patch [new file with mode: 0644]
debian/patches/pve/0018-migrate-fix-possible-unitialised-return-value.patch [deleted file]
debian/patches/pve/0019-block-rbd-disable-rbd_cache_writethrough_until_flush.patch [deleted file]
debian/patches/pve/0019-block-snapshot-qmp_snapshot_drive-add-aiocontext.patch [new file with mode: 0644]
debian/patches/pve/0020-block-snapshot-qmp_delete_drive_snapshot-add-aiocont.patch [new file with mode: 0644]
debian/patches/pve/0020-block-snapshot-qmp_snapshot_drive-add-aiocontext.patch [deleted file]
debian/patches/pve/0021-block-snapshot-qmp_delete_drive_snapshot-add-aiocont.patch [deleted file]
debian/patches/pve/0021-glusterfs-no-default-logfile-if-daemonized.patch [new file with mode: 0644]
debian/patches/pve/0022-glusterfs-allow-partial-reads.patch [new file with mode: 0644]
debian/patches/pve/0022-glusterfs-no-default-logfile-if-daemonized.patch [deleted file]
debian/patches/pve/0023-block-add-the-zeroinit-block-driver-filter.patch [new file with mode: 0644]
debian/patches/pve/0023-glusterfs-allow-partial-reads.patch [deleted file]
debian/patches/pve/0024-block-add-the-zeroinit-block-driver-filter.patch [deleted file]
debian/patches/pve/0024-qemu-img-dd-add-osize-and-read-from-to-stdin-stdout.patch [new file with mode: 0644]
debian/patches/pve/0025-backup-modify-job-api.patch [new file with mode: 0644]
debian/patches/pve/0025-qemu-img-dd-add-osize-and-read-from-to-stdin-stdout.patch [deleted file]
debian/patches/pve/0026-backup-introduce-vma-archive-format.patch [new file with mode: 0644]
debian/patches/pve/0026-backup-modify-job-api.patch [deleted file]
debian/patches/pve/0027-adding-old-vma-files.patch [new file with mode: 0644]
debian/patches/pve/0027-backup-introduce-vma-archive-format.patch [deleted file]
debian/patches/pve/0028-adding-old-vma-files.patch [deleted file]
debian/patches/pve/0029-backup-fix-race-in-backup-stop-command.patch [deleted file]
debian/patches/pve/0030-vma-add-throttling-options-to-drive-mapping-fifo-pro.patch [deleted file]
debian/patches/series
keycodemapdb/LICENSE.BSD [new file with mode: 0644]
keycodemapdb/LICENSE.GPL2 [new file with mode: 0644]
keycodemapdb/README [new file with mode: 0644]
keycodemapdb/data/README [new file with mode: 0644]
keycodemapdb/data/keymaps.csv [new file with mode: 0644]
keycodemapdb/tests/.gitignore [new file with mode: 0644]
keycodemapdb/tests/Makefile [new file with mode: 0644]
keycodemapdb/tests/javascript [new file with mode: 0755]
keycodemapdb/tests/python2 [new file with mode: 0755]
keycodemapdb/tests/python3 [new file with mode: 0755]
keycodemapdb/tests/stdc++.cc [new file with mode: 0644]
keycodemapdb/tests/stdc.c [new file with mode: 0644]
keycodemapdb/tests/test.py [new file with mode: 0644]
keycodemapdb/thirdparty/LICENSE-argparse.txt [new file with mode: 0644]
keycodemapdb/thirdparty/__init__.py [new file with mode: 0644]
keycodemapdb/thirdparty/argparse.py [new file with mode: 0644]
keycodemapdb/tools/keymap-gen [new file with mode: 0755]
qemu

index c760a7f19cde0190d4dc770a7975fbe2d7e5be32..db62caee34c921f48e3bcd31a3e48c7b7e8e464f 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
 # also update debian/changelog
 # also update debian/changelog
-KVMVER=2.9.1
-KVMPKGREL=9
+KVMVER=2.11.1
+KVMPKGREL=1
 
 KVMPACKAGE = pve-qemu-kvm
 KVMSRC = qemu
 
 KVMPACKAGE = pve-qemu-kvm
 KVMSRC = qemu
@@ -25,18 +25,31 @@ submodule:
 .PHONY: deb kvm
 deb kvm: $(DEBS)
 $(DEB_DBG): $(DEB)
 .PHONY: deb kvm
 deb kvm: $(DEBS)
 $(DEB_DBG): $(DEB)
-$(DEB): | submodule
+$(DEB): keycodemapdb | submodule
        rm -f *.deb
        rm -rf $(BUILDSRC)
        mkdir $(BUILDSRC)
        cp -a $(KVMSRC)/* $(BUILDSRC)/
        cp -a debian $(BUILDSRC)/debian
        rm -f *.deb
        rm -rf $(BUILDSRC)
        mkdir $(BUILDSRC)
        cp -a $(KVMSRC)/* $(BUILDSRC)/
        cp -a debian $(BUILDSRC)/debian
+       rm -rf $(BUILDSRC)/ui/keycodemapdb
+       cp -a keycodemapdb $(BUILDSRC)/ui/
        echo "git clone git://git.proxmox.com/git/pve-qemu-kvm.git\\ngit checkout $(GITVERSION)" > $(BUILDSRC)/debian/SOURCE
        # set package version
        sed -i 's/^pkgversion="".*/pkgversion="${KVMPACKAGE}_${KVMVER}-${KVMPKGREL}"/' $(BUILDSRC)/configure
        cd $(BUILDSRC); dpkg-buildpackage -b -rfakeroot -us -uc
        lintian $(DEBS) || true
 
        echo "git clone git://git.proxmox.com/git/pve-qemu-kvm.git\\ngit checkout $(GITVERSION)" > $(BUILDSRC)/debian/SOURCE
        # set package version
        sed -i 's/^pkgversion="".*/pkgversion="${KVMPACKAGE}_${KVMVER}-${KVMPKGREL}"/' $(BUILDSRC)/configure
        cd $(BUILDSRC); dpkg-buildpackage -b -rfakeroot -us -uc
        lintian $(DEBS) || true
 
+.PHONY: update
+update:
+       cd $(KVMSRC) && git submodule deinit ui/keycodemapdb || true
+       rm -rf $(KVMSRC)/ui/keycodemapdb
+       mkdir $(KVMSRC)/ui/keycodemapdb
+       cd $(KVMSRC) && git submodule update --init ui/keycodemapdb
+       rm -rf keycodemapdb
+       mkdir keycodemapdb
+       cp -R $(KVMSRC)/ui/keycodemapdb/* keycodemapdb/
+       git add keycodemapdb
+
 .PHONY: upload
 upload: $(DEBS)
        tar cf - ${DEBS} | ssh repoman@repo.proxmox.com upload --product pve --dist stretch
 .PHONY: upload
 upload: $(DEBS)
        tar cf - ${DEBS} | ssh repoman@repo.proxmox.com upload --product pve --dist stretch
index 9a5bfa5e713351f275409acebbeb7d572d5151a0..8989aa422cefef5ab652aa0123b7671eb26cb2d4 100644 (file)
@@ -1,3 +1,9 @@
+pve-qemu-kvm (2.11.1-1) stable; urgency=medium
+
+  * update to 2.11.1
+
+ -- Proxmox Support Team <support@proxmox.com>  Thu, 22 Feb 2018 12:34:43 +0100
+
 pve-qemu-kvm (2.9.1-9) stable; urgency=medium
 
   * add EPYC and EPYC-IPBP cpu models
 pve-qemu-kvm (2.9.1-9) stable; urgency=medium
 
   * add EPYC and EPYC-IPBP cpu models
index 09714dde6708c9f37fa063b51445dede4756eceb..1b43e71c6fbdca5c48d9d38f6cab7213a0ff2def 100644 (file)
@@ -9,7 +9,7 @@ This reverts commit b8eb5512fd8a115f164edbbe897cdf8884920ccb.
  1 file changed, 9 insertions(+)
 
 diff --git a/hw/intc/apic_common.c b/hw/intc/apic_common.c
  1 file changed, 9 insertions(+)
 
 diff --git a/hw/intc/apic_common.c b/hw/intc/apic_common.c
-index 1ef56f8d10..31fb73e9fb 100644
+index 78903ea909..cdfbec5e47 100644
 --- a/hw/intc/apic_common.c
 +++ b/hw/intc/apic_common.c
 @@ -257,6 +257,15 @@ static void apic_reset_common(DeviceState *dev)
 --- a/hw/intc/apic_common.c
 +++ b/hw/intc/apic_common.c
 @@ -257,6 +257,15 @@ static void apic_reset_common(DeviceState *dev)
diff --git a/debian/patches/extra/0002-ratelimit-don-t-align-wait-time-with-slices.patch b/debian/patches/extra/0002-ratelimit-don-t-align-wait-time-with-slices.patch
new file mode 100644 (file)
index 0000000..9502552
--- /dev/null
@@ -0,0 +1,47 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Wolfgang Bumiller <w.bumiller@proxmox.com>
+Date: Tue, 6 Feb 2018 11:34:34 +0100
+Subject: [PATCH] ratelimit: don't align wait time with slices
+
+It is possible for rate limited writes to keep overshooting a slice's
+quota by a tiny amount causing the slice-aligned waiting period to
+effectively halve the rate.
+
+Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
+---
+ include/qemu/ratelimit.h | 11 +++++------
+ 1 file changed, 5 insertions(+), 6 deletions(-)
+
+diff --git a/include/qemu/ratelimit.h b/include/qemu/ratelimit.h
+index 8dece483f5..1b38291823 100644
+--- a/include/qemu/ratelimit.h
++++ b/include/qemu/ratelimit.h
+@@ -36,7 +36,7 @@ typedef struct {
+ static inline int64_t ratelimit_calculate_delay(RateLimit *limit, uint64_t n)
+ {
+     int64_t now = qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
+-    uint64_t delay_slices;
++    double delay_slices;
+     assert(limit->slice_quota && limit->slice_ns);
+@@ -55,12 +55,11 @@ static inline int64_t ratelimit_calculate_delay(RateLimit *limit, uint64_t n)
+         return 0;
+     }
+-    /* Quota exceeded. Calculate the next time slice we may start
+-     * sending data again. */
+-    delay_slices = (limit->dispatched + limit->slice_quota - 1) /
+-        limit->slice_quota;
++    /* Quota exceeded. Wait based on the excess amount and then start a new
++     * slice. */
++    delay_slices = (double)limit->dispatched / limit->slice_quota;
+     limit->slice_end_time = limit->slice_start_time +
+-        delay_slices * limit->slice_ns;
++        (uint64_t)(delay_slices * limit->slice_ns);
+     return limit->slice_end_time - now;
+ }
+-- 
+2.11.0
+
diff --git a/debian/patches/extra/0002-virtio-serial-fix-segfault-on-disconnect.patch b/debian/patches/extra/0002-virtio-serial-fix-segfault-on-disconnect.patch
deleted file mode 100644 (file)
index 05ed7e8..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Stefan Hajnoczi <stefanha@redhat.com>
-Date: Fri, 2 Jun 2017 10:54:24 +0100
-Subject: [PATCH] virtio-serial: fix segfault on disconnect
-
-Since commit d4c19cdeeb2f1e474bc426a6da261f1d7346eb5b ("virtio-serial:
-add missing virtio_detach_element() call") the following commands may
-cause QEMU to segfault:
-
-  $ qemu -M accel=kvm -cpu host -m 1G \
-         -drive if=virtio,file=test.img,format=raw \
-         -device virtio-serial-pci,id=virtio-serial0 \
-         -chardev socket,id=channel1,path=/tmp/chardev.sock,server,nowait \
-         -device virtserialport,chardev=channel1,bus=virtio-serial0.0,id=port1
-  $ nc -U /tmp/chardev.sock
-  ^C
-
-  (guest)$ cat /dev/zero >/dev/vport0p1
-
-The segfault is non-deterministic: if the event loop notices the socket
-has been closed then there is no crash.  The disconnect has to happen
-right before QEMU attempts to write data to the socket.
-
-The backtrace is as follows:
-
-  Thread 1 "qemu-system-x86" received signal SIGSEGV, Segmentation fault.
-  0x00005555557e0698 in do_flush_queued_data (port=0x5555582cedf0, vq=0x7fffcc854290, vdev=0x55555807b1d0) at hw/char/virtio-serial-bus.c:180
-  180           for (i = port->iov_idx; i < port->elem->out_num; i++) {
-  #1  0x000055555580d363 in virtio_queue_notify_vq (vq=0x7fffcc854290) at hw/virtio/virtio.c:1524
-  #2  0x000055555580d363 in virtio_queue_host_notifier_read (n=0x7fffcc8542f8) at hw/virtio/virtio.c:2430
-  #3  0x0000555555b3482c in aio_dispatch_handlers (ctx=ctx@entry=0x5555566b8c80) at util/aio-posix.c:399
-  #4  0x0000555555b350d8 in aio_dispatch (ctx=0x5555566b8c80) at util/aio-posix.c:430
-  #5  0x0000555555b3212e in aio_ctx_dispatch (source=<optimized out>, callback=<optimized out>, user_data=<optimized out>) at util/async.c:261
-  #6  0x00007fffde71de52 in g_main_context_dispatch () at /lib64/libglib-2.0.so.0
-  #7  0x0000555555b34353 in glib_pollfds_poll () at util/main-loop.c:213
-  #8  0x0000555555b34353 in os_host_main_loop_wait (timeout=<optimized out>) at util/main-loop.c:261
-  #9  0x0000555555b34353 in main_loop_wait (nonblocking=<optimized out>) at util/main-loop.c:517
-  #10 0x0000555555773207 in main_loop () at vl.c:1917
-  #11 0x0000555555773207 in main (argc=<optimized out>, argv=<optimized out>, envp=<optimized out>) at vl.c:4751
-
-The do_flush_queued_data() function does not anticipate chardev close
-events during vsc->have_data().  It expects port->elem to remain
-non-NULL for the duration its for loop.
-
-The fix is simply to return from do_flush_queued_data() if the port
-closes because the close event already frees port->elem and drains the
-virtqueue - there is nothing left for do_flush_queued_data() to do.
-
-Reported-by: Sitong Liu <siliu@redhat.com>
-Reported-by: Min Deng <mdeng@redhat.com>
-Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
-Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
-Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
----
- hw/char/virtio-serial-bus.c | 3 +++
- 1 file changed, 3 insertions(+)
-
-diff --git a/hw/char/virtio-serial-bus.c b/hw/char/virtio-serial-bus.c
-index aa9c11ae92..f5bc173844 100644
---- a/hw/char/virtio-serial-bus.c
-+++ b/hw/char/virtio-serial-bus.c
-@@ -186,6 +186,9 @@ static void do_flush_queued_data(VirtIOSerialPort *port, VirtQueue *vq,
-                                   port->elem->out_sg[i].iov_base
-                                   + port->iov_offset,
-                                   buf_size);
-+            if (!port->elem) { /* bail if we got disconnected */
-+                return;
-+            }
-             if (port->throttled) {
-                 port->iov_idx = i;
-                 if (ret > 0) {
--- 
-2.11.0
-
diff --git a/debian/patches/extra/0003-megasas-always-store-SCSIRequest-into-MegasasCmd.patch b/debian/patches/extra/0003-megasas-always-store-SCSIRequest-into-MegasasCmd.patch
deleted file mode 100644 (file)
index bd2755a..0000000
+++ /dev/null
@@ -1,125 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Paolo Bonzini <pbonzini@redhat.com>
-Date: Thu, 1 Jun 2017 17:26:14 +0200
-Subject: [PATCH] megasas: always store SCSIRequest* into MegasasCmd
-
-This ensures that the request is unref'ed properly, and avoids a
-segmentation fault in the new qtest testcase that is added.
-This is CVE-2017-9503.
-
-Reported-by: Zhangyanyu <zyy4013@stu.ouc.edu.cn>
-Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
----
- hw/scsi/megasas.c | 31 ++++++++++++++++---------------
- 1 file changed, 16 insertions(+), 15 deletions(-)
-
-diff --git a/hw/scsi/megasas.c b/hw/scsi/megasas.c
-index 135662df31..734fdaef90 100644
---- a/hw/scsi/megasas.c
-+++ b/hw/scsi/megasas.c
-@@ -609,6 +609,9 @@ static void megasas_reset_frames(MegasasState *s)
- static void megasas_abort_command(MegasasCmd *cmd)
- {
-     /* Never abort internal commands.  */
-+    if (cmd->dcmd_opcode != -1) {
-+        return;
-+    }
-     if (cmd->req != NULL) {
-         scsi_req_cancel(cmd->req);
-     }
-@@ -1017,7 +1020,6 @@ static int megasas_pd_get_info_submit(SCSIDevice *sdev, int lun,
-     uint64_t pd_size;
-     uint16_t pd_id = ((sdev->id & 0xFF) << 8) | (lun & 0xFF);
-     uint8_t cmdbuf[6];
--    SCSIRequest *req;
-     size_t len, resid;
-     if (!cmd->iov_buf) {
-@@ -1026,8 +1028,8 @@ static int megasas_pd_get_info_submit(SCSIDevice *sdev, int lun,
-         info->inquiry_data[0] = 0x7f; /* Force PQual 0x3, PType 0x1f */
-         info->vpd_page83[0] = 0x7f;
-         megasas_setup_inquiry(cmdbuf, 0, sizeof(info->inquiry_data));
--        req = scsi_req_new(sdev, cmd->index, lun, cmdbuf, cmd);
--        if (!req) {
-+        cmd->req = scsi_req_new(sdev, cmd->index, lun, cmdbuf, cmd);
-+        if (!cmd->req) {
-             trace_megasas_dcmd_req_alloc_failed(cmd->index,
-                                                 "PD get info std inquiry");
-             g_free(cmd->iov_buf);
-@@ -1036,26 +1038,26 @@ static int megasas_pd_get_info_submit(SCSIDevice *sdev, int lun,
-         }
-         trace_megasas_dcmd_internal_submit(cmd->index,
-                                            "PD get info std inquiry", lun);
--        len = scsi_req_enqueue(req);
-+        len = scsi_req_enqueue(cmd->req);
-         if (len > 0) {
-             cmd->iov_size = len;
--            scsi_req_continue(req);
-+            scsi_req_continue(cmd->req);
-         }
-         return MFI_STAT_INVALID_STATUS;
-     } else if (info->inquiry_data[0] != 0x7f && info->vpd_page83[0] == 0x7f) {
-         megasas_setup_inquiry(cmdbuf, 0x83, sizeof(info->vpd_page83));
--        req = scsi_req_new(sdev, cmd->index, lun, cmdbuf, cmd);
--        if (!req) {
-+        cmd->req = scsi_req_new(sdev, cmd->index, lun, cmdbuf, cmd);
-+        if (!cmd->req) {
-             trace_megasas_dcmd_req_alloc_failed(cmd->index,
-                                                 "PD get info vpd inquiry");
-             return MFI_STAT_FLASH_ALLOC_FAIL;
-         }
-         trace_megasas_dcmd_internal_submit(cmd->index,
-                                            "PD get info vpd inquiry", lun);
--        len = scsi_req_enqueue(req);
-+        len = scsi_req_enqueue(cmd->req);
-         if (len > 0) {
-             cmd->iov_size = len;
--            scsi_req_continue(req);
-+            scsi_req_continue(cmd->req);
-         }
-         return MFI_STAT_INVALID_STATUS;
-     }
-@@ -1217,7 +1219,6 @@ static int megasas_ld_get_info_submit(SCSIDevice *sdev, int lun,
-     struct mfi_ld_info *info = cmd->iov_buf;
-     size_t dcmd_size = sizeof(struct mfi_ld_info);
-     uint8_t cdb[6];
--    SCSIRequest *req;
-     ssize_t len, resid;
-     uint16_t sdev_id = ((sdev->id & 0xFF) << 8) | (lun & 0xFF);
-     uint64_t ld_size;
-@@ -1226,8 +1227,8 @@ static int megasas_ld_get_info_submit(SCSIDevice *sdev, int lun,
-         cmd->iov_buf = g_malloc0(dcmd_size);
-         info = cmd->iov_buf;
-         megasas_setup_inquiry(cdb, 0x83, sizeof(info->vpd_page83));
--        req = scsi_req_new(sdev, cmd->index, lun, cdb, cmd);
--        if (!req) {
-+        cmd->req = scsi_req_new(sdev, cmd->index, lun, cdb, cmd);
-+        if (!cmd->req) {
-             trace_megasas_dcmd_req_alloc_failed(cmd->index,
-                                                 "LD get info vpd inquiry");
-             g_free(cmd->iov_buf);
-@@ -1236,10 +1237,10 @@ static int megasas_ld_get_info_submit(SCSIDevice *sdev, int lun,
-         }
-         trace_megasas_dcmd_internal_submit(cmd->index,
-                                            "LD get info vpd inquiry", lun);
--        len = scsi_req_enqueue(req);
-+        len = scsi_req_enqueue(cmd->req);
-         if (len > 0) {
-             cmd->iov_size = len;
--            scsi_req_continue(req);
-+            scsi_req_continue(cmd->req);
-         }
-         return MFI_STAT_INVALID_STATUS;
-     }
-@@ -1851,7 +1852,7 @@ static void megasas_command_complete(SCSIRequest *req, uint32_t status,
-         return;
-     }
--    if (cmd->req == NULL) {
-+    if (cmd->dcmd_opcode != -1) {
-         /*
-          * Internal command complete
-          */
--- 
-2.11.0
-
diff --git a/debian/patches/extra/0004-slirp-check-len-against-dhcp-options-array-end.patch b/debian/patches/extra/0004-slirp-check-len-against-dhcp-options-array-end.patch
deleted file mode 100644 (file)
index 2832d63..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Prasad J Pandit <pjp@fedoraproject.org>
-Date: Mon, 17 Jul 2017 17:33:26 +0530
-Subject: [PATCH] slirp: check len against dhcp options array end
-
-While parsing dhcp options string in 'dhcp_decode', if an options'
-length 'len' appeared towards the end of 'bp_vend' array, ensuing
-read could lead to an OOB memory access issue. Add check to avoid it.
-
-This is CVE-2017-11434.
-
-Reported-by: Reno Robert <renorobert@gmail.com>
-Signed-off-by: Prasad J Pandit <pjp@fedoraproject.org>
-Signed-off-by: Samuel Thibault <samuel.thibault@ens-lyon.org>
----
- slirp/bootp.c | 3 +++
- 1 file changed, 3 insertions(+)
-
-diff --git a/slirp/bootp.c b/slirp/bootp.c
-index 5a4646c182..5dd1a415b5 100644
---- a/slirp/bootp.c
-+++ b/slirp/bootp.c
-@@ -123,6 +123,9 @@ static void dhcp_decode(const struct bootp_t *bp, int *pmsg_type,
-             if (p >= p_end)
-                 break;
-             len = *p++;
-+            if (p + len > p_end) {
-+                break;
-+            }
-             DPRINTF("dhcp: tag=%d len=%d\n", tag, len);
-             switch(tag) {
--- 
-2.11.0
-
diff --git a/debian/patches/extra/0005-IDE-Do-not-flush-empty-CDROM-drives.patch b/debian/patches/extra/0005-IDE-Do-not-flush-empty-CDROM-drives.patch
deleted file mode 100644 (file)
index 86f970c..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Stefan Hajnoczi <stefanha@redhat.com>
-Date: Wed, 9 Aug 2017 17:02:11 +0100
-Subject: [PATCH] IDE: Do not flush empty CDROM drives
-
-The block backend changed in a way that flushing empty CDROM drives now
-crashes.  Amend IDE to avoid doing so until the root problem can be
-addressed for 2.11.
-
-Original patch by John Snow <jsnow@redhat.com>.
-
-Reported-by: Kieron Shorrock <kshorrock@paloaltonetworks.com>
-Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
-Reviewed-by: Eric Blake <eblake@redhat.com>
-Message-id: 20170809160212.29976-2-stefanha@redhat.com
-Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
----
- hw/ide/core.c | 10 +++++++++-
- 1 file changed, 9 insertions(+), 1 deletion(-)
-
-diff --git a/hw/ide/core.c b/hw/ide/core.c
-index 0b48b64d3a..bea39536b0 100644
---- a/hw/ide/core.c
-+++ b/hw/ide/core.c
-@@ -1063,7 +1063,15 @@ static void ide_flush_cache(IDEState *s)
-     s->status |= BUSY_STAT;
-     ide_set_retry(s);
-     block_acct_start(blk_get_stats(s->blk), &s->acct, 0, BLOCK_ACCT_FLUSH);
--    s->pio_aiocb = blk_aio_flush(s->blk, ide_flush_cb, s);
-+
-+    if (blk_bs(s->blk)) {
-+        s->pio_aiocb = blk_aio_flush(s->blk, ide_flush_cb, s);
-+    } else {
-+        /* XXX blk_aio_flush() crashes when blk_bs(blk) is NULL, remove this
-+         * temporary workaround when blk_aio_*() functions handle NULL blk_bs.
-+         */
-+        ide_flush_cb(s, 0);
-+    }
- }
- static void ide_cfata_metadata_inquiry(IDEState *s)
--- 
-2.11.0
-
diff --git a/debian/patches/extra/0006-bitmap-add-bitmap_copy_and_clear_atomic.patch b/debian/patches/extra/0006-bitmap-add-bitmap_copy_and_clear_atomic.patch
deleted file mode 100644 (file)
index cd584a2..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Gerd Hoffmann <kraxel@redhat.com>
-Date: Fri, 21 Apr 2017 11:16:24 +0200
-Subject: [PATCH] bitmap: add bitmap_copy_and_clear_atomic
-
-Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
-Message-id: 20170421091632.30900-2-kraxel@redhat.com
-Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
----
- include/qemu/bitmap.h |  2 ++
- util/bitmap.c         | 11 +++++++++++
- 2 files changed, 13 insertions(+)
-
-diff --git a/include/qemu/bitmap.h b/include/qemu/bitmap.h
-index 63ea2d0b1e..c318da12d7 100644
---- a/include/qemu/bitmap.h
-+++ b/include/qemu/bitmap.h
-@@ -220,6 +220,8 @@ void bitmap_set(unsigned long *map, long i, long len);
- void bitmap_set_atomic(unsigned long *map, long i, long len);
- void bitmap_clear(unsigned long *map, long start, long nr);
- bool bitmap_test_and_clear_atomic(unsigned long *map, long start, long nr);
-+void bitmap_copy_and_clear_atomic(unsigned long *dst, unsigned long *src,
-+                                  long nr);
- unsigned long bitmap_find_next_zero_area(unsigned long *map,
-                                          unsigned long size,
-                                          unsigned long start,
-diff --git a/util/bitmap.c b/util/bitmap.c
-index c1a84ca5e3..efced9a7d8 100644
---- a/util/bitmap.c
-+++ b/util/bitmap.c
-@@ -287,6 +287,17 @@ bool bitmap_test_and_clear_atomic(unsigned long *map, long start, long nr)
-     return dirty != 0;
- }
-+void bitmap_copy_and_clear_atomic(unsigned long *dst, unsigned long *src,
-+                                  long nr)
-+{
-+    while (nr > 0) {
-+        *dst = atomic_xchg(src, 0);
-+        dst++;
-+        src++;
-+        nr -= BITS_PER_LONG;
-+    }
-+}
-+
- #define ALIGN_MASK(x,mask)      (((x)+(mask))&~(mask))
- /**
--- 
-2.11.0
-
diff --git a/debian/patches/extra/0007-memory-add-support-getting-and-using-a-dirty-bitmap-.patch b/debian/patches/extra/0007-memory-add-support-getting-and-using-a-dirty-bitmap-.patch
deleted file mode 100644 (file)
index 8b202fb..0000000
+++ /dev/null
@@ -1,241 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Gerd Hoffmann <kraxel@redhat.com>
-Date: Fri, 21 Apr 2017 11:16:25 +0200
-Subject: [PATCH] memory: add support getting and using a dirty bitmap copy.
-
-This patch adds support for getting and using a local copy of the dirty
-bitmap.
-
-memory_region_snapshot_and_clear_dirty() will create a snapshot of the
-dirty bitmap for the specified range, clear the dirty bitmap and return
-the copy.  The returned bitmap can be a bit larger than requested, the
-range is expanded so the code can copy unsigned longs from the bitmap
-and avoid atomic bit update operations.
-
-memory_region_snapshot_get_dirty() will return the dirty status of
-pages, pretty much like memory_region_get_dirty(), but using the copy
-returned by memory_region_copy_and_clear_dirty().
-
-Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
-Message-id: 20170421091632.30900-3-kraxel@redhat.com
-Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
----
- exec.c                  | 75 +++++++++++++++++++++++++++++++++++++++++++++++++
- include/exec/memory.h   | 47 +++++++++++++++++++++++++++++++
- include/exec/ram_addr.h |  7 +++++
- include/qemu/typedefs.h |  1 +
- memory.c                | 17 +++++++++++
- 5 files changed, 147 insertions(+)
-
-diff --git a/exec.c b/exec.c
-index fcb5b16131..07c2c8ea88 100644
---- a/exec.c
-+++ b/exec.c
-@@ -223,6 +223,12 @@ struct CPUAddressSpace {
-     MemoryListener tcg_as_listener;
- };
-+struct DirtyBitmapSnapshot {
-+    ram_addr_t start;
-+    ram_addr_t end;
-+    unsigned long dirty[];
-+};
-+
- #endif
- #if !defined(CONFIG_USER_ONLY)
-@@ -1061,6 +1067,75 @@ bool cpu_physical_memory_test_and_clear_dirty(ram_addr_t start,
-     return dirty;
- }
-+DirtyBitmapSnapshot *cpu_physical_memory_snapshot_and_clear_dirty
-+     (ram_addr_t start, ram_addr_t length, unsigned client)
-+{
-+    DirtyMemoryBlocks *blocks;
-+    unsigned long align = 1UL << (TARGET_PAGE_BITS + BITS_PER_LEVEL);
-+    ram_addr_t first = QEMU_ALIGN_DOWN(start, align);
-+    ram_addr_t last  = QEMU_ALIGN_UP(start + length, align);
-+    DirtyBitmapSnapshot *snap;
-+    unsigned long page, end, dest;
-+
-+    snap = g_malloc0(sizeof(*snap) +
-+                     ((last - first) >> (TARGET_PAGE_BITS + 3)));
-+    snap->start = first;
-+    snap->end   = last;
-+
-+    page = first >> TARGET_PAGE_BITS;
-+    end  = last  >> TARGET_PAGE_BITS;
-+    dest = 0;
-+
-+    rcu_read_lock();
-+
-+    blocks = atomic_rcu_read(&ram_list.dirty_memory[client]);
-+
-+    while (page < end) {
-+        unsigned long idx = page / DIRTY_MEMORY_BLOCK_SIZE;
-+        unsigned long offset = page % DIRTY_MEMORY_BLOCK_SIZE;
-+        unsigned long num = MIN(end - page, DIRTY_MEMORY_BLOCK_SIZE - offset);
-+
-+        assert(QEMU_IS_ALIGNED(offset, (1 << BITS_PER_LEVEL)));
-+        assert(QEMU_IS_ALIGNED(num,    (1 << BITS_PER_LEVEL)));
-+        offset >>= BITS_PER_LEVEL;
-+
-+        bitmap_copy_and_clear_atomic(snap->dirty + dest,
-+                                     blocks->blocks[idx] + offset,
-+                                     num);
-+        page += num;
-+        dest += num >> BITS_PER_LEVEL;
-+    }
-+
-+    rcu_read_unlock();
-+
-+    if (tcg_enabled()) {
-+        tlb_reset_dirty_range_all(start, length);
-+    }
-+
-+    return snap;
-+}
-+
-+bool cpu_physical_memory_snapshot_get_dirty(DirtyBitmapSnapshot *snap,
-+                                            ram_addr_t start,
-+                                            ram_addr_t length)
-+{
-+    unsigned long page, end;
-+
-+    assert(start >= snap->start);
-+    assert(start + length <= snap->end);
-+
-+    end = TARGET_PAGE_ALIGN(start + length - snap->start) >> TARGET_PAGE_BITS;
-+    page = (start - snap->start) >> TARGET_PAGE_BITS;
-+
-+    while (page < end) {
-+        if (test_bit(page, snap->dirty)) {
-+            return true;
-+        }
-+        page++;
-+    }
-+    return false;
-+}
-+
- /* Called from RCU critical section */
- hwaddr memory_region_section_get_iotlb(CPUState *cpu,
-                                        MemoryRegionSection *section,
-diff --git a/include/exec/memory.h b/include/exec/memory.h
-index f20b191793..1e15e79d00 100644
---- a/include/exec/memory.h
-+++ b/include/exec/memory.h
-@@ -871,6 +871,53 @@ void memory_region_set_dirty(MemoryRegion *mr, hwaddr addr,
-  */
- bool memory_region_test_and_clear_dirty(MemoryRegion *mr, hwaddr addr,
-                                         hwaddr size, unsigned client);
-+
-+/**
-+ * memory_region_snapshot_and_clear_dirty: Get a snapshot of the dirty
-+ *                                         bitmap and clear it.
-+ *
-+ * Creates a snapshot of the dirty bitmap, clears the dirty bitmap and
-+ * returns the snapshot.  The snapshot can then be used to query dirty
-+ * status, using memory_region_snapshot_get_dirty.  Unlike
-+ * memory_region_test_and_clear_dirty this allows to query the same
-+ * page multiple times, which is especially useful for display updates
-+ * where the scanlines often are not page aligned.
-+ *
-+ * The dirty bitmap region which gets copyed into the snapshot (and
-+ * cleared afterwards) can be larger than requested.  The boundaries
-+ * are rounded up/down so complete bitmap longs (covering 64 pages on
-+ * 64bit hosts) can be copied over into the bitmap snapshot.  Which
-+ * isn't a problem for display updates as the extra pages are outside
-+ * the visible area, and in case the visible area changes a full
-+ * display redraw is due anyway.  Should other use cases for this
-+ * function emerge we might have to revisit this implementation
-+ * detail.
-+ *
-+ * Use g_free to release DirtyBitmapSnapshot.
-+ *
-+ * @mr: the memory region being queried.
-+ * @addr: the address (relative to the start of the region) being queried.
-+ * @size: the size of the range being queried.
-+ * @client: the user of the logging information; typically %DIRTY_MEMORY_VGA.
-+ */
-+DirtyBitmapSnapshot *memory_region_snapshot_and_clear_dirty(MemoryRegion *mr,
-+                                                            hwaddr addr,
-+                                                            hwaddr size,
-+                                                            unsigned client);
-+
-+/**
-+ * memory_region_snapshot_get_dirty: Check whether a range of bytes is dirty
-+ *                                   in the specified dirty bitmap snapshot.
-+ *
-+ * @mr: the memory region being queried.
-+ * @snap: the dirty bitmap snapshot
-+ * @addr: the address (relative to the start of the region) being queried.
-+ * @size: the size of the range being queried.
-+ */
-+bool memory_region_snapshot_get_dirty(MemoryRegion *mr,
-+                                      DirtyBitmapSnapshot *snap,
-+                                      hwaddr addr, hwaddr size);
-+
- /**
-  * memory_region_sync_dirty_bitmap: Synchronize a region's dirty bitmap with
-  *                                  any external TLBs (e.g. kvm)
-diff --git a/include/exec/ram_addr.h b/include/exec/ram_addr.h
-index b05dc84ab9..2b63d7f59e 100644
---- a/include/exec/ram_addr.h
-+++ b/include/exec/ram_addr.h
-@@ -343,6 +343,13 @@ bool cpu_physical_memory_test_and_clear_dirty(ram_addr_t start,
-                                               ram_addr_t length,
-                                               unsigned client);
-+DirtyBitmapSnapshot *cpu_physical_memory_snapshot_and_clear_dirty
-+    (ram_addr_t start, ram_addr_t length, unsigned client);
-+
-+bool cpu_physical_memory_snapshot_get_dirty(DirtyBitmapSnapshot *snap,
-+                                            ram_addr_t start,
-+                                            ram_addr_t length);
-+
- static inline void cpu_physical_memory_clear_dirty_range(ram_addr_t start,
-                                                          ram_addr_t length)
- {
-diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h
-index e95f28cfec..f08d327aec 100644
---- a/include/qemu/typedefs.h
-+++ b/include/qemu/typedefs.h
-@@ -23,6 +23,7 @@ typedef struct CPUAddressSpace CPUAddressSpace;
- typedef struct CPUState CPUState;
- typedef struct DeviceListener DeviceListener;
- typedef struct DeviceState DeviceState;
-+typedef struct DirtyBitmapSnapshot DirtyBitmapSnapshot;
- typedef struct DisplayChangeListener DisplayChangeListener;
- typedef struct DisplayState DisplayState;
- typedef struct DisplaySurface DisplaySurface;
-diff --git a/memory.c b/memory.c
-index 4c95aaf39c..8a0648551f 100644
---- a/memory.c
-+++ b/memory.c
-@@ -1716,6 +1716,23 @@ bool memory_region_test_and_clear_dirty(MemoryRegion *mr, hwaddr addr,
-                 memory_region_get_ram_addr(mr) + addr, size, client);
- }
-+DirtyBitmapSnapshot *memory_region_snapshot_and_clear_dirty(MemoryRegion *mr,
-+                                                            hwaddr addr,
-+                                                            hwaddr size,
-+                                                            unsigned client)
-+{
-+    assert(mr->ram_block);
-+    return cpu_physical_memory_snapshot_and_clear_dirty(
-+                memory_region_get_ram_addr(mr) + addr, size, client);
-+}
-+
-+bool memory_region_snapshot_get_dirty(MemoryRegion *mr, DirtyBitmapSnapshot *snap,
-+                                      hwaddr addr, hwaddr size)
-+{
-+    assert(mr->ram_block);
-+    return cpu_physical_memory_snapshot_get_dirty(snap,
-+                memory_region_get_ram_addr(mr) + addr, size);
-+}
- void memory_region_sync_dirty_bitmap(MemoryRegion *mr)
- {
--- 
-2.11.0
-
diff --git a/debian/patches/extra/0008-vga-add-vga_scanline_invalidated-helper.patch b/debian/patches/extra/0008-vga-add-vga_scanline_invalidated-helper.patch
deleted file mode 100644 (file)
index 78227ee..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Gerd Hoffmann <kraxel@redhat.com>
-Date: Fri, 21 Apr 2017 11:16:26 +0200
-Subject: [PATCH] vga: add vga_scanline_invalidated helper
-
-Add vga_scanline_invalidated helper to check whenever a scanline was
-invalidated.  Add a sanity check to fix OOB read access for display
-heights larger than 2048.
-
-Only cirrus uses this, for hardware cursor rendering, so having this
-work properly for the first 2048 scanlines only shouldn't be a problem
-as the cirrus can't handle large resolutions anyway.  Also changing the
-invalidated_y_table size would break live migration.
-
-Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
-Message-id: 20170421091632.30900-4-kraxel@redhat.com
-Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
----
- hw/display/vga.c | 14 +++++++++++---
- 1 file changed, 11 insertions(+), 3 deletions(-)
-
-diff --git a/hw/display/vga.c b/hw/display/vga.c
-index 69c3e1d674..3991b88aac 100644
---- a/hw/display/vga.c
-+++ b/hw/display/vga.c
-@@ -1434,6 +1434,14 @@ void vga_invalidate_scanlines(VGACommonState *s, int y1, int y2)
-     }
- }
-+static bool vga_scanline_invalidated(VGACommonState *s, int y)
-+{
-+    if (y >= VGA_MAX_HEIGHT) {
-+        return false;
-+    }
-+    return s->invalidated_y_table[y >> 5] & (1 << (y & 0x1f));
-+}
-+
- void vga_sync_dirty_bitmap(VGACommonState *s)
- {
-     memory_region_sync_dirty_bitmap(&s->vram);
-@@ -1638,8 +1646,8 @@ static void vga_draw_graphic(VGACommonState *s, int full_update)
-         page1 = addr + bwidth - 1;
-         update |= memory_region_get_dirty(&s->vram, page0, page1 - page0,
-                                           DIRTY_MEMORY_VGA);
--        /* explicit invalidation for the hardware cursor */
--        update |= (s->invalidated_y_table[y >> 5] >> (y & 0x1f)) & 1;
-+        /* explicit invalidation for the hardware cursor (cirrus only) */
-+        update |= vga_scanline_invalidated(s, y);
-         if (update) {
-             if (y_start < 0)
-                 y_start = y;
-@@ -1686,7 +1694,7 @@ static void vga_draw_graphic(VGACommonState *s, int full_update)
-                                   page_max - page_min,
-                                   DIRTY_MEMORY_VGA);
-     }
--    memset(s->invalidated_y_table, 0, ((height + 31) >> 5) * 4);
-+    memset(s->invalidated_y_table, 0, sizeof(s->invalidated_y_table));
- }
- static void vga_draw_blank(VGACommonState *s, int full_update)
--- 
-2.11.0
-
diff --git a/debian/patches/extra/0009-vga-make-display-updates-thread-safe.patch b/debian/patches/extra/0009-vga-make-display-updates-thread-safe.patch
deleted file mode 100644 (file)
index 6c1edf8..0000000
+++ /dev/null
@@ -1,114 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Gerd Hoffmann <kraxel@redhat.com>
-Date: Fri, 21 Apr 2017 11:16:27 +0200
-Subject: [PATCH] vga: make display updates thread safe.
-
-The vga code clears the dirty bits *after* reading the framebuffer
-memory.  So if the guest framebuffer updates hits the race window
-between vga reading the framebuffer and vga clearing the dirty bits
-vga will miss that update
-
-Fix it by using the new memory_region_copy_and_clear_dirty()
-memory_region_copy_get_dirty() functions.  That way we clear the
-dirty bitmap before reading the framebuffer.  Any guest display
-updates happening in parallel will be properly tracked in the
-dirty bitmap then and the next display refresh will pick them up.
-
-Problem triggers with mttcg only.  Before mttcg was merged tcg
-never ran in parallel to vga emulation.  Using kvm will hide the
-problem too, due to qemu operating on a userspace copy of the
-kernel's dirty bitmap.
-
-Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
-Message-id: 20170421091632.30900-5-kraxel@redhat.com
-Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
----
- hw/display/vga.c | 36 +++++++++++++++++-------------------
- 1 file changed, 17 insertions(+), 19 deletions(-)
-
-diff --git a/hw/display/vga.c b/hw/display/vga.c
-index 3991b88aac..b2516c8d21 100644
---- a/hw/display/vga.c
-+++ b/hw/display/vga.c
-@@ -1465,7 +1465,8 @@ static void vga_draw_graphic(VGACommonState *s, int full_update)
-     DisplaySurface *surface = qemu_console_surface(s->con);
-     int y1, y, update, linesize, y_start, double_scan, mask, depth;
-     int width, height, shift_control, line_offset, bwidth, bits;
--    ram_addr_t page0, page1, page_min, page_max;
-+    ram_addr_t page0, page1;
-+    DirtyBitmapSnapshot *snap = NULL;
-     int disp_width, multi_scan, multi_run;
-     uint8_t *d;
-     uint32_t v, addr1, addr;
-@@ -1480,9 +1481,6 @@ static void vga_draw_graphic(VGACommonState *s, int full_update)
-     full_update |= update_basic_params(s);
--    if (!full_update)
--        vga_sync_dirty_bitmap(s);
--
-     s->get_resolution(s, &width, &height);
-     disp_width = width;
-@@ -1625,11 +1623,17 @@ static void vga_draw_graphic(VGACommonState *s, int full_update)
-     addr1 = (s->start_addr * 4);
-     bwidth = (width * bits + 7) / 8;
-     y_start = -1;
--    page_min = -1;
--    page_max = 0;
-     d = surface_data(surface);
-     linesize = surface_stride(surface);
-     y1 = 0;
-+
-+    if (!full_update) {
-+        vga_sync_dirty_bitmap(s);
-+        snap = memory_region_snapshot_and_clear_dirty(&s->vram, addr1,
-+                                                      bwidth * height,
-+                                                      DIRTY_MEMORY_VGA);
-+    }
-+
-     for(y = 0; y < height; y++) {
-         addr = addr1;
-         if (!(s->cr[VGA_CRTC_MODE] & 1)) {
-@@ -1644,17 +1648,17 @@ static void vga_draw_graphic(VGACommonState *s, int full_update)
-         update = full_update;
-         page0 = addr;
-         page1 = addr + bwidth - 1;
--        update |= memory_region_get_dirty(&s->vram, page0, page1 - page0,
--                                          DIRTY_MEMORY_VGA);
-+        if (full_update) {
-+            update = 1;
-+        } else {
-+            update = memory_region_snapshot_get_dirty(&s->vram, snap,
-+                                                      page0, page1 - page0);
-+        }
-         /* explicit invalidation for the hardware cursor (cirrus only) */
-         update |= vga_scanline_invalidated(s, y);
-         if (update) {
-             if (y_start < 0)
-                 y_start = y;
--            if (page0 < page_min)
--                page_min = page0;
--            if (page1 > page_max)
--                page_max = page1;
-             if (!(is_buffer_shared(surface))) {
-                 vga_draw_line(s, d, s->vram_ptr + addr, width);
-                 if (s->cursor_draw_line)
-@@ -1687,13 +1691,7 @@ static void vga_draw_graphic(VGACommonState *s, int full_update)
-         dpy_gfx_update(s->con, 0, y_start,
-                        disp_width, y - y_start);
-     }
--    /* reset modified pages */
--    if (page_max >= page_min) {
--        memory_region_reset_dirty(&s->vram,
--                                  page_min,
--                                  page_max - page_min,
--                                  DIRTY_MEMORY_VGA);
--    }
-+    g_free(snap);
-     memset(s->invalidated_y_table, 0, sizeof(s->invalidated_y_table));
- }
--- 
-2.11.0
-
diff --git a/debian/patches/extra/0010-vga-fix-display-update-region-calculation.patch b/debian/patches/extra/0010-vga-fix-display-update-region-calculation.patch
deleted file mode 100644 (file)
index 96f35ee..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Gerd Hoffmann <kraxel@redhat.com>
-Date: Tue, 9 May 2017 12:48:39 +0200
-Subject: [PATCH] vga: fix display update region calculation
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-vga display update mis-calculated the region for the dirty bitmap
-snapshot in case the scanlines are padded.  This can triggere an
-assert in cpu_physical_memory_snapshot_get_dirty().
-
-Fixes: fec5e8c92becad223df9d972770522f64aafdb72
-Reported-by: Kevin Wolf <kwolf@redhat.com>
-Reported-by: 李强 <liqiang6-s@360.cn>
-Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
-Message-id: 20170509104839.19415-1-kraxel@redhat.com
----
- hw/display/vga.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/hw/display/vga.c b/hw/display/vga.c
-index b2516c8d21..dcc95f88e2 100644
---- a/hw/display/vga.c
-+++ b/hw/display/vga.c
-@@ -1630,7 +1630,7 @@ static void vga_draw_graphic(VGACommonState *s, int full_update)
-     if (!full_update) {
-         vga_sync_dirty_bitmap(s);
-         snap = memory_region_snapshot_and_clear_dirty(&s->vram, addr1,
--                                                      bwidth * height,
-+                                                      line_offset * height,
-                                                       DIRTY_MEMORY_VGA);
-     }
--- 
-2.11.0
-
diff --git a/debian/patches/extra/0011-vga-fix-display-update-region-calculation-split-scre.patch b/debian/patches/extra/0011-vga-fix-display-update-region-calculation-split-scre.patch
deleted file mode 100644 (file)
index 9ad4652..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Gerd Hoffmann <kraxel@redhat.com>
-Date: Fri, 1 Sep 2017 14:57:38 +0200
-Subject: [PATCH] vga: fix display update region calculation (split screen)
-
-vga display update mis-calculated the region for the dirty bitmap
-snapshot in case split screen mode is used.  This can trigger an
-assert in cpu_physical_memory_snapshot_get_dirty().
-
-Impact:  DoS for privileged guest users.
-
-Fixes: CVE-2017-13673
-Fixes: fec5e8c92becad223df9d972770522f64aafdb72
-Cc: P J P <ppandit@redhat.com>
-Reported-by: David Buchanan <d@vidbuchanan.co.uk>
-Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
-Message-id: 20170828123307.15392-1-kraxel@redhat.com
----
- hw/display/vga.c | 10 ++++++++--
- 1 file changed, 8 insertions(+), 2 deletions(-)
-
-diff --git a/hw/display/vga.c b/hw/display/vga.c
-index dcc95f88e2..533d8d7895 100644
---- a/hw/display/vga.c
-+++ b/hw/display/vga.c
-@@ -1628,9 +1628,15 @@ static void vga_draw_graphic(VGACommonState *s, int full_update)
-     y1 = 0;
-     if (!full_update) {
-+        ram_addr_t region_start = addr1;
-+        ram_addr_t region_end = addr1 + line_offset * height;
-         vga_sync_dirty_bitmap(s);
--        snap = memory_region_snapshot_and_clear_dirty(&s->vram, addr1,
--                                                      line_offset * height,
-+        if (s->line_compare < height) {
-+            /* split screen mode */
-+            region_start = 0;
-+        }
-+        snap = memory_region_snapshot_and_clear_dirty(&s->vram, region_start,
-+                                                      region_end - region_start,
-                                                       DIRTY_MEMORY_VGA);
-     }
--- 
-2.11.0
-
diff --git a/debian/patches/extra/0012-vga-stop-passing-pointers-to-vga_draw_line-functions.patch b/debian/patches/extra/0012-vga-stop-passing-pointers-to-vga_draw_line-functions.patch
deleted file mode 100644 (file)
index 12395c4..0000000
+++ /dev/null
@@ -1,497 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Gerd Hoffmann <kraxel@redhat.com>
-Date: Fri, 1 Sep 2017 14:57:39 +0200
-Subject: [PATCH] vga: stop passing pointers to vga_draw_line* functions
-
-Instead pass around the address (aka offset into vga memory).
-Add vga_read_* helper functions which apply vbe_size_mask to
-the address, to make sure the address stays within the valid
-range, similar to the cirrus blitter fixes (commits ffaf857778
-and 026aeffcb4).
-
-Impact:  DoS for privileged guest users.  qemu crashes with
-a segfault, when hitting the guard page after vga memory
-allocation, while reading vga memory for display updates.
-
-Fixes: CVE-2017-13672
-Cc: P J P <ppandit@redhat.com>
-Reported-by: David Buchanan <d@vidbuchanan.co.uk>
-Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
-Message-id: 20170828122906.18993-1-kraxel@redhat.com
----
- hw/display/vga-helpers.h | 202 ++++++++++++++++++++++++++---------------------
- hw/display/vga.c         |   5 +-
- hw/display/vga_int.h     |   1 +
- 3 files changed, 114 insertions(+), 94 deletions(-)
-
-diff --git a/hw/display/vga-helpers.h b/hw/display/vga-helpers.h
-index 94f6de2046..5a752b3f9e 100644
---- a/hw/display/vga-helpers.h
-+++ b/hw/display/vga-helpers.h
-@@ -95,20 +95,46 @@ static void vga_draw_glyph9(uint8_t *d, int linesize,
-     } while (--h);
- }
-+static inline uint8_t vga_read_byte(VGACommonState *vga, uint32_t addr)
-+{
-+    return vga->vram_ptr[addr & vga->vbe_size_mask];
-+}
-+
-+static inline uint16_t vga_read_word_le(VGACommonState *vga, uint32_t addr)
-+{
-+    uint32_t offset = addr & vga->vbe_size_mask & ~1;
-+    uint16_t *ptr = (uint16_t *)(vga->vram_ptr + offset);
-+    return lduw_le_p(ptr);
-+}
-+
-+static inline uint16_t vga_read_word_be(VGACommonState *vga, uint32_t addr)
-+{
-+    uint32_t offset = addr & vga->vbe_size_mask & ~1;
-+    uint16_t *ptr = (uint16_t *)(vga->vram_ptr + offset);
-+    return lduw_be_p(ptr);
-+}
-+
-+static inline uint32_t vga_read_dword_le(VGACommonState *vga, uint32_t addr)
-+{
-+    uint32_t offset = addr & vga->vbe_size_mask & ~3;
-+    uint32_t *ptr = (uint32_t *)(vga->vram_ptr + offset);
-+    return ldl_le_p(ptr);
-+}
-+
- /*
-  * 4 color mode
-  */
--static void vga_draw_line2(VGACommonState *s1, uint8_t *d,
--                           const uint8_t *s, int width)
-+static void vga_draw_line2(VGACommonState *vga, uint8_t *d,
-+                           uint32_t addr, int width)
- {
-     uint32_t plane_mask, *palette, data, v;
-     int x;
--    palette = s1->last_palette;
--    plane_mask = mask16[s1->ar[VGA_ATC_PLANE_ENABLE] & 0xf];
-+    palette = vga->last_palette;
-+    plane_mask = mask16[vga->ar[VGA_ATC_PLANE_ENABLE] & 0xf];
-     width >>= 3;
-     for(x = 0; x < width; x++) {
--        data = ((uint32_t *)s)[0];
-+        data = vga_read_dword_le(vga, addr);
-         data &= plane_mask;
-         v = expand2[GET_PLANE(data, 0)];
-         v |= expand2[GET_PLANE(data, 2)] << 2;
-@@ -124,7 +150,7 @@ static void vga_draw_line2(VGACommonState *s1, uint8_t *d,
-         ((uint32_t *)d)[6] = palette[(v >> 4) & 0xf];
-         ((uint32_t *)d)[7] = palette[(v >> 0) & 0xf];
-         d += 32;
--        s += 4;
-+        addr += 4;
-     }
- }
-@@ -134,17 +160,17 @@ static void vga_draw_line2(VGACommonState *s1, uint8_t *d,
- /*
-  * 4 color mode, dup2 horizontal
-  */
--static void vga_draw_line2d2(VGACommonState *s1, uint8_t *d,
--                             const uint8_t *s, int width)
-+static void vga_draw_line2d2(VGACommonState *vga, uint8_t *d,
-+                             uint32_t addr, int width)
- {
-     uint32_t plane_mask, *palette, data, v;
-     int x;
--    palette = s1->last_palette;
--    plane_mask = mask16[s1->ar[VGA_ATC_PLANE_ENABLE] & 0xf];
-+    palette = vga->last_palette;
-+    plane_mask = mask16[vga->ar[VGA_ATC_PLANE_ENABLE] & 0xf];
-     width >>= 3;
-     for(x = 0; x < width; x++) {
--        data = ((uint32_t *)s)[0];
-+        data = vga_read_dword_le(vga, addr);
-         data &= plane_mask;
-         v = expand2[GET_PLANE(data, 0)];
-         v |= expand2[GET_PLANE(data, 2)] << 2;
-@@ -160,24 +186,24 @@ static void vga_draw_line2d2(VGACommonState *s1, uint8_t *d,
-         PUT_PIXEL2(d, 6, palette[(v >> 4) & 0xf]);
-         PUT_PIXEL2(d, 7, palette[(v >> 0) & 0xf]);
-         d += 64;
--        s += 4;
-+        addr += 4;
-     }
- }
- /*
-  * 16 color mode
-  */
--static void vga_draw_line4(VGACommonState *s1, uint8_t *d,
--                           const uint8_t *s, int width)
-+static void vga_draw_line4(VGACommonState *vga, uint8_t *d,
-+                           uint32_t addr, int width)
- {
-     uint32_t plane_mask, data, v, *palette;
-     int x;
--    palette = s1->last_palette;
--    plane_mask = mask16[s1->ar[VGA_ATC_PLANE_ENABLE] & 0xf];
-+    palette = vga->last_palette;
-+    plane_mask = mask16[vga->ar[VGA_ATC_PLANE_ENABLE] & 0xf];
-     width >>= 3;
-     for(x = 0; x < width; x++) {
--        data = ((uint32_t *)s)[0];
-+        data = vga_read_dword_le(vga, addr);
-         data &= plane_mask;
-         v = expand4[GET_PLANE(data, 0)];
-         v |= expand4[GET_PLANE(data, 1)] << 1;
-@@ -192,24 +218,24 @@ static void vga_draw_line4(VGACommonState *s1, uint8_t *d,
-         ((uint32_t *)d)[6] = palette[(v >> 4) & 0xf];
-         ((uint32_t *)d)[7] = palette[(v >> 0) & 0xf];
-         d += 32;
--        s += 4;
-+        addr += 4;
-     }
- }
- /*
-  * 16 color mode, dup2 horizontal
-  */
--static void vga_draw_line4d2(VGACommonState *s1, uint8_t *d,
--                             const uint8_t *s, int width)
-+static void vga_draw_line4d2(VGACommonState *vga, uint8_t *d,
-+                             uint32_t addr, int width)
- {
-     uint32_t plane_mask, data, v, *palette;
-     int x;
--    palette = s1->last_palette;
--    plane_mask = mask16[s1->ar[VGA_ATC_PLANE_ENABLE] & 0xf];
-+    palette = vga->last_palette;
-+    plane_mask = mask16[vga->ar[VGA_ATC_PLANE_ENABLE] & 0xf];
-     width >>= 3;
-     for(x = 0; x < width; x++) {
--        data = ((uint32_t *)s)[0];
-+        data = vga_read_dword_le(vga, addr);
-         data &= plane_mask;
-         v = expand4[GET_PLANE(data, 0)];
-         v |= expand4[GET_PLANE(data, 1)] << 1;
-@@ -224,7 +250,7 @@ static void vga_draw_line4d2(VGACommonState *s1, uint8_t *d,
-         PUT_PIXEL2(d, 6, palette[(v >> 4) & 0xf]);
-         PUT_PIXEL2(d, 7, palette[(v >> 0) & 0xf]);
-         d += 64;
--        s += 4;
-+        addr += 4;
-     }
- }
-@@ -233,21 +259,21 @@ static void vga_draw_line4d2(VGACommonState *s1, uint8_t *d,
-  *
-  * XXX: add plane_mask support (never used in standard VGA modes)
-  */
--static void vga_draw_line8d2(VGACommonState *s1, uint8_t *d,
--                             const uint8_t *s, int width)
-+static void vga_draw_line8d2(VGACommonState *vga, uint8_t *d,
-+                             uint32_t addr, int width)
- {
-     uint32_t *palette;
-     int x;
--    palette = s1->last_palette;
-+    palette = vga->last_palette;
-     width >>= 3;
-     for(x = 0; x < width; x++) {
--        PUT_PIXEL2(d, 0, palette[s[0]]);
--        PUT_PIXEL2(d, 1, palette[s[1]]);
--        PUT_PIXEL2(d, 2, palette[s[2]]);
--        PUT_PIXEL2(d, 3, palette[s[3]]);
-+        PUT_PIXEL2(d, 0, palette[vga_read_byte(vga, addr + 0)]);
-+        PUT_PIXEL2(d, 1, palette[vga_read_byte(vga, addr + 1)]);
-+        PUT_PIXEL2(d, 2, palette[vga_read_byte(vga, addr + 2)]);
-+        PUT_PIXEL2(d, 3, palette[vga_read_byte(vga, addr + 3)]);
-         d += 32;
--        s += 4;
-+        addr += 4;
-     }
- }
-@@ -256,63 +282,63 @@ static void vga_draw_line8d2(VGACommonState *s1, uint8_t *d,
-  *
-  * XXX: add plane_mask support (never used in standard VGA modes)
-  */
--static void vga_draw_line8(VGACommonState *s1, uint8_t *d,
--                           const uint8_t *s, int width)
-+static void vga_draw_line8(VGACommonState *vga, uint8_t *d,
-+                           uint32_t addr, int width)
- {
-     uint32_t *palette;
-     int x;
--    palette = s1->last_palette;
-+    palette = vga->last_palette;
-     width >>= 3;
-     for(x = 0; x < width; x++) {
--        ((uint32_t *)d)[0] = palette[s[0]];
--        ((uint32_t *)d)[1] = palette[s[1]];
--        ((uint32_t *)d)[2] = palette[s[2]];
--        ((uint32_t *)d)[3] = palette[s[3]];
--        ((uint32_t *)d)[4] = palette[s[4]];
--        ((uint32_t *)d)[5] = palette[s[5]];
--        ((uint32_t *)d)[6] = palette[s[6]];
--        ((uint32_t *)d)[7] = palette[s[7]];
-+        ((uint32_t *)d)[0] = palette[vga_read_byte(vga, addr + 0)];
-+        ((uint32_t *)d)[1] = palette[vga_read_byte(vga, addr + 1)];
-+        ((uint32_t *)d)[2] = palette[vga_read_byte(vga, addr + 2)];
-+        ((uint32_t *)d)[3] = palette[vga_read_byte(vga, addr + 3)];
-+        ((uint32_t *)d)[4] = palette[vga_read_byte(vga, addr + 4)];
-+        ((uint32_t *)d)[5] = palette[vga_read_byte(vga, addr + 5)];
-+        ((uint32_t *)d)[6] = palette[vga_read_byte(vga, addr + 6)];
-+        ((uint32_t *)d)[7] = palette[vga_read_byte(vga, addr + 7)];
-         d += 32;
--        s += 8;
-+        addr += 8;
-     }
- }
- /*
-  * 15 bit color
-  */
--static void vga_draw_line15_le(VGACommonState *s1, uint8_t *d,
--                               const uint8_t *s, int width)
-+static void vga_draw_line15_le(VGACommonState *vga, uint8_t *d,
-+                               uint32_t addr, int width)
- {
-     int w;
-     uint32_t v, r, g, b;
-     w = width;
-     do {
--        v = lduw_le_p((void *)s);
-+        v = vga_read_word_le(vga, addr);
-         r = (v >> 7) & 0xf8;
-         g = (v >> 2) & 0xf8;
-         b = (v << 3) & 0xf8;
-         ((uint32_t *)d)[0] = rgb_to_pixel32(r, g, b);
--        s += 2;
-+        addr += 2;
-         d += 4;
-     } while (--w != 0);
- }
--static void vga_draw_line15_be(VGACommonState *s1, uint8_t *d,
--                               const uint8_t *s, int width)
-+static void vga_draw_line15_be(VGACommonState *vga, uint8_t *d,
-+                               uint32_t addr, int width)
- {
-     int w;
-     uint32_t v, r, g, b;
-     w = width;
-     do {
--        v = lduw_be_p((void *)s);
-+        v = vga_read_word_be(vga, addr);
-         r = (v >> 7) & 0xf8;
-         g = (v >> 2) & 0xf8;
-         b = (v << 3) & 0xf8;
-         ((uint32_t *)d)[0] = rgb_to_pixel32(r, g, b);
--        s += 2;
-+        addr += 2;
-         d += 4;
-     } while (--w != 0);
- }
-@@ -320,38 +346,38 @@ static void vga_draw_line15_be(VGACommonState *s1, uint8_t *d,
- /*
-  * 16 bit color
-  */
--static void vga_draw_line16_le(VGACommonState *s1, uint8_t *d,
--                               const uint8_t *s, int width)
-+static void vga_draw_line16_le(VGACommonState *vga, uint8_t *d,
-+                               uint32_t addr, int width)
- {
-     int w;
-     uint32_t v, r, g, b;
-     w = width;
-     do {
--        v = lduw_le_p((void *)s);
-+        v = vga_read_word_le(vga, addr);
-         r = (v >> 8) & 0xf8;
-         g = (v >> 3) & 0xfc;
-         b = (v << 3) & 0xf8;
-         ((uint32_t *)d)[0] = rgb_to_pixel32(r, g, b);
--        s += 2;
-+        addr += 2;
-         d += 4;
-     } while (--w != 0);
- }
--static void vga_draw_line16_be(VGACommonState *s1, uint8_t *d,
--                               const uint8_t *s, int width)
-+static void vga_draw_line16_be(VGACommonState *vga, uint8_t *d,
-+                               uint32_t addr, int width)
- {
-     int w;
-     uint32_t v, r, g, b;
-     w = width;
-     do {
--        v = lduw_be_p((void *)s);
-+        v = vga_read_word_be(vga, addr);
-         r = (v >> 8) & 0xf8;
-         g = (v >> 3) & 0xfc;
-         b = (v << 3) & 0xf8;
-         ((uint32_t *)d)[0] = rgb_to_pixel32(r, g, b);
--        s += 2;
-+        addr += 2;
-         d += 4;
-     } while (--w != 0);
- }
-@@ -359,36 +385,36 @@ static void vga_draw_line16_be(VGACommonState *s1, uint8_t *d,
- /*
-  * 24 bit color
-  */
--static void vga_draw_line24_le(VGACommonState *s1, uint8_t *d,
--                               const uint8_t *s, int width)
-+static void vga_draw_line24_le(VGACommonState *vga, uint8_t *d,
-+                               uint32_t addr, int width)
- {
-     int w;
-     uint32_t r, g, b;
-     w = width;
-     do {
--        b = s[0];
--        g = s[1];
--        r = s[2];
-+        b = vga_read_byte(vga, addr + 0);
-+        g = vga_read_byte(vga, addr + 1);
-+        r = vga_read_byte(vga, addr + 2);
-         ((uint32_t *)d)[0] = rgb_to_pixel32(r, g, b);
--        s += 3;
-+        addr += 3;
-         d += 4;
-     } while (--w != 0);
- }
--static void vga_draw_line24_be(VGACommonState *s1, uint8_t *d,
--                               const uint8_t *s, int width)
-+static void vga_draw_line24_be(VGACommonState *vga, uint8_t *d,
-+                               uint32_t addr, int width)
- {
-     int w;
-     uint32_t r, g, b;
-     w = width;
-     do {
--        r = s[0];
--        g = s[1];
--        b = s[2];
-+        r = vga_read_byte(vga, addr + 0);
-+        g = vga_read_byte(vga, addr + 1);
-+        b = vga_read_byte(vga, addr + 2);
-         ((uint32_t *)d)[0] = rgb_to_pixel32(r, g, b);
--        s += 3;
-+        addr += 3;
-         d += 4;
-     } while (--w != 0);
- }
-@@ -396,44 +422,36 @@ static void vga_draw_line24_be(VGACommonState *s1, uint8_t *d,
- /*
-  * 32 bit color
-  */
--static void vga_draw_line32_le(VGACommonState *s1, uint8_t *d,
--                               const uint8_t *s, int width)
-+static void vga_draw_line32_le(VGACommonState *vga, uint8_t *d,
-+                               uint32_t addr, int width)
- {
--#ifndef HOST_WORDS_BIGENDIAN
--    memcpy(d, s, width * 4);
--#else
-     int w;
-     uint32_t r, g, b;
-     w = width;
-     do {
--        b = s[0];
--        g = s[1];
--        r = s[2];
-+        b = vga_read_byte(vga, addr + 0);
-+        g = vga_read_byte(vga, addr + 1);
-+        r = vga_read_byte(vga, addr + 2);
-         ((uint32_t *)d)[0] = rgb_to_pixel32(r, g, b);
--        s += 4;
-+        addr += 4;
-         d += 4;
-     } while (--w != 0);
--#endif
- }
--static void vga_draw_line32_be(VGACommonState *s1, uint8_t *d,
--                               const uint8_t *s, int width)
-+static void vga_draw_line32_be(VGACommonState *vga, uint8_t *d,
-+                               uint32_t addr, int width)
- {
--#ifdef HOST_WORDS_BIGENDIAN
--    memcpy(d, s, width * 4);
--#else
-     int w;
-     uint32_t r, g, b;
-     w = width;
-     do {
--        r = s[1];
--        g = s[2];
--        b = s[3];
-+        r = vga_read_byte(vga, addr + 1);
-+        g = vga_read_byte(vga, addr + 2);
-+        b = vga_read_byte(vga, addr + 3);
-         ((uint32_t *)d)[0] = rgb_to_pixel32(r, g, b);
--        s += 4;
-+        addr += 4;
-         d += 4;
-     } while (--w != 0);
--#endif
- }
-diff --git a/hw/display/vga.c b/hw/display/vga.c
-index 533d8d7895..13e4a5d55d 100644
---- a/hw/display/vga.c
-+++ b/hw/display/vga.c
-@@ -1005,7 +1005,7 @@ void vga_mem_writeb(VGACommonState *s, hwaddr addr, uint32_t val)
- }
- typedef void vga_draw_line_func(VGACommonState *s1, uint8_t *d,
--                                const uint8_t *s, int width);
-+                                uint32_t srcaddr, int width);
- #include "vga-helpers.h"
-@@ -1666,7 +1666,7 @@ static void vga_draw_graphic(VGACommonState *s, int full_update)
-             if (y_start < 0)
-                 y_start = y;
-             if (!(is_buffer_shared(surface))) {
--                vga_draw_line(s, d, s->vram_ptr + addr, width);
-+                vga_draw_line(s, d, addr, width);
-                 if (s->cursor_draw_line)
-                     s->cursor_draw_line(s, d, y);
-             }
-@@ -2170,6 +2170,7 @@ void vga_common_init(VGACommonState *s, Object *obj, bool global_vmstate)
-     if (!s->vbe_size) {
-         s->vbe_size = s->vram_size;
-     }
-+    s->vbe_size_mask = s->vbe_size - 1;
-     s->is_vbe_vmstate = 1;
-     memory_region_init_ram(&s->vram, obj, "vga.vram", s->vram_size,
-diff --git a/hw/display/vga_int.h b/hw/display/vga_int.h
-index dd6c958da3..ad34a1f048 100644
---- a/hw/display/vga_int.h
-+++ b/hw/display/vga_int.h
-@@ -94,6 +94,7 @@ typedef struct VGACommonState {
-     uint32_t vram_size;
-     uint32_t vram_size_mb; /* property */
-     uint32_t vbe_size;
-+    uint32_t vbe_size_mask;
-     uint32_t latch;
-     bool has_chain4_alias;
-     MemoryRegion chain4_alias;
--- 
-2.11.0
-
diff --git a/debian/patches/extra/0013-multiboot-validate-multiboot-header-address-values.patch b/debian/patches/extra/0013-multiboot-validate-multiboot-header-address-values.patch
deleted file mode 100644 (file)
index 37d12af..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Prasad J Pandit <pjp@fedoraproject.org>
-Date: Thu, 7 Sep 2017 12:02:56 +0530
-Subject: [PATCH] multiboot: validate multiboot header address values
-
-While loading kernel via multiboot-v1 image, (flags & 0x00010000)
-indicates that multiboot header contains valid addresses to load
-the kernel image. These addresses are used to compute kernel
-size and kernel text offset in the OS image. Validate these
-address values to avoid an OOB access issue.
-
-This is CVE-2017-14167.
-
-Reported-by: Thomas Garnier <thgarnie@google.com>
-Signed-off-by: Prasad J Pandit <pjp@fedoraproject.org>
----
- hw/i386/multiboot.c | 19 +++++++++++++++++++
- 1 file changed, 19 insertions(+)
-
-diff --git a/hw/i386/multiboot.c b/hw/i386/multiboot.c
-index f13e23139b..22688d376d 100644
---- a/hw/i386/multiboot.c
-+++ b/hw/i386/multiboot.c
-@@ -221,15 +221,34 @@ int load_multiboot(FWCfgState *fw_cfg,
-         uint32_t mh_header_addr = ldl_p(header+i+12);
-         uint32_t mh_load_end_addr = ldl_p(header+i+20);
-         uint32_t mh_bss_end_addr = ldl_p(header+i+24);
-+
-         mh_load_addr = ldl_p(header+i+16);
-+        if (mh_header_addr < mh_load_addr) {
-+            fprintf(stderr, "invalid mh_load_addr address\n");
-+            exit(1);
-+        }
-+
-         uint32_t mb_kernel_text_offset = i - (mh_header_addr - mh_load_addr);
-         uint32_t mb_load_size = 0;
-         mh_entry_addr = ldl_p(header+i+28);
-         if (mh_load_end_addr) {
-+            if (mh_bss_end_addr < mh_load_addr) {
-+                fprintf(stderr, "invalid mh_bss_end_addr address\n");
-+                exit(1);
-+            }
-             mb_kernel_size = mh_bss_end_addr - mh_load_addr;
-+
-+            if (mh_load_end_addr < mh_load_addr) {
-+                fprintf(stderr, "invalid mh_load_end_addr address\n");
-+                exit(1);
-+            }
-             mb_load_size = mh_load_end_addr - mh_load_addr;
-         } else {
-+            if (kernel_file_size < mb_kernel_text_offset) {
-+                fprintf(stderr, "invalid kernel_file_size\n");
-+                exit(1);
-+            }
-             mb_kernel_size = kernel_file_size - mb_kernel_text_offset;
-             mb_load_size = mb_kernel_size;
-         }
--- 
-2.11.0
-
diff --git a/debian/patches/extra/0014-virtio-fix-descriptor-counting-in-virtqueue_pop.patch b/debian/patches/extra/0014-virtio-fix-descriptor-counting-in-virtqueue_pop.patch
deleted file mode 100644 (file)
index 526e67f..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Wolfgang Bumiller <w.bumiller@proxmox.com>
-Date: Wed, 20 Sep 2017 08:09:33 +0200
-Subject: [PATCH] virtio: fix descriptor counting in virtqueue_pop
-
-While changing the s/g list allocation, commit 3b3b0628
-also changed the descriptor counting to count iovec entries
-as split by cpu_physical_memory_map(). Previously only the
-actual descriptor entries were counted and the split into
-the iovec happened afterwards in virtqueue_map().
-Count the entries again instead to avoid erroneous
-"Looped descriptor" errors.
-
-Reported-by: Hans Middelhoek <h.middelhoek@ospito.nl>
-Link: https://forum.proxmox.com/threads/vm-crash-with-memory-hotplug.35904/
-Fixes: 3b3b0628217e ("virtio: slim down allocation of VirtQueueElements")
-Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
-Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
-Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
----
- hw/virtio/virtio.c | 6 +++---
- 1 file changed, 3 insertions(+), 3 deletions(-)
-
-diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c
-index 890b4d7eb7..33bb770177 100644
---- a/hw/virtio/virtio.c
-+++ b/hw/virtio/virtio.c
-@@ -834,7 +834,7 @@ void *virtqueue_pop(VirtQueue *vq, size_t sz)
-     int64_t len;
-     VirtIODevice *vdev = vq->vdev;
-     VirtQueueElement *elem = NULL;
--    unsigned out_num, in_num;
-+    unsigned out_num, in_num, elem_entries;
-     hwaddr addr[VIRTQUEUE_MAX_SIZE];
-     struct iovec iov[VIRTQUEUE_MAX_SIZE];
-     VRingDesc desc;
-@@ -852,7 +852,7 @@ void *virtqueue_pop(VirtQueue *vq, size_t sz)
-     smp_rmb();
-     /* When we start there are none of either input nor output. */
--    out_num = in_num = 0;
-+    out_num = in_num = elem_entries = 0;
-     max = vq->vring.num;
-@@ -922,7 +922,7 @@ void *virtqueue_pop(VirtQueue *vq, size_t sz)
-         }
-         /* If we've got too many, that implies a descriptor loop. */
--        if ((in_num + out_num) > max) {
-+        if (++elem_entries > max) {
-             virtio_error(vdev, "Looped descriptor");
-             goto err_undo_map;
-         }
--- 
-2.11.0
-
diff --git a/debian/patches/extra/0015-nbd-server-CVE-2017-15119-Reject-options-larger-than.patch b/debian/patches/extra/0015-nbd-server-CVE-2017-15119-Reject-options-larger-than.patch
deleted file mode 100644 (file)
index 4f966dc..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Wolfgang Bumiller <w.bumiller@proxmox.com>
-Date: Wed, 29 Nov 2017 09:39:55 +0100
-Subject: [PATCH] nbd/server: CVE-2017-15119 Reject options larger than 32M
-
-Backported-from: fdad35ef6c58
----
- nbd/server.c | 6 ++++++
- 1 file changed, 6 insertions(+)
-
-diff --git a/nbd/server.c b/nbd/server.c
-index a98bb21a0a..4d6da8ac06 100644
---- a/nbd/server.c
-+++ b/nbd/server.c
-@@ -489,6 +489,12 @@ static int nbd_negotiate_options(NBDClient *client)
-         }
-         length = be32_to_cpu(length);
-+        if (length > NBD_MAX_BUFFER_SIZE) {
-+            LOG("len (%" PRIu32" ) is larger than max len (%u)",
-+                length, NBD_MAX_BUFFER_SIZE);
-+            return -EINVAL;
-+        }
-+
-         TRACE("Checking option 0x%" PRIx32, clientflags);
-         if (client->tlscreds &&
-             client->ioc == (QIOChannel *)client->sioc) {
--- 
-2.11.0
-
diff --git a/debian/patches/extra/0016-vga-migration-Update-memory-map-in-post_load.patch b/debian/patches/extra/0016-vga-migration-Update-memory-map-in-post_load.patch
deleted file mode 100644 (file)
index 5941926..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: "Dr. David Alan Gilbert" <dgilbert@redhat.com>
-Date: Fri, 4 Aug 2017 12:33:29 +0100
-Subject: [PATCH] vga/migration: Update memory map in post_load
-
-After migration the chain4 alias mapping added by 80763888 (in 2011)
-might be missing, since there's no call to vga_update_memory_access
-in the post_load after the registers are updated.  Add it back.
-
-Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
-Reviewed-by: Juan Quintela <quintela@redhat.com>
-Message-id: 20170804113329.13609-1-dgilbert@redhat.com
-Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
----
- hw/display/vga.c | 1 +
- 1 file changed, 1 insertion(+)
-
-diff --git a/hw/display/vga.c b/hw/display/vga.c
-index 13e4a5d55d..a99d831e04 100644
---- a/hw/display/vga.c
-+++ b/hw/display/vga.c
-@@ -2050,6 +2050,7 @@ static int vga_common_post_load(void *opaque, int version_id)
-     /* force refresh */
-     s->graphic_mode = -1;
-     vbe_update_vgaregs(s);
-+    vga_update_memory_access(s);
-     return 0;
- }
--- 
-2.11.0
-
diff --git a/debian/patches/extra/0017-vga-drop-line_offset-variable.patch b/debian/patches/extra/0017-vga-drop-line_offset-variable.patch
deleted file mode 100644 (file)
index d441d02..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Gerd Hoffmann <kraxel@redhat.com>
-Date: Tue, 10 Oct 2017 16:13:21 +0200
-Subject: [PATCH] vga: drop line_offset variable
-
-Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
----
- hw/display/vga.c | 7 +++----
- 1 file changed, 3 insertions(+), 4 deletions(-)
-
-diff --git a/hw/display/vga.c b/hw/display/vga.c
-index a99d831e04..77af807a51 100644
---- a/hw/display/vga.c
-+++ b/hw/display/vga.c
-@@ -1464,7 +1464,7 @@ static void vga_draw_graphic(VGACommonState *s, int full_update)
- {
-     DisplaySurface *surface = qemu_console_surface(s->con);
-     int y1, y, update, linesize, y_start, double_scan, mask, depth;
--    int width, height, shift_control, line_offset, bwidth, bits;
-+    int width, height, shift_control, bwidth, bits;
-     ram_addr_t page0, page1;
-     DirtyBitmapSnapshot *snap = NULL;
-     int disp_width, multi_scan, multi_run;
-@@ -1614,7 +1614,6 @@ static void vga_draw_graphic(VGACommonState *s, int full_update)
-         s->cursor_invalidate(s);
-     }
--    line_offset = s->line_offset;
- #if 0
-     printf("w=%d h=%d v=%d line_offset=%d cr[0x09]=0x%02x cr[0x17]=0x%02x linecmp=%d sr[0x01]=0x%02x\n",
-            width, height, v, line_offset, s->cr[9], s->cr[VGA_CRTC_MODE],
-@@ -1629,7 +1628,7 @@ static void vga_draw_graphic(VGACommonState *s, int full_update)
-     if (!full_update) {
-         ram_addr_t region_start = addr1;
--        ram_addr_t region_end = addr1 + line_offset * height;
-+        ram_addr_t region_end = addr1 + s->line_offset * height;
-         vga_sync_dirty_bitmap(s);
-         if (s->line_compare < height) {
-             /* split screen mode */
-@@ -1681,7 +1680,7 @@ static void vga_draw_graphic(VGACommonState *s, int full_update)
-         if (!multi_run) {
-             mask = (s->cr[VGA_CRTC_MODE] & 3) ^ 3;
-             if ((y1 & mask) == mask)
--                addr1 += line_offset;
-+                addr1 += s->line_offset;
-             y1++;
-             multi_run = multi_scan;
-         } else {
--- 
-2.11.0
-
diff --git a/debian/patches/extra/0018-vga-handle-cirrus-vbe-mode-wraparounds.patch b/debian/patches/extra/0018-vga-handle-cirrus-vbe-mode-wraparounds.patch
deleted file mode 100644 (file)
index 9fe31bb..0000000
+++ /dev/null
@@ -1,103 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Gerd Hoffmann <kraxel@redhat.com>
-Date: Tue, 10 Oct 2017 16:13:22 +0200
-Subject: [PATCH] vga: handle cirrus vbe mode wraparounds.
-
-Commit "3d90c62548 vga: stop passing pointers to vga_draw_line*
-functions" is incomplete.  It doesn't handle the case that the vga
-rendering code tries to create a shared surface, i.e. a pixman image
-backed by vga video memory.  That can not work in case the guest display
-wraps from end of video memory to the start.  So force shadowing in that
-case.  Also adjust the snapshot region calculation.
-
-Can trigger with cirrus only, when programming vbe modes using the bochs
-api (stdvga, also qxl and virtio-vga in vga compat mode) wrap arounds
-can't happen.
-
-Fixes: CVE-2017-13672
-Fixes: 3d90c6254863693a6b13d918d2b8682e08bbc681
-Cc: P J P <ppandit@redhat.com>
-Reported-by: David Buchanan <d@vidbuchanan.co.uk>
-Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
-Message-id: 20171010141323.14049-3-kraxel@redhat.com
----
- hw/display/vga.c | 28 +++++++++++++++++++++-------
- 1 file changed, 21 insertions(+), 7 deletions(-)
-
-diff --git a/hw/display/vga.c b/hw/display/vga.c
-index 77af807a51..7bdbf7441e 100644
---- a/hw/display/vga.c
-+++ b/hw/display/vga.c
-@@ -1465,13 +1465,13 @@ static void vga_draw_graphic(VGACommonState *s, int full_update)
-     DisplaySurface *surface = qemu_console_surface(s->con);
-     int y1, y, update, linesize, y_start, double_scan, mask, depth;
-     int width, height, shift_control, bwidth, bits;
--    ram_addr_t page0, page1;
-+    ram_addr_t page0, page1, region_start, region_end;
-     DirtyBitmapSnapshot *snap = NULL;
-     int disp_width, multi_scan, multi_run;
-     uint8_t *d;
-     uint32_t v, addr1, addr;
-     vga_draw_line_func *vga_draw_line = NULL;
--    bool share_surface;
-+    bool share_surface, force_shadow = false;
-     pixman_format_code_t format;
- #ifdef HOST_WORDS_BIGENDIAN
-     bool byteswap = !s->big_endian_fb;
-@@ -1484,6 +1484,15 @@ static void vga_draw_graphic(VGACommonState *s, int full_update)
-     s->get_resolution(s, &width, &height);
-     disp_width = width;
-+    region_start = (s->start_addr * 4);
-+    region_end = region_start + s->line_offset * height;
-+    if (region_end > s->vbe_size) {
-+        /* wraps around (can happen with cirrus vbe modes) */
-+        region_start = 0;
-+        region_end = s->vbe_size;
-+        force_shadow = true;
-+    }
-+
-     shift_control = (s->gr[VGA_GFX_MODE] >> 5) & 3;
-     double_scan = (s->cr[VGA_CRTC_MAX_SCAN] >> 7);
-     if (shift_control != 1) {
-@@ -1523,7 +1532,7 @@ static void vga_draw_graphic(VGACommonState *s, int full_update)
-     format = qemu_default_pixman_format(depth, !byteswap);
-     if (format) {
-         share_surface = dpy_gfx_check_format(s->con, format)
--            && !s->force_shadow;
-+            && !s->force_shadow && !force_shadow;
-     } else {
-         share_surface = false;
-     }
-@@ -1627,8 +1636,6 @@ static void vga_draw_graphic(VGACommonState *s, int full_update)
-     y1 = 0;
-     if (!full_update) {
--        ram_addr_t region_start = addr1;
--        ram_addr_t region_end = addr1 + s->line_offset * height;
-         vga_sync_dirty_bitmap(s);
-         if (s->line_compare < height) {
-             /* split screen mode */
-@@ -1651,10 +1658,17 @@ static void vga_draw_graphic(VGACommonState *s, int full_update)
-             addr = (addr & ~0x8000) | ((y1 & 2) << 14);
-         }
-         update = full_update;
--        page0 = addr;
--        page1 = addr + bwidth - 1;
-+        page0 = addr & s->vbe_size_mask;
-+        page1 = (addr + bwidth - 1) & s->vbe_size_mask;
-         if (full_update) {
-             update = 1;
-+        } else if (page1 < page0) {
-+            /* scanline wraps from end of video memory to the start */
-+            assert(force_shadow);
-+            update = memory_region_snapshot_get_dirty(&s->vram, snap,
-+                                                      page0, 0);
-+            update |= memory_region_snapshot_get_dirty(&s->vram, snap,
-+                                                       page1, 0);
-         } else {
-             update = memory_region_snapshot_get_dirty(&s->vram, snap,
-                                                       page0, page1 - page0);
--- 
-2.11.0
-
diff --git a/debian/patches/extra/0019-vga-add-ram_addr_t-cast.patch b/debian/patches/extra/0019-vga-add-ram_addr_t-cast.patch
deleted file mode 100644 (file)
index bc89a7e..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Gerd Hoffmann <kraxel@redhat.com>
-Date: Tue, 10 Oct 2017 16:13:23 +0200
-Subject: [PATCH] vga: add ram_addr_t cast
-
-Reported by Coverity.
-
-Fixes: CID 1381409
-Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
-Message-id: 20171010141323.14049-4-kraxel@redhat.com
----
- hw/display/vga.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/hw/display/vga.c b/hw/display/vga.c
-index 7bdbf7441e..63ba404ef2 100644
---- a/hw/display/vga.c
-+++ b/hw/display/vga.c
-@@ -1485,7 +1485,7 @@ static void vga_draw_graphic(VGACommonState *s, int full_update)
-     disp_width = width;
-     region_start = (s->start_addr * 4);
--    region_end = region_start + s->line_offset * height;
-+    region_end = region_start + (ram_addr_t)s->line_offset * height;
-     if (region_end > s->vbe_size) {
-         /* wraps around (can happen with cirrus vbe modes) */
-         region_start = 0;
--- 
-2.11.0
-
diff --git a/debian/patches/extra/0020-vga-fix-region-checks-in-wraparound-case.patch b/debian/patches/extra/0020-vga-fix-region-checks-in-wraparound-case.patch
deleted file mode 100644 (file)
index 371403d..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Gerd Hoffmann <kraxel@redhat.com>
-Date: Mon, 30 Oct 2017 11:28:30 +0100
-Subject: [PATCH] vga: fix region checks in wraparound case
-
-Cc: "Dr. David Alan Gilbert" <dgilbert@redhat.com>
-Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
-Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
-Message-id: 20171030102830.4469-1-kraxel@redhat.com
----
- hw/display/vga.c | 4 ++--
- 1 file changed, 2 insertions(+), 2 deletions(-)
-
-diff --git a/hw/display/vga.c b/hw/display/vga.c
-index 63ba404ef2..a58d8bcd67 100644
---- a/hw/display/vga.c
-+++ b/hw/display/vga.c
-@@ -1666,9 +1666,9 @@ static void vga_draw_graphic(VGACommonState *s, int full_update)
-             /* scanline wraps from end of video memory to the start */
-             assert(force_shadow);
-             update = memory_region_snapshot_get_dirty(&s->vram, snap,
--                                                      page0, 0);
-+                                                      page0, s->vbe_size - page0);
-             update |= memory_region_snapshot_get_dirty(&s->vram, snap,
--                                                       page1, 0);
-+                                                       0, page1);
-         } else {
-             update = memory_region_snapshot_get_dirty(&s->vram, snap,
-                                                       page0, page1 - page0);
--- 
-2.11.0
-
diff --git a/debian/patches/extra/0021-io-monitor-encoutput-buffer-size-from-websocket-GSou.patch b/debian/patches/extra/0021-io-monitor-encoutput-buffer-size-from-websocket-GSou.patch
deleted file mode 100644 (file)
index 3a9e498..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: "Daniel P. Berrange" <berrange@redhat.com>
-Date: Mon, 9 Oct 2017 14:43:42 +0100
-Subject: [PATCH] io: monitor encoutput buffer size from websocket GSource
-
-The websocket GSource is monitoring the size of the rawoutput
-buffer to determine if the channel can accepts more writes.
-The rawoutput buffer, however, is merely a temporary staging
-buffer before data is copied into the encoutput buffer. Thus
-its size will always be zero when the GSource runs.
-
-This flaw causes the encoutput buffer to grow without bound
-if the other end of the underlying data channel doesn't
-read data being sent. This can be seen with VNC if a client
-is on a slow WAN link and the guest OS is sending many screen
-updates. A malicious VNC client can act like it is on a slow
-link by playing a video in the guest and then reading data
-very slowly, causing QEMU host memory to expand arbitrarily.
-
-This issue is assigned CVE-2017-15268, publically reported in
-
-  https://bugs.launchpad.net/qemu/+bug/1718964
-
-Reviewed-by: Eric Blake <eblake@redhat.com>
-Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
----
- io/channel-websock.c | 4 ++--
- 1 file changed, 2 insertions(+), 2 deletions(-)
-
-diff --git a/io/channel-websock.c b/io/channel-websock.c
-index 8fabadea2f..882bbb4cbc 100644
---- a/io/channel-websock.c
-+++ b/io/channel-websock.c
-@@ -26,7 +26,7 @@
- #include "trace.h"
--/* Max amount to allow in rawinput/rawoutput buffers */
-+/* Max amount to allow in rawinput/encoutput buffers */
- #define QIO_CHANNEL_WEBSOCK_MAX_BUFFER 8192
- #define QIO_CHANNEL_WEBSOCK_CLIENT_KEY_LEN 24
-@@ -1006,7 +1006,7 @@ qio_channel_websock_source_prepare(GSource *source,
-     if (wsource->wioc->rawinput.offset) {
-         cond |= G_IO_IN;
-     }
--    if (wsource->wioc->rawoutput.offset < QIO_CHANNEL_WEBSOCK_MAX_BUFFER) {
-+    if (wsource->wioc->encoutput.offset < QIO_CHANNEL_WEBSOCK_MAX_BUFFER) {
-         cond |= G_IO_OUT;
-     }
--- 
-2.11.0
-
diff --git a/debian/patches/extra/0022-9pfs-use-g_malloc0-to-allocate-space-for-xattr.patch b/debian/patches/extra/0022-9pfs-use-g_malloc0-to-allocate-space-for-xattr.patch
deleted file mode 100644 (file)
index 649a77a..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Prasad J Pandit <pjp@fedoraproject.org>
-Date: Mon, 16 Oct 2017 14:21:59 +0200
-Subject: [PATCH] 9pfs: use g_malloc0 to allocate space for xattr
-
-9p back-end first queries the size of an extended attribute,
-allocates space for it via g_malloc() and then retrieves its
-value into allocated buffer. Race between querying attribute
-size and retrieving its could lead to memory bytes disclosure.
-Use g_malloc0() to avoid it.
-
-Reported-by: Tuomas Tynkkynen <tuomas.tynkkynen@iki.fi>
-Signed-off-by: Prasad J Pandit <pjp@fedoraproject.org>
-Signed-off-by: Greg Kurz <groug@kaod.org>
----
- hw/9pfs/9p.c | 4 ++--
- 1 file changed, 2 insertions(+), 2 deletions(-)
-
-diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c
-index c80ba67389..aaf9935ef4 100644
---- a/hw/9pfs/9p.c
-+++ b/hw/9pfs/9p.c
-@@ -3220,7 +3220,7 @@ static void coroutine_fn v9fs_xattrwalk(void *opaque)
-         xattr_fidp->fid_type = P9_FID_XATTR;
-         xattr_fidp->fs.xattr.xattrwalk_fid = true;
-         if (size) {
--            xattr_fidp->fs.xattr.value = g_malloc(size);
-+            xattr_fidp->fs.xattr.value = g_malloc0(size);
-             err = v9fs_co_llistxattr(pdu, &xattr_fidp->path,
-                                      xattr_fidp->fs.xattr.value,
-                                      xattr_fidp->fs.xattr.len);
-@@ -3253,7 +3253,7 @@ static void coroutine_fn v9fs_xattrwalk(void *opaque)
-         xattr_fidp->fid_type = P9_FID_XATTR;
-         xattr_fidp->fs.xattr.xattrwalk_fid = true;
-         if (size) {
--            xattr_fidp->fs.xattr.value = g_malloc(size);
-+            xattr_fidp->fs.xattr.value = g_malloc0(size);
-             err = v9fs_co_lgetxattr(pdu, &xattr_fidp->path,
-                                     &name, xattr_fidp->fs.xattr.value,
-                                     xattr_fidp->fs.xattr.len);
--- 
-2.11.0
-
diff --git a/debian/patches/extra/0023-cirrus-fix-oob-access-in-mode4and5-write-functions.patch b/debian/patches/extra/0023-cirrus-fix-oob-access-in-mode4and5-write-functions.patch
deleted file mode 100644 (file)
index 789998c..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Gerd Hoffmann <kraxel@redhat.com>
-Date: Wed, 11 Oct 2017 10:43:14 +0200
-Subject: [PATCH] cirrus: fix oob access in mode4and5 write functions
-
-Move dst calculation into the loop, so we apply the mask on each
-interation and will not overflow vga memory.
-
-Cc: Prasad J Pandit <pjp@fedoraproject.org>
-Reported-by: Niu Guoxiang <niuguoxiang@huawei.com>
-Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
-Message-id: 20171011084314.21752-1-kraxel@redhat.com
----
- hw/display/cirrus_vga.c | 6 ++----
- 1 file changed, 2 insertions(+), 4 deletions(-)
-
-diff --git a/hw/display/cirrus_vga.c b/hw/display/cirrus_vga.c
-index afc290ab91..077a8cb74f 100644
---- a/hw/display/cirrus_vga.c
-+++ b/hw/display/cirrus_vga.c
-@@ -2038,15 +2038,14 @@ static void cirrus_mem_writeb_mode4and5_8bpp(CirrusVGAState * s,
-     unsigned val = mem_value;
-     uint8_t *dst;
--    dst = s->vga.vram_ptr + (offset &= s->cirrus_addr_mask);
-     for (x = 0; x < 8; x++) {
-+        dst = s->vga.vram_ptr + ((offset + x) & s->cirrus_addr_mask);
-       if (val & 0x80) {
-           *dst = s->cirrus_shadow_gr1;
-       } else if (mode == 5) {
-           *dst = s->cirrus_shadow_gr0;
-       }
-       val <<= 1;
--      dst++;
-     }
-     memory_region_set_dirty(&s->vga.vram, offset, 8);
- }
-@@ -2060,8 +2059,8 @@ static void cirrus_mem_writeb_mode4and5_16bpp(CirrusVGAState * s,
-     unsigned val = mem_value;
-     uint8_t *dst;
--    dst = s->vga.vram_ptr + (offset &= s->cirrus_addr_mask);
-     for (x = 0; x < 8; x++) {
-+        dst = s->vga.vram_ptr + ((offset + 2 * x) & s->cirrus_addr_mask & ~1);
-       if (val & 0x80) {
-           *dst = s->cirrus_shadow_gr1;
-           *(dst + 1) = s->vga.gr[0x11];
-@@ -2070,7 +2069,6 @@ static void cirrus_mem_writeb_mode4and5_16bpp(CirrusVGAState * s,
-           *(dst + 1) = s->vga.gr[0x10];
-       }
-       val <<= 1;
--      dst += 2;
-     }
-     memory_region_set_dirty(&s->vga.vram, offset, 16);
- }
--- 
-2.11.0
-
diff --git a/debian/patches/extra/0024-virtio-check-VirtQueue-Vring-object-is-set.patch b/debian/patches/extra/0024-virtio-check-VirtQueue-Vring-object-is-set.patch
deleted file mode 100644 (file)
index 84c046a..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Prasad J Pandit <pjp@fedoraproject.org>
-Date: Wed, 29 Nov 2017 23:14:27 +0530
-Subject: [PATCH] virtio: check VirtQueue Vring object is set
-
-A guest could attempt to use an uninitialised VirtQueue object
-or unset Vring.align leading to a arithmetic exception. Add check
-to avoid it.
-
-Reported-by: Zhangboxian <zhangboxian@huawei.com>
-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>
-Reviewed-by: Cornelia Huck <cohuck@redhat.com>
----
- hw/virtio/virtio.c | 14 +++++++++++---
- 1 file changed, 11 insertions(+), 3 deletions(-)
-
-diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c
-index 33bb770177..76b9a9907c 100644
---- a/hw/virtio/virtio.c
-+++ b/hw/virtio/virtio.c
-@@ -183,7 +183,7 @@ void virtio_queue_update_rings(VirtIODevice *vdev, int n)
- {
-     VRing *vring = &vdev->vq[n].vring;
--    if (!vring->desc) {
-+    if (!vring->num || !vring->desc || !vring->align) {
-         /* not yet setup -> nothing to do */
-         return;
-     }
-@@ -1416,6 +1416,9 @@ void virtio_config_modern_writel(VirtIODevice *vdev,
- void virtio_queue_set_addr(VirtIODevice *vdev, int n, hwaddr addr)
- {
-+    if (!vdev->vq[n].vring.num) {
-+        return;
-+    }
-     vdev->vq[n].vring.desc = addr;
-     virtio_queue_update_rings(vdev, n);
- }
-@@ -1428,6 +1431,9 @@ hwaddr virtio_queue_get_addr(VirtIODevice *vdev, int n)
- void virtio_queue_set_rings(VirtIODevice *vdev, int n, hwaddr desc,
-                             hwaddr avail, hwaddr used)
- {
-+    if (!vdev->vq[n].vring.num) {
-+        return;
-+    }
-     vdev->vq[n].vring.desc = desc;
-     vdev->vq[n].vring.avail = avail;
-     vdev->vq[n].vring.used = used;
-@@ -1496,8 +1502,10 @@ void virtio_queue_set_align(VirtIODevice *vdev, int n, int align)
-      */
-     assert(k->has_variable_vring_alignment);
--    vdev->vq[n].vring.align = align;
--    virtio_queue_update_rings(vdev, n);
-+    if (align) {
-+        vdev->vq[n].vring.align = align;
-+        virtio_queue_update_rings(vdev, n);
-+    }
- }
- static bool virtio_queue_notify_aio_vq(VirtQueue *vq)
--- 
-2.11.0
-
diff --git a/debian/patches/extra/0025-block-gluster-glfs_lseek-workaround.patch b/debian/patches/extra/0025-block-gluster-glfs_lseek-workaround.patch
deleted file mode 100644 (file)
index e42c1fd..0000000
+++ /dev/null
@@ -1,107 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Jeff Cody <jcody@redhat.com>
-Date: Tue, 23 May 2017 13:27:50 -0400
-Subject: [PATCH] block/gluster: glfs_lseek() workaround
-
-On current released versions of glusterfs, glfs_lseek() will sometimes
-return invalid values for SEEK_DATA or SEEK_HOLE.  For SEEK_DATA and
-SEEK_HOLE, the returned value should be >= the passed offset, or < 0 in
-the case of error:
-
-LSEEK(2):
-
-    off_t lseek(int fd, off_t offset, int whence);
-
-    [...]
-
-    SEEK_HOLE
-              Adjust  the file offset to the next hole in the file greater
-              than or equal to offset.  If offset points into the middle of
-              a hole, then the file offset is set to offset.  If there is no
-              hole past offset, then the file offset is adjusted to the end
-              of the file (i.e., there is  an implicit hole at the end of
-              any file).
-
-    [...]
-
-    RETURN VALUE
-              Upon  successful  completion,  lseek()  returns  the resulting
-              offset location as measured in bytes from the beginning of the
-              file.  On error, the value (off_t) -1 is returned and errno is
-              set to indicate the error
-
-However, occasionally glfs_lseek() for SEEK_HOLE/DATA will return a
-value less than the passed offset, yet greater than zero.
-
-For instance, here are example values observed from this call:
-
-    offs = glfs_lseek(s->fd, start, SEEK_HOLE);
-    if (offs < 0) {
-        return -errno;          /* D1 and (H3 or H4) */
-    }
-
-start == 7608336384
-offs == 7607877632
-
-This causes QEMU to abort on the assert test.  When this value is
-returned, errno is also 0.
-
-This is a reported and known bug to glusterfs:
-https://bugzilla.redhat.com/show_bug.cgi?id=1425293
-
-Although this is being fixed in gluster, we still should work around it
-in QEMU, given that multiple released versions of gluster behave this
-way.
-
-This patch treats the return case of (offs < start) the same as if an
-error value other than ENXIO is returned; we will assume we learned
-nothing, and there are no holes in the file.
-
-Signed-off-by: Jeff Cody <jcody@redhat.com>
-Reviewed-by: Eric Blake <eblake@redhat.com>
-Reviewed-by: Niels de Vos <ndevos@redhat.com>
-Message-id: 87c0140e9407c08f6e74b04131b610f2e27c014c.1495560397.git.jcody@redhat.com
-Signed-off-by: Jeff Cody <jcody@redhat.com>
----
- block/gluster.c | 18 ++++++++++++++++--
- 1 file changed, 16 insertions(+), 2 deletions(-)
-
-diff --git a/block/gluster.c b/block/gluster.c
-index 4fdf68f1fc..06421ef79d 100644
---- a/block/gluster.c
-+++ b/block/gluster.c
-@@ -1287,7 +1287,14 @@ static int find_allocation(BlockDriverState *bs, off_t start,
-     if (offs < 0) {
-         return -errno;          /* D3 or D4 */
-     }
--    assert(offs >= start);
-+
-+    if (offs < start) {
-+        /* This is not a valid return by lseek().  We are safe to just return
-+         * -EIO in this case, and we'll treat it like D4. Unfortunately some
-+         *  versions of gluster server will return offs < start, so an assert
-+         *  here will unnecessarily abort QEMU. */
-+        return -EIO;
-+    }
-     if (offs > start) {
-         /* D2: in hole, next data at offs */
-@@ -1319,7 +1326,14 @@ static int find_allocation(BlockDriverState *bs, off_t start,
-     if (offs < 0) {
-         return -errno;          /* D1 and (H3 or H4) */
-     }
--    assert(offs >= start);
-+
-+    if (offs < start) {
-+        /* This is not a valid return by lseek().  We are safe to just return
-+         * -EIO in this case, and we'll treat it like H4. Unfortunately some
-+         *  versions of gluster server will return offs < start, so an assert
-+         *  here will unnecessarily abort QEMU. */
-+        return -EIO;
-+    }
-     if (offs > start) {
-         /*
--- 
-2.11.0
-
diff --git a/debian/patches/extra/0026-gluster-add-support-for-PREALLOC_MODE_FALLOC.patch b/debian/patches/extra/0026-gluster-add-support-for-PREALLOC_MODE_FALLOC.patch
deleted file mode 100644 (file)
index f794745..0000000
+++ /dev/null
@@ -1,180 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Niels de Vos <ndevos@redhat.com>
-Date: Sun, 28 May 2017 12:01:14 +0530
-Subject: [PATCH] gluster: add support for PREALLOC_MODE_FALLOC
-
-Add missing support for "preallocation=falloc" to the Gluster block
-driver. This change bases its logic on that of block/file-posix.c and
-removed the gluster_supports_zerofill() and qemu_gluster_zerofill()
-functions in favour of #ifdef checks in an easy to read
-switch-statement.
-
-Both glfs_zerofill() and glfs_fallocate() have been introduced with
-GlusterFS 3.5.0 (pkg-config glusterfs-api = 6). A #define for the
-availability of glfs_fallocate() has been added to ./configure.
-
-Reported-by: Satheesaran Sundaramoorthi <sasundar@redhat.com>
-Signed-off-by: Niels de Vos <ndevos@redhat.com>
-Message-id: 20170528063114.28691-1-ndevos@redhat.com
-URL: https://bugzilla.redhat.com/1450759
-Signed-off-by: Niels de Vos <ndevos@redhat.com>
-Signed-off-by: Jeff Cody <jcody@redhat.com>
----
- block/gluster.c | 76 ++++++++++++++++++++++++++++++---------------------------
- configure       |  6 +++++
- 2 files changed, 46 insertions(+), 36 deletions(-)
-
-diff --git a/block/gluster.c b/block/gluster.c
-index 06421ef79d..8108c89c7f 100644
---- a/block/gluster.c
-+++ b/block/gluster.c
-@@ -975,29 +975,6 @@ static coroutine_fn int qemu_gluster_co_pwrite_zeroes(BlockDriverState *bs,
-     qemu_coroutine_yield();
-     return acb.ret;
- }
--
--static inline bool gluster_supports_zerofill(void)
--{
--    return 1;
--}
--
--static inline int qemu_gluster_zerofill(struct glfs_fd *fd, int64_t offset,
--                                        int64_t size)
--{
--    return glfs_zerofill(fd, offset, size);
--}
--
--#else
--static inline bool gluster_supports_zerofill(void)
--{
--    return 0;
--}
--
--static inline int qemu_gluster_zerofill(struct glfs_fd *fd, int64_t offset,
--                                        int64_t size)
--{
--    return 0;
--}
- #endif
- static int qemu_gluster_create(const char *filename,
-@@ -1007,9 +984,10 @@ static int qemu_gluster_create(const char *filename,
-     struct glfs *glfs;
-     struct glfs_fd *fd;
-     int ret = 0;
--    int prealloc = 0;
-+    PreallocMode prealloc;
-     int64_t total_size = 0;
-     char *tmp = NULL;
-+    Error *local_err = NULL;
-     gconf = g_new0(BlockdevOptionsGluster, 1);
-     gconf->debug = qemu_opt_get_number_del(opts, GLUSTER_OPT_DEBUG,
-@@ -1037,13 +1015,12 @@ static int qemu_gluster_create(const char *filename,
-                           BDRV_SECTOR_SIZE);
-     tmp = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC);
--    if (!tmp || !strcmp(tmp, "off")) {
--        prealloc = 0;
--    } else if (!strcmp(tmp, "full") && gluster_supports_zerofill()) {
--        prealloc = 1;
--    } else {
--        error_setg(errp, "Invalid preallocation mode: '%s'"
--                         " or GlusterFS doesn't support zerofill API", tmp);
-+    prealloc = qapi_enum_parse(PreallocMode_lookup, tmp,
-+                               PREALLOC_MODE__MAX, PREALLOC_MODE_OFF,
-+                               &local_err);
-+    g_free(tmp);
-+    if (local_err) {
-+        error_propagate(errp, local_err);
-         ret = -EINVAL;
-         goto out;
-     }
-@@ -1052,21 +1029,48 @@ static int qemu_gluster_create(const char *filename,
-                     O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, S_IRUSR | S_IWUSR);
-     if (!fd) {
-         ret = -errno;
--    } else {
-+        goto out;
-+    }
-+
-+    switch (prealloc) {
-+#ifdef CONFIG_GLUSTERFS_FALLOCATE
-+    case PREALLOC_MODE_FALLOC:
-+        if (glfs_fallocate(fd, 0, 0, total_size)) {
-+            error_setg(errp, "Could not preallocate data for the new file");
-+            ret = -errno;
-+        }
-+        break;
-+#endif /* CONFIG_GLUSTERFS_FALLOCATE */
-+#ifdef CONFIG_GLUSTERFS_ZEROFILL
-+    case PREALLOC_MODE_FULL:
-         if (!glfs_ftruncate(fd, total_size)) {
--            if (prealloc && qemu_gluster_zerofill(fd, 0, total_size)) {
-+            if (glfs_zerofill(fd, 0, total_size)) {
-+                error_setg(errp, "Could not zerofill the new file");
-                 ret = -errno;
-             }
-         } else {
-+            error_setg(errp, "Could not resize file");
-             ret = -errno;
-         }
--
--        if (glfs_close(fd) != 0) {
-+        break;
-+#endif /* CONFIG_GLUSTERFS_ZEROFILL */
-+    case PREALLOC_MODE_OFF:
-+        if (glfs_ftruncate(fd, total_size) != 0) {
-             ret = -errno;
-+            error_setg(errp, "Could not resize file");
-         }
-+        break;
-+    default:
-+        ret = -EINVAL;
-+        error_setg(errp, "Unsupported preallocation mode: %s",
-+                   PreallocMode_lookup[prealloc]);
-+        break;
-+    }
-+
-+    if (glfs_close(fd) != 0) {
-+        ret = -errno;
-     }
- out:
--    g_free(tmp);
-     qapi_free_BlockdevOptionsGluster(gconf);
-     glfs_clear_preopened(glfs);
-     return ret;
-diff --git a/configure b/configure
-index 841f7a8fae..3667da6f07 100755
---- a/configure
-+++ b/configure
-@@ -300,6 +300,7 @@ seccomp=""
- glusterfs=""
- glusterfs_xlator_opt="no"
- glusterfs_discard="no"
-+glusterfs_fallocate="no"
- glusterfs_zerofill="no"
- gtk=""
- gtkabi=""
-@@ -3537,6 +3538,7 @@ if test "$glusterfs" != "no" ; then
-       glusterfs_discard="yes"
-     fi
-     if $pkg_config --atleast-version=6 glusterfs-api; then
-+      glusterfs_fallocate="yes"
-       glusterfs_zerofill="yes"
-     fi
-   else
-@@ -5717,6 +5719,10 @@ if test "$glusterfs_discard" = "yes" ; then
-   echo "CONFIG_GLUSTERFS_DISCARD=y" >> $config_host_mak
- fi
-+if test "$glusterfs_fallocate" = "yes" ; then
-+  echo "CONFIG_GLUSTERFS_FALLOCATE=y" >> $config_host_mak
-+fi
-+
- if test "$glusterfs_zerofill" = "yes" ; then
-   echo "CONFIG_GLUSTERFS_ZEROFILL=y" >> $config_host_mak
- fi
--- 
-2.11.0
-
diff --git a/debian/patches/extra/0027-target-i386-Use-host_vendor_fms-in-max_x86_cpu_initf.patch b/debian/patches/extra/0027-target-i386-Use-host_vendor_fms-in-max_x86_cpu_initf.patch
deleted file mode 100644 (file)
index 3cc2f0a..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Eduardo Habkost <ehabkost@redhat.com>
-Date: Wed, 12 Jul 2017 13:20:56 -0300
-Subject: [PATCH] target/i386: Use host_vendor_fms() in max_x86_cpu_initfn()
-
-The existing code duplicated the logic in host_vendor_fms(), so
-reuse the helper function instead.
-
-Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
-Message-Id: <20170712162058.10538-3-ehabkost@redhat.com>
-Reviewed-by: Igor Mammedov <imammedo@redhat.com>
-Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
----
- target/i386/cpu.c | 9 ++-------
- 1 file changed, 2 insertions(+), 7 deletions(-)
-
-diff --git a/target/i386/cpu.c b/target/i386/cpu.c
-index 4b3bfb3802..1affd3bb5b 100644
---- a/target/i386/cpu.c
-+++ b/target/i386/cpu.c
-@@ -1592,13 +1592,8 @@ static void max_x86_cpu_initfn(Object *obj)
-         X86CPUDefinition host_cpudef = { };
-         uint32_t eax = 0, ebx = 0, ecx = 0, edx = 0;
--        host_cpuid(0x0, 0, &eax, &ebx, &ecx, &edx);
--        x86_cpu_vendor_words2str(host_cpudef.vendor, ebx, edx, ecx);
--
--        host_cpuid(0x1, 0, &eax, &ebx, &ecx, &edx);
--        host_cpudef.family = ((eax >> 8) & 0x0F) + ((eax >> 20) & 0xFF);
--        host_cpudef.model = ((eax >> 4) & 0x0F) | ((eax & 0xF0000) >> 12);
--        host_cpudef.stepping = eax & 0x0F;
-+        host_vendor_fms(host_cpudef.vendor, &host_cpudef.family,
-+                        &host_cpudef.model, &host_cpudef.stepping);
-         cpu_x86_fill_model_id(host_cpudef.model_id);
--- 
-2.11.0
-
diff --git a/debian/patches/extra/0028-target-i386-Define-CPUID_MODEL_ID_SZ-macro.patch b/debian/patches/extra/0028-target-i386-Define-CPUID_MODEL_ID_SZ-macro.patch
deleted file mode 100644 (file)
index f46cb61..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Eduardo Habkost <ehabkost@redhat.com>
-Date: Wed, 12 Jul 2017 13:20:57 -0300
-Subject: [PATCH] target/i386: Define CPUID_MODEL_ID_SZ macro
-
-Document cpu_x86_fill_model_id() and define CPUID_MODEL_ID_SZ to
-help callers use the right buffer size.
-
-Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
-Message-Id: <20170712162058.10538-4-ehabkost@redhat.com>
-Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
----
- target/i386/cpu.c | 11 +++++++++++
- 1 file changed, 11 insertions(+)
-
-diff --git a/target/i386/cpu.c b/target/i386/cpu.c
-index 1affd3bb5b..54832dd591 100644
---- a/target/i386/cpu.c
-+++ b/target/i386/cpu.c
-@@ -1541,6 +1541,17 @@ static bool lmce_supported(void)
-     return !!(mce_cap & MCG_LMCE_P);
- }
-+#define CPUID_MODEL_ID_SZ 48
-+
-+/**
-+ * cpu_x86_fill_model_id:
-+ * Get CPUID model ID string from host CPU.
-+ *
-+ * @str should have at least CPUID_MODEL_ID_SZ bytes
-+ *
-+ * The function does NOT add a null terminator to the string
-+ * automatically.
-+ */
- static int cpu_x86_fill_model_id(char *str)
- {
-     uint32_t eax = 0, ebx = 0, ecx = 0, edx = 0;
--- 
-2.11.0
-
diff --git a/debian/patches/extra/0029-target-i386-Don-t-use-x86_cpu_load_def-on-max-CPU-mo.patch b/debian/patches/extra/0029-target-i386-Don-t-use-x86_cpu_load_def-on-max-CPU-mo.patch
deleted file mode 100644 (file)
index d8f9bfa..0000000
+++ /dev/null
@@ -1,92 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Eduardo Habkost <ehabkost@redhat.com>
-Date: Wed, 12 Jul 2017 13:20:58 -0300
-Subject: [PATCH] target/i386: Don't use x86_cpu_load_def() on "max" CPU model
-
-When commit 0bacd8b3046f ('i386: Don't set CPUClass::cpu_def on
-"max" model') removed the CPUClass::cpu_def field, we kept using
-the x86_cpu_load_def() helper directly in max_x86_cpu_initfn(),
-emulating the previous behavior when CPUClass::cpu_def was set.
-
-However, x86_cpu_load_def() is intended to help initialization of
-CPU models from the builtin_x86_defs table, and does lots of
-other steps that are not necessary for "max".
-
-One of the things x86_cpu_load_def() do is to set the properties
-listed at tcg_default_props/kvm_default_props.  We must not do
-that on the "max" CPU model, otherwise under KVM we will
-incorrectly report all KVM features as always available, and the
-"svm" feature as always unavailable.  The latter caused the bug
-reported at:
-
-  https://bugzilla.redhat.com/show_bug.cgi?id=1467599
-  ("Unable to start domain: the CPU is incompatible with host CPU:
-  Host CPU does not provide required features: svm")
-
-Replace x86_cpu_load_def() with simple object_property_set*()
-calls.  In addition to fixing the above bug, this makes the KVM
-branch in max_x86_cpu_initfn() very similar to the existing TCG
-branch.
-
-For reference, the full list of steps performed by
-x86_cpu_load_def() is:
-
-* Setting min-level and min-xlevel.  Already done by
-  max_x86_cpu_initfn().
-* Setting family/model/stepping/model-id.  Done by the code added
-  to max_x86_cpu_initfn() in this patch.
-* Copying def->features.  Wrong because "-cpu max" features need to
-  be calculated at realize time.  This was not a problem in the
-  current code because host_cpudef.features was all zeroes.
-* x86_cpu_apply_props() calls.  This causes the bug above, and
-  shouldn't be done.
-* Setting CPUID_EXT_HYPERVISOR.  Not needed because it is already
-  reported by x86_cpu_get_supported_feature_word(), and because
-  "-cpu max" features need to be calculated at realize time.
-* Setting CPU vendor to host CPU vendor if on KVM mode.
-  Redundant, because max_x86_cpu_initfn() already sets it to the
-  host CPU vendor.
-
-Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
-Message-Id: <20170712162058.10538-5-ehabkost@redhat.com>
-Reviewed-by: Igor Mammedov <imammedo@redhat.com>
-Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
----
- target/i386/cpu.c | 18 ++++++++++++------
- 1 file changed, 12 insertions(+), 6 deletions(-)
-
-diff --git a/target/i386/cpu.c b/target/i386/cpu.c
-index 54832dd591..3d53cb4c86 100644
---- a/target/i386/cpu.c
-+++ b/target/i386/cpu.c
-@@ -1600,15 +1600,21 @@ static void max_x86_cpu_initfn(Object *obj)
-     cpu->max_features = true;
-     if (kvm_enabled()) {
--        X86CPUDefinition host_cpudef = { };
--        uint32_t eax = 0, ebx = 0, ecx = 0, edx = 0;
-+        char vendor[CPUID_VENDOR_SZ + 1] = { 0 };
-+        char model_id[CPUID_MODEL_ID_SZ + 1] = { 0 };
-+        int family, model, stepping;
--        host_vendor_fms(host_cpudef.vendor, &host_cpudef.family,
--                        &host_cpudef.model, &host_cpudef.stepping);
-+        host_vendor_fms(vendor, &family, &model, &stepping);
--        cpu_x86_fill_model_id(host_cpudef.model_id);
-+        cpu_x86_fill_model_id(model_id);
--        x86_cpu_load_def(cpu, &host_cpudef, &error_abort);
-+        object_property_set_str(OBJECT(cpu), vendor, "vendor", &error_abort);
-+        object_property_set_int(OBJECT(cpu), family, "family", &error_abort);
-+        object_property_set_int(OBJECT(cpu), model, "model", &error_abort);
-+        object_property_set_int(OBJECT(cpu), stepping, "stepping",
-+                                &error_abort);
-+        object_property_set_str(OBJECT(cpu), model_id, "model-id",
-+                                &error_abort);
-         env->cpuid_min_level =
-             kvm_arch_get_supported_cpuid(s, 0x0, 0, R_EAX);
--- 
-2.11.0
-
diff --git a/debian/patches/extra/0030-i386-Change-X86CPUDefinition-model_id-to-const-char.patch b/debian/patches/extra/0030-i386-Change-X86CPUDefinition-model_id-to-const-char.patch
deleted file mode 100644 (file)
index 0db5d5d..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Eduardo Habkost <ehabkost@redhat.com>
-Date: Tue, 9 Jan 2018 13:45:13 -0200
-Subject: [PATCH] i386: Change X86CPUDefinition::model_id to const char*
-
-It is valid to have a 48-character model ID on CPUID, however the
-definition of X86CPUDefinition::model_id is char[48], which can
-make the compiler drop the null terminator from the string.
-
-If a CPU model happens to have 48 bytes on model_id, "-cpu help"
-will print garbage and the object_property_set_str() call at
-x86_cpu_load_def() will read data outside the model_id array.
-
-We could increase the array size to 49, but this would mean the
-compiler would not issue a warning if a 49-char string is used by
-mistake for model_id.
-
-To make things simpler, simply change model_id to be const char*,
-and validate the string length using an assert() on
-x86_cpu_cpudef_class_init.
-
-Reported-by: "Dr. David Alan Gilbert" <dgilbert@redhat.com>
-Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
----
- target/i386/cpu.c | 9 ++++++++-
- 1 file changed, 8 insertions(+), 1 deletion(-)
-
-diff --git a/target/i386/cpu.c b/target/i386/cpu.c
-index 3d53cb4c86..c673521016 100644
---- a/target/i386/cpu.c
-+++ b/target/i386/cpu.c
-@@ -753,7 +753,7 @@ struct X86CPUDefinition {
-     int model;
-     int stepping;
-     FeatureWordArray features;
--    char model_id[48];
-+    const char *model_id;
- };
- static X86CPUDefinition builtin_x86_defs[] = {
-@@ -922,6 +922,7 @@ static X86CPUDefinition builtin_x86_defs[] = {
-         .features[FEAT_1_EDX] =
-             I486_FEATURES,
-         .xlevel = 0,
-+        .model_id = "",
-     },
-     {
-         .name = "pentium",
-@@ -933,6 +934,7 @@ static X86CPUDefinition builtin_x86_defs[] = {
-         .features[FEAT_1_EDX] =
-             PENTIUM_FEATURES,
-         .xlevel = 0,
-+        .model_id = "",
-     },
-     {
-         .name = "pentium2",
-@@ -944,6 +946,7 @@ static X86CPUDefinition builtin_x86_defs[] = {
-         .features[FEAT_1_EDX] =
-             PENTIUM2_FEATURES,
-         .xlevel = 0,
-+        .model_id = "",
-     },
-     {
-         .name = "pentium3",
-@@ -955,6 +958,7 @@ static X86CPUDefinition builtin_x86_defs[] = {
-         .features[FEAT_1_EDX] =
-             PENTIUM3_FEATURES,
-         .xlevel = 0,
-+        .model_id = "",
-     },
-     {
-         .name = "athlon",
-@@ -2617,6 +2621,9 @@ static void x86_register_cpudef_type(X86CPUDefinition *def)
-      * they shouldn't be set on the CPU model table.
-      */
-     assert(!(def->features[FEAT_8000_0001_EDX] & CPUID_EXT2_AMD_ALIASES));
-+    /* catch mistakes instead of silently truncating model_id when too long */
-+    assert(def->model_id && strlen(def->model_id) <= 48);
-+
-     type_register(&ti);
-     g_free(typename);
--- 
-2.11.0
-
diff --git a/debian/patches/extra/0031-i386-Add-support-for-SPEC_CTRL-MSR.patch b/debian/patches/extra/0031-i386-Add-support-for-SPEC_CTRL-MSR.patch
deleted file mode 100644 (file)
index 2a6f6d6..0000000
+++ /dev/null
@@ -1,135 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Paolo Bonzini <pbonzini@redhat.com>
-Date: Tue, 9 Jan 2018 13:45:14 -0200
-Subject: [PATCH] i386: Add support for SPEC_CTRL MSR
-
-Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
----
- target/i386/cpu.h     |  3 +++
- target/i386/kvm.c     | 15 +++++++++++++++
- target/i386/machine.c | 20 ++++++++++++++++++++
- 3 files changed, 38 insertions(+)
-
-diff --git a/target/i386/cpu.h b/target/i386/cpu.h
-index c4602ca80d..cc322d6b39 100644
---- a/target/i386/cpu.h
-+++ b/target/i386/cpu.h
-@@ -333,6 +333,7 @@
- #define MSR_IA32_APICBASE_BASE          (0xfffffU<<12)
- #define MSR_IA32_FEATURE_CONTROL        0x0000003a
- #define MSR_TSC_ADJUST                  0x0000003b
-+#define MSR_IA32_SPEC_CTRL              0x48
- #define MSR_IA32_TSCDEADLINE            0x6e0
- #define FEATURE_CONTROL_LOCKED                    (1<<0)
-@@ -1080,6 +1081,8 @@ typedef struct CPUX86State {
-     uint32_t pkru;
-+    uint64_t spec_ctrl;
-+
-     /* End of state preserved by INIT (dummy marker).  */
-     struct {} end_init_save;
-diff --git a/target/i386/kvm.c b/target/i386/kvm.c
-index 55865dbee0..9f83c79338 100644
---- a/target/i386/kvm.c
-+++ b/target/i386/kvm.c
-@@ -89,6 +89,7 @@ static bool has_msr_hv_runtime;
- static bool has_msr_hv_synic;
- static bool has_msr_hv_stimer;
- static bool has_msr_xss;
-+static bool has_msr_spec_ctrl;
- static bool has_msr_architectural_pmu;
- static uint32_t num_architectural_pmu_counters;
-@@ -1140,6 +1141,10 @@ static int kvm_get_supported_msrs(KVMState *s)
-                     has_msr_hv_stimer = true;
-                     continue;
-                 }
-+                if (kvm_msr_list->indices[i] == MSR_IA32_SPEC_CTRL) {
-+                    has_msr_spec_ctrl = true;
-+                    continue;
-+                }
-             }
-         }
-@@ -1667,6 +1672,9 @@ static int kvm_put_msrs(X86CPU *cpu, int level)
-     if (has_msr_xss) {
-         kvm_msr_entry_add(cpu, MSR_IA32_XSS, env->xss);
-     }
-+    if (has_msr_spec_ctrl) {
-+        kvm_msr_entry_add(cpu, MSR_IA32_SPEC_CTRL, env->spec_ctrl);
-+    }
- #ifdef TARGET_X86_64
-     if (lm_capable_kernel) {
-         kvm_msr_entry_add(cpu, MSR_CSTAR, env->cstar);
-@@ -1675,6 +1683,7 @@ static int kvm_put_msrs(X86CPU *cpu, int level)
-         kvm_msr_entry_add(cpu, MSR_LSTAR, env->lstar);
-     }
- #endif
-+
-     /*
-      * The following MSRs have side effects on the guest or are too heavy
-      * for normal writeback. Limit them to reset or full state updates.
-@@ -2081,6 +2090,9 @@ static int kvm_get_msrs(X86CPU *cpu)
-     if (has_msr_xss) {
-         kvm_msr_entry_add(cpu, MSR_IA32_XSS, 0);
-     }
-+    if (has_msr_spec_ctrl) {
-+        kvm_msr_entry_add(cpu, MSR_IA32_SPEC_CTRL, 0);
-+    }
-     if (!env->tsc_valid) {
-@@ -2430,6 +2442,9 @@ static int kvm_get_msrs(X86CPU *cpu)
-                 env->mtrr_var[MSR_MTRRphysIndex(index)].base = msrs[i].data;
-             }
-             break;
-+        case MSR_IA32_SPEC_CTRL:
-+            env->spec_ctrl = msrs[i].data;
-+            break;
-         }
-     }
-diff --git a/target/i386/machine.c b/target/i386/machine.c
-index 78ae2f986b..8c0d5437fa 100644
---- a/target/i386/machine.c
-+++ b/target/i386/machine.c
-@@ -927,6 +927,25 @@ static const VMStateDescription vmstate_mcg_ext_ctl = {
-     }
- };
-+static bool spec_ctrl_needed(void *opaque)
-+{
-+    X86CPU *cpu = opaque;
-+    CPUX86State *env = &cpu->env;
-+
-+    return env->spec_ctrl != 0;
-+}
-+
-+static const VMStateDescription vmstate_spec_ctrl = {
-+    .name = "cpu/spec_ctrl",
-+    .version_id = 1,
-+    .minimum_version_id = 1,
-+    .needed = spec_ctrl_needed,
-+    .fields = (VMStateField[]){
-+        VMSTATE_UINT64(env.spec_ctrl, X86CPU),
-+        VMSTATE_END_OF_LIST()
-+    }
-+};
-+
- VMStateDescription vmstate_x86_cpu = {
-     .name = "cpu",
-     .version_id = 12,
-@@ -1053,6 +1072,7 @@ VMStateDescription vmstate_x86_cpu = {
- #ifdef TARGET_X86_64
-         &vmstate_pkru,
- #endif
-+        &vmstate_spec_ctrl,
-         &vmstate_mcg_ext_ctl,
-         NULL
-     }
--- 
-2.11.0
-
diff --git a/debian/patches/extra/0032-i386-Add-spec-ctrl-CPUID-bit.patch b/debian/patches/extra/0032-i386-Add-spec-ctrl-CPUID-bit.patch
deleted file mode 100644 (file)
index 254d17a..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Eduardo Habkost <ehabkost@redhat.com>
-Date: Tue, 9 Jan 2018 13:45:15 -0200
-Subject: [PATCH] i386: Add spec-ctrl CPUID bit
-
-Add the feature name and a CPUID_7_0_EDX_SPEC_CTRL macro.
-
-Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
----
- target/i386/cpu.c | 2 +-
- target/i386/cpu.h | 1 +
- 2 files changed, 2 insertions(+), 1 deletion(-)
-
-diff --git a/target/i386/cpu.c b/target/i386/cpu.c
-index c673521016..faf1ff6dcc 100644
---- a/target/i386/cpu.c
-+++ b/target/i386/cpu.c
-@@ -460,7 +460,7 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = {
-             NULL, NULL, NULL, NULL,
-             NULL, NULL, NULL, NULL,
-             NULL, NULL, NULL, NULL,
--            NULL, NULL, NULL, NULL,
-+            NULL, NULL, "spec-ctrl", NULL,
-             NULL, NULL, NULL, NULL,
-         },
-         .cpuid_eax = 7,
-diff --git a/target/i386/cpu.h b/target/i386/cpu.h
-index cc322d6b39..71261f4819 100644
---- a/target/i386/cpu.h
-+++ b/target/i386/cpu.h
-@@ -640,6 +640,7 @@ typedef uint32_t FeatureWordArray[FEATURE_WORDS];
- #define CPUID_7_0_EDX_AVX512_4VNNIW (1U << 2) /* AVX512 Neural Network Instructions */
- #define CPUID_7_0_EDX_AVX512_4FMAPS (1U << 3) /* AVX512 Multiply Accumulation Single Precision */
-+#define CPUID_7_0_EDX_SPEC_CTRL     (1U << 26) /* Speculation Control */
- #define CPUID_XSAVE_XSAVEOPT   (1U << 0)
- #define CPUID_XSAVE_XSAVEC     (1U << 1)
--- 
-2.11.0
-
diff --git a/debian/patches/extra/0033-i386-Add-FEAT_8000_0008_EBX-CPUID-feature-word.patch b/debian/patches/extra/0033-i386-Add-FEAT_8000_0008_EBX-CPUID-feature-word.patch
deleted file mode 100644 (file)
index 27b98fe..0000000
+++ /dev/null
@@ -1,83 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Eduardo Habkost <ehabkost@redhat.com>
-Date: Tue, 9 Jan 2018 13:45:16 -0200
-Subject: [PATCH] i386: Add FEAT_8000_0008_EBX CPUID feature word
-
-Add the new feature word and the "ibpb" feature flag.
-
-Based on a patch by Paolo Bonzini.
-
-Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
----
- target/i386/cpu.c | 19 ++++++++++++++++++-
- target/i386/cpu.h |  3 +++
- 2 files changed, 21 insertions(+), 1 deletion(-)
-
-diff --git a/target/i386/cpu.c b/target/i386/cpu.c
-index faf1ff6dcc..eee365b78d 100644
---- a/target/i386/cpu.c
-+++ b/target/i386/cpu.c
-@@ -484,6 +484,22 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = {
-         .tcg_features = TCG_APM_FEATURES,
-         .unmigratable_flags = CPUID_APM_INVTSC,
-     },
-+    [FEAT_8000_0008_EBX] = {
-+        .feat_names = {
-+            NULL, NULL, NULL, NULL,
-+            NULL, NULL, NULL, NULL,
-+            NULL, NULL, NULL, NULL,
-+            "ibpb", NULL, NULL, NULL,
-+            NULL, NULL, NULL, NULL,
-+            NULL, NULL, NULL, NULL,
-+            NULL, NULL, NULL, NULL,
-+            NULL, NULL, NULL, NULL,
-+        },
-+        .cpuid_eax = 0x80000008,
-+        .cpuid_reg = R_EBX,
-+        .tcg_features = 0,
-+        .unmigratable_flags = 0,
-+    },
-     [FEAT_XSAVE] = {
-         .feat_names = {
-             "xsaveopt", "xsavec", "xgetbv1", "xsaves",
-@@ -2984,7 +3000,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count,
-         } else {
-             *eax = cpu->phys_bits;
-         }
--        *ebx = 0;
-+        *ebx = env->features[FEAT_8000_0008_EBX];
-         *ecx = 0;
-         *edx = 0;
-         if (cs->nr_cores * cs->nr_threads > 1) {
-@@ -3440,6 +3456,7 @@ static void x86_cpu_expand_features(X86CPU *cpu, Error **errp)
-         x86_cpu_adjust_feat_level(cpu, FEAT_8000_0001_EDX);
-         x86_cpu_adjust_feat_level(cpu, FEAT_8000_0001_ECX);
-         x86_cpu_adjust_feat_level(cpu, FEAT_8000_0007_EDX);
-+        x86_cpu_adjust_feat_level(cpu, FEAT_8000_0008_EBX);
-         x86_cpu_adjust_feat_level(cpu, FEAT_C000_0001_EDX);
-         x86_cpu_adjust_feat_level(cpu, FEAT_SVM);
-         x86_cpu_adjust_feat_level(cpu, FEAT_XSAVE);
-diff --git a/target/i386/cpu.h b/target/i386/cpu.h
-index 71261f4819..1ebee91930 100644
---- a/target/i386/cpu.h
-+++ b/target/i386/cpu.h
-@@ -452,6 +452,7 @@ typedef enum FeatureWord {
-     FEAT_8000_0001_EDX, /* CPUID[8000_0001].EDX */
-     FEAT_8000_0001_ECX, /* CPUID[8000_0001].ECX */
-     FEAT_8000_0007_EDX, /* CPUID[8000_0007].EDX */
-+    FEAT_8000_0008_EBX, /* CPUID[8000_0008].EBX */
-     FEAT_C000_0001_EDX, /* CPUID[C000_0001].EDX */
-     FEAT_KVM,           /* CPUID[4000_0001].EAX (KVM_CPUID_FEATURES) */
-     FEAT_HYPERV_EAX,    /* CPUID[4000_0003].EAX */
-@@ -642,6 +643,8 @@ typedef uint32_t FeatureWordArray[FEATURE_WORDS];
- #define CPUID_7_0_EDX_AVX512_4FMAPS (1U << 3) /* AVX512 Multiply Accumulation Single Precision */
- #define CPUID_7_0_EDX_SPEC_CTRL     (1U << 26) /* Speculation Control */
-+#define CPUID_8000_0008_EBX_IBPB    (1U << 12) /* Indirect Branch Prediction Barrier */
-+
- #define CPUID_XSAVE_XSAVEOPT   (1U << 0)
- #define CPUID_XSAVE_XSAVEC     (1U << 1)
- #define CPUID_XSAVE_XGETBV1    (1U << 2)
--- 
-2.11.0
-
diff --git a/debian/patches/extra/0034-i386-Add-new-IBRS-versions-of-Intel-CPU-models.patch b/debian/patches/extra/0034-i386-Add-new-IBRS-versions-of-Intel-CPU-models.patch
deleted file mode 100644 (file)
index 54e2835..0000000
+++ /dev/null
@@ -1,518 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Eduardo Habkost <ehabkost@redhat.com>
-Date: Tue, 9 Jan 2018 13:45:17 -0200
-Subject: [PATCH] i386: Add new -IBRS versions of Intel CPU models
-
-The new MSR IA32_SPEC_CTRL MSR was introduced by a recent Intel
-microcode updated and can be used by OSes to mitigate
-CVE-2017-5715.  Unfortunately we can't change the existing CPU
-models without breaking existing setups, so users need to
-explicitly update their VM configuration to use the new *-IBRS
-CPU model if they want to expose IBRS to guests.
-
-The new CPU models are simple copies of the existing CPU models,
-with just CPUID_7_0_EDX_SPEC_CTRL added and model_id updated.
-
-Cc: Jiri Denemark <jdenemar@redhat.com>
-Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
----
- target/i386/cpu.c | 427 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
- 1 file changed, 426 insertions(+), 1 deletion(-)
-
-diff --git a/target/i386/cpu.c b/target/i386/cpu.c
-index eee365b78d..e4a2d5a012 100644
---- a/target/i386/cpu.c
-+++ b/target/i386/cpu.c
-@@ -1085,6 +1085,31 @@ static X86CPUDefinition builtin_x86_defs[] = {
-         .model_id = "Intel Core i7 9xx (Nehalem Class Core i7)",
-     },
-     {
-+        .name = "Nehalem-IBRS",
-+        .level = 11,
-+        .vendor = CPUID_VENDOR_INTEL,
-+        .family = 6,
-+        .model = 26,
-+        .stepping = 3,
-+        .features[FEAT_1_EDX] =
-+            CPUID_VME | CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX |
-+            CPUID_CLFLUSH | CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | CPUID_MCA |
-+            CPUID_PGE | CPUID_MTRR | CPUID_SEP | CPUID_APIC | CPUID_CX8 |
-+            CPUID_MCE | CPUID_PAE | CPUID_MSR | CPUID_TSC | CPUID_PSE |
-+            CPUID_DE | CPUID_FP87,
-+        .features[FEAT_1_ECX] =
-+            CPUID_EXT_POPCNT | CPUID_EXT_SSE42 | CPUID_EXT_SSE41 |
-+            CPUID_EXT_CX16 | CPUID_EXT_SSSE3 | CPUID_EXT_SSE3,
-+        .features[FEAT_7_0_EDX] =
-+            CPUID_7_0_EDX_SPEC_CTRL,
-+        .features[FEAT_8000_0001_EDX] =
-+            CPUID_EXT2_LM | CPUID_EXT2_SYSCALL | CPUID_EXT2_NX,
-+        .features[FEAT_8000_0001_ECX] =
-+            CPUID_EXT3_LAHF_LM,
-+        .xlevel = 0x80000008,
-+        .model_id = "Intel Core i7 9xx (Nehalem Core i7, IBRS update)",
-+    },
-+    {
-         .name = "Westmere",
-         .level = 11,
-         .vendor = CPUID_VENDOR_INTEL,
-@@ -1111,6 +1136,34 @@ static X86CPUDefinition builtin_x86_defs[] = {
-         .model_id = "Westmere E56xx/L56xx/X56xx (Nehalem-C)",
-     },
-     {
-+        .name = "Westmere-IBRS",
-+        .level = 11,
-+        .vendor = CPUID_VENDOR_INTEL,
-+        .family = 6,
-+        .model = 44,
-+        .stepping = 1,
-+        .features[FEAT_1_EDX] =
-+            CPUID_VME | CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX |
-+            CPUID_CLFLUSH | CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | CPUID_MCA |
-+            CPUID_PGE | CPUID_MTRR | CPUID_SEP | CPUID_APIC | CPUID_CX8 |
-+            CPUID_MCE | CPUID_PAE | CPUID_MSR | CPUID_TSC | CPUID_PSE |
-+            CPUID_DE | CPUID_FP87,
-+        .features[FEAT_1_ECX] =
-+            CPUID_EXT_AES | CPUID_EXT_POPCNT | CPUID_EXT_SSE42 |
-+            CPUID_EXT_SSE41 | CPUID_EXT_CX16 | CPUID_EXT_SSSE3 |
-+            CPUID_EXT_PCLMULQDQ | CPUID_EXT_SSE3,
-+        .features[FEAT_8000_0001_EDX] =
-+            CPUID_EXT2_LM | CPUID_EXT2_SYSCALL | CPUID_EXT2_NX,
-+        .features[FEAT_8000_0001_ECX] =
-+            CPUID_EXT3_LAHF_LM,
-+        .features[FEAT_7_0_EDX] =
-+            CPUID_7_0_EDX_SPEC_CTRL,
-+        .features[FEAT_6_EAX] =
-+            CPUID_6_EAX_ARAT,
-+        .xlevel = 0x80000008,
-+        .model_id = "Westmere E56xx/L56xx/X56xx (IBRS update)",
-+    },
-+    {
-         .name = "SandyBridge",
-         .level = 0xd,
-         .vendor = CPUID_VENDOR_INTEL,
-@@ -1142,6 +1195,39 @@ static X86CPUDefinition builtin_x86_defs[] = {
-         .model_id = "Intel Xeon E312xx (Sandy Bridge)",
-     },
-     {
-+        .name = "SandyBridge-IBRS",
-+        .level = 0xd,
-+        .vendor = CPUID_VENDOR_INTEL,
-+        .family = 6,
-+        .model = 42,
-+        .stepping = 1,
-+        .features[FEAT_1_EDX] =
-+            CPUID_VME | CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX |
-+            CPUID_CLFLUSH | CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | CPUID_MCA |
-+            CPUID_PGE | CPUID_MTRR | CPUID_SEP | CPUID_APIC | CPUID_CX8 |
-+            CPUID_MCE | CPUID_PAE | CPUID_MSR | CPUID_TSC | CPUID_PSE |
-+            CPUID_DE | CPUID_FP87,
-+        .features[FEAT_1_ECX] =
-+            CPUID_EXT_AVX | CPUID_EXT_XSAVE | CPUID_EXT_AES |
-+            CPUID_EXT_TSC_DEADLINE_TIMER | CPUID_EXT_POPCNT |
-+            CPUID_EXT_X2APIC | CPUID_EXT_SSE42 | CPUID_EXT_SSE41 |
-+            CPUID_EXT_CX16 | CPUID_EXT_SSSE3 | CPUID_EXT_PCLMULQDQ |
-+            CPUID_EXT_SSE3,
-+        .features[FEAT_8000_0001_EDX] =
-+            CPUID_EXT2_LM | CPUID_EXT2_RDTSCP | CPUID_EXT2_NX |
-+            CPUID_EXT2_SYSCALL,
-+        .features[FEAT_8000_0001_ECX] =
-+            CPUID_EXT3_LAHF_LM,
-+        .features[FEAT_7_0_EDX] =
-+            CPUID_7_0_EDX_SPEC_CTRL,
-+        .features[FEAT_XSAVE] =
-+            CPUID_XSAVE_XSAVEOPT,
-+        .features[FEAT_6_EAX] =
-+            CPUID_6_EAX_ARAT,
-+        .xlevel = 0x80000008,
-+        .model_id = "Intel Xeon E312xx (Sandy Bridge, IBRS update)",
-+    },
-+    {
-         .name = "IvyBridge",
-         .level = 0xd,
-         .vendor = CPUID_VENDOR_INTEL,
-@@ -1176,6 +1262,42 @@ static X86CPUDefinition builtin_x86_defs[] = {
-         .model_id = "Intel Xeon E3-12xx v2 (Ivy Bridge)",
-     },
-     {
-+        .name = "IvyBridge-IBRS",
-+        .level = 0xd,
-+        .vendor = CPUID_VENDOR_INTEL,
-+        .family = 6,
-+        .model = 58,
-+        .stepping = 9,
-+        .features[FEAT_1_EDX] =
-+            CPUID_VME | CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX |
-+            CPUID_CLFLUSH | CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | CPUID_MCA |
-+            CPUID_PGE | CPUID_MTRR | CPUID_SEP | CPUID_APIC | CPUID_CX8 |
-+            CPUID_MCE | CPUID_PAE | CPUID_MSR | CPUID_TSC | CPUID_PSE |
-+            CPUID_DE | CPUID_FP87,
-+        .features[FEAT_1_ECX] =
-+            CPUID_EXT_AVX | CPUID_EXT_XSAVE | CPUID_EXT_AES |
-+            CPUID_EXT_TSC_DEADLINE_TIMER | CPUID_EXT_POPCNT |
-+            CPUID_EXT_X2APIC | CPUID_EXT_SSE42 | CPUID_EXT_SSE41 |
-+            CPUID_EXT_CX16 | CPUID_EXT_SSSE3 | CPUID_EXT_PCLMULQDQ |
-+            CPUID_EXT_SSE3 | CPUID_EXT_F16C | CPUID_EXT_RDRAND,
-+        .features[FEAT_7_0_EBX] =
-+            CPUID_7_0_EBX_FSGSBASE | CPUID_7_0_EBX_SMEP |
-+            CPUID_7_0_EBX_ERMS,
-+        .features[FEAT_8000_0001_EDX] =
-+            CPUID_EXT2_LM | CPUID_EXT2_RDTSCP | CPUID_EXT2_NX |
-+            CPUID_EXT2_SYSCALL,
-+        .features[FEAT_8000_0001_ECX] =
-+            CPUID_EXT3_LAHF_LM,
-+        .features[FEAT_7_0_EDX] =
-+            CPUID_7_0_EDX_SPEC_CTRL,
-+        .features[FEAT_XSAVE] =
-+            CPUID_XSAVE_XSAVEOPT,
-+        .features[FEAT_6_EAX] =
-+            CPUID_6_EAX_ARAT,
-+        .xlevel = 0x80000008,
-+        .model_id = "Intel Xeon E3-12xx v2 (Ivy Bridge, IBRS)",
-+    },
-+    {
-         .name = "Haswell-noTSX",
-         .level = 0xd,
-         .vendor = CPUID_VENDOR_INTEL,
-@@ -1210,7 +1332,46 @@ static X86CPUDefinition builtin_x86_defs[] = {
-             CPUID_6_EAX_ARAT,
-         .xlevel = 0x80000008,
-         .model_id = "Intel Core Processor (Haswell, no TSX)",
--    },    {
-+    },
-+    {
-+        .name = "Haswell-noTSX-IBRS",
-+        .level = 0xd,
-+        .vendor = CPUID_VENDOR_INTEL,
-+        .family = 6,
-+        .model = 60,
-+        .stepping = 1,
-+        .features[FEAT_1_EDX] =
-+            CPUID_VME | CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX |
-+            CPUID_CLFLUSH | CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | CPUID_MCA |
-+            CPUID_PGE | CPUID_MTRR | CPUID_SEP | CPUID_APIC | CPUID_CX8 |
-+            CPUID_MCE | CPUID_PAE | CPUID_MSR | CPUID_TSC | CPUID_PSE |
-+            CPUID_DE | CPUID_FP87,
-+        .features[FEAT_1_ECX] =
-+            CPUID_EXT_AVX | CPUID_EXT_XSAVE | CPUID_EXT_AES |
-+            CPUID_EXT_POPCNT | CPUID_EXT_X2APIC | CPUID_EXT_SSE42 |
-+            CPUID_EXT_SSE41 | CPUID_EXT_CX16 | CPUID_EXT_SSSE3 |
-+            CPUID_EXT_PCLMULQDQ | CPUID_EXT_SSE3 |
-+            CPUID_EXT_TSC_DEADLINE_TIMER | CPUID_EXT_FMA | CPUID_EXT_MOVBE |
-+            CPUID_EXT_PCID | CPUID_EXT_F16C | CPUID_EXT_RDRAND,
-+        .features[FEAT_8000_0001_EDX] =
-+            CPUID_EXT2_LM | CPUID_EXT2_RDTSCP | CPUID_EXT2_NX |
-+            CPUID_EXT2_SYSCALL,
-+        .features[FEAT_8000_0001_ECX] =
-+            CPUID_EXT3_ABM | CPUID_EXT3_LAHF_LM,
-+        .features[FEAT_7_0_EDX] =
-+            CPUID_7_0_EDX_SPEC_CTRL,
-+        .features[FEAT_7_0_EBX] =
-+            CPUID_7_0_EBX_FSGSBASE | CPUID_7_0_EBX_BMI1 |
-+            CPUID_7_0_EBX_AVX2 | CPUID_7_0_EBX_SMEP |
-+            CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_ERMS | CPUID_7_0_EBX_INVPCID,
-+        .features[FEAT_XSAVE] =
-+            CPUID_XSAVE_XSAVEOPT,
-+        .features[FEAT_6_EAX] =
-+            CPUID_6_EAX_ARAT,
-+        .xlevel = 0x80000008,
-+        .model_id = "Intel Core Processor (Haswell, no TSX, IBRS)",
-+    },
-+    {
-         .name = "Haswell",
-         .level = 0xd,
-         .vendor = CPUID_VENDOR_INTEL,
-@@ -1248,6 +1409,45 @@ static X86CPUDefinition builtin_x86_defs[] = {
-         .model_id = "Intel Core Processor (Haswell)",
-     },
-     {
-+        .name = "Haswell-IBRS",
-+        .level = 0xd,
-+        .vendor = CPUID_VENDOR_INTEL,
-+        .family = 6,
-+        .model = 60,
-+        .stepping = 4,
-+        .features[FEAT_1_EDX] =
-+            CPUID_VME | CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX |
-+            CPUID_CLFLUSH | CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | CPUID_MCA |
-+            CPUID_PGE | CPUID_MTRR | CPUID_SEP | CPUID_APIC | CPUID_CX8 |
-+            CPUID_MCE | CPUID_PAE | CPUID_MSR | CPUID_TSC | CPUID_PSE |
-+            CPUID_DE | CPUID_FP87,
-+        .features[FEAT_1_ECX] =
-+            CPUID_EXT_AVX | CPUID_EXT_XSAVE | CPUID_EXT_AES |
-+            CPUID_EXT_POPCNT | CPUID_EXT_X2APIC | CPUID_EXT_SSE42 |
-+            CPUID_EXT_SSE41 | CPUID_EXT_CX16 | CPUID_EXT_SSSE3 |
-+            CPUID_EXT_PCLMULQDQ | CPUID_EXT_SSE3 |
-+            CPUID_EXT_TSC_DEADLINE_TIMER | CPUID_EXT_FMA | CPUID_EXT_MOVBE |
-+            CPUID_EXT_PCID | CPUID_EXT_F16C | CPUID_EXT_RDRAND,
-+        .features[FEAT_8000_0001_EDX] =
-+            CPUID_EXT2_LM | CPUID_EXT2_RDTSCP | CPUID_EXT2_NX |
-+            CPUID_EXT2_SYSCALL,
-+        .features[FEAT_8000_0001_ECX] =
-+            CPUID_EXT3_ABM | CPUID_EXT3_LAHF_LM,
-+        .features[FEAT_7_0_EDX] =
-+            CPUID_7_0_EDX_SPEC_CTRL,
-+        .features[FEAT_7_0_EBX] =
-+            CPUID_7_0_EBX_FSGSBASE | CPUID_7_0_EBX_BMI1 |
-+            CPUID_7_0_EBX_HLE | CPUID_7_0_EBX_AVX2 | CPUID_7_0_EBX_SMEP |
-+            CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_ERMS | CPUID_7_0_EBX_INVPCID |
-+            CPUID_7_0_EBX_RTM,
-+        .features[FEAT_XSAVE] =
-+            CPUID_XSAVE_XSAVEOPT,
-+        .features[FEAT_6_EAX] =
-+            CPUID_6_EAX_ARAT,
-+        .xlevel = 0x80000008,
-+        .model_id = "Intel Core Processor (Haswell, IBRS)",
-+    },
-+    {
-         .name = "Broadwell-noTSX",
-         .level = 0xd,
-         .vendor = CPUID_VENDOR_INTEL,
-@@ -1286,6 +1486,46 @@ static X86CPUDefinition builtin_x86_defs[] = {
-         .model_id = "Intel Core Processor (Broadwell, no TSX)",
-     },
-     {
-+        .name = "Broadwell-noTSX-IBRS",
-+        .level = 0xd,
-+        .vendor = CPUID_VENDOR_INTEL,
-+        .family = 6,
-+        .model = 61,
-+        .stepping = 2,
-+        .features[FEAT_1_EDX] =
-+            CPUID_VME | CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX |
-+            CPUID_CLFLUSH | CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | CPUID_MCA |
-+            CPUID_PGE | CPUID_MTRR | CPUID_SEP | CPUID_APIC | CPUID_CX8 |
-+            CPUID_MCE | CPUID_PAE | CPUID_MSR | CPUID_TSC | CPUID_PSE |
-+            CPUID_DE | CPUID_FP87,
-+        .features[FEAT_1_ECX] =
-+            CPUID_EXT_AVX | CPUID_EXT_XSAVE | CPUID_EXT_AES |
-+            CPUID_EXT_POPCNT | CPUID_EXT_X2APIC | CPUID_EXT_SSE42 |
-+            CPUID_EXT_SSE41 | CPUID_EXT_CX16 | CPUID_EXT_SSSE3 |
-+            CPUID_EXT_PCLMULQDQ | CPUID_EXT_SSE3 |
-+            CPUID_EXT_TSC_DEADLINE_TIMER | CPUID_EXT_FMA | CPUID_EXT_MOVBE |
-+            CPUID_EXT_PCID | CPUID_EXT_F16C | CPUID_EXT_RDRAND,
-+        .features[FEAT_8000_0001_EDX] =
-+            CPUID_EXT2_LM | CPUID_EXT2_RDTSCP | CPUID_EXT2_NX |
-+            CPUID_EXT2_SYSCALL,
-+        .features[FEAT_8000_0001_ECX] =
-+            CPUID_EXT3_ABM | CPUID_EXT3_LAHF_LM | CPUID_EXT3_3DNOWPREFETCH,
-+        .features[FEAT_7_0_EDX] =
-+            CPUID_7_0_EDX_SPEC_CTRL,
-+        .features[FEAT_7_0_EBX] =
-+            CPUID_7_0_EBX_FSGSBASE | CPUID_7_0_EBX_BMI1 |
-+            CPUID_7_0_EBX_AVX2 | CPUID_7_0_EBX_SMEP |
-+            CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_ERMS | CPUID_7_0_EBX_INVPCID |
-+            CPUID_7_0_EBX_RDSEED | CPUID_7_0_EBX_ADX |
-+            CPUID_7_0_EBX_SMAP,
-+        .features[FEAT_XSAVE] =
-+            CPUID_XSAVE_XSAVEOPT,
-+        .features[FEAT_6_EAX] =
-+            CPUID_6_EAX_ARAT,
-+        .xlevel = 0x80000008,
-+        .model_id = "Intel Core Processor (Broadwell, no TSX, IBRS)",
-+    },
-+    {
-         .name = "Broadwell",
-         .level = 0xd,
-         .vendor = CPUID_VENDOR_INTEL,
-@@ -1324,6 +1564,46 @@ static X86CPUDefinition builtin_x86_defs[] = {
-         .model_id = "Intel Core Processor (Broadwell)",
-     },
-     {
-+        .name = "Broadwell-IBRS",
-+        .level = 0xd,
-+        .vendor = CPUID_VENDOR_INTEL,
-+        .family = 6,
-+        .model = 61,
-+        .stepping = 2,
-+        .features[FEAT_1_EDX] =
-+            CPUID_VME | CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX |
-+            CPUID_CLFLUSH | CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | CPUID_MCA |
-+            CPUID_PGE | CPUID_MTRR | CPUID_SEP | CPUID_APIC | CPUID_CX8 |
-+            CPUID_MCE | CPUID_PAE | CPUID_MSR | CPUID_TSC | CPUID_PSE |
-+            CPUID_DE | CPUID_FP87,
-+        .features[FEAT_1_ECX] =
-+            CPUID_EXT_AVX | CPUID_EXT_XSAVE | CPUID_EXT_AES |
-+            CPUID_EXT_POPCNT | CPUID_EXT_X2APIC | CPUID_EXT_SSE42 |
-+            CPUID_EXT_SSE41 | CPUID_EXT_CX16 | CPUID_EXT_SSSE3 |
-+            CPUID_EXT_PCLMULQDQ | CPUID_EXT_SSE3 |
-+            CPUID_EXT_TSC_DEADLINE_TIMER | CPUID_EXT_FMA | CPUID_EXT_MOVBE |
-+            CPUID_EXT_PCID | CPUID_EXT_F16C | CPUID_EXT_RDRAND,
-+        .features[FEAT_8000_0001_EDX] =
-+            CPUID_EXT2_LM | CPUID_EXT2_RDTSCP | CPUID_EXT2_NX |
-+            CPUID_EXT2_SYSCALL,
-+        .features[FEAT_8000_0001_ECX] =
-+            CPUID_EXT3_ABM | CPUID_EXT3_LAHF_LM | CPUID_EXT3_3DNOWPREFETCH,
-+        .features[FEAT_7_0_EDX] =
-+            CPUID_7_0_EDX_SPEC_CTRL,
-+        .features[FEAT_7_0_EBX] =
-+            CPUID_7_0_EBX_FSGSBASE | CPUID_7_0_EBX_BMI1 |
-+            CPUID_7_0_EBX_HLE | CPUID_7_0_EBX_AVX2 | CPUID_7_0_EBX_SMEP |
-+            CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_ERMS | CPUID_7_0_EBX_INVPCID |
-+            CPUID_7_0_EBX_RTM | CPUID_7_0_EBX_RDSEED | CPUID_7_0_EBX_ADX |
-+            CPUID_7_0_EBX_SMAP,
-+        .features[FEAT_XSAVE] =
-+            CPUID_XSAVE_XSAVEOPT,
-+        .features[FEAT_6_EAX] =
-+            CPUID_6_EAX_ARAT,
-+        .xlevel = 0x80000008,
-+        .model_id = "Intel Core Processor (Broadwell, IBRS)",
-+    },
-+    {
-         .name = "Skylake-Client",
-         .level = 0xd,
-         .vendor = CPUID_VENDOR_INTEL,
-@@ -1369,6 +1649,151 @@ static X86CPUDefinition builtin_x86_defs[] = {
-         .model_id = "Intel Core Processor (Skylake)",
-     },
-     {
-+        .name = "Skylake-Client-IBRS",
-+        .level = 0xd,
-+        .vendor = CPUID_VENDOR_INTEL,
-+        .family = 6,
-+        .model = 94,
-+        .stepping = 3,
-+        .features[FEAT_1_EDX] =
-+            CPUID_VME | CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX |
-+            CPUID_CLFLUSH | CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | CPUID_MCA |
-+            CPUID_PGE | CPUID_MTRR | CPUID_SEP | CPUID_APIC | CPUID_CX8 |
-+            CPUID_MCE | CPUID_PAE | CPUID_MSR | CPUID_TSC | CPUID_PSE |
-+            CPUID_DE | CPUID_FP87,
-+        .features[FEAT_1_ECX] =
-+            CPUID_EXT_AVX | CPUID_EXT_XSAVE | CPUID_EXT_AES |
-+            CPUID_EXT_POPCNT | CPUID_EXT_X2APIC | CPUID_EXT_SSE42 |
-+            CPUID_EXT_SSE41 | CPUID_EXT_CX16 | CPUID_EXT_SSSE3 |
-+            CPUID_EXT_PCLMULQDQ | CPUID_EXT_SSE3 |
-+            CPUID_EXT_TSC_DEADLINE_TIMER | CPUID_EXT_FMA | CPUID_EXT_MOVBE |
-+            CPUID_EXT_PCID | CPUID_EXT_F16C | CPUID_EXT_RDRAND,
-+        .features[FEAT_8000_0001_EDX] =
-+            CPUID_EXT2_LM | CPUID_EXT2_RDTSCP | CPUID_EXT2_NX |
-+            CPUID_EXT2_SYSCALL,
-+        .features[FEAT_8000_0001_ECX] =
-+            CPUID_EXT3_ABM | CPUID_EXT3_LAHF_LM | CPUID_EXT3_3DNOWPREFETCH,
-+        .features[FEAT_7_0_EDX] =
-+            CPUID_7_0_EDX_SPEC_CTRL,
-+        .features[FEAT_7_0_EBX] =
-+            CPUID_7_0_EBX_FSGSBASE | CPUID_7_0_EBX_BMI1 |
-+            CPUID_7_0_EBX_HLE | CPUID_7_0_EBX_AVX2 | CPUID_7_0_EBX_SMEP |
-+            CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_ERMS | CPUID_7_0_EBX_INVPCID |
-+            CPUID_7_0_EBX_RTM | CPUID_7_0_EBX_RDSEED | CPUID_7_0_EBX_ADX |
-+            CPUID_7_0_EBX_SMAP | CPUID_7_0_EBX_MPX,
-+        /* Missing: XSAVES (not supported by some Linux versions,
-+         * including v4.1 to v4.12).
-+         * KVM doesn't yet expose any XSAVES state save component,
-+         * and the only one defined in Skylake (processor tracing)
-+         * probably will block migration anyway.
-+         */
-+        .features[FEAT_XSAVE] =
-+            CPUID_XSAVE_XSAVEOPT | CPUID_XSAVE_XSAVEC |
-+            CPUID_XSAVE_XGETBV1,
-+        .features[FEAT_6_EAX] =
-+            CPUID_6_EAX_ARAT,
-+        .xlevel = 0x80000008,
-+        .model_id = "Intel Core Processor (Skylake, IBRS)",
-+    },
-+    {
-+        .name = "Skylake-Server",
-+        .level = 0xd,
-+        .vendor = CPUID_VENDOR_INTEL,
-+        .family = 6,
-+        .model = 85,
-+        .stepping = 4,
-+        .features[FEAT_1_EDX] =
-+            CPUID_VME | CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX |
-+            CPUID_CLFLUSH | CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | CPUID_MCA |
-+            CPUID_PGE | CPUID_MTRR | CPUID_SEP | CPUID_APIC | CPUID_CX8 |
-+            CPUID_MCE | CPUID_PAE | CPUID_MSR | CPUID_TSC | CPUID_PSE |
-+            CPUID_DE | CPUID_FP87,
-+        .features[FEAT_1_ECX] =
-+            CPUID_EXT_AVX | CPUID_EXT_XSAVE | CPUID_EXT_AES |
-+            CPUID_EXT_POPCNT | CPUID_EXT_X2APIC | CPUID_EXT_SSE42 |
-+            CPUID_EXT_SSE41 | CPUID_EXT_CX16 | CPUID_EXT_SSSE3 |
-+            CPUID_EXT_PCLMULQDQ | CPUID_EXT_SSE3 |
-+            CPUID_EXT_TSC_DEADLINE_TIMER | CPUID_EXT_FMA | CPUID_EXT_MOVBE |
-+            CPUID_EXT_PCID | CPUID_EXT_F16C | CPUID_EXT_RDRAND,
-+        .features[FEAT_8000_0001_EDX] =
-+            CPUID_EXT2_LM | CPUID_EXT2_PDPE1GB | CPUID_EXT2_RDTSCP |
-+            CPUID_EXT2_NX | CPUID_EXT2_SYSCALL,
-+        .features[FEAT_8000_0001_ECX] =
-+            CPUID_EXT3_ABM | CPUID_EXT3_LAHF_LM | CPUID_EXT3_3DNOWPREFETCH,
-+        .features[FEAT_7_0_EBX] =
-+            CPUID_7_0_EBX_FSGSBASE | CPUID_7_0_EBX_BMI1 |
-+            CPUID_7_0_EBX_HLE | CPUID_7_0_EBX_AVX2 | CPUID_7_0_EBX_SMEP |
-+            CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_ERMS | CPUID_7_0_EBX_INVPCID |
-+            CPUID_7_0_EBX_RTM | CPUID_7_0_EBX_RDSEED | CPUID_7_0_EBX_ADX |
-+            CPUID_7_0_EBX_SMAP | CPUID_7_0_EBX_MPX | CPUID_7_0_EBX_CLWB |
-+            CPUID_7_0_EBX_AVX512F | CPUID_7_0_EBX_AVX512DQ |
-+            CPUID_7_0_EBX_AVX512BW | CPUID_7_0_EBX_AVX512CD |
-+            CPUID_7_0_EBX_AVX512VL,
-+        /* Missing: XSAVES (not supported by some Linux versions,
-+         * including v4.1 to v4.12).
-+         * KVM doesn't yet expose any XSAVES state save component,
-+         * and the only one defined in Skylake (processor tracing)
-+         * probably will block migration anyway.
-+         */
-+        .features[FEAT_XSAVE] =
-+            CPUID_XSAVE_XSAVEOPT | CPUID_XSAVE_XSAVEC |
-+            CPUID_XSAVE_XGETBV1,
-+        .features[FEAT_6_EAX] =
-+            CPUID_6_EAX_ARAT,
-+        .xlevel = 0x80000008,
-+        .model_id = "Intel Xeon Processor (Skylake)",
-+    },
-+    {
-+        .name = "Skylake-Server-IBRS",
-+        .level = 0xd,
-+        .vendor = CPUID_VENDOR_INTEL,
-+        .family = 6,
-+        .model = 85,
-+        .stepping = 4,
-+        .features[FEAT_1_EDX] =
-+            CPUID_VME | CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX |
-+            CPUID_CLFLUSH | CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | CPUID_MCA |
-+            CPUID_PGE | CPUID_MTRR | CPUID_SEP | CPUID_APIC | CPUID_CX8 |
-+            CPUID_MCE | CPUID_PAE | CPUID_MSR | CPUID_TSC | CPUID_PSE |
-+            CPUID_DE | CPUID_FP87,
-+        .features[FEAT_1_ECX] =
-+            CPUID_EXT_AVX | CPUID_EXT_XSAVE | CPUID_EXT_AES |
-+            CPUID_EXT_POPCNT | CPUID_EXT_X2APIC | CPUID_EXT_SSE42 |
-+            CPUID_EXT_SSE41 | CPUID_EXT_CX16 | CPUID_EXT_SSSE3 |
-+            CPUID_EXT_PCLMULQDQ | CPUID_EXT_SSE3 |
-+            CPUID_EXT_TSC_DEADLINE_TIMER | CPUID_EXT_FMA | CPUID_EXT_MOVBE |
-+            CPUID_EXT_PCID | CPUID_EXT_F16C | CPUID_EXT_RDRAND,
-+        .features[FEAT_8000_0001_EDX] =
-+            CPUID_EXT2_LM | CPUID_EXT2_PDPE1GB | CPUID_EXT2_RDTSCP |
-+            CPUID_EXT2_NX | CPUID_EXT2_SYSCALL,
-+        .features[FEAT_8000_0001_ECX] =
-+            CPUID_EXT3_ABM | CPUID_EXT3_LAHF_LM | CPUID_EXT3_3DNOWPREFETCH,
-+        .features[FEAT_7_0_EDX] =
-+            CPUID_7_0_EDX_SPEC_CTRL,
-+        .features[FEAT_7_0_EBX] =
-+            CPUID_7_0_EBX_FSGSBASE | CPUID_7_0_EBX_BMI1 |
-+            CPUID_7_0_EBX_HLE | CPUID_7_0_EBX_AVX2 | CPUID_7_0_EBX_SMEP |
-+            CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_ERMS | CPUID_7_0_EBX_INVPCID |
-+            CPUID_7_0_EBX_RTM | CPUID_7_0_EBX_RDSEED | CPUID_7_0_EBX_ADX |
-+            CPUID_7_0_EBX_SMAP | CPUID_7_0_EBX_MPX | CPUID_7_0_EBX_CLWB |
-+            CPUID_7_0_EBX_AVX512F | CPUID_7_0_EBX_AVX512DQ |
-+            CPUID_7_0_EBX_AVX512BW | CPUID_7_0_EBX_AVX512CD |
-+            CPUID_7_0_EBX_AVX512VL,
-+        /* Missing: XSAVES (not supported by some Linux versions,
-+         * including v4.1 to v4.12).
-+         * KVM doesn't yet expose any XSAVES state save component,
-+         * and the only one defined in Skylake (processor tracing)
-+         * probably will block migration anyway.
-+         */
-+        .features[FEAT_XSAVE] =
-+            CPUID_XSAVE_XSAVEOPT | CPUID_XSAVE_XSAVEC |
-+            CPUID_XSAVE_XGETBV1,
-+        .features[FEAT_6_EAX] =
-+            CPUID_6_EAX_ARAT,
-+        .xlevel = 0x80000008,
-+        .model_id = "Intel Xeon Processor (Skylake, IBRS)",
-+    },
-+    {
-         .name = "Opteron_G1",
-         .level = 5,
-         .vendor = CPUID_VENDOR_AMD,
--- 
-2.11.0
-
diff --git a/debian/patches/extra/0035-ratelimit-don-t-align-wait-time-with-slices.patch b/debian/patches/extra/0035-ratelimit-don-t-align-wait-time-with-slices.patch
deleted file mode 100644 (file)
index ef8c525..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Wolfgang Bumiller <w.bumiller@proxmox.com>
-Date: Tue, 6 Feb 2018 11:34:34 +0100
-Subject: [PATCH] ratelimit: don't align wait time with slices
-
-It is possible for rate limited writes to keep overshooting a slice's
-quota by a tiny amount causing the slice-aligned waiting period to
-effectively halve the rate.
-
-Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
----
- include/qemu/ratelimit.h | 11 +++++------
- 1 file changed, 5 insertions(+), 6 deletions(-)
-
-diff --git a/include/qemu/ratelimit.h b/include/qemu/ratelimit.h
-index 8da1232574..324ff04fe2 100644
---- a/include/qemu/ratelimit.h
-+++ b/include/qemu/ratelimit.h
-@@ -35,7 +35,7 @@ typedef struct {
- static inline int64_t ratelimit_calculate_delay(RateLimit *limit, uint64_t n)
- {
-     int64_t now = qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
--    uint64_t delay_slices;
-+    double delay_slices;
-     assert(limit->slice_quota && limit->slice_ns);
-@@ -54,12 +54,11 @@ static inline int64_t ratelimit_calculate_delay(RateLimit *limit, uint64_t n)
-         return 0;
-     }
--    /* Quota exceeded. Calculate the next time slice we may start
--     * sending data again. */
--    delay_slices = (limit->dispatched + limit->slice_quota - 1) /
--        limit->slice_quota;
-+    /* Quota exceeded. Wait based on the excess amount and then start a new
-+     * slice. */
-+    delay_slices = (double)limit->dispatched / limit->slice_quota;
-     limit->slice_end_time = limit->slice_start_time +
--        delay_slices * limit->slice_ns;
-+        (uint64_t)(delay_slices * limit->slice_ns);
-     return limit->slice_end_time - now;
- }
--- 
-2.11.0
-
diff --git a/debian/patches/extra/0036-nbd-make-it-thread-safe-fix-qcow2-over-nbd.patch b/debian/patches/extra/0036-nbd-make-it-thread-safe-fix-qcow2-over-nbd.patch
deleted file mode 100644 (file)
index b6edf9b..0000000
+++ /dev/null
@@ -1,121 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Paolo Bonzini <pbonzini@redhat.com>
-Date: Thu, 1 Jun 2017 12:44:56 +0200
-Subject: [PATCH] nbd: make it thread-safe, fix qcow2 over nbd
-
-NBD is not thread safe, because it accesses s->in_flight without
-a CoMutex.  Fixing this will be required for multiqueue.
-CoQueue doesn't have spurious wakeups but, when another coroutine can
-run between qemu_co_queue_next's wakeup and qemu_co_queue_wait's
-re-locking of the mutex, the wait condition can become false and
-a loop is necessary.
-
-In fact, it turns out that the loop is necessary even without this
-multi-threaded scenario.  A particular sequence of coroutine wakeups
-is happening ~80% of the time when starting a guest with qcow2 image
-served over NBD (i.e. qemu-nbd --format=raw, and QEMU's -drive option
-has -format=qcow2).  This patch fixes that issue too.
-
-Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
----
- block/nbd-client.c | 30 +++++++++---------------------
- 1 file changed, 9 insertions(+), 21 deletions(-)
-
-diff --git a/block/nbd-client.c b/block/nbd-client.c
-index 56eb0e2e16..282679b4f8 100644
---- a/block/nbd-client.c
-+++ b/block/nbd-client.c
-@@ -114,6 +114,10 @@ static int nbd_co_send_request(BlockDriverState *bs,
-     int rc, ret, i;
-     qemu_co_mutex_lock(&s->send_mutex);
-+    while (s->in_flight == MAX_NBD_REQUESTS) {
-+        qemu_co_queue_wait(&s->free_sema, &s->send_mutex);
-+    }
-+    s->in_flight++;
-     for (i = 0; i < MAX_NBD_REQUESTS; i++) {
-         if (s->recv_coroutine[i] == NULL) {
-@@ -176,20 +180,6 @@ static void nbd_co_receive_reply(NBDClientSession *s,
-     }
- }
--static void nbd_coroutine_start(NBDClientSession *s,
--                                NBDRequest *request)
--{
--    /* Poor man semaphore.  The free_sema is locked when no other request
--     * can be accepted, and unlocked after receiving one reply.  */
--    if (s->in_flight == MAX_NBD_REQUESTS) {
--        qemu_co_queue_wait(&s->free_sema, NULL);
--        assert(s->in_flight < MAX_NBD_REQUESTS);
--    }
--    s->in_flight++;
--
--    /* s->recv_coroutine[i] is set as soon as we get the send_lock.  */
--}
--
- static void nbd_coroutine_end(BlockDriverState *bs,
-                               NBDRequest *request)
- {
-@@ -197,13 +187,16 @@ static void nbd_coroutine_end(BlockDriverState *bs,
-     int i = HANDLE_TO_INDEX(s, request->handle);
-     s->recv_coroutine[i] = NULL;
--    s->in_flight--;
--    qemu_co_queue_next(&s->free_sema);
-     /* Kick the read_reply_co to get the next reply.  */
-     if (s->read_reply_co) {
-         aio_co_wake(s->read_reply_co);
-     }
-+
-+    qemu_co_mutex_lock(&s->send_mutex);
-+    s->in_flight--;
-+    qemu_co_queue_next(&s->free_sema);
-+    qemu_co_mutex_unlock(&s->send_mutex);
- }
- int nbd_client_co_preadv(BlockDriverState *bs, uint64_t offset,
-@@ -221,7 +214,6 @@ int nbd_client_co_preadv(BlockDriverState *bs, uint64_t offset,
-     assert(bytes <= NBD_MAX_BUFFER_SIZE);
-     assert(!flags);
--    nbd_coroutine_start(client, &request);
-     ret = nbd_co_send_request(bs, &request, NULL);
-     if (ret < 0) {
-         reply.error = -ret;
-@@ -251,7 +243,6 @@ int nbd_client_co_pwritev(BlockDriverState *bs, uint64_t offset,
-     assert(bytes <= NBD_MAX_BUFFER_SIZE);
--    nbd_coroutine_start(client, &request);
-     ret = nbd_co_send_request(bs, &request, qiov);
-     if (ret < 0) {
-         reply.error = -ret;
-@@ -286,7 +277,6 @@ int nbd_client_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset,
-         request.flags |= NBD_CMD_FLAG_NO_HOLE;
-     }
--    nbd_coroutine_start(client, &request);
-     ret = nbd_co_send_request(bs, &request, NULL);
-     if (ret < 0) {
-         reply.error = -ret;
-@@ -311,7 +301,6 @@ int nbd_client_co_flush(BlockDriverState *bs)
-     request.from = 0;
-     request.len = 0;
--    nbd_coroutine_start(client, &request);
-     ret = nbd_co_send_request(bs, &request, NULL);
-     if (ret < 0) {
-         reply.error = -ret;
-@@ -337,7 +326,6 @@ int nbd_client_co_pdiscard(BlockDriverState *bs, int64_t offset, int count)
-         return 0;
-     }
--    nbd_coroutine_start(client, &request);
-     ret = nbd_co_send_request(bs, &request, NULL);
-     if (ret < 0) {
-         reply.error = -ret;
--- 
-2.11.0
-
diff --git a/debian/patches/extra/0037-nbd-strict-nbd_wr_syncv.patch b/debian/patches/extra/0037-nbd-strict-nbd_wr_syncv.patch
deleted file mode 100644 (file)
index c149913..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
-Date: Tue, 16 May 2017 12:45:29 +0300
-Subject: [PATCH] nbd: strict nbd_wr_syncv
-
-nbd_wr_syncv is called either from coroutine or from client negotiation
-code, when socket is in blocking mode. So, -EAGAIN is impossible.
-
-Furthermore, EAGAIN is confusing, as, what to read/write again? With
-EAGAIN as a return code we don't know how much data is already
-read or written by the function, so in case of EAGAIN the whole
-communication is broken.
-
-Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
-Message-Id: <20170516094533.6160-2-vsementsov@virtuozzo.com>
-Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
----
- nbd/common.c | 11 ++++++-----
- 1 file changed, 6 insertions(+), 5 deletions(-)
-
-diff --git a/nbd/common.c b/nbd/common.c
-index dccbb8e9de..4db45b3ede 100644
---- a/nbd/common.c
-+++ b/nbd/common.c
-@@ -20,6 +20,10 @@
- #include "qapi/error.h"
- #include "nbd-internal.h"
-+/* nbd_wr_syncv
-+ * The function may be called from coroutine or from non-coroutine context.
-+ * When called from non-coroutine context @ioc must be in blocking mode.
-+ */
- ssize_t nbd_wr_syncv(QIOChannel *ioc,
-                      struct iovec *iov,
-                      size_t niov,
-@@ -42,11 +46,8 @@ ssize_t nbd_wr_syncv(QIOChannel *ioc,
-             len = qio_channel_writev(ioc, local_iov, nlocal_iov, &local_err);
-         }
-         if (len == QIO_CHANNEL_ERR_BLOCK) {
--            if (qemu_in_coroutine()) {
--                qio_channel_yield(ioc, do_read ? G_IO_IN : G_IO_OUT);
--            } else {
--                return -EAGAIN;
--            }
-+            assert(qemu_in_coroutine());
-+            qio_channel_yield(ioc, do_read ? G_IO_IN : G_IO_OUT);
-             continue;
-         }
-         if (len < 0) {
--- 
-2.11.0
-
diff --git a/debian/patches/extra/0038-nbd-read_sync-and-friends-return-0-on-success.patch b/debian/patches/extra/0038-nbd-read_sync-and-friends-return-0-on-success.patch
deleted file mode 100644 (file)
index 91d5205..0000000
+++ /dev/null
@@ -1,606 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
-Date: Tue, 16 May 2017 12:45:30 +0300
-Subject: [PATCH] nbd: read_sync and friends: return 0 on success
-
-functions read_sync, drop_sync, write_sync, and also
-nbd_negotiate_write, nbd_negotiate_read, nbd_negotiate_drop_sync
-returns number of processed bytes. But what this number can be,
-except requested number of bytes?
-
-Actually, underlying nbd_wr_syncv function returns a value >= 0 and
-!= requested_bytes only on eof on read operation. So, firstly, it is
-impossible on write (let's add an assert) and on read it actually
-means, that communication is broken (except nbd_receive_reply, see
-below).
-
-Most of callers operate like this:
-   if (func(..., size) != size) {
-       /* error path */
-   }
-, i.e.:
-  1. They are not interested in partial success
-  2. Extra duplications in code (especially bad are duplications of
-     magic numbers)
-  3. User doesn't see actual error message, as return code is lost.
-     (this patch doesn't fix this point, but it makes fixing easier)
-
-Several callers handles ret >= 0 and != requested-size separately, by
-just returning EINVAL in this case. This patch makes read_sync and
-friends return EINVAL in this case, so final behavior is the same.
-
-And only one caller - nbd_receive_reply() does something not so
-obvious. It returns EINVAL for ret > 0 and != requested-size, like
-previous group, but for ret == 0 it returns 0. The only caller of
-nbd_receive_reply() - nbd_read_reply_entry() handles ret == 0 in the
-same way as ret < 0, so for now it doesn't matter. However, in
-following commits error path handling will be improved and we'll need
-to distinguish success from fail in this case too. So, this patch adds
-separate helper for this case - read_sync_eof.
-
-Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
-Message-Id: <20170516094533.6160-3-vsementsov@virtuozzo.com>
-Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
----
- nbd/client.c       | 63 ++++++++++++++++------------------------
- nbd/nbd-internal.h | 34 +++++++++++++++++++---
- nbd/server.c       | 84 +++++++++++++++++++++---------------------------------
- 3 files changed, 88 insertions(+), 93 deletions(-)
-
-diff --git a/nbd/client.c b/nbd/client.c
-index a58fb02cb4..6b74a628f1 100644
---- a/nbd/client.c
-+++ b/nbd/client.c
-@@ -86,9 +86,9 @@ static QTAILQ_HEAD(, NBDExport) exports = QTAILQ_HEAD_INITIALIZER(exports);
- */
--/* Discard length bytes from channel.  Return -errno on failure, or
-- * the amount of bytes consumed. */
--static ssize_t drop_sync(QIOChannel *ioc, size_t size)
-+/* Discard length bytes from channel.  Return -errno on failure and 0 on
-+ * success*/
-+static int drop_sync(QIOChannel *ioc, size_t size)
- {
-     ssize_t ret = 0;
-     char small[1024];
-@@ -96,14 +96,13 @@ static ssize_t drop_sync(QIOChannel *ioc, size_t size)
-     buffer = sizeof(small) >= size ? small : g_malloc(MIN(65536, size));
-     while (size > 0) {
--        ssize_t count = read_sync(ioc, buffer, MIN(65536, size));
-+        ssize_t count = MIN(65536, size);
-+        ret = read_sync(ioc, buffer, MIN(65536, size));
--        if (count <= 0) {
-+        if (ret < 0) {
-             goto cleanup;
-         }
--        assert(count <= size);
-         size -= count;
--        ret += count;
-     }
-  cleanup:
-@@ -136,12 +135,12 @@ static int nbd_send_option_request(QIOChannel *ioc, uint32_t opt,
-     stl_be_p(&req.option, opt);
-     stl_be_p(&req.length, len);
--    if (write_sync(ioc, &req, sizeof(req)) != sizeof(req)) {
-+    if (write_sync(ioc, &req, sizeof(req)) < 0) {
-         error_setg(errp, "Failed to send option request header");
-         return -1;
-     }
--    if (len && write_sync(ioc, (char *) data, len) != len) {
-+    if (len && write_sync(ioc, (char *) data, len) < 0) {
-         error_setg(errp, "Failed to send option request data");
-         return -1;
-     }
-@@ -170,7 +169,7 @@ static int nbd_receive_option_reply(QIOChannel *ioc, uint32_t opt,
-                                     nbd_opt_reply *reply, Error **errp)
- {
-     QEMU_BUILD_BUG_ON(sizeof(*reply) != 20);
--    if (read_sync(ioc, reply, sizeof(*reply)) != sizeof(*reply)) {
-+    if (read_sync(ioc, reply, sizeof(*reply)) < 0) {
-         error_setg(errp, "failed to read option reply");
-         nbd_send_opt_abort(ioc);
-         return -1;
-@@ -219,7 +218,7 @@ static int nbd_handle_reply_err(QIOChannel *ioc, nbd_opt_reply *reply,
-             goto cleanup;
-         }
-         msg = g_malloc(reply->length + 1);
--        if (read_sync(ioc, msg, reply->length) != reply->length) {
-+        if (read_sync(ioc, msg, reply->length) < 0) {
-             error_setg(errp, "failed to read option error message");
-             goto cleanup;
-         }
-@@ -321,7 +320,7 @@ static int nbd_receive_list(QIOChannel *ioc, const char *want, bool *match,
-         nbd_send_opt_abort(ioc);
-         return -1;
-     }
--    if (read_sync(ioc, &namelen, sizeof(namelen)) != sizeof(namelen)) {
-+    if (read_sync(ioc, &namelen, sizeof(namelen)) < 0) {
-         error_setg(errp, "failed to read option name length");
-         nbd_send_opt_abort(ioc);
-         return -1;
-@@ -334,7 +333,7 @@ static int nbd_receive_list(QIOChannel *ioc, const char *want, bool *match,
-         return -1;
-     }
-     if (namelen != strlen(want)) {
--        if (drop_sync(ioc, len) != len) {
-+        if (drop_sync(ioc, len) < 0) {
-             error_setg(errp, "failed to skip export name with wrong length");
-             nbd_send_opt_abort(ioc);
-             return -1;
-@@ -343,14 +342,14 @@ static int nbd_receive_list(QIOChannel *ioc, const char *want, bool *match,
-     }
-     assert(namelen < sizeof(name));
--    if (read_sync(ioc, name, namelen) != namelen) {
-+    if (read_sync(ioc, name, namelen) < 0) {
-         error_setg(errp, "failed to read export name");
-         nbd_send_opt_abort(ioc);
-         return -1;
-     }
-     name[namelen] = '\0';
-     len -= namelen;
--    if (drop_sync(ioc, len) != len) {
-+    if (drop_sync(ioc, len) < 0) {
-         error_setg(errp, "failed to read export description");
-         nbd_send_opt_abort(ioc);
-         return -1;
-@@ -477,7 +476,7 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint16_t *flags,
-         goto fail;
-     }
--    if (read_sync(ioc, buf, 8) != 8) {
-+    if (read_sync(ioc, buf, 8) < 0) {
-         error_setg(errp, "Failed to read data");
-         goto fail;
-     }
-@@ -503,7 +502,7 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint16_t *flags,
-         goto fail;
-     }
--    if (read_sync(ioc, &magic, sizeof(magic)) != sizeof(magic)) {
-+    if (read_sync(ioc, &magic, sizeof(magic)) < 0) {
-         error_setg(errp, "Failed to read magic");
-         goto fail;
-     }
-@@ -515,8 +514,7 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint16_t *flags,
-         uint16_t globalflags;
-         bool fixedNewStyle = false;
--        if (read_sync(ioc, &globalflags, sizeof(globalflags)) !=
--            sizeof(globalflags)) {
-+        if (read_sync(ioc, &globalflags, sizeof(globalflags)) < 0) {
-             error_setg(errp, "Failed to read server flags");
-             goto fail;
-         }
-@@ -534,8 +532,7 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint16_t *flags,
-         }
-         /* client requested flags */
-         clientflags = cpu_to_be32(clientflags);
--        if (write_sync(ioc, &clientflags, sizeof(clientflags)) !=
--            sizeof(clientflags)) {
-+        if (write_sync(ioc, &clientflags, sizeof(clientflags)) < 0) {
-             error_setg(errp, "Failed to send clientflags field");
-             goto fail;
-         }
-@@ -573,13 +570,13 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint16_t *flags,
-         }
-         /* Read the response */
--        if (read_sync(ioc, &s, sizeof(s)) != sizeof(s)) {
-+        if (read_sync(ioc, &s, sizeof(s)) < 0) {
-             error_setg(errp, "Failed to read export length");
-             goto fail;
-         }
-         *size = be64_to_cpu(s);
--        if (read_sync(ioc, flags, sizeof(*flags)) != sizeof(*flags)) {
-+        if (read_sync(ioc, flags, sizeof(*flags)) < 0) {
-             error_setg(errp, "Failed to read export flags");
-             goto fail;
-         }
-@@ -596,14 +593,14 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint16_t *flags,
-             goto fail;
-         }
--        if (read_sync(ioc, &s, sizeof(s)) != sizeof(s)) {
-+        if (read_sync(ioc, &s, sizeof(s)) < 0) {
-             error_setg(errp, "Failed to read export length");
-             goto fail;
-         }
-         *size = be64_to_cpu(s);
-         TRACE("Size is %" PRIu64, *size);
--        if (read_sync(ioc, &oldflags, sizeof(oldflags)) != sizeof(oldflags)) {
-+        if (read_sync(ioc, &oldflags, sizeof(oldflags)) < 0) {
-             error_setg(errp, "Failed to read export flags");
-             goto fail;
-         }
-@@ -619,7 +616,7 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint16_t *flags,
-     }
-     TRACE("Size is %" PRIu64 ", export flags %" PRIx16, *size, *flags);
--    if (zeroes && drop_sync(ioc, 124) != 124) {
-+    if (zeroes && drop_sync(ioc, 124) < 0) {
-         error_setg(errp, "Failed to read reserved block");
-         goto fail;
-     }
-@@ -744,7 +741,6 @@ int nbd_disconnect(int fd)
- ssize_t nbd_send_request(QIOChannel *ioc, NBDRequest *request)
- {
-     uint8_t buf[NBD_REQUEST_SIZE];
--    ssize_t ret;
-     TRACE("Sending request to server: "
-           "{ .from = %" PRIu64", .len = %" PRIu32 ", .handle = %" PRIu64
-@@ -759,16 +755,7 @@ ssize_t nbd_send_request(QIOChannel *ioc, NBDRequest *request)
-     stq_be_p(buf + 16, request->from);
-     stl_be_p(buf + 24, request->len);
--    ret = write_sync(ioc, buf, sizeof(buf));
--    if (ret < 0) {
--        return ret;
--    }
--
--    if (ret != sizeof(buf)) {
--        LOG("writing to socket failed");
--        return -EINVAL;
--    }
--    return 0;
-+    return write_sync(ioc, buf, sizeof(buf));
- }
- ssize_t nbd_receive_reply(QIOChannel *ioc, NBDReply *reply)
-@@ -777,7 +764,7 @@ ssize_t nbd_receive_reply(QIOChannel *ioc, NBDReply *reply)
-     uint32_t magic;
-     ssize_t ret;
--    ret = read_sync(ioc, buf, sizeof(buf));
-+    ret = read_sync_eof(ioc, buf, sizeof(buf));
-     if (ret <= 0) {
-         return ret;
-     }
-diff --git a/nbd/nbd-internal.h b/nbd/nbd-internal.h
-index f43d990a05..e6bbc7c4b4 100644
---- a/nbd/nbd-internal.h
-+++ b/nbd/nbd-internal.h
-@@ -94,7 +94,13 @@
- #define NBD_ENOSPC     28
- #define NBD_ESHUTDOWN  108
--static inline ssize_t read_sync(QIOChannel *ioc, void *buffer, size_t size)
-+/* read_sync_eof
-+ * Tries to read @size bytes from @ioc. Returns number of bytes actually read.
-+ * May return a value >= 0 and < size only on EOF, i.e. when iteratively called
-+ * qio_channel_readv() returns 0. So, there are no needs to call read_sync_eof
-+ * iteratively.
-+ */
-+static inline ssize_t read_sync_eof(QIOChannel *ioc, void *buffer, size_t size)
- {
-     struct iovec iov = { .iov_base = buffer, .iov_len = size };
-     /* Sockets are kept in blocking mode in the negotiation phase.  After
-@@ -105,12 +111,32 @@ static inline ssize_t read_sync(QIOChannel *ioc, void *buffer, size_t size)
-     return nbd_wr_syncv(ioc, &iov, 1, size, true);
- }
--static inline ssize_t write_sync(QIOChannel *ioc, const void *buffer,
--                                 size_t size)
-+/* read_sync
-+ * Reads @size bytes from @ioc. Returns 0 on success.
-+ */
-+static inline int read_sync(QIOChannel *ioc, void *buffer, size_t size)
-+{
-+    ssize_t ret = read_sync_eof(ioc, buffer, size);
-+
-+    if (ret >= 0 && ret != size) {
-+        ret = -EINVAL;
-+    }
-+
-+    return ret < 0 ? ret : 0;
-+}
-+
-+/* write_sync
-+ * Writes @size bytes to @ioc. Returns 0 on success.
-+ */
-+static inline int write_sync(QIOChannel *ioc, const void *buffer, size_t size)
- {
-     struct iovec iov = { .iov_base = (void *) buffer, .iov_len = size };
--    return nbd_wr_syncv(ioc, &iov, 1, size, false);
-+    ssize_t ret = nbd_wr_syncv(ioc, &iov, 1, size, false);
-+
-+    assert(ret < 0 || ret == size);
-+
-+    return ret < 0 ? ret : 0;
- }
- struct NBDTLSHandshakeData {
-diff --git a/nbd/server.c b/nbd/server.c
-index 4d6da8ac06..c2a5909ad6 100644
---- a/nbd/server.c
-+++ b/nbd/server.c
-@@ -112,7 +112,7 @@ static gboolean nbd_negotiate_continue(QIOChannel *ioc,
-     return TRUE;
- }
--static ssize_t nbd_negotiate_read(QIOChannel *ioc, void *buffer, size_t size)
-+static int nbd_negotiate_read(QIOChannel *ioc, void *buffer, size_t size)
- {
-     ssize_t ret;
-     guint watch;
-@@ -130,8 +130,7 @@ static ssize_t nbd_negotiate_read(QIOChannel *ioc, void *buffer, size_t size)
- }
--static ssize_t nbd_negotiate_write(QIOChannel *ioc, const void *buffer,
--                                   size_t size)
-+static int nbd_negotiate_write(QIOChannel *ioc, const void *buffer, size_t size)
- {
-     ssize_t ret;
-     guint watch;
-@@ -148,24 +147,24 @@ static ssize_t nbd_negotiate_write(QIOChannel *ioc, const void *buffer,
-     return ret;
- }
--static ssize_t nbd_negotiate_drop_sync(QIOChannel *ioc, size_t size)
-+static int nbd_negotiate_drop_sync(QIOChannel *ioc, size_t size)
- {
--    ssize_t ret, dropped = size;
-+    ssize_t ret;
-     uint8_t *buffer = g_malloc(MIN(65536, size));
-     while (size > 0) {
--        ret = nbd_negotiate_read(ioc, buffer, MIN(65536, size));
-+        size_t count = MIN(65536, size);
-+        ret = nbd_negotiate_read(ioc, buffer, count);
-         if (ret < 0) {
-             g_free(buffer);
-             return ret;
-         }
--        assert(ret <= size);
--        size -= ret;
-+        size -= count;
-     }
-     g_free(buffer);
--    return dropped;
-+    return 0;
- }
- /* Basic flow for negotiation
-@@ -206,22 +205,22 @@ static int nbd_negotiate_send_rep_len(QIOChannel *ioc, uint32_t type,
-           type, opt, len);
-     magic = cpu_to_be64(NBD_REP_MAGIC);
--    if (nbd_negotiate_write(ioc, &magic, sizeof(magic)) != sizeof(magic)) {
-+    if (nbd_negotiate_write(ioc, &magic, sizeof(magic)) < 0) {
-         LOG("write failed (rep magic)");
-         return -EINVAL;
-     }
-     opt = cpu_to_be32(opt);
--    if (nbd_negotiate_write(ioc, &opt, sizeof(opt)) != sizeof(opt)) {
-+    if (nbd_negotiate_write(ioc, &opt, sizeof(opt)) < 0) {
-         LOG("write failed (rep opt)");
-         return -EINVAL;
-     }
-     type = cpu_to_be32(type);
--    if (nbd_negotiate_write(ioc, &type, sizeof(type)) != sizeof(type)) {
-+    if (nbd_negotiate_write(ioc, &type, sizeof(type)) < 0) {
-         LOG("write failed (rep type)");
-         return -EINVAL;
-     }
-     len = cpu_to_be32(len);
--    if (nbd_negotiate_write(ioc, &len, sizeof(len)) != sizeof(len)) {
-+    if (nbd_negotiate_write(ioc, &len, sizeof(len)) < 0) {
-         LOG("write failed (rep data length)");
-         return -EINVAL;
-     }
-@@ -256,7 +255,7 @@ nbd_negotiate_send_rep_err(QIOChannel *ioc, uint32_t type,
-     if (ret < 0) {
-         goto out;
-     }
--    if (nbd_negotiate_write(ioc, msg, len) != len) {
-+    if (nbd_negotiate_write(ioc, msg, len) < 0) {
-         LOG("write failed (error message)");
-         ret = -EIO;
-     } else {
-@@ -287,15 +286,15 @@ static int nbd_negotiate_send_rep_list(QIOChannel *ioc, NBDExport *exp)
-     }
-     len = cpu_to_be32(name_len);
--    if (nbd_negotiate_write(ioc, &len, sizeof(len)) != sizeof(len)) {
-+    if (nbd_negotiate_write(ioc, &len, sizeof(len)) < 0) {
-         LOG("write failed (name length)");
-         return -EINVAL;
-     }
--    if (nbd_negotiate_write(ioc, name, name_len) != name_len) {
-+    if (nbd_negotiate_write(ioc, name, name_len) < 0) {
-         LOG("write failed (name buffer)");
-         return -EINVAL;
-     }
--    if (nbd_negotiate_write(ioc, desc, desc_len) != desc_len) {
-+    if (nbd_negotiate_write(ioc, desc, desc_len) < 0) {
-         LOG("write failed (description buffer)");
-         return -EINVAL;
-     }
-@@ -309,7 +308,7 @@ static int nbd_negotiate_handle_list(NBDClient *client, uint32_t length)
-     NBDExport *exp;
-     if (length) {
--        if (nbd_negotiate_drop_sync(client->ioc, length) != length) {
-+        if (nbd_negotiate_drop_sync(client->ioc, length) < 0) {
-             return -EIO;
-         }
-         return nbd_negotiate_send_rep_err(client->ioc,
-@@ -340,7 +339,7 @@ static int nbd_negotiate_handle_export_name(NBDClient *client, uint32_t length)
-         LOG("Bad length received");
-         goto fail;
-     }
--    if (nbd_negotiate_read(client->ioc, name, length) != length) {
-+    if (nbd_negotiate_read(client->ioc, name, length) < 0) {
-         LOG("read failed");
-         goto fail;
-     }
-@@ -373,7 +372,7 @@ static QIOChannel *nbd_negotiate_handle_starttls(NBDClient *client,
-     TRACE("Setting up TLS");
-     ioc = client->ioc;
-     if (length) {
--        if (nbd_negotiate_drop_sync(ioc, length) != length) {
-+        if (nbd_negotiate_drop_sync(ioc, length) < 0) {
-             return NULL;
-         }
-         nbd_negotiate_send_rep_err(ioc, NBD_REP_ERR_INVALID, NBD_OPT_STARTTLS,
-@@ -437,8 +436,7 @@ static int nbd_negotiate_options(NBDClient *client)
-         ...           Rest of request
-     */
--    if (nbd_negotiate_read(client->ioc, &flags, sizeof(flags)) !=
--        sizeof(flags)) {
-+    if (nbd_negotiate_read(client->ioc, &flags, sizeof(flags)) < 0) {
-         LOG("read failed");
-         return -EIO;
-     }
-@@ -464,8 +462,7 @@ static int nbd_negotiate_options(NBDClient *client)
-         uint32_t clientflags, length;
-         uint64_t magic;
--        if (nbd_negotiate_read(client->ioc, &magic, sizeof(magic)) !=
--            sizeof(magic)) {
-+        if (nbd_negotiate_read(client->ioc, &magic, sizeof(magic)) < 0) {
-             LOG("read failed");
-             return -EINVAL;
-         }
-@@ -476,14 +473,14 @@ static int nbd_negotiate_options(NBDClient *client)
-         }
-         if (nbd_negotiate_read(client->ioc, &clientflags,
--                               sizeof(clientflags)) != sizeof(clientflags)) {
-+                               sizeof(clientflags)) < 0)
-+        {
-             LOG("read failed");
-             return -EINVAL;
-         }
-         clientflags = be32_to_cpu(clientflags);
--        if (nbd_negotiate_read(client->ioc, &length, sizeof(length)) !=
--            sizeof(length)) {
-+        if (nbd_negotiate_read(client->ioc, &length, sizeof(length)) < 0) {
-             LOG("read failed");
-             return -EINVAL;
-         }
-@@ -519,7 +516,7 @@ static int nbd_negotiate_options(NBDClient *client)
-                 return -EINVAL;
-             default:
--                if (nbd_negotiate_drop_sync(client->ioc, length) != length) {
-+                if (nbd_negotiate_drop_sync(client->ioc, length) < 0) {
-                     return -EIO;
-                 }
-                 ret = nbd_negotiate_send_rep_err(client->ioc,
-@@ -557,7 +554,7 @@ static int nbd_negotiate_options(NBDClient *client)
-                 return nbd_negotiate_handle_export_name(client, length);
-             case NBD_OPT_STARTTLS:
--                if (nbd_negotiate_drop_sync(client->ioc, length) != length) {
-+                if (nbd_negotiate_drop_sync(client->ioc, length) < 0) {
-                     return -EIO;
-                 }
-                 if (client->tlscreds) {
-@@ -576,7 +573,7 @@ static int nbd_negotiate_options(NBDClient *client)
-                 }
-                 break;
-             default:
--                if (nbd_negotiate_drop_sync(client->ioc, length) != length) {
-+                if (nbd_negotiate_drop_sync(client->ioc, length) < 0) {
-                     return -EIO;
-                 }
-                 ret = nbd_negotiate_send_rep_err(client->ioc,
-@@ -665,12 +662,12 @@ static coroutine_fn int nbd_negotiate(NBDClientNewData *data)
-             TRACE("TLS cannot be enabled with oldstyle protocol");
-             goto fail;
-         }
--        if (nbd_negotiate_write(client->ioc, buf, sizeof(buf)) != sizeof(buf)) {
-+        if (nbd_negotiate_write(client->ioc, buf, sizeof(buf)) < 0) {
-             LOG("write failed");
-             goto fail;
-         }
-     } else {
--        if (nbd_negotiate_write(client->ioc, buf, 18) != 18) {
-+        if (nbd_negotiate_write(client->ioc, buf, 18) < 0) {
-             LOG("write failed");
-             goto fail;
-         }
-@@ -685,7 +682,7 @@ static coroutine_fn int nbd_negotiate(NBDClientNewData *data)
-         stq_be_p(buf + 18, client->exp->size);
-         stw_be_p(buf + 26, client->exp->nbdflags | myflags);
-         len = client->no_zeroes ? 10 : sizeof(buf) - 18;
--        if (nbd_negotiate_write(client->ioc, buf + 18, len) != len) {
-+        if (nbd_negotiate_write(client->ioc, buf + 18, len) < 0) {
-             LOG("write failed");
-             goto fail;
-         }
-@@ -708,11 +705,6 @@ static ssize_t nbd_receive_request(QIOChannel *ioc, NBDRequest *request)
-         return ret;
-     }
--    if (ret != sizeof(buf)) {
--        LOG("read failed");
--        return -EINVAL;
--    }
--
-     /* Request
-        [ 0 ..  3]   magic   (NBD_REQUEST_MAGIC)
-        [ 4 ..  5]   flags   (NBD_CMD_FLAG_FUA, ...)
-@@ -743,7 +735,6 @@ static ssize_t nbd_receive_request(QIOChannel *ioc, NBDRequest *request)
- static ssize_t nbd_send_reply(QIOChannel *ioc, NBDReply *reply)
- {
-     uint8_t buf[NBD_REPLY_SIZE];
--    ssize_t ret;
-     reply->error = system_errno_to_nbd_errno(reply->error);
-@@ -760,16 +751,7 @@ static ssize_t nbd_send_reply(QIOChannel *ioc, NBDReply *reply)
-     stl_be_p(buf + 4, reply->error);
-     stq_be_p(buf + 8, reply->handle);
--    ret = write_sync(ioc, buf, sizeof(buf));
--    if (ret < 0) {
--        return ret;
--    }
--
--    if (ret != sizeof(buf)) {
--        LOG("writing to socket failed");
--        return -EINVAL;
--    }
--    return 0;
-+    return write_sync(ioc, buf, sizeof(buf));
- }
- #define MAX_NBD_REQUESTS 16
-@@ -1073,7 +1055,7 @@ static ssize_t nbd_co_send_reply(NBDRequestData *req, NBDReply *reply,
-         rc = nbd_send_reply(client->ioc, reply);
-         if (rc >= 0) {
-             ret = write_sync(client->ioc, req->data, len);
--            if (ret != len) {
-+            if (ret < 0) {
-                 rc = -EIO;
-             }
-         }
-@@ -1147,7 +1129,7 @@ static ssize_t nbd_co_receive_request(NBDRequestData *req,
-     if (request->type == NBD_CMD_WRITE) {
-         TRACE("Reading %" PRIu32 " byte(s)", request->len);
--        if (read_sync(client->ioc, req->data, request->len) != request->len) {
-+        if (read_sync(client->ioc, req->data, request->len) < 0) {
-             LOG("reading from socket failed");
-             rc = -EIO;
-             goto out;
--- 
-2.11.0
-
diff --git a/debian/patches/extra/0039-nbd-make-nbd_drop-public.patch b/debian/patches/extra/0039-nbd-make-nbd_drop-public.patch
deleted file mode 100644 (file)
index 03bc7cf..0000000
+++ /dev/null
@@ -1,151 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Eric Blake <eblake@redhat.com>
-Date: Wed, 19 Jul 2017 18:02:01 +0200
-Subject: [PATCH] nbd: make nbd_drop public
-
-RH-Author: Eric Blake <eblake@redhat.com>
-Message-id: <20170719180202.23329-4-eblake@redhat.com>
-Patchwork-id: 75814
-O-Subject: [RHEV-7.4.z qemu-kvm-rhev PATCH 3/4] nbd: make nbd_drop public
-Bugzilla: 1467509
-RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
-RH-Acked-by: Paolo Bonzini <pbonzini@redhat.com>
-RH-Acked-by: Jeffrey Cody <jcody@redhat.com>
-
-From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
-
-Following commit will reuse it for nbd server too.
-
-Reviewed-by: Eric Blake <eblake@redhat.com>
-Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
-Message-Id: <20170602150150.258222-3-vsementsov@virtuozzo.com>
-Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
-(cherry picked from commit 44298024d30ad36439707b89715a76333f58791b)
-
- Conflicts:
-       nbd/client.c, nbd/nbd_internal.h, nbd/common.c - missing errp
-       addition (e44ed99) and bulk rename (d1fdf25)
-
-Signed-off-by: Eric Blake <eblake@redhat.com>
-Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
----
- nbd/client.c       | 32 +++-----------------------------
- nbd/common.c       | 26 ++++++++++++++++++++++++++
- nbd/nbd-internal.h |  2 ++
- 3 files changed, 31 insertions(+), 29 deletions(-)
-
-diff --git a/nbd/client.c b/nbd/client.c
-index 6b74a628f1..1652f28e9f 100644
---- a/nbd/client.c
-+++ b/nbd/client.c
-@@ -86,32 +86,6 @@ static QTAILQ_HEAD(, NBDExport) exports = QTAILQ_HEAD_INITIALIZER(exports);
- */
--/* Discard length bytes from channel.  Return -errno on failure and 0 on
-- * success*/
--static int drop_sync(QIOChannel *ioc, size_t size)
--{
--    ssize_t ret = 0;
--    char small[1024];
--    char *buffer;
--
--    buffer = sizeof(small) >= size ? small : g_malloc(MIN(65536, size));
--    while (size > 0) {
--        ssize_t count = MIN(65536, size);
--        ret = read_sync(ioc, buffer, MIN(65536, size));
--
--        if (ret < 0) {
--            goto cleanup;
--        }
--        size -= count;
--    }
--
-- cleanup:
--    if (buffer != small) {
--        g_free(buffer);
--    }
--    return ret;
--}
--
- /* Send an option request.
-  *
-  * The request is for option @opt, with @data containing @len bytes of
-@@ -333,7 +307,7 @@ static int nbd_receive_list(QIOChannel *ioc, const char *want, bool *match,
-         return -1;
-     }
-     if (namelen != strlen(want)) {
--        if (drop_sync(ioc, len) < 0) {
-+        if (nbd_drop(ioc, len) < 0) {
-             error_setg(errp, "failed to skip export name with wrong length");
-             nbd_send_opt_abort(ioc);
-             return -1;
-@@ -349,7 +323,7 @@ static int nbd_receive_list(QIOChannel *ioc, const char *want, bool *match,
-     }
-     name[namelen] = '\0';
-     len -= namelen;
--    if (drop_sync(ioc, len) < 0) {
-+    if (nbd_drop(ioc, len) < 0) {
-         error_setg(errp, "failed to read export description");
-         nbd_send_opt_abort(ioc);
-         return -1;
-@@ -616,7 +590,7 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint16_t *flags,
-     }
-     TRACE("Size is %" PRIu64 ", export flags %" PRIx16, *size, *flags);
--    if (zeroes && drop_sync(ioc, 124) < 0) {
-+    if (zeroes && nbd_drop(ioc, 124) < 0) {
-         error_setg(errp, "Failed to read reserved block");
-         goto fail;
-     }
-diff --git a/nbd/common.c b/nbd/common.c
-index 4db45b3ede..9a54010c25 100644
---- a/nbd/common.c
-+++ b/nbd/common.c
-@@ -71,6 +71,32 @@ ssize_t nbd_wr_syncv(QIOChannel *ioc,
-     return done;
- }
-+/* Discard length bytes from channel.  Return -errno on failure and 0 on
-+ * success */
-+int nbd_drop(QIOChannel *ioc, size_t size)
-+{
-+    ssize_t ret = 0;
-+    char small[1024];
-+    char *buffer;
-+
-+    buffer = sizeof(small) >= size ? small : g_malloc(MIN(65536, size));
-+    while (size > 0) {
-+        ssize_t count = MIN(65536, size);
-+        ret = read_sync(ioc, buffer, MIN(65536, size));
-+
-+        if (ret < 0) {
-+            goto cleanup;
-+        }
-+        size -= count;
-+    }
-+
-+ cleanup:
-+    if (buffer != small) {
-+        g_free(buffer);
-+    }
-+    return ret;
-+}
-+
- void nbd_tls_handshake(QIOTask *task,
-                        void *opaque)
-diff --git a/nbd/nbd-internal.h b/nbd/nbd-internal.h
-index e6bbc7c4b4..c02378c553 100644
---- a/nbd/nbd-internal.h
-+++ b/nbd/nbd-internal.h
-@@ -149,4 +149,6 @@ struct NBDTLSHandshakeData {
- void nbd_tls_handshake(QIOTask *task,
-                        void *opaque);
-+int nbd_drop(QIOChannel *ioc, size_t size);
-+
- #endif
--- 
-2.11.0
-
diff --git a/debian/patches/extra/0040-nbd-server-get-rid-of-nbd_negotiate_read-and-friends.patch b/debian/patches/extra/0040-nbd-server-get-rid-of-nbd_negotiate_read-and-friends.patch
deleted file mode 100644 (file)
index 638e22f..0000000
+++ /dev/null
@@ -1,292 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Eric Blake <eblake@redhat.com>
-Date: Wed, 19 Jul 2017 18:02:02 +0200
-Subject: [PATCH] nbd/server: get rid of nbd_negotiate_read and friends
-
-RH-Author: Eric Blake <eblake@redhat.com>
-Message-id: <20170719180202.23329-5-eblake@redhat.com>
-Patchwork-id: 75816
-O-Subject: [RHEV-7.4.z qemu-kvm-rhev PATCH 4/4] nbd/server: get rid of nbd_negotiate_read and friends
-Bugzilla: 1467509
-RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
-RH-Acked-by: Paolo Bonzini <pbonzini@redhat.com>
-RH-Acked-by: Jeffrey Cody <jcody@redhat.com>
-
-From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
-
-Functions nbd_negotiate_{read,write,drop_sync} were introduced in
-1a6245a5b, when nbd_rwv (was nbd_wr_sync) was working through
-qemu_co_sendv_recvv (the path is nbd_wr_sync -> qemu_co_{recv/send} ->
-qemu_co_send_recv -> qemu_co_sendv_recvv), which just yields, without
-setting any handlers. But starting from ff82911cd nbd_rwv (was
-nbd_wr_syncv) works through qio_channel_yield() which sets handlers, so
-watchers are redundant in nbd_negotiate_{read,write,drop_sync}, then,
-let's just use nbd_{read,write,drop} functions.
-
-Functions nbd_{read,write,drop} has errp parameter, which is unused in
-this patch. This will be fixed later.
-
-Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
-Reviewed-by: Eric Blake <eblake@redhat.com>
-Message-Id: <20170602150150.258222-4-vsementsov@virtuozzo.com>
-Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
-(cherry picked from commit 2b0bbc4f8809c972bad134bc1a2570dbb01dea0b)
-
-Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
-
-Conflicts:
-       nbd/server.c - missing errp addition (e44ed99) and bulk
-       rename (d1fdf25)
-Fixes CVE-2017-7539
-
-Signed-off-by: Eric Blake <eblake@redhat.com>
----
- nbd/server.c | 106 ++++++++++++-----------------------------------------------
- 1 file changed, 21 insertions(+), 85 deletions(-)
-
-diff --git a/nbd/server.c b/nbd/server.c
-index c2a5909ad6..ddf93d4717 100644
---- a/nbd/server.c
-+++ b/nbd/server.c
-@@ -104,69 +104,6 @@ struct NBDClient {
- static void nbd_client_receive_next_request(NBDClient *client);
--static gboolean nbd_negotiate_continue(QIOChannel *ioc,
--                                       GIOCondition condition,
--                                       void *opaque)
--{
--    qemu_coroutine_enter(opaque);
--    return TRUE;
--}
--
--static int nbd_negotiate_read(QIOChannel *ioc, void *buffer, size_t size)
--{
--    ssize_t ret;
--    guint watch;
--
--    assert(qemu_in_coroutine());
--    /* Negotiation are always in main loop. */
--    watch = qio_channel_add_watch(ioc,
--                                  G_IO_IN,
--                                  nbd_negotiate_continue,
--                                  qemu_coroutine_self(),
--                                  NULL);
--    ret = read_sync(ioc, buffer, size);
--    g_source_remove(watch);
--    return ret;
--
--}
--
--static int nbd_negotiate_write(QIOChannel *ioc, const void *buffer, size_t size)
--{
--    ssize_t ret;
--    guint watch;
--
--    assert(qemu_in_coroutine());
--    /* Negotiation are always in main loop. */
--    watch = qio_channel_add_watch(ioc,
--                                  G_IO_OUT,
--                                  nbd_negotiate_continue,
--                                  qemu_coroutine_self(),
--                                  NULL);
--    ret = write_sync(ioc, buffer, size);
--    g_source_remove(watch);
--    return ret;
--}
--
--static int nbd_negotiate_drop_sync(QIOChannel *ioc, size_t size)
--{
--    ssize_t ret;
--    uint8_t *buffer = g_malloc(MIN(65536, size));
--
--    while (size > 0) {
--        size_t count = MIN(65536, size);
--        ret = nbd_negotiate_read(ioc, buffer, count);
--        if (ret < 0) {
--            g_free(buffer);
--            return ret;
--        }
--
--        size -= count;
--    }
--
--    g_free(buffer);
--    return 0;
--}
--
- /* Basic flow for negotiation
-    Server         Client
-@@ -205,22 +142,22 @@ static int nbd_negotiate_send_rep_len(QIOChannel *ioc, uint32_t type,
-           type, opt, len);
-     magic = cpu_to_be64(NBD_REP_MAGIC);
--    if (nbd_negotiate_write(ioc, &magic, sizeof(magic)) < 0) {
-+    if (write_sync(ioc, &magic, sizeof(magic)) < 0) {
-         LOG("write failed (rep magic)");
-         return -EINVAL;
-     }
-     opt = cpu_to_be32(opt);
--    if (nbd_negotiate_write(ioc, &opt, sizeof(opt)) < 0) {
-+    if (write_sync(ioc, &opt, sizeof(opt)) < 0) {
-         LOG("write failed (rep opt)");
-         return -EINVAL;
-     }
-     type = cpu_to_be32(type);
--    if (nbd_negotiate_write(ioc, &type, sizeof(type)) < 0) {
-+    if (write_sync(ioc, &type, sizeof(type)) < 0) {
-         LOG("write failed (rep type)");
-         return -EINVAL;
-     }
-     len = cpu_to_be32(len);
--    if (nbd_negotiate_write(ioc, &len, sizeof(len)) < 0) {
-+    if (write_sync(ioc, &len, sizeof(len)) < 0) {
-         LOG("write failed (rep data length)");
-         return -EINVAL;
-     }
-@@ -255,7 +192,7 @@ nbd_negotiate_send_rep_err(QIOChannel *ioc, uint32_t type,
-     if (ret < 0) {
-         goto out;
-     }
--    if (nbd_negotiate_write(ioc, msg, len) < 0) {
-+    if (write_sync(ioc, msg, len) < 0) {
-         LOG("write failed (error message)");
-         ret = -EIO;
-     } else {
-@@ -286,15 +223,15 @@ static int nbd_negotiate_send_rep_list(QIOChannel *ioc, NBDExport *exp)
-     }
-     len = cpu_to_be32(name_len);
--    if (nbd_negotiate_write(ioc, &len, sizeof(len)) < 0) {
-+    if (write_sync(ioc, &len, sizeof(len)) < 0) {
-         LOG("write failed (name length)");
-         return -EINVAL;
-     }
--    if (nbd_negotiate_write(ioc, name, name_len) < 0) {
-+    if (write_sync(ioc, name, name_len) < 0) {
-         LOG("write failed (name buffer)");
-         return -EINVAL;
-     }
--    if (nbd_negotiate_write(ioc, desc, desc_len) < 0) {
-+    if (write_sync(ioc, desc, desc_len) < 0) {
-         LOG("write failed (description buffer)");
-         return -EINVAL;
-     }
-@@ -308,7 +245,7 @@ static int nbd_negotiate_handle_list(NBDClient *client, uint32_t length)
-     NBDExport *exp;
-     if (length) {
--        if (nbd_negotiate_drop_sync(client->ioc, length) < 0) {
-+        if (nbd_drop(client->ioc, length) < 0) {
-             return -EIO;
-         }
-         return nbd_negotiate_send_rep_err(client->ioc,
-@@ -339,7 +276,7 @@ static int nbd_negotiate_handle_export_name(NBDClient *client, uint32_t length)
-         LOG("Bad length received");
-         goto fail;
-     }
--    if (nbd_negotiate_read(client->ioc, name, length) < 0) {
-+    if (read_sync(client->ioc, name, length) < 0) {
-         LOG("read failed");
-         goto fail;
-     }
-@@ -372,7 +309,7 @@ static QIOChannel *nbd_negotiate_handle_starttls(NBDClient *client,
-     TRACE("Setting up TLS");
-     ioc = client->ioc;
-     if (length) {
--        if (nbd_negotiate_drop_sync(ioc, length) < 0) {
-+        if (nbd_drop(ioc, length) < 0) {
-             return NULL;
-         }
-         nbd_negotiate_send_rep_err(ioc, NBD_REP_ERR_INVALID, NBD_OPT_STARTTLS,
-@@ -436,7 +373,7 @@ static int nbd_negotiate_options(NBDClient *client)
-         ...           Rest of request
-     */
--    if (nbd_negotiate_read(client->ioc, &flags, sizeof(flags)) < 0) {
-+    if (read_sync(client->ioc, &flags, sizeof(flags)) < 0) {
-         LOG("read failed");
-         return -EIO;
-     }
-@@ -462,7 +399,7 @@ static int nbd_negotiate_options(NBDClient *client)
-         uint32_t clientflags, length;
-         uint64_t magic;
--        if (nbd_negotiate_read(client->ioc, &magic, sizeof(magic)) < 0) {
-+        if (read_sync(client->ioc, &magic, sizeof(magic)) < 0) {
-             LOG("read failed");
-             return -EINVAL;
-         }
-@@ -472,15 +409,14 @@ static int nbd_negotiate_options(NBDClient *client)
-             return -EINVAL;
-         }
--        if (nbd_negotiate_read(client->ioc, &clientflags,
--                               sizeof(clientflags)) < 0)
-+        if (read_sync(client->ioc, &clientflags, sizeof(clientflags)) < 0)
-         {
-             LOG("read failed");
-             return -EINVAL;
-         }
-         clientflags = be32_to_cpu(clientflags);
--        if (nbd_negotiate_read(client->ioc, &length, sizeof(length)) < 0) {
-+        if (read_sync(client->ioc, &length, sizeof(length)) < 0) {
-             LOG("read failed");
-             return -EINVAL;
-         }
-@@ -516,7 +452,7 @@ static int nbd_negotiate_options(NBDClient *client)
-                 return -EINVAL;
-             default:
--                if (nbd_negotiate_drop_sync(client->ioc, length) < 0) {
-+                if (nbd_drop(client->ioc, length) < 0) {
-                     return -EIO;
-                 }
-                 ret = nbd_negotiate_send_rep_err(client->ioc,
-@@ -554,7 +490,7 @@ static int nbd_negotiate_options(NBDClient *client)
-                 return nbd_negotiate_handle_export_name(client, length);
-             case NBD_OPT_STARTTLS:
--                if (nbd_negotiate_drop_sync(client->ioc, length) < 0) {
-+                if (nbd_drop(client->ioc, length) < 0) {
-                     return -EIO;
-                 }
-                 if (client->tlscreds) {
-@@ -573,7 +509,7 @@ static int nbd_negotiate_options(NBDClient *client)
-                 }
-                 break;
-             default:
--                if (nbd_negotiate_drop_sync(client->ioc, length) < 0) {
-+                if (nbd_drop(client->ioc, length) < 0) {
-                     return -EIO;
-                 }
-                 ret = nbd_negotiate_send_rep_err(client->ioc,
-@@ -662,12 +598,12 @@ static coroutine_fn int nbd_negotiate(NBDClientNewData *data)
-             TRACE("TLS cannot be enabled with oldstyle protocol");
-             goto fail;
-         }
--        if (nbd_negotiate_write(client->ioc, buf, sizeof(buf)) < 0) {
-+        if (write_sync(client->ioc, buf, sizeof(buf)) < 0) {
-             LOG("write failed");
-             goto fail;
-         }
-     } else {
--        if (nbd_negotiate_write(client->ioc, buf, 18) < 0) {
-+        if (write_sync(client->ioc, buf, 18) < 0) {
-             LOG("write failed");
-             goto fail;
-         }
-@@ -682,7 +618,7 @@ static coroutine_fn int nbd_negotiate(NBDClientNewData *data)
-         stq_be_p(buf + 18, client->exp->size);
-         stw_be_p(buf + 26, client->exp->nbdflags | myflags);
-         len = client->no_zeroes ? 10 : sizeof(buf) - 18;
--        if (nbd_negotiate_write(client->ioc, buf + 18, len) < 0) {
-+        if (write_sync(client->ioc, buf + 18, len) < 0) {
-             LOG("write failed");
-             goto fail;
-         }
--- 
-2.11.0
-
diff --git a/debian/patches/extra/0041-nbd-client-Fix-regression-when-server-sends-garbage.patch b/debian/patches/extra/0041-nbd-client-Fix-regression-when-server-sends-garbage.patch
deleted file mode 100644 (file)
index 2d7560d..0000000
+++ /dev/null
@@ -1,153 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Eric Blake <eblake@redhat.com>
-Date: Wed, 27 Sep 2017 17:57:19 +0200
-Subject: [PATCH] nbd-client: Fix regression when server sends garbage
-
-RH-Author: Eric Blake <eblake@redhat.com>
-Message-id: <20170927175725.20023-2-eblake@redhat.com>
-Patchwork-id: 76672
-O-Subject: [RHEV-7.4.z qemu-kvm-rhev PATCH 1/7] nbd-client: Fix regression when server sends garbage
-Bugzilla: 1495474
-RH-Acked-by: Max Reitz <mreitz@redhat.com>
-RH-Acked-by: Jeffrey Cody <jcody@redhat.com>
-RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
-
-When we switched NBD to use coroutines for qemu 2.9 (in particular,
-commit a12a712a), we introduced a regression: if a server sends us
-garbage (such as a corrupted magic number), we quit the read loop
-but do not stop sending further queued commands, resulting in the
-client hanging when it never reads the response to those additional
-commands.  In qemu 2.8, we properly detected that the server is no
-longer reliable, and cancelled all existing pending commands with
-EIO, then tore down the socket so that all further command attempts
-get EPIPE.
-
-Restore the proper behavior of quitting (almost) all communication
-with a broken server: Once we know we are out of sync or otherwise
-can't trust the server, we must assume that any further incoming
-data is unreliable and therefore end all pending commands with EIO,
-and quit trying to send any further commands.  As an exception, we
-still (try to) send NBD_CMD_DISC to let the server know we are going
-away (in part, because it is easier to do that than to further
-refactor nbd_teardown_connection, and in part because it is the
-only command where we do not have to wait for a reply).
-
-Based on a patch by Vladimir Sementsov-Ogievskiy.
-
-A malicious server can be created with the following hack,
-followed by setting NBD_SERVER_DEBUG to a non-zero value in the
-environment when running qemu-nbd:
-
-| --- a/nbd/server.c
-| +++ b/nbd/server.c
-| @@ -919,6 +919,17 @@ static int nbd_send_reply(QIOChannel *ioc, NBDReply *reply, Error **errp)
-|      stl_be_p(buf + 4, reply->error);
-|      stq_be_p(buf + 8, reply->handle);
-|
-| +    static int debug;
-| +    static int count;
-| +    if (!count++) {
-| +        const char *str = getenv("NBD_SERVER_DEBUG");
-| +        if (str) {
-| +            debug = atoi(str);
-| +        }
-| +    }
-| +    if (debug && !(count % debug)) {
-| +        buf[0] = 0;
-| +    }
-|      return nbd_write(ioc, buf, sizeof(buf), errp);
-|  }
-
-Reported-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
-Signed-off-by: Eric Blake <eblake@redhat.com>
-Message-Id: <20170814213426.24681-1-eblake@redhat.com>
-Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
-(cherry picked from commit 72b6ffc76653214b69a94a7b1643ff80df134486)
-Signed-off-by: Eric Blake <eblake@redhat.com>
-Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
-
-Conflicts:
-       block/nbd-client.c
----
- block/nbd-client.c | 17 +++++++++++++----
- block/nbd-client.h |  1 +
- 2 files changed, 14 insertions(+), 4 deletions(-)
-
-diff --git a/block/nbd-client.c b/block/nbd-client.c
-index 282679b4f8..701b4ce2eb 100644
---- a/block/nbd-client.c
-+++ b/block/nbd-client.c
-@@ -71,7 +71,7 @@ static coroutine_fn void nbd_read_reply_entry(void *opaque)
-     uint64_t i;
-     int ret;
--    for (;;) {
-+    while (!s->quit) {
-         assert(s->reply.handle == 0);
-         ret = nbd_receive_reply(s->ioc, &s->reply);
-         if (ret <= 0) {
-@@ -102,6 +102,9 @@ static coroutine_fn void nbd_read_reply_entry(void *opaque)
-         qemu_coroutine_yield();
-     }
-+    if (ret < 0) {
-+        s->quit = true;
-+    }
-     nbd_recv_coroutines_enter_all(s);
-     s->read_reply_co = NULL;
- }
-@@ -130,6 +133,10 @@ static int nbd_co_send_request(BlockDriverState *bs,
-     assert(i < MAX_NBD_REQUESTS);
-     request->handle = INDEX_TO_HANDLE(s, i);
-+    if (s->quit) {
-+        qemu_co_mutex_unlock(&s->send_mutex);
-+        return -EIO;
-+    }
-     if (!s->ioc) {
-         qemu_co_mutex_unlock(&s->send_mutex);
-         return -EPIPE;
-@@ -138,7 +145,7 @@ static int nbd_co_send_request(BlockDriverState *bs,
-     if (qiov) {
-         qio_channel_set_cork(s->ioc, true);
-         rc = nbd_send_request(s->ioc, request);
--        if (rc >= 0) {
-+        if (rc >= 0 && !s->quit) {
-             ret = nbd_wr_syncv(s->ioc, qiov->iov, qiov->niov, request->len,
-                                false);
-             if (ret != request->len) {
-@@ -149,6 +156,9 @@ static int nbd_co_send_request(BlockDriverState *bs,
-     } else {
-         rc = nbd_send_request(s->ioc, request);
-     }
-+    if (rc < 0) {
-+        s->quit = true;
-+    }
-     qemu_co_mutex_unlock(&s->send_mutex);
-     return rc;
- }
-@@ -163,8 +173,7 @@ static void nbd_co_receive_reply(NBDClientSession *s,
-     /* Wait until we're woken up by nbd_read_reply_entry.  */
-     qemu_coroutine_yield();
-     *reply = s->reply;
--    if (reply->handle != request->handle ||
--        !s->ioc) {
-+    if (reply->handle != request->handle || !s->ioc || s->quit) {
-         reply->error = EIO;
-     } else {
-         if (qiov && reply->error == 0) {
-diff --git a/block/nbd-client.h b/block/nbd-client.h
-index 891ba44a20..9774a8ebbb 100644
---- a/block/nbd-client.h
-+++ b/block/nbd-client.h
-@@ -30,6 +30,7 @@ typedef struct NBDClientSession {
-     Coroutine *recv_coroutine[MAX_NBD_REQUESTS];
-     NBDReply reply;
-+    bool quit;
- } NBDClientSession;
- NBDClientSession *nbd_get_client_session(BlockDriverState *bs);
--- 
-2.11.0
-
diff --git a/debian/patches/extra/0042-fix-build-failure-in-nbd_read_reply_entry.patch b/debian/patches/extra/0042-fix-build-failure-in-nbd_read_reply_entry.patch
deleted file mode 100644 (file)
index 27421cd..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Eric Blake <eblake@redhat.com>
-Date: Wed, 27 Sep 2017 17:57:20 +0200
-Subject: [PATCH] fix build failure in nbd_read_reply_entry()
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-RH-Author: Eric Blake <eblake@redhat.com>
-Message-id: <20170927175725.20023-3-eblake@redhat.com>
-Patchwork-id: 76668
-O-Subject: [RHEV-7.4.z qemu-kvm-rhev PATCH 2/7] fix build failure in nbd_read_reply_entry()
-Bugzilla: 1495474
-RH-Acked-by: Max Reitz <mreitz@redhat.com>
-RH-Acked-by: Jeffrey Cody <jcody@redhat.com>
-RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
-
-From: Igor Mammedov <imammedo@redhat.com>
-
-travis builds fail at HEAD at rc3 master with
-
-  block/nbd-client.c: In function ‘nbd_read_reply_entry’:
-  block/nbd-client.c:110:8: error: ‘ret’ may be used uninitialized in this function [-Werror=uninitialized]
-
-fix it by initializing 'ret' to 0
-
-Signed-off-by: Igor Mammedov <imammedo@redhat.com>
-Reviewed-by: Eric Blake <eblake@redhat.com>
-Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
-(cherry picked from commit d0a180131c6655487b47ea72e7da0a909a479a3c)
-Signed-off-by: Eric Blake <eblake@redhat.com>
-Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
-
-Conflicts:
-       block/nbd-client.c - context
----
- block/nbd-client.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/block/nbd-client.c b/block/nbd-client.c
-index 701b4ce2eb..256dabeeef 100644
---- a/block/nbd-client.c
-+++ b/block/nbd-client.c
-@@ -69,7 +69,7 @@ static coroutine_fn void nbd_read_reply_entry(void *opaque)
- {
-     NBDClientSession *s = opaque;
-     uint64_t i;
--    int ret;
-+    int ret = 0;
-     while (!s->quit) {
-         assert(s->reply.handle == 0);
--- 
-2.11.0
-
diff --git a/debian/patches/extra/0043-nbd-client-avoid-spurious-qio_channel_yield-re-entry.patch b/debian/patches/extra/0043-nbd-client-avoid-spurious-qio_channel_yield-re-entry.patch
deleted file mode 100644 (file)
index ec3c61e..0000000
+++ /dev/null
@@ -1,184 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Eric Blake <eblake@redhat.com>
-Date: Wed, 27 Sep 2017 17:57:21 +0200
-Subject: [PATCH] nbd-client: avoid spurious qio_channel_yield() re-entry
-
-RH-Author: Eric Blake <eblake@redhat.com>
-Message-id: <20170927175725.20023-4-eblake@redhat.com>
-Patchwork-id: 76671
-O-Subject: [RHEV-7.4.z qemu-kvm-rhev PATCH 3/7] nbd-client: avoid spurious qio_channel_yield() re-entry
-Bugzilla: 1495474
-RH-Acked-by: Max Reitz <mreitz@redhat.com>
-RH-Acked-by: Jeffrey Cody <jcody@redhat.com>
-RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
-
-From: Stefan Hajnoczi <stefanha@redhat.com>
-
-The following scenario leads to an assertion failure in
-qio_channel_yield():
-
-1. Request coroutine calls qio_channel_yield() successfully when sending
-   would block on the socket.  It is now yielded.
-2. nbd_read_reply_entry() calls nbd_recv_coroutines_enter_all() because
-   nbd_receive_reply() failed.
-3. Request coroutine is entered and returns from qio_channel_yield().
-   Note that the socket fd handler has not fired yet so
-   ioc->write_coroutine is still set.
-4. Request coroutine attempts to send the request body with nbd_rwv()
-   but the socket would still block.  qio_channel_yield() is called
-   again and assert(!ioc->write_coroutine) is hit.
-
-The problem is that nbd_read_reply_entry() does not distinguish between
-request coroutines that are waiting to receive a reply and those that
-are not.
-
-This patch adds a per-request bool receiving flag so
-nbd_read_reply_entry() can avoid spurious aio_wake() calls.
-
-Reported-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
-Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
-Message-Id: <20170822125113.5025-1-stefanha@redhat.com>
-Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
-Tested-by: Eric Blake <eblake@redhat.com>
-Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
-Signed-off-by: Eric Blake <eblake@redhat.com>
-(cherry picked from commit 40f4a21895b5a7eae4011593837069f63460d983)
-Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
----
- block/nbd-client.c | 35 ++++++++++++++++++++++-------------
- block/nbd-client.h |  7 ++++++-
- 2 files changed, 28 insertions(+), 14 deletions(-)
-
-diff --git a/block/nbd-client.c b/block/nbd-client.c
-index 256dabeeef..f7bca3f996 100644
---- a/block/nbd-client.c
-+++ b/block/nbd-client.c
-@@ -38,8 +38,10 @@ static void nbd_recv_coroutines_enter_all(NBDClientSession *s)
-     int i;
-     for (i = 0; i < MAX_NBD_REQUESTS; i++) {
--        if (s->recv_coroutine[i]) {
--            aio_co_wake(s->recv_coroutine[i]);
-+        NBDClientRequest *req = &s->requests[i];
-+
-+        if (req->coroutine && req->receiving) {
-+            aio_co_wake(req->coroutine);
-         }
-     }
- }
-@@ -83,28 +85,28 @@ static coroutine_fn void nbd_read_reply_entry(void *opaque)
-          * one coroutine is called until the reply finishes.
-          */
-         i = HANDLE_TO_INDEX(s, s->reply.handle);
--        if (i >= MAX_NBD_REQUESTS || !s->recv_coroutine[i]) {
-+        if (i >= MAX_NBD_REQUESTS ||
-+            !s->requests[i].coroutine ||
-+            !s->requests[i].receiving) {
-             break;
-         }
--        /* We're woken up by the recv_coroutine itself.  Note that there
-+        /* We're woken up again by the request itself.  Note that there
-          * is no race between yielding and reentering read_reply_co.  This
-          * is because:
-          *
--         * - if recv_coroutine[i] runs on the same AioContext, it is only
-+         * - if the request runs on the same AioContext, it is only
-          *   entered after we yield
-          *
--         * - if recv_coroutine[i] runs on a different AioContext, reentering
-+         * - if the request runs on a different AioContext, reentering
-          *   read_reply_co happens through a bottom half, which can only
-          *   run after we yield.
-          */
--        aio_co_wake(s->recv_coroutine[i]);
-+        aio_co_wake(s->requests[i].coroutine);
-         qemu_coroutine_yield();
-     }
--    if (ret < 0) {
--        s->quit = true;
--    }
-+    s->quit = true;
-     nbd_recv_coroutines_enter_all(s);
-     s->read_reply_co = NULL;
- }
-@@ -123,14 +125,17 @@ static int nbd_co_send_request(BlockDriverState *bs,
-     s->in_flight++;
-     for (i = 0; i < MAX_NBD_REQUESTS; i++) {
--        if (s->recv_coroutine[i] == NULL) {
--            s->recv_coroutine[i] = qemu_coroutine_self();
-+        if (s->requests[i].coroutine == NULL) {
-             break;
-         }
-     }
-     g_assert(qemu_in_coroutine());
-     assert(i < MAX_NBD_REQUESTS);
-+
-+    s->requests[i].coroutine = qemu_coroutine_self();
-+    s->requests[i].receiving = false;
-+
-     request->handle = INDEX_TO_HANDLE(s, i);
-     if (s->quit) {
-@@ -168,10 +173,13 @@ static void nbd_co_receive_reply(NBDClientSession *s,
-                                  NBDReply *reply,
-                                  QEMUIOVector *qiov)
- {
-+    int i = HANDLE_TO_INDEX(s, request->handle);
-     int ret;
-     /* Wait until we're woken up by nbd_read_reply_entry.  */
-+    s->requests[i].receiving = true;
-     qemu_coroutine_yield();
-+    s->requests[i].receiving = false;
-     *reply = s->reply;
-     if (reply->handle != request->handle || !s->ioc || s->quit) {
-         reply->error = EIO;
-@@ -181,6 +189,7 @@ static void nbd_co_receive_reply(NBDClientSession *s,
-                                true);
-             if (ret != request->len) {
-                 reply->error = EIO;
-+                s->quit = true;
-             }
-         }
-@@ -195,7 +204,7 @@ static void nbd_coroutine_end(BlockDriverState *bs,
-     NBDClientSession *s = nbd_get_client_session(bs);
-     int i = HANDLE_TO_INDEX(s, request->handle);
--    s->recv_coroutine[i] = NULL;
-+    s->requests[i].coroutine = NULL;
-     /* Kick the read_reply_co to get the next reply.  */
-     if (s->read_reply_co) {
-diff --git a/block/nbd-client.h b/block/nbd-client.h
-index 9774a8ebbb..f97792ff49 100644
---- a/block/nbd-client.h
-+++ b/block/nbd-client.h
-@@ -17,6 +17,11 @@
- #define MAX_NBD_REQUESTS    16
-+typedef struct {
-+    Coroutine *coroutine;
-+    bool receiving;         /* waiting for read_reply_co? */
-+} NBDClientRequest;
-+
- typedef struct NBDClientSession {
-     QIOChannelSocket *sioc; /* The master data channel */
-     QIOChannel *ioc; /* The current I/O channel which may differ (eg TLS) */
-@@ -28,7 +33,7 @@ typedef struct NBDClientSession {
-     Coroutine *read_reply_co;
-     int in_flight;
--    Coroutine *recv_coroutine[MAX_NBD_REQUESTS];
-+    NBDClientRequest requests[MAX_NBD_REQUESTS];
-     NBDReply reply;
-     bool quit;
- } NBDClientSession;
--- 
-2.11.0
-
diff --git a/debian/patches/extra/0044-nbd-client-avoid-read_reply_co-entry-if-send-failed.patch b/debian/patches/extra/0044-nbd-client-avoid-read_reply_co-entry-if-send-failed.patch
deleted file mode 100644 (file)
index 17baf21..0000000
+++ /dev/null
@@ -1,160 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Eric Blake <eblake@redhat.com>
-Date: Wed, 27 Sep 2017 17:57:22 +0200
-Subject: [PATCH] nbd-client: avoid read_reply_co entry if send failed
-
-RH-Author: Eric Blake <eblake@redhat.com>
-Message-id: <20170927175725.20023-5-eblake@redhat.com>
-Patchwork-id: 76674
-O-Subject: [RHEV-7.4.z qemu-kvm-rhev PATCH 4/7] nbd-client: avoid read_reply_co entry if send failed
-Bugzilla: 1495474
-RH-Acked-by: Max Reitz <mreitz@redhat.com>
-RH-Acked-by: Jeffrey Cody <jcody@redhat.com>
-RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
-
-From: Stefan Hajnoczi <stefanha@redhat.com>
-
-The following segfault is encountered if the NBD server closes the UNIX
-domain socket immediately after negotiation:
-
-  Program terminated with signal SIGSEGV, Segmentation fault.
-  #0  aio_co_schedule (ctx=0x0, co=0xd3c0ff2ef0) at util/async.c:441
-  441       QSLIST_INSERT_HEAD_ATOMIC(&ctx->scheduled_coroutines,
-  (gdb) bt
-  #0  0x000000d3c01a50f8 in aio_co_schedule (ctx=0x0, co=0xd3c0ff2ef0) at util/async.c:441
-  #1  0x000000d3c012fa90 in nbd_coroutine_end (bs=bs@entry=0xd3c0fec650, request=<optimized out>) at block/nbd-client.c:207
-  #2  0x000000d3c012fb58 in nbd_client_co_preadv (bs=0xd3c0fec650, offset=0, bytes=<optimized out>, qiov=0x7ffc10a91b20, flags=0) at block/nbd-client.c:237
-  #3  0x000000d3c0128e63 in bdrv_driver_preadv (bs=bs@entry=0xd3c0fec650, offset=offset@entry=0, bytes=bytes@entry=512, qiov=qiov@entry=0x7ffc10a91b20, flags=0) at block/io.c:836
-  #4  0x000000d3c012c3e0 in bdrv_aligned_preadv (child=child@entry=0xd3c0ff51d0, req=req@entry=0x7f31885d6e90, offset=offset@entry=0, bytes=bytes@entry=512, align=align@entry=1, qiov=qiov@entry=0x7ffc10a91b20, f
-+lags=0) at block/io.c:1086
-  #5  0x000000d3c012c6b8 in bdrv_co_preadv (child=0xd3c0ff51d0, offset=offset@entry=0, bytes=bytes@entry=512, qiov=qiov@entry=0x7ffc10a91b20, flags=flags@entry=0) at block/io.c:1182
-  #6  0x000000d3c011cc17 in blk_co_preadv (blk=0xd3c0ff4f80, offset=0, bytes=512, qiov=0x7ffc10a91b20, flags=0) at block/block-backend.c:1032
-  #7  0x000000d3c011ccec in blk_read_entry (opaque=0x7ffc10a91b40) at block/block-backend.c:1079
-  #8  0x000000d3c01bbb96 in coroutine_trampoline (i0=<optimized out>, i1=<optimized out>) at util/coroutine-ucontext.c:79
-  #9  0x00007f3196cb8600 in __start_context () at /lib64/libc.so.6
-
-The problem is that nbd_client_init() uses
-nbd_client_attach_aio_context() -> aio_co_schedule(new_context,
-client->read_reply_co).  Execution of read_reply_co is deferred to a BH
-which doesn't run until later.
-
-In the mean time blk_co_preadv() can be called and nbd_coroutine_end()
-calls aio_wake() on read_reply_co.  At this point in time
-read_reply_co's ctx isn't set because it has never been entered yet.
-
-This patch simplifies the nbd_co_send_request() ->
-nbd_co_receive_reply() -> nbd_coroutine_end() lifecycle to just
-nbd_co_send_request() -> nbd_co_receive_reply().  The request is "ended"
-if an error occurs at any point.  Callers no longer have to invoke
-nbd_coroutine_end().
-
-This cleanup also eliminates the segfault because we don't call
-aio_co_schedule() to wake up s->read_reply_co if sending the request
-failed.  It is only necessary to wake up s->read_reply_co if a reply was
-received.
-
-Note this only happens with UNIX domain sockets on Linux.  It doesn't
-seem possible to reproduce this with TCP sockets.
-
-Suggested-by: Paolo Bonzini <pbonzini@redhat.com>
-Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
-Message-Id: <20170829122745.14309-2-stefanha@redhat.com>
-Signed-off-by: Eric Blake <eblake@redhat.com>
-(cherry picked from commit 3c2d5183f9fa4eac3d17d841e26da65a0181ae7b)
-Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
----
- block/nbd-client.c | 25 +++++++++----------------
- 1 file changed, 9 insertions(+), 16 deletions(-)
-
-diff --git a/block/nbd-client.c b/block/nbd-client.c
-index f7bca3f996..434acf647f 100644
---- a/block/nbd-client.c
-+++ b/block/nbd-client.c
-@@ -139,12 +139,12 @@ static int nbd_co_send_request(BlockDriverState *bs,
-     request->handle = INDEX_TO_HANDLE(s, i);
-     if (s->quit) {
--        qemu_co_mutex_unlock(&s->send_mutex);
--        return -EIO;
-+        rc = -EIO;
-+        goto err;
-     }
-     if (!s->ioc) {
--        qemu_co_mutex_unlock(&s->send_mutex);
--        return -EPIPE;
-+        rc = -EPIPE;
-+        goto err;
-     }
-     if (qiov) {
-@@ -161,8 +161,13 @@ static int nbd_co_send_request(BlockDriverState *bs,
-     } else {
-         rc = nbd_send_request(s->ioc, request);
-     }
-+
-+err:
-     if (rc < 0) {
-         s->quit = true;
-+        s->requests[i].coroutine = NULL;
-+        s->in_flight--;
-+        qemu_co_queue_next(&s->free_sema);
-     }
-     qemu_co_mutex_unlock(&s->send_mutex);
-     return rc;
-@@ -196,13 +201,6 @@ static void nbd_co_receive_reply(NBDClientSession *s,
-         /* Tell the read handler to read another header.  */
-         s->reply.handle = 0;
-     }
--}
--
--static void nbd_coroutine_end(BlockDriverState *bs,
--                              NBDRequest *request)
--{
--    NBDClientSession *s = nbd_get_client_session(bs);
--    int i = HANDLE_TO_INDEX(s, request->handle);
-     s->requests[i].coroutine = NULL;
-@@ -238,7 +236,6 @@ int nbd_client_co_preadv(BlockDriverState *bs, uint64_t offset,
-     } else {
-         nbd_co_receive_reply(client, &request, &reply, qiov);
-     }
--    nbd_coroutine_end(bs, &request);
-     return -reply.error;
- }
-@@ -267,7 +264,6 @@ int nbd_client_co_pwritev(BlockDriverState *bs, uint64_t offset,
-     } else {
-         nbd_co_receive_reply(client, &request, &reply, NULL);
-     }
--    nbd_coroutine_end(bs, &request);
-     return -reply.error;
- }
-@@ -301,7 +297,6 @@ int nbd_client_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset,
-     } else {
-         nbd_co_receive_reply(client, &request, &reply, NULL);
-     }
--    nbd_coroutine_end(bs, &request);
-     return -reply.error;
- }
-@@ -325,7 +320,6 @@ int nbd_client_co_flush(BlockDriverState *bs)
-     } else {
-         nbd_co_receive_reply(client, &request, &reply, NULL);
-     }
--    nbd_coroutine_end(bs, &request);
-     return -reply.error;
- }
-@@ -350,7 +344,6 @@ int nbd_client_co_pdiscard(BlockDriverState *bs, int64_t offset, int count)
-     } else {
-         nbd_co_receive_reply(client, &request, &reply, NULL);
-     }
--    nbd_coroutine_end(bs, &request);
-     return -reply.error;
- }
--- 
-2.11.0
-
diff --git a/debian/patches/extra/0045-qemu-iotests-improve-nbd-fault-injector.py-startup-p.patch b/debian/patches/extra/0045-qemu-iotests-improve-nbd-fault-injector.py-startup-p.patch
deleted file mode 100644 (file)
index 1053a7f..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Eric Blake <eblake@redhat.com>
-Date: Wed, 27 Sep 2017 17:57:23 +0200
-Subject: [PATCH] qemu-iotests: improve nbd-fault-injector.py startup protocol
-
-RH-Author: Eric Blake <eblake@redhat.com>
-Message-id: <20170927175725.20023-6-eblake@redhat.com>
-Patchwork-id: 76675
-O-Subject: [RHEV-7.4.z qemu-kvm-rhev PATCH 5/7] qemu-iotests: improve nbd-fault-injector.py startup protocol
-Bugzilla: 1495474
-RH-Acked-by: Max Reitz <mreitz@redhat.com>
-RH-Acked-by: Jeffrey Cody <jcody@redhat.com>
-RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
-
-From: Stefan Hajnoczi <stefanha@redhat.com>
-
-Currently 083 waits for the nbd-fault-injector.py server to start up by
-looping until netstat shows the TCP listen socket.
-
-The startup protocol can be simplified by passing a 0 port number to
-nbd-fault-injector.py.  The kernel will allocate a port in bind(2) and
-the final port number can be printed by nbd-fault-injector.py.
-
-This should make it slightly nicer and less TCP-specific to wait for
-server startup.  This patch changes nbd-fault-injector.py, the next one
-will rewrite server startup in 083.
-
-Reviewed-by: Eric Blake <eblake@redhat.com>
-Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
-Message-Id: <20170829122745.14309-3-stefanha@redhat.com>
-Signed-off-by: Eric Blake <eblake@redhat.com>
-(cherry picked from commit 6e592fc92234a58c7156c385840633c17dedd24f)
-Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
----
- tests/qemu-iotests/nbd-fault-injector.py | 4 ++++
- 1 file changed, 4 insertions(+)
-
-diff --git a/tests/qemu-iotests/nbd-fault-injector.py b/tests/qemu-iotests/nbd-fault-injector.py
-index 6c07191a5a..1c10dcb51c 100755
---- a/tests/qemu-iotests/nbd-fault-injector.py
-+++ b/tests/qemu-iotests/nbd-fault-injector.py
-@@ -235,11 +235,15 @@ def open_socket(path):
-         sock = socket.socket()
-         sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
-         sock.bind((host, int(port)))
-+
-+        # If given port was 0 the final port number is now available
-+        path = '%s:%d' % sock.getsockname()
-     else:
-         sock = socket.socket(socket.AF_UNIX)
-         sock.bind(path)
-     sock.listen(0)
-     print 'Listening on %s' % path
-+    sys.stdout.flush() # another process may be waiting, show message now
-     return sock
- def usage(args):
--- 
-2.11.0
-
diff --git a/debian/patches/extra/0046-qemu-iotests-test-NBD-over-UNIX-domain-sockets-in-08.patch b/debian/patches/extra/0046-qemu-iotests-test-NBD-over-UNIX-domain-sockets-in-08.patch
deleted file mode 100644 (file)
index 4c4d618..0000000
+++ /dev/null
@@ -1,454 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Eric Blake <eblake@redhat.com>
-Date: Wed, 27 Sep 2017 17:57:24 +0200
-Subject: [PATCH] qemu-iotests: test NBD over UNIX domain sockets in 083
-
-RH-Author: Eric Blake <eblake@redhat.com>
-Message-id: <20170927175725.20023-7-eblake@redhat.com>
-Patchwork-id: 76670
-O-Subject: [RHEV-7.4.z qemu-kvm-rhev PATCH 6/7] qemu-iotests: test NBD over UNIX domain sockets in 083
-Bugzilla: 1495474
-RH-Acked-by: Max Reitz <mreitz@redhat.com>
-RH-Acked-by: Jeffrey Cody <jcody@redhat.com>
-RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
-
-From: Stefan Hajnoczi <stefanha@redhat.com>
-
-083 only tests TCP.  Some failures might be specific to UNIX domain
-sockets.
-
-A few adjustments are necessary:
-
-1. Generating a port number and waiting for server startup is
-   TCP-specific.  Use the new nbd-fault-injector.py startup protocol to
-   fetch the address.  This is a little more elegant because we don't
-   need netstat anymore.
-
-2. The NBD filter does not work for the UNIX domain sockets URIs we
-   generate and must be extended.
-
-3. Run all tests twice: once for TCP and once for UNIX domain sockets.
-
-Reviewed-by: Eric Blake <eblake@redhat.com>
-Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
-Message-Id: <20170829122745.14309-4-stefanha@redhat.com>
-Signed-off-by: Eric Blake <eblake@redhat.com>
-(cherry picked from commit 02d2d860d25e439f0e88658c701668ab684568fb)
-Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
-
-Conflicts:
-       tests/qemu-iotests/083.out - error message improvements not backported
-
-Signed-off-by: Eric Blake <eblake@redhat.com>
----
- tests/qemu-iotests/083           | 136 +++++++++++++++++++++++--------------
- tests/qemu-iotests/083.out       | 143 ++++++++++++++++++++++++++++++++++-----
- tests/qemu-iotests/common.filter |   4 +-
- 3 files changed, 212 insertions(+), 71 deletions(-)
-
-diff --git a/tests/qemu-iotests/083 b/tests/qemu-iotests/083
-index bff9360048..0306f112da 100755
---- a/tests/qemu-iotests/083
-+++ b/tests/qemu-iotests/083
-@@ -27,6 +27,14 @@ echo "QA output created by $seq"
- here=`pwd`
- status=1      # failure is the default!
-+_cleanup()
-+{
-+      rm -f nbd.sock
-+      rm -f nbd-fault-injector.out
-+      rm -f nbd-fault-injector.conf
-+}
-+trap "_cleanup; exit \$status" 0 1 2 3 15
-+
- # get standard environment, filters and checks
- . ./common.rc
- . ./common.filter
-@@ -35,81 +43,105 @@ _supported_fmt generic
- _supported_proto nbd
- _supported_os Linux
--# Pick a TCP port based on our pid.  This way multiple instances of this test
--# can run in parallel without conflicting.
--choose_tcp_port() {
--      echo $((($$ % 31744) + 1024)) # 1024 <= port < 32768
--}
--
--wait_for_tcp_port() {
--      while ! (netstat --tcp --listening --numeric | \
--               grep "$1.*0\\.0\\.0\\.0:\\*.*LISTEN") >/dev/null 2>&1; do
--              sleep 0.1
-+check_disconnect() {
-+      local event export_name=foo extra_args nbd_addr nbd_url proto when
-+
-+      while true; do
-+              case $1 in
-+              --classic-negotiation)
-+                      shift
-+                      extra_args=--classic-negotiation
-+                      export_name=
-+                      ;;
-+              --tcp)
-+                      shift
-+                      proto=tcp
-+                      ;;
-+              --unix)
-+                      shift
-+                      proto=unix
-+                      ;;
-+              *)
-+                      break
-+                      ;;
-+              esac
-       done
--}
--check_disconnect() {
-       event=$1
-       when=$2
--      negotiation=$3
-       echo "=== Check disconnect $when $event ==="
-       echo
--      port=$(choose_tcp_port)
--
-       cat > "$TEST_DIR/nbd-fault-injector.conf" <<EOF
- [inject-error]
- event=$event
- when=$when
- EOF
--      if [ "$negotiation" = "--classic-negotiation" ]; then
--              extra_args=--classic-negotiation
--              nbd_url="nbd:127.0.0.1:$port"
-+      if [ "$proto" = "tcp" ]; then
-+              nbd_addr="127.0.0.1:0"
-       else
--              nbd_url="nbd:127.0.0.1:$port:exportname=foo"
-+              nbd_addr="$TEST_DIR/nbd.sock"
-+      fi
-+
-+      rm -f "$TEST_DIR/nbd.sock"
-+
-+      $PYTHON nbd-fault-injector.py $extra_args "$nbd_addr" "$TEST_DIR/nbd-fault-injector.conf" >"$TEST_DIR/nbd-fault-injector.out" 2>&1 &
-+
-+      # Wait for server to be ready
-+      while ! grep -q 'Listening on ' "$TEST_DIR/nbd-fault-injector.out"; do
-+              sleep 0.1
-+      done
-+
-+      # Extract the final address (port number has now been assigned in tcp case)
-+      nbd_addr=$(sed 's/Listening on \(.*\)$/\1/' "$TEST_DIR/nbd-fault-injector.out")
-+
-+      if [ "$proto" = "tcp" ]; then
-+              nbd_url="nbd+tcp://$nbd_addr/$export_name"
-+      else
-+              nbd_url="nbd+unix:///$export_name?socket=$nbd_addr"
-       fi
--      $PYTHON nbd-fault-injector.py $extra_args "127.0.0.1:$port" "$TEST_DIR/nbd-fault-injector.conf" >/dev/null 2>&1 &
--      wait_for_tcp_port "127\\.0\\.0\\.1:$port"
-       $QEMU_IO -c "read 0 512" "$nbd_url" 2>&1 | _filter_qemu_io | _filter_nbd
-       echo
- }
--for event in neg1 "export" neg2 request reply data; do
--      for when in before after; do
--              check_disconnect "$event" "$when"
--      done
--
--      # Also inject short replies from the NBD server
--      case "$event" in
--      neg1)
--              for when in 8 16; do
--                      check_disconnect "$event" "$when"
--              done
--              ;;
--      "export")
--              for when in 4 12 16; do
--                      check_disconnect "$event" "$when"
-+for proto in tcp unix; do
-+      for event in neg1 "export" neg2 request reply data; do
-+              for when in before after; do
-+                      check_disconnect "--$proto" "$event" "$when"
-               done
--              ;;
--      neg2)
--              for when in 8 10; do
--                      check_disconnect "$event" "$when"
--              done
--              ;;
--      reply)
--              for when in 4 8; do
--                      check_disconnect "$event" "$when"
--              done
--              ;;
--      esac
--done
--# Also check classic negotiation without export information
--for when in before 8 16 24 28 after; do
--      check_disconnect "neg-classic" "$when" --classic-negotiation
-+              # Also inject short replies from the NBD server
-+              case "$event" in
-+              neg1)
-+                      for when in 8 16; do
-+                              check_disconnect "--$proto" "$event" "$when"
-+                      done
-+                      ;;
-+              "export")
-+                      for when in 4 12 16; do
-+                              check_disconnect "--$proto" "$event" "$when"
-+                      done
-+                      ;;
-+              neg2)
-+                      for when in 8 10; do
-+                              check_disconnect "--$proto" "$event" "$when"
-+                      done
-+                      ;;
-+              reply)
-+                      for when in 4 8; do
-+                              check_disconnect "--$proto" "$event" "$when"
-+                      done
-+                      ;;
-+              esac
-+      done
-+
-+      # Also check classic negotiation without export information
-+      for when in before 8 16 24 28 after; do
-+              check_disconnect "--$proto" --classic-negotiation "neg-classic" "$when"
-+      done
- done
- # success, all done
-diff --git a/tests/qemu-iotests/083.out b/tests/qemu-iotests/083.out
-index 0c13888ba1..7419722cd7 100644
---- a/tests/qemu-iotests/083.out
-+++ b/tests/qemu-iotests/083.out
-@@ -1,43 +1,43 @@
- QA output created by 083
- === Check disconnect before neg1 ===
--can't open device nbd:127.0.0.1:PORT:exportname=foo
-+can't open device nbd+tcp://127.0.0.1:PORT/foo
- === Check disconnect after neg1 ===
--can't open device nbd:127.0.0.1:PORT:exportname=foo
-+can't open device nbd+tcp://127.0.0.1:PORT/foo
- === Check disconnect 8 neg1 ===
--can't open device nbd:127.0.0.1:PORT:exportname=foo
-+can't open device nbd+tcp://127.0.0.1:PORT/foo
- === Check disconnect 16 neg1 ===
--can't open device nbd:127.0.0.1:PORT:exportname=foo
-+can't open device nbd+tcp://127.0.0.1:PORT/foo
- === Check disconnect before export ===
--can't open device nbd:127.0.0.1:PORT:exportname=foo
-+can't open device nbd+tcp://127.0.0.1:PORT/foo
- === Check disconnect after export ===
--can't open device nbd:127.0.0.1:PORT:exportname=foo
-+can't open device nbd+tcp://127.0.0.1:PORT/foo
- === Check disconnect 4 export ===
--can't open device nbd:127.0.0.1:PORT:exportname=foo
-+can't open device nbd+tcp://127.0.0.1:PORT/foo
- === Check disconnect 12 export ===
--can't open device nbd:127.0.0.1:PORT:exportname=foo
-+can't open device nbd+tcp://127.0.0.1:PORT/foo
- === Check disconnect 16 export ===
--can't open device nbd:127.0.0.1:PORT:exportname=foo
-+can't open device nbd+tcp://127.0.0.1:PORT/foo
- === Check disconnect before neg2 ===
--can't open device nbd:127.0.0.1:PORT:exportname=foo
-+can't open device nbd+tcp://127.0.0.1:PORT/foo
- === Check disconnect after neg2 ===
-@@ -45,11 +45,11 @@ read failed: Input/output error
- === Check disconnect 8 neg2 ===
--can't open device nbd:127.0.0.1:PORT:exportname=foo
-+can't open device nbd+tcp://127.0.0.1:PORT/foo
- === Check disconnect 10 neg2 ===
--can't open device nbd:127.0.0.1:PORT:exportname=foo
-+can't open device nbd+tcp://127.0.0.1:PORT/foo
- === Check disconnect before request ===
-@@ -86,23 +86,132 @@ read 512/512 bytes at offset 0
- === Check disconnect before neg-classic ===
--can't open device nbd:127.0.0.1:PORT
-+can't open device nbd+tcp://127.0.0.1:PORT/
- === Check disconnect 8 neg-classic ===
--can't open device nbd:127.0.0.1:PORT
-+can't open device nbd+tcp://127.0.0.1:PORT/
- === Check disconnect 16 neg-classic ===
--can't open device nbd:127.0.0.1:PORT
-+can't open device nbd+tcp://127.0.0.1:PORT/
- === Check disconnect 24 neg-classic ===
--can't open device nbd:127.0.0.1:PORT
-+can't open device nbd+tcp://127.0.0.1:PORT/
- === Check disconnect 28 neg-classic ===
--can't open device nbd:127.0.0.1:PORT
-+can't open device nbd+tcp://127.0.0.1:PORT/
-+
-+=== Check disconnect after neg-classic ===
-+
-+read failed: Input/output error
-+
-+=== Check disconnect before neg1 ===
-+
-+can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock
-+
-+=== Check disconnect after neg1 ===
-+
-+can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock
-+
-+=== Check disconnect 8 neg1 ===
-+
-+can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock
-+
-+=== Check disconnect 16 neg1 ===
-+
-+can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock
-+
-+=== Check disconnect before export ===
-+
-+can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock
-+
-+=== Check disconnect after export ===
-+
-+can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock
-+
-+=== Check disconnect 4 export ===
-+
-+can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock
-+
-+=== Check disconnect 12 export ===
-+
-+can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock
-+
-+=== Check disconnect 16 export ===
-+
-+can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock
-+
-+=== Check disconnect before neg2 ===
-+
-+can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock
-+
-+=== Check disconnect after neg2 ===
-+
-+read failed: Input/output error
-+
-+=== Check disconnect 8 neg2 ===
-+
-+can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock
-+
-+=== Check disconnect 10 neg2 ===
-+
-+can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock
-+
-+=== Check disconnect before request ===
-+
-+read failed: Input/output error
-+
-+=== Check disconnect after request ===
-+
-+read failed: Input/output error
-+
-+=== Check disconnect before reply ===
-+
-+read failed: Input/output error
-+
-+=== Check disconnect after reply ===
-+
-+read failed: Input/output error
-+
-+=== Check disconnect 4 reply ===
-+
-+read failed: Input/output error
-+
-+=== Check disconnect 8 reply ===
-+
-+read failed: Input/output error
-+
-+=== Check disconnect before data ===
-+
-+read failed: Input/output error
-+
-+=== Check disconnect after data ===
-+
-+read 512/512 bytes at offset 0
-+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
-+
-+=== Check disconnect before neg-classic ===
-+
-+can't open device nbd+unix:///?socket=TEST_DIR/nbd.sock
-+
-+=== Check disconnect 8 neg-classic ===
-+
-+can't open device nbd+unix:///?socket=TEST_DIR/nbd.sock
-+
-+=== Check disconnect 16 neg-classic ===
-+
-+can't open device nbd+unix:///?socket=TEST_DIR/nbd.sock
-+
-+=== Check disconnect 24 neg-classic ===
-+
-+can't open device nbd+unix:///?socket=TEST_DIR/nbd.sock
-+
-+=== Check disconnect 28 neg-classic ===
-+
-+can't open device nbd+unix:///?socket=TEST_DIR/nbd.sock
- === Check disconnect after neg-classic ===
-diff --git a/tests/qemu-iotests/common.filter b/tests/qemu-iotests/common.filter
-index 104001358b..65ec315f25 100644
---- a/tests/qemu-iotests/common.filter
-+++ b/tests/qemu-iotests/common.filter
-@@ -153,9 +153,9 @@ _filter_nbd()
-     #
-     # Filter out the TCP port number since this changes between runs.
-     sed -e '/nbd\/.*\.c:/d' \
--        -e 's#nbd:\(//\)\?127\.0\.0\.1:[0-9]*#nbd:\1127.0.0.1:PORT#g' \
-+        -e 's#127\.0\.0\.1:[0-9]*#127.0.0.1:PORT#g' \
-         -e "s#?socket=$TEST_DIR#?socket=TEST_DIR#g" \
--        -e 's#\(exportname=foo\|PORT\): Failed to .*$#\1#'
-+        -e 's#\(foo\|PORT/\?\|.sock\): Failed to .*$#\1#'
- }
- # make sure this script returns success
--- 
-2.11.0
-
diff --git a/debian/patches/extra/0047-block-nbd-client-nbd_co_send_request-fix-return-code.patch b/debian/patches/extra/0047-block-nbd-client-nbd_co_send_request-fix-return-code.patch
deleted file mode 100644 (file)
index 84b7815..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Eric Blake <eblake@redhat.com>
-Date: Wed, 27 Sep 2017 17:57:25 +0200
-Subject: [PATCH] block/nbd-client: nbd_co_send_request: fix return code
-
-RH-Author: Eric Blake <eblake@redhat.com>
-Message-id: <20170927175725.20023-8-eblake@redhat.com>
-Patchwork-id: 76673
-O-Subject: [RHEV-7.4.z qemu-kvm-rhev PATCH 7/7] block/nbd-client: nbd_co_send_request: fix return code
-Bugzilla: 1495474
-RH-Acked-by: Max Reitz <mreitz@redhat.com>
-RH-Acked-by: Jeffrey Cody <jcody@redhat.com>
-RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
-
-From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
-
-It's incorrect to return success rc >= 0 if we skip qio_channel_writev_all()
-call due to s->quit.
-
-Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
-Reviewed-by: Eric Blake <eblake@redhat.com>
-Message-Id: <20170920124507.18841-4-vsementsov@virtuozzo.com>
-Signed-off-by: Eric Blake <eblake@redhat.com>
-(cherry picked from commit a693437037328a95d815ad5aec37ac2f8e130e58)
-Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
----
- block/nbd-client.c | 2 ++
- 1 file changed, 2 insertions(+)
-
-diff --git a/block/nbd-client.c b/block/nbd-client.c
-index 434acf647f..76789c1829 100644
---- a/block/nbd-client.c
-+++ b/block/nbd-client.c
-@@ -156,6 +156,8 @@ static int nbd_co_send_request(BlockDriverState *bs,
-             if (ret != request->len) {
-                 rc = -EIO;
-             }
-+        } else if (rc >= 0) {
-+            rc = -EIO;
-         }
-         qio_channel_set_cork(s->ioc, false);
-     } else {
--- 
-2.11.0
-
diff --git a/debian/patches/extra/0048-target-i386-cpu-Add-new-EPYC-CPU-model.patch b/debian/patches/extra/0048-target-i386-cpu-Add-new-EPYC-CPU-model.patch
deleted file mode 100644 (file)
index 8a20217..0000000
+++ /dev/null
@@ -1,86 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Brijesh Singh <brijesh.singh@amd.com>
-Date: Tue, 15 Aug 2017 12:00:51 -0500
-Subject: [PATCH] target-i386/cpu: Add new EPYC CPU model
-
-Add a new base CPU model called 'EPYC' to model processors from AMD EPYC
-family (which includes EPYC 76xx,75xx,74xx, 73xx and 72xx).
-
-The following features bits have been added/removed compare to Opteron_G5
-
-Added: monitor, movbe, rdrand, mmxext, ffxsr, rdtscp, cr8legacy, osvw,
-       fsgsbase, bmi1, avx2, smep, bmi2, rdseed, adx, smap, clfshopt, sha
-       xsaveopt, xsavec, xgetbv1, arat
-
-Removed: xop, fma4, tbm
-
-Cc: Paolo Bonzini <pbonzini@redhat.com>
-Cc: Richard Henderson <rth@twiddle.net>
-Cc: Eduardo Habkost <ehabkost@redhat.com>
-Cc: Tom Lendacky <Thomas.Lendacky@amd.com>
-Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
-Message-Id: <20170815170051.127257-1-brijesh.singh@amd.com>
-Reviewed-by: Eduardo Habkost <ehabkost@redhat.com>
-Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
----
- target/i386/cpu.c | 44 ++++++++++++++++++++++++++++++++++++++++++++
- 1 file changed, 44 insertions(+)
-
-diff --git a/target/i386/cpu.c b/target/i386/cpu.c
-index e4a2d5a012..e977c6c616 100644
---- a/target/i386/cpu.c
-+++ b/target/i386/cpu.c
-@@ -1923,6 +1923,50 @@ static X86CPUDefinition builtin_x86_defs[] = {
-         .xlevel = 0x8000001A,
-         .model_id = "AMD Opteron 63xx class CPU",
-     },
-+    {
-+        .name = "EPYC",
-+        .level = 0xd,
-+        .vendor = CPUID_VENDOR_AMD,
-+        .family = 23,
-+        .model = 1,
-+        .stepping = 2,
-+        .features[FEAT_1_EDX] =
-+            CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX | CPUID_CLFLUSH |
-+            CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | CPUID_MCA | CPUID_PGE |
-+            CPUID_MTRR | CPUID_SEP | CPUID_APIC | CPUID_CX8 | CPUID_MCE |
-+            CPUID_PAE | CPUID_MSR | CPUID_TSC | CPUID_PSE | CPUID_DE |
-+            CPUID_VME | CPUID_FP87,
-+        .features[FEAT_1_ECX] =
-+            CPUID_EXT_RDRAND | CPUID_EXT_F16C | CPUID_EXT_AVX |
-+            CPUID_EXT_XSAVE | CPUID_EXT_AES |  CPUID_EXT_POPCNT |
-+            CPUID_EXT_MOVBE | CPUID_EXT_SSE42 | CPUID_EXT_SSE41 |
-+            CPUID_EXT_CX16 | CPUID_EXT_FMA | CPUID_EXT_SSSE3 |
-+            CPUID_EXT_MONITOR | CPUID_EXT_PCLMULQDQ | CPUID_EXT_SSE3,
-+        .features[FEAT_8000_0001_EDX] =
-+            CPUID_EXT2_LM | CPUID_EXT2_RDTSCP | CPUID_EXT2_PDPE1GB |
-+            CPUID_EXT2_FFXSR | CPUID_EXT2_MMXEXT | CPUID_EXT2_NX |
-+            CPUID_EXT2_SYSCALL,
-+        .features[FEAT_8000_0001_ECX] =
-+            CPUID_EXT3_OSVW | CPUID_EXT3_3DNOWPREFETCH |
-+            CPUID_EXT3_MISALIGNSSE | CPUID_EXT3_SSE4A | CPUID_EXT3_ABM |
-+            CPUID_EXT3_CR8LEG | CPUID_EXT3_SVM | CPUID_EXT3_LAHF_LM,
-+        .features[FEAT_7_0_EBX] =
-+            CPUID_7_0_EBX_FSGSBASE | CPUID_7_0_EBX_BMI1 | CPUID_7_0_EBX_AVX2 |
-+            CPUID_7_0_EBX_SMEP | CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_RDSEED |
-+            CPUID_7_0_EBX_ADX | CPUID_7_0_EBX_SMAP | CPUID_7_0_EBX_CLFLUSHOPT |
-+            CPUID_7_0_EBX_SHA_NI,
-+        /* Missing: XSAVES (not supported by some Linux versions,
-+         * including v4.1 to v4.12).
-+         * KVM doesn't yet expose any XSAVES state save component.
-+         */
-+        .features[FEAT_XSAVE] =
-+            CPUID_XSAVE_XSAVEOPT | CPUID_XSAVE_XSAVEC |
-+            CPUID_XSAVE_XGETBV1,
-+        .features[FEAT_6_EAX] =
-+            CPUID_6_EAX_ARAT,
-+        .xlevel = 0x8000000A,
-+        .model_id = "AMD EPYC Processor",
-+    },
- };
- typedef struct PropValue {
--- 
-2.11.0
-
diff --git a/debian/patches/extra/0049-i386-Add-EPYC-IBPB-CPU-model.patch b/debian/patches/extra/0049-i386-Add-EPYC-IBPB-CPU-model.patch
deleted file mode 100644 (file)
index 665aefe..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Eduardo Habkost <ehabkost@redhat.com>
-Date: Tue, 9 Jan 2018 13:45:18 -0200
-Subject: [PATCH] i386: Add EPYC-IBPB CPU model
-
-EPYC-IBPB is a copy of the EPYC CPU model with
-just CPUID_8000_0008_EBX_IBPB added.
-
-Cc: Jiri Denemark <jdenemar@redhat.com>
-Cc: Tom Lendacky <thomas.lendacky@amd.com>
-Cc: Brijesh Singh <brijesh.singh@amd.com>
-Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
-Message-Id: <20180109154519.25634-7-ehabkost@redhat.com>
-Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
----
- target/i386/cpu.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++
- 1 file changed, 46 insertions(+)
-
-diff --git a/target/i386/cpu.c b/target/i386/cpu.c
-index e977c6c616..95e6a1f122 100644
---- a/target/i386/cpu.c
-+++ b/target/i386/cpu.c
-@@ -1967,6 +1967,52 @@ static X86CPUDefinition builtin_x86_defs[] = {
-         .xlevel = 0x8000000A,
-         .model_id = "AMD EPYC Processor",
-     },
-+    {
-+        .name = "EPYC-IBPB",
-+        .level = 0xd,
-+        .vendor = CPUID_VENDOR_AMD,
-+        .family = 23,
-+        .model = 1,
-+        .stepping = 2,
-+        .features[FEAT_1_EDX] =
-+            CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX | CPUID_CLFLUSH |
-+            CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | CPUID_MCA | CPUID_PGE |
-+            CPUID_MTRR | CPUID_SEP | CPUID_APIC | CPUID_CX8 | CPUID_MCE |
-+            CPUID_PAE | CPUID_MSR | CPUID_TSC | CPUID_PSE | CPUID_DE |
-+            CPUID_VME | CPUID_FP87,
-+        .features[FEAT_1_ECX] =
-+            CPUID_EXT_RDRAND | CPUID_EXT_F16C | CPUID_EXT_AVX |
-+            CPUID_EXT_XSAVE | CPUID_EXT_AES |  CPUID_EXT_POPCNT |
-+            CPUID_EXT_MOVBE | CPUID_EXT_SSE42 | CPUID_EXT_SSE41 |
-+            CPUID_EXT_CX16 | CPUID_EXT_FMA | CPUID_EXT_SSSE3 |
-+            CPUID_EXT_MONITOR | CPUID_EXT_PCLMULQDQ | CPUID_EXT_SSE3,
-+        .features[FEAT_8000_0001_EDX] =
-+            CPUID_EXT2_LM | CPUID_EXT2_RDTSCP | CPUID_EXT2_PDPE1GB |
-+            CPUID_EXT2_FFXSR | CPUID_EXT2_MMXEXT | CPUID_EXT2_NX |
-+            CPUID_EXT2_SYSCALL,
-+        .features[FEAT_8000_0001_ECX] =
-+            CPUID_EXT3_OSVW | CPUID_EXT3_3DNOWPREFETCH |
-+            CPUID_EXT3_MISALIGNSSE | CPUID_EXT3_SSE4A | CPUID_EXT3_ABM |
-+            CPUID_EXT3_CR8LEG | CPUID_EXT3_SVM | CPUID_EXT3_LAHF_LM,
-+        .features[FEAT_8000_0008_EBX] =
-+            CPUID_8000_0008_EBX_IBPB,
-+        .features[FEAT_7_0_EBX] =
-+            CPUID_7_0_EBX_FSGSBASE | CPUID_7_0_EBX_BMI1 | CPUID_7_0_EBX_AVX2 |
-+            CPUID_7_0_EBX_SMEP | CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_RDSEED |
-+            CPUID_7_0_EBX_ADX | CPUID_7_0_EBX_SMAP | CPUID_7_0_EBX_CLFLUSHOPT |
-+            CPUID_7_0_EBX_SHA_NI,
-+        /* Missing: XSAVES (not supported by some Linux versions,
-+         * including v4.1 to v4.12).
-+         * KVM doesn't yet expose any XSAVES state save component.
-+         */
-+        .features[FEAT_XSAVE] =
-+            CPUID_XSAVE_XSAVEOPT | CPUID_XSAVE_XSAVEC |
-+            CPUID_XSAVE_XGETBV1,
-+        .features[FEAT_6_EAX] =
-+            CPUID_6_EAX_ARAT,
-+        .xlevel = 0x8000000A,
-+        .model_id = "AMD EPYC Processor (with IBPB)",
-+    },
- };
- typedef struct PropValue {
--- 
-2.11.0
-
diff --git a/debian/patches/pve/0001-block-file-change-locking-default-to-off.patch b/debian/patches/pve/0001-block-file-change-locking-default-to-off.patch
new file mode 100644 (file)
index 0000000..370795c
--- /dev/null
@@ -0,0 +1,39 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Wolfgang Bumiller <w.bumiller@proxmox.com>
+Date: Wed, 29 Nov 2017 11:11:46 +0100
+Subject: [PATCH] block/file: change locking default to off
+
+'auto' only checks whether the system generally supports OFD
+locks but not whether the storage the file resides on
+supports any locking, causing issues with NFS.
+
+Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
+---
+ block/file-posix.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/block/file-posix.c b/block/file-posix.c
+index 36ee89e940..4bff572d07 100644
+--- a/block/file-posix.c
++++ b/block/file-posix.c
+@@ -405,7 +405,7 @@ static QemuOptsList raw_runtime_opts = {
+         {
+             .name = "locking",
+             .type = QEMU_OPT_STRING,
+-            .help = "file locking mode (on/off/auto, default: auto)",
++            .help = "file locking mode (on/off/auto, default: off)",
+         },
+         {
+             .name = "pr-manager",
+@@ -481,7 +481,7 @@ static int raw_open_common(BlockDriverState *bs, QDict *options,
+         s->use_lock = false;
+         break;
+     case ON_OFF_AUTO_AUTO:
+-        s->use_lock = qemu_has_ofd_lock();
++        s->use_lock = false;
+         break;
+     default:
+         abort();
+-- 
+2.11.0
+
diff --git a/debian/patches/pve/0001-fr-ca-keymap-corrections.patch b/debian/patches/pve/0001-fr-ca-keymap-corrections.patch
deleted file mode 100644 (file)
index acbfd93..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-From 0000000000000000000000000000000000000000 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] fr-ca keymap corrections
-
----
- pc-bios/keymaps/fr-ca | 9 +++++++++
- 1 file changed, 9 insertions(+)
-
-diff --git a/pc-bios/keymaps/fr-ca b/pc-bios/keymaps/fr-ca
-index b645208e42..92912409e1 100644
---- a/pc-bios/keymaps/fr-ca
-+++ b/pc-bios/keymaps/fr-ca
-@@ -14,22 +14,31 @@ bar 0x29 shift
- twosuperior 0x9 altgr
- threesuperior 0xa altgr
- onequarter 0xb altgr
-+minus 0x0c
- onehalf 0xc altgr
-+equal 0xd
- threequarters 0xd altgr
- section 0x18 altgr
- paragraph 0x19 altgr
- bracketleft 0x1a altgr
- bracketright 0x1b altgr
-+semicolon 0x27
-+colon 0x27 shift
- asciitilde 0x27 altgr
- braceleft 0x28 altgr
-+numbersign 0x29
- braceright 0x2b altgr
- less 0x2b
- greater 0x2b shift
- guillemotleft 0x56
- guillemotright 0x56 shift
- degree 0x56 altgr
-+comma 0x33
- mu 0x32 altgr
-+apostrophe 0x33 shift
-+period 0x34 shift
- eacute 0x35
-+Eacute 0x35 shift
- dead_acute 0x35 altgr
- dead_grave 0x28
- dead_circumflex 0x1a
--- 
-2.11.0
-
index ec0ad709dac4babafbb4f0fb4aa0cd9745fa5be5..f5bb5645c7ca283284d83bf248115da7dfa548da 100644 (file)
@@ -8,10 +8,10 @@ Subject: [PATCH] Adjust network script path to /etc/kvm/
  1 file changed, 3 insertions(+), 2 deletions(-)
 
 diff --git a/include/net/net.h b/include/net/net.h
  1 file changed, 3 insertions(+), 2 deletions(-)
 
 diff --git a/include/net/net.h b/include/net/net.h
-index 99b28d5b38..40c39f00e6 100644
+index 1c55a93588..13ecb9cc8c 100644
 --- a/include/net/net.h
 +++ b/include/net/net.h
 --- a/include/net/net.h
 +++ b/include/net/net.h
-@@ -214,8 +214,9 @@ void qmp_netdev_add(QDict *qdict, QObject **ret, Error **errp);
+@@ -220,8 +220,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);
  
  int net_hub_id_for_client(NetClientState *nc, int *id);
  NetClientState *net_hub_port_find(int hub_id);
  
index cd5ed3d1523b4b88df5551296778f2ccb42828df..71d858897d3e66fa56b496e651af7a000cef94aa 100644 (file)
@@ -8,12 +8,12 @@ Subject: [PATCH] qemu-img: return success on info without snapshots
  1 file changed, 2 insertions(+), 1 deletion(-)
 
 diff --git a/qemu-img.c b/qemu-img.c
  1 file changed, 2 insertions(+), 1 deletion(-)
 
 diff --git a/qemu-img.c b/qemu-img.c
-index e4a2686f56..c7804d63ee 100644
+index 68b375f998..f8a39dd195 100644
 --- a/qemu-img.c
 +++ b/qemu-img.c
 --- a/qemu-img.c
 +++ b/qemu-img.c
-@@ -2596,7 +2596,8 @@ static int img_info(int argc, char **argv)
-     list = collect_image_info_list(image_opts, filename, fmt, chain);
+@@ -2594,7 +2594,8 @@ static int img_info(int argc, char **argv)
+     list = collect_image_info_list(image_opts, filename, fmt, chain,
+                                    force_share);
      if (!list) {
 -        return 1;
 +      // return success if snapshot does not exists
      if (!list) {
 -        return 1;
 +      // return success if snapshot does not exists
index be95fa41ff39b9f24953a24ad4bbea103f62adcd..3b859c9ae32871593db7b32a39563c19fd3a1f68 100644 (file)
@@ -4,24 +4,24 @@ Date: Wed, 9 Dec 2015 14:27:05 +0100
 Subject: [PATCH] use kvm by default
 
 ---
 Subject: [PATCH] use kvm by default
 
 ---
- accel.c | 4 ++--
+ accel/accel.c | 4 ++--
  1 file changed, 2 insertions(+), 2 deletions(-)
 
  1 file changed, 2 insertions(+), 2 deletions(-)
 
-diff --git a/accel.c b/accel.c
-index 664bb88422..ddb23a3f1d 100644
---- a/accel.c
-+++ b/accel.c
-@@ -87,8 +87,8 @@ void configure_accelerator(MachineState *ms)
+diff --git a/accel/accel.c b/accel/accel.c
+index 8ae40e1e13..df21981026 100644
+--- a/accel/accel.c
++++ b/accel/accel.c
+@@ -79,8 +79,8 @@ void configure_accelerator(MachineState *ms)
  
  
-     p = qemu_opt_get(qemu_get_machine_opts(), "accel");
-     if (p == NULL) {
+     accel = qemu_opt_get(qemu_get_machine_opts(), "accel");
+     if (accel == NULL) {
 -        /* Use the default "accelerator", tcg */
 -        /* Use the default "accelerator", tcg */
--        p = "tcg";
+-        accel = "tcg";
 +        /* Use the default "accelerator", kvm */
 +        /* Use the default "accelerator", kvm */
-+        p = "kvm";
++        accel = "kvm";
      }
  
      }
  
-     while (!accel_initialised && *p != '\0') {
+     p = accel;
 -- 
 2.11.0
 
 -- 
 2.11.0
 
index deb21094620dcf474d1006d89f0c1bcc00d94f8e..58e02023375993d3dc65b6a7e9c73ce97c1f1724 100644 (file)
@@ -8,14 +8,14 @@ command.
 ---
  hmp.c                      | 30 +++++++++++++++++++++++++++++-
  hw/virtio/virtio-balloon.c | 33 +++++++++++++++++++++++++++++++--
 ---
  hmp.c                      | 30 +++++++++++++++++++++++++++++-
  hw/virtio/virtio-balloon.c | 33 +++++++++++++++++++++++++++++++--
- qapi-schema.json           | 23 +++++++++++++++++++++--
- 3 files changed, 81 insertions(+), 5 deletions(-)
+ qapi-schema.json           | 22 +++++++++++++++++++++-
+ 3 files changed, 81 insertions(+), 4 deletions(-)
 
 diff --git a/hmp.c b/hmp.c
 
 diff --git a/hmp.c b/hmp.c
-index edb8970461..904542d026 100644
+index 35a7041824..4e1d571003 100644
 --- a/hmp.c
 +++ b/hmp.c
 --- a/hmp.c
 +++ b/hmp.c
-@@ -723,7 +723,35 @@ void hmp_info_balloon(Monitor *mon, const QDict *qdict)
+@@ -789,7 +789,35 @@ void hmp_info_balloon(Monitor *mon, const QDict *qdict)
          return;
      }
  
          return;
      }
  
@@ -53,10 +53,10 @@ index edb8970461..904542d026 100644
      qapi_free_BalloonInfo(info);
  }
 diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c
      qapi_free_BalloonInfo(info);
  }
 diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c
-index a705e0ec55..158e13e795 100644
+index 37cde38982..1feaf77223 100644
 --- a/hw/virtio/virtio-balloon.c
 +++ b/hw/virtio/virtio-balloon.c
 --- 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,
+@@ -380,8 +380,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_stat(void *opaque, BalloonInfo *info)
  {
      VirtIOBalloon *dev = opaque;
@@ -97,14 +97,13 @@ index a705e0ec55..158e13e795 100644
  
  static void virtio_balloon_to_target(void *opaque, ram_addr_t target)
 diff --git a/qapi-schema.json b/qapi-schema.json
  
  static void virtio_balloon_to_target(void *opaque, ram_addr_t target)
 diff --git a/qapi-schema.json b/qapi-schema.json
-index 250e4dc49b..f38b85bf6a 100644
+index 18457954a8..ebc22fe5a6 100644
 --- a/qapi-schema.json
 +++ b/qapi-schema.json
 --- a/qapi-schema.json
 +++ b/qapi-schema.json
-@@ -1900,10 +1900,29 @@
+@@ -623,10 +623,30 @@
  #
  # @actual: the number of bytes the balloon currently contains
  #
  #
  # @actual: the number of bytes the balloon currently contains
  #
--# Since: 0.14.0
 +# @last_update: time when stats got updated from guest
 +#
 +# @mem_swapped_in: number of pages swapped in within the guest
 +# @last_update: time when stats got updated from guest
 +#
 +# @mem_swapped_in: number of pages swapped in within the guest
@@ -112,7 +111,7 @@ index 250e4dc49b..f38b85bf6a 100644
 +# @mem_swapped_out: number of pages swapped out within the guest
 +#
 +# @major_page_faults: number of major page faults within the guest
 +# @mem_swapped_out: number of pages swapped out within the guest
 +#
 +# @major_page_faults: number of major page faults within the guest
- #
++#
 +# @minor_page_faults: number of minor page faults within the guest
 +# 
 +# @free_mem: amount of memory (in bytes) free in the guest
 +# @minor_page_faults: number of minor page faults within the guest
 +# 
 +# @free_mem: amount of memory (in bytes) free in the guest
@@ -120,8 +119,9 @@ index 250e4dc49b..f38b85bf6a 100644
 +# @total_mem: amount of memory (in bytes) visible to the guest
 +#
 +# @max_mem: amount of memory (in bytes) assigned to the guest
 +# @total_mem: amount of memory (in bytes) visible to the guest
 +#
 +# @max_mem: amount of memory (in bytes) assigned to the guest
-+# 
-+# Since: 0.14.0
++#
+ # Since: 0.14.0
+ #
  ##
 -{ 'struct': 'BalloonInfo', 'data': {'actual': 'int' } }
 +{ 'struct': 'BalloonInfo',
  ##
 -{ 'struct': 'BalloonInfo', 'data': {'actual': 'int' } }
 +{ 'struct': 'BalloonInfo',
index 25dd6fca7ec6c19554177c11436d759663013eb8..b1952b5f84b1f30c3894d44f993d676c69471537 100644 (file)
@@ -4,25 +4,25 @@ Date: Wed, 9 Dec 2015 14:30:21 +0100
 Subject: [PATCH] set the CPU model to kvm64/32 instead of qemu64/32
 
 ---
 Subject: [PATCH] set the CPU model to kvm64/32 instead of qemu64/32
 
 ---
hw/i386/pc.c | 4 ++--
target/i386/cpu.h | 4 ++--
  1 file changed, 2 insertions(+), 2 deletions(-)
 
  1 file changed, 2 insertions(+), 2 deletions(-)
 
-diff --git a/hw/i386/pc.c b/hw/i386/pc.c
-index 610050eb4f..e96901435a 100644
---- a/hw/i386/pc.c
-+++ b/hw/i386/pc.c
-@@ -1151,9 +1151,9 @@ void pc_cpus_init(PCMachineState *pcms)
-     /* init CPUs */
-     if (machine->cpu_model == NULL) {
+diff --git a/target/i386/cpu.h b/target/i386/cpu.h
+index f3d0ebb673..660e42977b 100644
+--- a/target/i386/cpu.h
++++ b/target/i386/cpu.h
+@@ -1521,9 +1521,9 @@ uint64_t cpu_get_tsc(CPUX86State *env);
+ #define X86_CPU_TYPE_NAME(name) (name X86_CPU_TYPE_SUFFIX)
  #ifdef TARGET_X86_64
  #ifdef TARGET_X86_64
--        machine->cpu_model = "qemu64";
-+        machine->cpu_model = "kvm64";
+-#define TARGET_DEFAULT_CPU_TYPE X86_CPU_TYPE_NAME("qemu64")
++#define TARGET_DEFAULT_CPU_TYPE X86_CPU_TYPE_NAME("kvm64")
  #else
  #else
--        machine->cpu_model = "qemu32";
-+        machine->cpu_model = "kvm32";
+-#define TARGET_DEFAULT_CPU_TYPE X86_CPU_TYPE_NAME("qemu32")
++#define TARGET_DEFAULT_CPU_TYPE X86_CPU_TYPE_NAME("kvm32")
  #endif
  #endif
-     }
  
  
+ #define cpu_signal_handler cpu_x86_signal_handler
 -- 
 2.11.0
 
 -- 
 2.11.0
 
index 09cb67d0cef75b39bff6a492e10b6710412824ab..7f380235a0537b67b38244d866bd26e7331299f1 100644 (file)
@@ -10,10 +10,10 @@ provide '*is-current' in MachineInfo struct
  2 files changed, 8 insertions(+), 1 deletion(-)
 
 diff --git a/qapi-schema.json b/qapi-schema.json
  2 files changed, 8 insertions(+), 1 deletion(-)
 
 diff --git a/qapi-schema.json b/qapi-schema.json
-index f38b85bf6a..51e150c6c1 100644
+index ebc22fe5a6..8f436ba1f3 100644
 --- a/qapi-schema.json
 +++ b/qapi-schema.json
 --- a/qapi-schema.json
 +++ b/qapi-schema.json
-@@ -4242,6 +4242,8 @@
+@@ -1914,6 +1914,8 @@
  #
  # @is-default: whether the machine is default
  #
  #
  # @is-default: whether the machine is default
  #
@@ -22,7 +22,7 @@ index f38b85bf6a..51e150c6c1 100644
  # @cpu-max: maximum number of CPUs supported by the machine type
  #           (since 1.5.0)
  #
  # @cpu-max: maximum number of CPUs supported by the machine type
  #           (since 1.5.0)
  #
-@@ -4251,7 +4253,7 @@
+@@ -1923,7 +1925,7 @@
  ##
  { 'struct': 'MachineInfo',
    'data': { 'name': 'str', '*alias': 'str',
  ##
  { 'struct': 'MachineInfo',
    'data': { 'name': 'str', '*alias': 'str',
@@ -32,10 +32,10 @@ index f38b85bf6a..51e150c6c1 100644
  
  ##
 diff --git a/vl.c b/vl.c
  
  ##
 diff --git a/vl.c b/vl.c
-index b719cc432e..46de1b9087 100644
+index 1ad1c04637..2e0fe15978 100644
 --- a/vl.c
 +++ b/vl.c
 --- a/vl.c
 +++ b/vl.c
-@@ -1518,6 +1518,11 @@ MachineInfoList *qmp_query_machines(Error **errp)
+@@ -1604,6 +1604,11 @@ MachineInfoList *qmp_query_machines(Error **errp)
          info->cpu_max = !mc->max_cpus ? 1 : mc->max_cpus;
          info->hotpluggable_cpus = mc->has_hotpluggable_cpus;
  
          info->cpu_max = !mc->max_cpus ? 1 : mc->max_cpus;
          info->hotpluggable_cpus = mc->has_hotpluggable_cpus;
  
index e73d3197221f68ebaf4cda45bbd4295e49d16d54..fd0b66cf49f08771574ae182e8a2390feb3b0598 100644 (file)
@@ -5,15 +5,15 @@ Subject: [PATCH] qapi: modify spice query
 
 Provide the last ticket in the SpiceInfo struct optionally.
 ---
 
 Provide the last ticket in the SpiceInfo struct optionally.
 ---
- qapi-schema.json | 3 +++
- ui/spice-core.c  | 5 +++++
+ qapi/ui.json    | 3 +++
+ ui/spice-core.c | 5 +++++
  2 files changed, 8 insertions(+)
 
  2 files changed, 8 insertions(+)
 
-diff --git a/qapi-schema.json b/qapi-schema.json
-index 51e150c6c1..1b14ff2476 100644
---- a/qapi-schema.json
-+++ b/qapi-schema.json
-@@ -1841,11 +1841,14 @@
+diff --git a/qapi/ui.json b/qapi/ui.json
+index 07b468f625..78c906ddcf 100644
+--- a/qapi/ui.json
++++ b/qapi/ui.json
+@@ -199,11 +199,14 @@
  #
  # @channels: a list of @SpiceChannel for each active spice channel
  #
  #
  # @channels: a list of @SpiceChannel for each active spice channel
  #
@@ -29,10 +29,10 @@ index 51e150c6c1..1b14ff2476 100644
  
  ##
 diff --git a/ui/spice-core.c b/ui/spice-core.c
  
  ##
 diff --git a/ui/spice-core.c b/ui/spice-core.c
-index 804abc5c0f..4a417310d3 100644
+index ea04dc69b5..05f5958b14 100644
 --- a/ui/spice-core.c
 +++ b/ui/spice-core.c
 --- a/ui/spice-core.c
 +++ b/ui/spice-core.c
-@@ -552,6 +552,11 @@ SpiceInfo *qmp_query_spice(Error **errp)
+@@ -551,6 +551,11 @@ SpiceInfo *qmp_query_spice(Error **errp)
      micro = SPICE_SERVER_VERSION & 0xff;
      info->compiled_version = g_strdup_printf("%d.%d.%d", major, minor, micro);
  
      micro = SPICE_SERVER_VERSION & 0xff;
      info->compiled_version = g_strdup_printf("%d.%d.%d", major, minor, micro);
  
index 402854b00b826d5163207259ca70bbf6120bc007..25b66dd8454d59600ddaa4dd51e4bee58ce833a9 100644 (file)
@@ -8,10 +8,10 @@ Subject: [PATCH] ui/spice: default to pve certs unless otherwise specified
  1 file changed, 9 insertions(+), 6 deletions(-)
 
 diff --git a/ui/spice-core.c b/ui/spice-core.c
  1 file changed, 9 insertions(+), 6 deletions(-)
 
 diff --git a/ui/spice-core.c b/ui/spice-core.c
-index 4a417310d3..af1dc8c653 100644
+index 05f5958b14..6e1a46d1e5 100644
 --- a/ui/spice-core.c
 +++ b/ui/spice-core.c
 --- a/ui/spice-core.c
 +++ b/ui/spice-core.c
-@@ -685,32 +685,35 @@ void qemu_spice_init(void)
+@@ -684,32 +684,35 @@ void qemu_spice_init(void)
  
      if (tls_port) {
          x509_dir = qemu_opt_get(opts, "x509-dir");
  
      if (tls_port) {
          x509_dir = qemu_opt_get(opts, "x509-dir");
index 28a637b9d4188df08e9877c172e18412736126c2..4244b6878205a1c321941632609436968c832415 100644 (file)
@@ -4,37 +4,45 @@ Date: Wed, 9 Dec 2015 16:04:32 +0100
 Subject: [PATCH] internal snapshot async
 
 ---
 Subject: [PATCH] internal snapshot async
 
 ---
- Makefile.objs           |   1 +
- hmp-commands-info.hx    |  13 ++
- hmp-commands.hx         |  32 +++
- hmp.c                   |  57 ++++++
- hmp.h                   |   5 +
- include/sysemu/sysemu.h |   5 +-
- migration/savevm.c      |  12 +-
- qapi-schema.json        |  68 +++++++
- qemu-options.hx         |  13 ++
- savevm-async.c          | 523 ++++++++++++++++++++++++++++++++++++++++++++++++
- vl.c                    |   8 +
- 11 files changed, 730 insertions(+), 7 deletions(-)
+ Makefile.objs                |   2 +-
+ hmp-commands-info.hx         |  13 ++
+ hmp-commands.hx              |  32 +++
+ hmp.c                        |  57 +++++
+ hmp.h                        |   5 +
+ include/migration/snapshot.h |   1 +
+ qapi-schema.json             |  32 +++
+ qapi/migration.json          |  34 +++
+ qemu-options.hx              |  13 ++
+ savevm-async.c               | 524 +++++++++++++++++++++++++++++++++++++++++++
+ vl.c                         |  10 +
+ 11 files changed, 722 insertions(+), 1 deletion(-)
  create mode 100644 savevm-async.c
 
 diff --git a/Makefile.objs b/Makefile.objs
  create mode 100644 savevm-async.c
 
 diff --git a/Makefile.objs b/Makefile.objs
-index 6167e7b17d..fbfbbb7f70 100644
+index 285c6f3c15..686247b556 100644
 --- a/Makefile.objs
 +++ b/Makefile.objs
 --- a/Makefile.objs
 +++ b/Makefile.objs
-@@ -50,6 +50,7 @@ common-obj-$(CONFIG_LINUX) += fsdev/
- common-obj-y += migration/
- common-obj-y += page_cache.o #aio.o
+@@ -41,6 +41,7 @@ io-obj-y = io/
+ ifeq ($(CONFIG_SOFTMMU),y)
+ common-obj-y = blockdev.o blockdev-nbd.o block/
+ common-obj-y += bootdevice.o iothread.o
 +common-obj-y += savevm-async.o
 +common-obj-y += savevm-async.o
+ common-obj-y += net/
+ common-obj-y += qdev-monitor.o device-hotplug.o
+ common-obj-$(CONFIG_WIN32) += os-win32.o
+@@ -49,7 +50,6 @@ common-obj-$(CONFIG_POSIX) += os-posix.o
+ common-obj-$(CONFIG_LINUX) += fsdev/
  
  
- common-obj-$(CONFIG_SPICE) += spice-qemu-char.o
+ common-obj-y += migration/
+-
+ common-obj-y += audio/
+ common-obj-y += hw/
  
 diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx
  
 diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx
-index a53f105c52..5fc57a2210 100644
+index 54c3e5eac6..3bf69a193c 100644
 --- a/hmp-commands-info.hx
 +++ b/hmp-commands-info.hx
 --- a/hmp-commands-info.hx
 +++ b/hmp-commands-info.hx
-@@ -560,6 +560,19 @@ Show current migration xbzrle cache size.
+@@ -566,6 +566,19 @@ Show current migration xbzrle cache size.
  ETEXI
  
      {
  ETEXI
  
      {
@@ -55,10 +63,10 @@ index a53f105c52..5fc57a2210 100644
          .args_type  = "",
          .params     = "",
 diff --git a/hmp-commands.hx b/hmp-commands.hx
          .args_type  = "",
          .params     = "",
 diff --git a/hmp-commands.hx b/hmp-commands.hx
-index 88192817b2..58940a762b 100644
+index 4afd57cf5f..b35bc6ab6c 100644
 --- a/hmp-commands.hx
 +++ b/hmp-commands.hx
 --- a/hmp-commands.hx
 +++ b/hmp-commands.hx
-@@ -1777,3 +1777,35 @@ ETEXI
+@@ -1873,3 +1873,35 @@ ETEXI
  STEXI
  @end table
  ETEXI
  STEXI
  @end table
  ETEXI
@@ -95,11 +103,11 @@ index 88192817b2..58940a762b 100644
 +        .cmd = hmp_savevm_end,
 +    },
 diff --git a/hmp.c b/hmp.c
 +        .cmd = hmp_savevm_end,
 +    },
 diff --git a/hmp.c b/hmp.c
-index 904542d026..f725d061e6 100644
+index 4e1d571003..b9ade681f0 100644
 --- a/hmp.c
 +++ b/hmp.c
 --- a/hmp.c
 +++ b/hmp.c
-@@ -2207,6 +2207,63 @@ void hmp_info_memory_devices(Monitor *mon, const QDict *qdict)
-     qapi_free_MemoryDeviceInfoList(info_list);
+@@ -2486,6 +2486,63 @@ void hmp_info_memory_devices(Monitor *mon, const QDict *qdict)
+     hmp_handle_error(mon, &err);
  }
  
 +void hmp_savevm_start(Monitor *mon, const QDict *qdict)
  }
  
 +void hmp_savevm_start(Monitor *mon, const QDict *qdict)
@@ -163,7 +171,7 @@ index 904542d026..f725d061e6 100644
  {
      IOThreadInfoList *info_list = qmp_query_iothreads(NULL);
 diff --git a/hmp.h b/hmp.h
  {
      IOThreadInfoList *info_list = qmp_query_iothreads(NULL);
 diff --git a/hmp.h b/hmp.h
-index 799fd371fa..0497afbf65 100644
+index a6f56b1f29..45ada581b6 100644
 --- a/hmp.h
 +++ b/hmp.h
 @@ -26,6 +26,7 @@ void hmp_info_status(Monitor *mon, const QDict *qdict);
 --- a/hmp.h
 +++ b/hmp.h
 @@ -26,6 +26,7 @@ void hmp_info_status(Monitor *mon, const QDict *qdict);
@@ -174,7 +182,7 @@ index 799fd371fa..0497afbf65 100644
  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(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);
+@@ -97,6 +98,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_netdev_del(Monitor *mon, const QDict *qdict);
  void hmp_getfd(Monitor *mon, const QDict *qdict);
  void hmp_closefd(Monitor *mon, const QDict *qdict);
@@ -185,97 +193,65 @@ index 799fd371fa..0497afbf65 100644
  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);
  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/sysemu/sysemu.h b/include/sysemu/sysemu.h
-index 576c7ce640..74623de16c 100644
---- a/include/sysemu/sysemu.h
-+++ b/include/sysemu/sysemu.h
-@@ -78,6 +78,7 @@ void qemu_remove_machine_init_done_notifier(Notifier *notify);
- void hmp_savevm(Monitor *mon, const QDict *qdict);
- int save_vmstate(Monitor *mon, const char *name);
- 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 3b19a4a274..feb0dc6834 100644
---- a/migration/savevm.c
-+++ b/migration/savevm.c
-@@ -970,11 +970,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) {
-@@ -1002,6 +1002,7 @@ void qemu_savevm_state_begin(QEMUFile *f,
-             break;
-         }
-     }
-+    return ret;
- }
- /*
-@@ -1105,7 +1106,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;
-@@ -1139,12 +1140,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;
-         }
-     }
+diff --git a/include/migration/snapshot.h b/include/migration/snapshot.h
+index c85b6ec75b..4411b7121d 100644
+--- a/include/migration/snapshot.h
++++ b/include/migration/snapshot.h
+@@ -17,5 +17,6 @@
  
  
-     if (iterable_only) {
--        return;
-+        return ret;
-     }
+ int save_snapshot(const char *name, Error **errp);
+ int load_snapshot(const char *name, Error **errp);
++int load_snapshot_from_blockdev(const char *filename, Error **errp);
  
  
-     vmdesc = qjson_new();
-@@ -1191,6 +1192,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,
+ #endif
 diff --git a/qapi-schema.json b/qapi-schema.json
 diff --git a/qapi-schema.json b/qapi-schema.json
-index 1b14ff2476..361700d37c 100644
+index 8f436ba1f3..348b527681 100644
 --- a/qapi-schema.json
 +++ b/qapi-schema.json
 --- a/qapi-schema.json
 +++ b/qapi-schema.json
-@@ -723,6 +723,40 @@
+@@ -2439,6 +2439,38 @@
+ { 'command': 'query-target', 'returns': 'TargetInfo' }
+ ##
++# @savevm-start:
++#
++# Prepare for snapshot and halt VM. Save VM state to statefile.
++#
++##
++{ 'command': 'savevm-start', 'data': { '*statefile': 'str' } }
++
++##
++# @snapshot-drive:
++#
++# Create an internal drive snapshot.
++#
++##
++{ 'command': 'snapshot-drive', 'data': { 'device': 'str', 'name': 'str' } }
++
++##
++# @delete-drive-snapshot:
++#
++# Delete a drive snapshot.
++#
++##
++{ 'command': 'delete-drive-snapshot', 'data': { 'device': 'str', 'name': 'str' } }
++
++##
++# @savevm-end:
++#
++# Resume VM after a snapshot.
++#
++##
++{ 'command': 'savevm-end' }
++
++##
+ # @AcpiTableOptions:
+ #
+ # Specify an ACPI table on the command line to load.
+diff --git a/qapi/migration.json b/qapi/migration.json
+index 03f57c9616..9ae55b81a2 100644
+--- a/qapi/migration.json
++++ b/qapi/migration.json
+@@ -170,6 +170,40 @@
             '*error-desc': 'str'} }
  
  ##
             '*error-desc': 'str'} }
  
  ##
@@ -316,55 +292,11 @@ index 1b14ff2476..361700d37c 100644
  # @query-migrate:
  #
  # Returns information about current migration process. If migration
  # @query-migrate:
  #
  # Returns information about current migration process. If migration
-@@ -4735,9 +4769,43 @@
- #
- # Since: 1.2.0
- ##
-+
- { 'command': 'query-target', 'returns': 'TargetInfo' }
- ##
-+# @savevm-start:
-+#
-+# Prepare for snapshot and halt VM. Save VM state to statefile.
-+#
-+##
-+{ 'command': 'savevm-start', 'data': { '*statefile': 'str' } }
-+
-+##
-+# @snapshot-drive:
-+#
-+# Create an internal drive snapshot.
-+#
-+##
-+{ 'command': 'snapshot-drive', 'data': { 'device': 'str', 'name': 'str' } }
-+
-+##
-+# @delete-drive-snapshot:
-+#
-+# Delete a drive snapshot.
-+#
-+##
-+{ 'command': 'delete-drive-snapshot', 'data': { 'device': 'str', 'name': 'str' } }
-+
-+##
-+# @savevm-end:
-+#
-+# Resume VM after a snapshot.
-+#
-+##
-+{ 'command': 'savevm-end' }
-+
-+
-+##
- # @QKeyCode:
- #
- # An enumeration of key name.
 diff --git a/qemu-options.hx b/qemu-options.hx
 diff --git a/qemu-options.hx b/qemu-options.hx
-index 48dfffd86a..cbcb27da9a 100644
+index 57f2c6a75f..7c054af8f9 100644
 --- a/qemu-options.hx
 +++ b/qemu-options.hx
 --- a/qemu-options.hx
 +++ b/qemu-options.hx
-@@ -3396,6 +3396,19 @@ STEXI
+@@ -3698,6 +3698,19 @@ STEXI
  Start right away with a saved state (@code{loadvm} in monitor)
  ETEXI
  
  Start right away with a saved state (@code{loadvm} in monitor)
  ETEXI
  
@@ -386,28 +318,23 @@ index 48dfffd86a..cbcb27da9a 100644
      "-daemonize      daemonize QEMU after initializing\n", QEMU_ARCH_ALL)
 diff --git a/savevm-async.c b/savevm-async.c
 new file mode 100644
      "-daemonize      daemonize QEMU after initializing\n", QEMU_ARCH_ALL)
 diff --git a/savevm-async.c b/savevm-async.c
 new file mode 100644
-index 0000000000..5fcb56d373
+index 0000000000..897134ab5a
 --- /dev/null
 +++ b/savevm-async.c
 --- /dev/null
 +++ b/savevm-async.c
-@@ -0,0 +1,523 @@
+@@ -0,0 +1,524 @@
 +#include "qemu/osdep.h"
 +#include "qemu/osdep.h"
-+#include "qemu-common.h"
++#include "migration/migration.h"
++#include "migration/savevm.h"
++#include "migration/snapshot.h"
++#include "migration/global_state.h"
++#include "migration/ram.h"
++#include "migration/qemu-file.h"
 +#include "qapi/qmp/qerror.h"
 +#include "qapi/qmp/qerror.h"
-+#include "qemu/error-report.h"
 +#include "sysemu/sysemu.h"
 +#include "qmp-commands.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 "block/block.h"
-+#include "qemu/timer.h"
 +#include "sysemu/block-backend.h"
 +#include "qapi/qmp/qstring.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 */
 +#include "qemu/cutils.h"
 +
 +/* #define DEBUG_SAVEVM_STATE */
@@ -498,7 +425,7 @@ index 0000000000..5fcb56d373
 +         * note: bdrv_read() need whole blocks, so we round up
 +         */
 +        size_t size = (snap_state.bs_pos + BDRV_SECTOR_SIZE) & BDRV_SECTOR_MASK;
 +         * note: bdrv_read() need whole blocks, so we round up
 +         */
 +        size_t size = (snap_state.bs_pos + BDRV_SECTOR_SIZE) & BDRV_SECTOR_MASK;
-+        blk_truncate(snap_state.target, size, NULL);
++        blk_truncate(snap_state.target, size, PREALLOC_MODE_OFF, NULL);
 +        blk_op_unblock_all(snap_state.target, snap_state.blocker);
 +        error_free(snap_state.blocker);
 +        snap_state.blocker = NULL;
 +        blk_op_unblock_all(snap_state.target, snap_state.blocker);
 +        error_free(snap_state.blocker);
 +        snap_state.blocker = NULL;
@@ -578,20 +505,17 @@ index 0000000000..5fcb56d373
 +{
 +    int ret;
 +    int64_t maxlen;
 +{
 +    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);
 +
 +    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, &params);
++    qemu_savevm_state_setup(snap_state.file);
++    ret = qemu_file_get_error(snap_state.file);
 +    qemu_mutex_lock_iothread();
 +
 +    if (ret < 0) {
 +    qemu_mutex_lock_iothread();
 +
 +    if (ret < 0) {
-+        save_snapshot_error("qemu_savevm_state_begin failed");
++        save_snapshot_error("qemu_savevm_state_setup failed");
 +        return;
 +    }
 +
 +        return;
 +    }
 +
@@ -613,7 +537,16 @@ index 0000000000..5fcb56d373
 +            if (store_and_stop())
 +                break;
 +            DPRINTF("savevm inerate finished\n");
 +            if (store_and_stop())
 +                break;
 +            DPRINTF("savevm inerate finished\n");
-+            qemu_savevm_state_complete_precopy(snap_state.file, false);
++            /* upstream made the return value here inconsistent
++             * (-1 instead of 'ret' in one case and 0 after flush which can
++             * still set a file error...)
++             */
++            (void)qemu_savevm_state_complete_precopy(snap_state.file, false, false);
++            ret = qemu_file_get_error(snap_state.file);
++            if (ret < 0) {
++                    save_snapshot_error("qemu_savevm_state_iterate error %d", ret);
++                    break;
++            }
 +            DPRINTF("save complete\n");
 +            save_snapshot_completed();
 +            break;
 +            DPRINTF("save complete\n");
 +            save_snapshot_completed();
 +            break;
@@ -867,7 +800,7 @@ index 0000000000..5fcb56d373
 +    .get_buffer = loadstate_get_buffer,
 +};
 +
 +    .get_buffer = loadstate_get_buffer,
 +};
 +
-+int load_state_from_blockdev(const char *filename)
++int load_snapshot_from_blockdev(const char *filename, Error **errp)
 +{
 +    BlockBackend *be;
 +    Error *local_err = NULL;
 +{
 +    BlockBackend *be;
 +    Error *local_err = NULL;
@@ -879,7 +812,7 @@ index 0000000000..5fcb56d373
 +    be = blk_new_open(filename, NULL, NULL, 0, &local_err);
 +
 +    if (!be) {
 +    be = blk_new_open(filename, NULL, NULL, 0, &local_err);
 +
 +    if (!be) {
-+        error_report("Could not open VM state file");
++        error_setg(errp, "Could not open VM state file");
 +        goto the_end;
 +    }
 +
 +        goto the_end;
 +    }
 +
@@ -889,17 +822,17 @@ index 0000000000..5fcb56d373
 +    /* restore the VM state */
 +    f = qemu_fopen_ops(be, &loadstate_file_ops);
 +    if (!f) {
 +    /* restore the VM state */
 +    f = qemu_fopen_ops(be, &loadstate_file_ops);
 +    if (!f) {
-+        error_report("Could not open VM state file");
++        error_setg(errp, "Could not open VM state file");
 +        goto the_end;
 +    }
 +
 +        goto the_end;
 +    }
 +
-+    qemu_system_reset(VMRESET_SILENT);
++    qemu_system_reset(SHUTDOWN_CAUSE_NONE);
 +    ret = qemu_loadvm_state(f);
 +
 +    qemu_fclose(f);
 +    migration_incoming_state_destroy();
 +    if (ret < 0) {
 +    ret = qemu_loadvm_state(f);
 +
 +    qemu_fclose(f);
 +    migration_incoming_state_destroy();
 +    if (ret < 0) {
-+        error_report("Error %d while loading VM state", ret);
++        error_setg_errno(errp, -ret, "Error while loading VM state");
 +        goto the_end;
 +    }
 +
 +        goto the_end;
 +    }
 +
@@ -914,10 +847,10 @@ index 0000000000..5fcb56d373
 +    return ret;
 +}
 diff --git a/vl.c b/vl.c
 +    return ret;
 +}
 diff --git a/vl.c b/vl.c
-index 46de1b9087..2132a77129 100644
+index 2e0fe15978..1bfbe95b22 100644
 --- a/vl.c
 +++ b/vl.c
 --- a/vl.c
 +++ b/vl.c
-@@ -2960,6 +2960,7 @@ int main(int argc, char **argv, char **envp)
+@@ -3109,6 +3109,7 @@ int main(int argc, char **argv, char **envp)
      int optind;
      const char *optarg;
      const char *loadvm = NULL;
      int optind;
      const char *optarg;
      const char *loadvm = NULL;
@@ -925,7 +858,7 @@ index 46de1b9087..2132a77129 100644
      MachineClass *machine_class;
      const char *cpu_model;
      const char *vga_model = NULL;
      MachineClass *machine_class;
      const char *cpu_model;
      const char *vga_model = NULL;
-@@ -3635,6 +3636,9 @@ int main(int argc, char **argv, char **envp)
+@@ -3785,6 +3786,9 @@ int main(int argc, char **argv, char **envp)
              case QEMU_OPTION_loadvm:
                  loadvm = optarg;
                  break;
              case QEMU_OPTION_loadvm:
                  loadvm = optarg;
                  break;
@@ -935,12 +868,14 @@ index 46de1b9087..2132a77129 100644
              case QEMU_OPTION_full_screen:
                  full_screen = 1;
                  break;
              case QEMU_OPTION_full_screen:
                  full_screen = 1;
                  break;
-@@ -4693,6 +4697,10 @@ int main(int argc, char **argv, char **envp)
-         if (load_vmstate(loadvm) < 0) {
+@@ -4891,6 +4895,12 @@ int main(int argc, char **argv, char **envp)
+             error_report_err(local_err);
              autostart = 0;
          }
 +    } else if (loadstate) {
              autostart = 0;
          }
 +    } else if (loadstate) {
-+        if (load_state_from_blockdev(loadstate) < 0) {
++        Error *local_err = NULL;
++        if (load_snapshot_from_blockdev(loadstate, &local_err) < 0) {
++            error_report_err(local_err);
 +            autostart = 0;
 +        }
      }
 +            autostart = 0;
 +        }
      }
index 67d09b2ce2bf2213d05d8d8b6321b50e434e14b1..bacbd91c3bb3dab05f97a64b1ded16e65d082d03 100644 (file)
@@ -4,14 +4,14 @@ Date: Tue, 8 Nov 2016 11:13:06 +0100
 Subject: [PATCH] convert savevm-async to threads
 
 ---
 Subject: [PATCH] convert savevm-async to threads
 
 ---
- savevm-async.c | 144 +++++++++++++++++++++++++++++++++++----------------------
- 1 file changed, 88 insertions(+), 56 deletions(-)
+ savevm-async.c | 143 +++++++++++++++++++++++++++++++++++----------------------
+ 1 file changed, 87 insertions(+), 56 deletions(-)
 
 diff --git a/savevm-async.c b/savevm-async.c
 
 diff --git a/savevm-async.c b/savevm-async.c
-index 5fcb56d373..97d51d3edd 100644
+index 897134ab5a..96523c88ae 100644
 --- a/savevm-async.c
 +++ b/savevm-async.c
 --- a/savevm-async.c
 +++ b/savevm-async.c
-@@ -48,6 +48,8 @@ static struct SnapshotState {
+@@ -43,6 +43,8 @@ static struct SnapshotState {
      int saved_vm_running;
      QEMUFile *file;
      int64_t total_time;
      int saved_vm_running;
      QEMUFile *file;
      int64_t total_time;
@@ -20,7 +20,7 @@ index 5fcb56d373..97d51d3edd 100644
  } snap_state;
  
  SaveVMInfo *qmp_query_savevm(Error **errp)
  } snap_state;
  
  SaveVMInfo *qmp_query_savevm(Error **errp)
-@@ -135,19 +137,6 @@ static void save_snapshot_error(const char *fmt, ...)
+@@ -130,19 +132,6 @@ static void save_snapshot_error(const char *fmt, ...)
      g_free (msg);
  
      snap_state.state = SAVE_STATE_ERROR;
      g_free (msg);
  
      snap_state.state = SAVE_STATE_ERROR;
@@ -40,7 +40,7 @@ index 5fcb56d373..97d51d3edd 100644
  }
  
  static int block_state_close(void *opaque)
  }
  
  static int block_state_close(void *opaque)
-@@ -156,51 +145,90 @@ static int block_state_close(void *opaque)
+@@ -151,48 +140,86 @@ static int block_state_close(void *opaque)
      return blk_flush(snap_state.target);
  }
  
      return blk_flush(snap_state.target);
  }
  
@@ -125,29 +125,25 @@ index 5fcb56d373..97d51d3edd 100644
  {
      int ret;
      int64_t maxlen;
  {
      int ret;
      int64_t maxlen;
-+
-     MigrationParams params = {
-         .blk = 0,
-         .shared = 0
-     };
  
 -    snap_state.state = SAVE_STATE_ACTIVE;
 +    rcu_register_thread();
  
 -    qemu_mutex_unlock_iothread();
      qemu_savevm_state_header(snap_state.file);
  
 -    snap_state.state = SAVE_STATE_ACTIVE;
 +    rcu_register_thread();
  
 -    qemu_mutex_unlock_iothread();
      qemu_savevm_state_header(snap_state.file);
-     ret = qemu_savevm_state_begin(snap_state.file, &params);
+     qemu_savevm_state_setup(snap_state.file);
+     ret = qemu_file_get_error(snap_state.file);
 -    qemu_mutex_lock_iothread();
  
      if (ret < 0) {
 -    qemu_mutex_lock_iothread();
  
      if (ret < 0) {
-         save_snapshot_error("qemu_savevm_state_begin failed");
+         save_snapshot_error("qemu_savevm_state_setup failed");
 -        return;
 +        rcu_unregister_thread();
 +        return NULL;
      }
  
      while (snap_state.state == SAVE_STATE_ACTIVE) {
 -        return;
 +        rcu_unregister_thread();
 +        return NULL;
      }
  
      while (snap_state.state == SAVE_STATE_ACTIVE) {
-@@ -209,41 +237,43 @@ static void process_savevm_co(void *opaque)
+@@ -201,17 +228,30 @@ static void process_savevm_co(void *opaque)
          qemu_savevm_state_pending(snap_state.file, 0, &pend_nonpost, &pend_post);
          pending_size = pend_post + pend_nonpost;
  
          qemu_savevm_state_pending(snap_state.file, 0, &pend_nonpost, &pend_post);
          pending_size = pend_post + pend_nonpost;
  
@@ -185,7 +181,12 @@ index 5fcb56d373..97d51d3edd 100644
 +                break;
 +            }
              DPRINTF("savevm inerate finished\n");
 +                break;
 +            }
              DPRINTF("savevm inerate finished\n");
-             qemu_savevm_state_complete_precopy(snap_state.file, false);
+             /* upstream made the return value here inconsistent
+              * (-1 instead of 'ret' in one case and 0 after flush which can
+@@ -223,28 +263,17 @@ static void process_savevm_co(void *opaque)
+                     save_snapshot_error("qemu_savevm_state_iterate error %d", ret);
+                     break;
+             }
 +            qemu_savevm_state_cleanup();
              DPRINTF("save complete\n");
 -            save_snapshot_completed();
 +            qemu_savevm_state_cleanup();
              DPRINTF("save complete\n");
 -            save_snapshot_completed();
@@ -216,7 +217,7 @@ index 5fcb56d373..97d51d3edd 100644
  }
  
  static const QEMUFileOps block_file_ops = {
  }
  
  static const QEMUFileOps block_file_ops = {
-@@ -306,8 +336,10 @@ void qmp_savevm_start(bool has_statefile, const char *statefile, Error **errp)
+@@ -307,8 +336,10 @@ void qmp_savevm_start(bool has_statefile, const char *statefile, Error **errp)
      error_setg(&snap_state.blocker, "block device is in use by savevm");
      blk_op_block_all(snap_state.target, snap_state.blocker);
  
      error_setg(&snap_state.blocker, "block device is in use by savevm");
      blk_op_block_all(snap_state.target, snap_state.blocker);
  
index e359662c43fcdcb68f5df67e835be3340387648e..28d3edbc733ff94f703e5a44eecc2d4b886f70d6 100644 (file)
@@ -4,15 +4,15 @@ Date: Wed, 9 Dec 2015 16:34:41 +0100
 Subject: [PATCH] qmp: add get_link_status
 
 ---
 Subject: [PATCH] qmp: add get_link_status
 
 ---
- net/net.c        | 27 +++++++++++++++++++++++++++
- qapi-schema.json | 16 ++++++++++++++++
- 2 files changed, 43 insertions(+)
+ net/net.c     | 27 +++++++++++++++++++++++++++
+ qapi/net.json | 15 +++++++++++++++
+ 2 files changed, 42 insertions(+)
 
 diff --git a/net/net.c b/net/net.c
 
 diff --git a/net/net.c b/net/net.c
-index 0ac3b9e80c..7410c1e5f3 100644
+index 39ef546708..3681aa2173 100644
 --- a/net/net.c
 +++ b/net/net.c
 --- a/net/net.c
 +++ b/net/net.c
-@@ -1373,6 +1373,33 @@ void hmp_info_network(Monitor *mon, const QDict *qdict)
+@@ -1399,6 +1399,33 @@ void hmp_info_network(Monitor *mon, const QDict *qdict)
      }
  }
  
      }
  }
  
@@ -46,19 +46,11 @@ index 0ac3b9e80c..7410c1e5f3 100644
  void qmp_set_link(const char *name, bool up, Error **errp)
  {
      NetClientState *ncs[MAX_QUEUE_NUM];
  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 361700d37c..5e82933ca1 100644
---- a/qapi-schema.json
-+++ b/qapi-schema.json
-@@ -56,6 +56,7 @@
- { 'pragma': {
-     # Commands allowed to return a non-dictionary:
-     'returns-whitelist': [
-+        'get_link_status',
-         'human-monitor-command',
-         'qom-get',
-         'query-migrate-cache-size',
-@@ -2537,6 +2538,21 @@
+diff --git a/qapi/net.json b/qapi/net.json
+index 4beff5d582..73334c8f3c 100644
+--- a/qapi/net.json
++++ b/qapi/net.json
+@@ -35,6 +35,21 @@
  { 'command': 'set_link', 'data': {'name': 'str', 'up': 'bool'} }
  
  ##
  { 'command': 'set_link', 'data': {'name': 'str', 'up': 'bool'} }
  
  ##
@@ -77,9 +69,9 @@ index 361700d37c..5e82933ca1 100644
 +{ 'command': 'get_link_status', 'data': {'name': 'str'}, 'returns': 'int'}
 +
 +##
 +{ 'command': 'get_link_status', 'data': {'name': 'str'}, 'returns': 'int'}
 +
 +##
- # @balloon:
+ # @netdev_add:
  #
  #
- # Request the balloon driver to change its balloon size.
+ # Add a network backend.
 -- 
 2.11.0
 
 -- 
 2.11.0
 
index 6abdb4531c5f8edab97e73b5719b19af5e1f3d33..bd356e1b7d7ac40f9d3c20fa8400b80bbef878c2 100644 (file)
@@ -9,10 +9,10 @@ Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
  1 file changed, 1 insertion(+), 1 deletion(-)
 
 diff --git a/hw/i386/pc.c b/hw/i386/pc.c
  1 file changed, 1 insertion(+), 1 deletion(-)
 
 diff --git a/hw/i386/pc.c b/hw/i386/pc.c
-index e96901435a..f049bbca9a 100644
+index 186545d2a4..603a7ce6bf 100644
 --- a/hw/i386/pc.c
 +++ b/hw/i386/pc.c
 --- a/hw/i386/pc.c
 +++ b/hw/i386/pc.c
-@@ -2123,7 +2123,7 @@ bool pc_machine_is_smm_enabled(PCMachineState *pcms)
+@@ -2135,7 +2135,7 @@ bool pc_machine_is_smm_enabled(PCMachineState *pcms)
      if (tcg_enabled() || qtest_enabled()) {
          smm_available = true;
      } else if (kvm_enabled()) {
      if (tcg_enabled() || qtest_enabled()) {
          smm_available = true;
      } else if (kvm_enabled()) {
index 1b5ade72a3e2c106fcae60d47c9c362241342e71..4b3a63ffcd575547e5114d69ceb3156c379d6733 100644 (file)
@@ -10,10 +10,10 @@ the version string, see PVE::QemuServer::kvm_user_version()
  1 file changed, 1 insertion(+), 1 deletion(-)
 
 diff --git a/vl.c b/vl.c
  1 file changed, 1 insertion(+), 1 deletion(-)
 
 diff --git a/vl.c b/vl.c
-index 2132a77129..5d888cd179 100644
+index 1bfbe95b22..75fde82180 100644
 --- a/vl.c
 +++ b/vl.c
 --- a/vl.c
 +++ b/vl.c
-@@ -1909,7 +1909,7 @@ static void main_loop(void)
+@@ -2006,7 +2006,7 @@ static void main_loop(void)
  
  static void version(void)
  {
  
  static void version(void)
  {
index 7f8b3b8648bf84bd4fbfa167688da5f8a7c42359..5e3115b874139798b63d395657238d67b75a6f86 100644 (file)
@@ -8,10 +8,10 @@ Subject: [PATCH] vnc: altgr emulation
  1 file changed, 25 insertions(+), 1 deletion(-)
 
 diff --git a/ui/vnc.c b/ui/vnc.c
  1 file changed, 25 insertions(+), 1 deletion(-)
 
 diff --git a/ui/vnc.c b/ui/vnc.c
-index b0314441c4..f30687884b 100644
+index 06abe7360e..03f8f61b2e 100644
 --- a/ui/vnc.c
 +++ b/ui/vnc.c
 --- a/ui/vnc.c
 +++ b/ui/vnc.c
-@@ -1631,6 +1631,10 @@ static void kbd_leds(void *opaque, int ledstate)
+@@ -1775,6 +1775,10 @@ static void kbd_leds(void *opaque, int ledstate)
  
  static void do_key_event(VncState *vs, int down, int keycode, int sym)
  {
  
  static void do_key_event(VncState *vs, int down, int keycode, int sym)
  {
@@ -22,7 +22,7 @@ index b0314441c4..f30687884b 100644
      /* QEMU console switch */
      switch(keycode) {
      case 0x2a:                          /* Left Shift */
      /* QEMU console switch */
      switch(keycode) {
      case 0x2a:                          /* Left Shift */
-@@ -1711,8 +1715,27 @@ static void do_key_event(VncState *vs, int down, int keycode, int sym)
+@@ -1855,8 +1859,27 @@ static void do_key_event(VncState *vs, int down, int keycode, int sym)
      }
  
      if (qemu_console_is_graphic(NULL)) {
      }
  
      if (qemu_console_is_graphic(NULL)) {
@@ -50,7 +50,7 @@ index b0314441c4..f30687884b 100644
      } else {
          bool numlock = vs->modifiers_state[0x45];
          bool control = (vs->modifiers_state[0x1d] ||
      } else {
          bool numlock = vs->modifiers_state[0x45];
          bool control = (vs->modifiers_state[0x1d] ||
-@@ -1852,7 +1875,8 @@ static void key_event(VncState *vs, int down, uint32_t sym)
+@@ -1996,7 +2019,8 @@ static void key_event(VncState *vs, int down, uint32_t sym)
          lsym = lsym - 'A' + 'a';
      }
  
          lsym = lsym - 'A' + 'a';
      }
  
index 8c74f13566a48080aca9c0fe5118c0c4c678ad5e..f7f213e3f4ceda77e863524c7443bb0bba70c1c3 100644 (file)
@@ -8,10 +8,10 @@ Subject: [PATCH] vnc: make x509 imply tls again
  1 file changed, 2 insertions(+), 3 deletions(-)
 
 diff --git a/ui/vnc.c b/ui/vnc.c
  1 file changed, 2 insertions(+), 3 deletions(-)
 
 diff --git a/ui/vnc.c b/ui/vnc.c
-index f30687884b..a345bf0d78 100644
+index 03f8f61b2e..4494cb1dd4 100644
 --- a/ui/vnc.c
 +++ b/ui/vnc.c
 --- a/ui/vnc.c
 +++ b/ui/vnc.c
-@@ -3881,9 +3881,8 @@ void vnc_display_open(const char *id, Error **errp)
+@@ -4034,9 +4034,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);
          const char *path;
          bool tls = false, x509 = false, x509verify = false;
          tls  = qemu_opt_get_bool(opts, "tls", false);
index e4c915a6e1a4a8d09caf6fbbe133abaa36c6e0e9..ae2b3cee5e97ed7306575f81c07485e7e87c74c4 100644 (file)
@@ -4,21 +4,22 @@ Date: Mon, 11 Jan 2016 10:40:31 +0100
 Subject: [PATCH] vnc: PVE VNC authentication
 
 ---
 Subject: [PATCH] vnc: PVE VNC authentication
 
 ---
- crypto/tlscreds.c         |  47 +++++++++++
+ crypto/tlscreds.c         |  47 ++++++++++++
  crypto/tlscredspriv.h     |   2 +
  crypto/tlscredspriv.h     |   2 +
- crypto/tlscredsx509.c     |  13 +--
+ crypto/tlscredsx509.c     |  13 ++--
  crypto/tlssession.c       |   1 +
  include/crypto/tlscreds.h |   1 +
  include/ui/console.h      |   1 +
  crypto/tlssession.c       |   1 +
  include/crypto/tlscreds.h |   1 +
  include/ui/console.h      |   1 +
+ qapi-schema.json          |   1 +
  qemu-options.hx           |   3 +
  qemu-options.hx           |   3 +
- ui/vnc-auth-vencrypt.c    | 197 ++++++++++++++++++++++++++++++++++++++--------
- ui/vnc.c                  | 140 +++++++++++++++++++++++++++++++-
+ ui/vnc-auth-vencrypt.c    | 182 ++++++++++++++++++++++++++++++++++++++--------
+ ui/vnc.c                  | 140 ++++++++++++++++++++++++++++++++++-
  ui/vnc.h                  |   4 +
  vl.c                      |   9 +++
  ui/vnc.h                  |   4 +
  vl.c                      |   9 +++
- 11 files changed, 377 insertions(+), 41 deletions(-)
+ 12 files changed, 364 insertions(+), 40 deletions(-)
 
 diff --git a/crypto/tlscreds.c b/crypto/tlscreds.c
 
 diff --git a/crypto/tlscreds.c b/crypto/tlscreds.c
-index a8965531b6..e9ae13ce47 100644
+index 3cd41035bb..e982da3451 100644
 --- a/crypto/tlscreds.c
 +++ b/crypto/tlscreds.c
 @@ -158,6 +158,33 @@ qcrypto_tls_creds_prop_get_verify(Object *obj,
 --- a/crypto/tlscreds.c
 +++ b/crypto/tlscreds.c
 @@ -158,6 +158,33 @@ qcrypto_tls_creds_prop_get_verify(Object *obj,
@@ -75,7 +76,7 @@ index a8965531b6..e9ae13ce47 100644
 +                            NULL);
 +    object_property_add_enum(obj, "endpoint",
 +                             "QCryptoTLSCredsEndpoint",
 +                            NULL);
 +    object_property_add_enum(obj, "endpoint",
 +                             "QCryptoTLSCredsEndpoint",
-+                             QCryptoTLSCredsEndpoint_lookup,
++                             &QCryptoTLSCredsEndpoint_lookup,
 +                             qcrypto_tls_creds_prop_get_endpoint,
 +                             qcrypto_tls_creds_prop_set_endpoint,
 +                             NULL);
 +                             qcrypto_tls_creds_prop_get_endpoint,
 +                             qcrypto_tls_creds_prop_set_endpoint,
 +                             NULL);
@@ -168,10 +169,10 @@ index ad47d88be7..f86d379f26 100644
  
  
 diff --git a/include/ui/console.h b/include/ui/console.h
  
  
 diff --git a/include/ui/console.h b/include/ui/console.h
-index d759338816..69f010e1db 100644
+index 580dfc57ee..383e5c88bd 100644
 --- a/include/ui/console.h
 +++ b/include/ui/console.h
 --- a/include/ui/console.h
 +++ b/include/ui/console.h
-@@ -462,6 +462,7 @@ static inline void cocoa_display_init(DisplayState *ds, int full_screen)
+@@ -466,6 +466,7 @@ static inline void cocoa_display_init(DisplayState *ds, int full_screen)
  #endif
  
  /* vnc.c */
  #endif
  
  /* vnc.c */
@@ -179,11 +180,23 @@ index d759338816..69f010e1db 100644
  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);
  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/qapi-schema.json b/qapi-schema.json
+index 348b527681..d2155cb00f 100644
+--- a/qapi-schema.json
++++ b/qapi-schema.json
+@@ -56,6 +56,7 @@
+ { 'pragma': {
+     # Commands allowed to return a non-dictionary:
+     'returns-whitelist': [
++        'get_link_status',
+         'human-monitor-command',
+         'qom-get',
+         'query-migrate-cache-size',
 diff --git a/qemu-options.hx b/qemu-options.hx
 diff --git a/qemu-options.hx b/qemu-options.hx
-index cbcb27da9a..0b1957c034 100644
+index 7c054af8f9..07129d55bc 100644
 --- a/qemu-options.hx
 +++ b/qemu-options.hx
 --- a/qemu-options.hx
 +++ b/qemu-options.hx
-@@ -513,6 +513,9 @@ STEXI
+@@ -583,6 +583,9 @@ STEXI
  @table @option
  ETEXI
  
  @table @option
  ETEXI
  
@@ -194,13 +207,13 @@ index cbcb27da9a..0b1957c034 100644
      "-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
      "-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 ffaab57550..594ca737a9 100644
+index 7833631275..c42acd3714 100644
 --- a/ui/vnc-auth-vencrypt.c
 +++ b/ui/vnc-auth-vencrypt.c
 --- a/ui/vnc-auth-vencrypt.c
 +++ b/ui/vnc-auth-vencrypt.c
-@@ -28,6 +28,108 @@
- #include "vnc.h"
+@@ -29,6 +29,108 @@
  #include "qapi/error.h"
  #include "qemu/main-loop.h"
  #include "qapi/error.h"
  #include "qemu/main-loop.h"
+ #include "trace.h"
 +#include "io/channel-socket.h"
 +
 +static int protocol_client_auth_plain(VncState *vs, uint8_t *data, size_t len)
 +#include "io/channel-socket.h"
 +
 +static int protocol_client_auth_plain(VncState *vs, uint8_t *data, size_t len)
@@ -228,7 +241,7 @@ index ffaab57550..594ca737a9 100644
 +
 +      VNC_DEBUG("AUTH PLAIN username: %s pw: %s\n", username, passwd);
 +
 +
 +      VNC_DEBUG("AUTH PLAIN username: %s pw: %s\n", username, passwd);
 +
-+      if (pve_auth_verify(clientip->u.inet.data->host, username, passwd) == 0) {
++      if (pve_auth_verify(clientip->u.inet.host, username, passwd) == 0) {
 +              vnc_write_u32(vs, 0); /* Accept auth completion */
 +              start_client_init(vs);
 +              qapi_free_SocketAddress(clientip);
 +              vnc_write_u32(vs, 0); /* Accept auth completion */
 +              start_client_init(vs);
 +              qapi_free_SocketAddress(clientip);
@@ -323,21 +336,20 @@ index ffaab57550..594ca737a9 100644
 +
      case VNC_AUTH_VENCRYPT_TLSVNC:
      case VNC_AUTH_VENCRYPT_X509VNC:
 +
      case VNC_AUTH_VENCRYPT_TLSVNC:
      case VNC_AUTH_VENCRYPT_X509VNC:
-        VNC_DEBUG("Start TLS auth VNC\n");
-@@ -88,45 +201,64 @@ static int protocol_client_vencrypt_auth(VncState *vs, uint8_t *data, size_t len
- {
+        start_auth_vnc(vs);
+@@ -90,45 +203,51 @@ static int protocol_client_vencrypt_auth(VncState *vs, uint8_t *data, size_t len
      int auth = read_u32(data, 0);
  
      int auth = read_u32(data, 0);
  
+     trace_vnc_auth_vencrypt_subauth(vs, auth);
 -    if (auth != vs->subauth) {
 +    if (auth != vs->subauth && auth != VNC_AUTH_VENCRYPT_PLAIN) {
 -    if (auth != vs->subauth) {
 +    if (auth != vs->subauth && auth != VNC_AUTH_VENCRYPT_PLAIN) {
-         VNC_DEBUG("Rejecting auth %d\n", auth);
+         trace_vnc_auth_fail(vs, vs->auth, "Unsupported sub-auth version", "");
          vnc_write_u8(vs, 0); /* Reject auth */
          vnc_flush(vs);
          vnc_client_error(vs);
      } else {
 -        Error *err = NULL;
 -        QIOChannelTLS *tls;
          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);
 -
 -        vnc_write_u8(vs, 1); /* Accept auth */
 -        vnc_flush(vs);
 -
@@ -352,7 +364,6 @@ index ffaab57550..594ca737a9 100644
 +        {
 +            Error *err = NULL;
 +            QIOChannelTLS *tls;
 +        {
 +            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);
  
 +            vnc_write_u8(vs, 1); /* Accept auth */
 +            vnc_flush(vs);
  
@@ -362,7 +373,8 @@ index ffaab57550..594ca737a9 100644
 -            vs->vd->tlsaclname,
 -            &err);
 -        if (!tls) {
 -            vs->vd->tlsaclname,
 -            &err);
 -        if (!tls) {
--            VNC_DEBUG("Failed to setup TLS %s\n", error_get_pretty(err));
+-            trace_vnc_auth_fail(vs, vs->auth, "TLS setup failed",
+-                                error_get_pretty(err));
 -            error_free(err);
 -            vnc_client_error(vs);
 -            return 0;
 -            error_free(err);
 -            vnc_client_error(vs);
 -            return 0;
@@ -371,46 +383,34 @@ index ffaab57550..594ca737a9 100644
 +                g_source_remove(vs->ioc_tag);
 +                vs->ioc_tag = 0;
 +            }
 +                g_source_remove(vs->ioc_tag);
 +                vs->ioc_tag = 0;
 +            }
--        qio_channel_set_name(QIO_CHANNEL(tls), "vnc-server-tls");
--        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) {
 +            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));
++                trace_vnc_auth_fail(vs, vs->auth, "TLS setup failed",
++                                    error_get_pretty(err));
 +                error_free(err);
 +                vnc_client_error(vs);
 +                return 0;
 +                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_set_name(QIO_CHANNEL(tls), "vnc-server-tls");
+-        object_unref(OBJECT(vs->ioc));
+-        vs->ioc = QIO_CHANNEL(tls);
+-        trace_vnc_client_io_wrap(vs, vs->ioc, "tls");
+-        vs->tls = qio_channel_tls_get_session(tls);
 +            qio_channel_set_name(QIO_CHANNEL(tls), "vnc-server-tls");
 +            qio_channel_set_name(QIO_CHANNEL(tls), "vnc-server-tls");
++            object_unref(OBJECT(vs->ioc));
++            vs->ioc = QIO_CHANNEL(tls);
++            trace_vnc_client_io_wrap(vs, vs->ioc, "tls");
++            vs->tls = qio_channel_tls_get_session(tls);
  
 -        qio_channel_tls_handshake(tls,
 -                                  vnc_tls_handshake_done,
 -                                  vs,
 -                                  NULL);
  
 -        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,
 +            qio_channel_tls_handshake(tls,
 +                                      vnc_tls_handshake_done,
 +                                      vs,
@@ -419,12 +419,9 @@ index ffaab57550..594ca737a9 100644
      }
      return 0;
  }
      }
      return 0;
  }
-@@ -140,10 +272,11 @@ static int protocol_client_vencrypt_init(VncState *vs, uint8_t *data, size_t len
-         vnc_flush(vs);
+@@ -144,8 +263,9 @@ static int protocol_client_vencrypt_init(VncState *vs, uint8_t *data, size_t len
          vnc_client_error(vs);
      } else {
          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_u8(vs, 0); /* Accept version */
 -        vnc_write_u8(vs, 1); /* Number of sub-auths */
 +        vnc_write_u8(vs, 2); /* Number of sub-auths */
@@ -434,10 +431,10 @@ index ffaab57550..594ca737a9 100644
          vnc_read_when(vs, protocol_client_vencrypt_auth, 4);
      }
 diff --git a/ui/vnc.c b/ui/vnc.c
          vnc_read_when(vs, protocol_client_vencrypt_auth, 4);
      }
 diff --git a/ui/vnc.c b/ui/vnc.c
-index a345bf0d78..42db7e386b 100644
+index 4494cb1dd4..1589cbe1b3 100644
 --- a/ui/vnc.c
 +++ b/ui/vnc.c
 --- a/ui/vnc.c
 +++ b/ui/vnc.c
-@@ -56,6 +56,125 @@ static const struct timeval VNC_REFRESH_LOSSY = { 2, 0 };
+@@ -55,6 +55,125 @@ static const struct timeval VNC_REFRESH_LOSSY = { 2, 0 };
  #include "vnc_keysym.h"
  #include "crypto/cipher.h"
  
  #include "vnc_keysym.h"
  #include "crypto/cipher.h"
  
@@ -563,7 +560,7 @@ index a345bf0d78..42db7e386b 100644
  static QTAILQ_HEAD(, VncDisplay) vnc_displays =
      QTAILQ_HEAD_INITIALIZER(vnc_displays);
  
  static QTAILQ_HEAD(, VncDisplay) vnc_displays =
      QTAILQ_HEAD_INITIALIZER(vnc_displays);
  
-@@ -3356,10 +3475,16 @@ vnc_display_setup_auth(int *auth,
+@@ -3507,10 +3626,16 @@ vnc_display_setup_auth(int *auth,
          if (password) {
              if (is_x509) {
                  VNC_DEBUG("Initializing VNC server with x509 password auth\n");
          if (password) {
              if (is_x509) {
                  VNC_DEBUG("Initializing VNC server with x509 password auth\n");
@@ -582,7 +579,7 @@ index a345bf0d78..42db7e386b 100644
              }
  
          } else if (sasl) {
              }
  
          } else if (sasl) {
-@@ -3393,6 +3518,7 @@ vnc_display_create_creds(bool x509,
+@@ -3544,6 +3669,7 @@ vnc_display_create_creds(bool x509,
                           bool x509verify,
                           const char *dir,
                           const char *id,
                           bool x509verify,
                           const char *dir,
                           const char *id,
@@ -590,7 +587,7 @@ index a345bf0d78..42db7e386b 100644
                           Error **errp)
  {
      gchar *credsid = g_strdup_printf("tlsvnc%s", id);
                           Error **errp)
  {
      gchar *credsid = g_strdup_printf("tlsvnc%s", id);
-@@ -3408,6 +3534,7 @@ vnc_display_create_creds(bool x509,
+@@ -3559,6 +3685,7 @@ vnc_display_create_creds(bool x509,
                                        "endpoint", "server",
                                        "dir", dir,
                                        "verify-peer", x509verify ? "yes" : "no",
                                        "endpoint", "server",
                                        "dir", dir,
                                        "verify-peer", x509verify ? "yes" : "no",
@@ -598,7 +595,7 @@ index a345bf0d78..42db7e386b 100644
                                        NULL);
      } else {
          creds = object_new_with_props(TYPE_QCRYPTO_TLS_CREDS_ANON,
                                        NULL);
      } else {
          creds = object_new_with_props(TYPE_QCRYPTO_TLS_CREDS_ANON,
-@@ -3415,6 +3542,7 @@ vnc_display_create_creds(bool x509,
+@@ -3566,6 +3693,7 @@ vnc_display_create_creds(bool x509,
                                        credsid,
                                        &err,
                                        "endpoint", "server",
                                        credsid,
                                        &err,
                                        "endpoint", "server",
@@ -606,7 +603,7 @@ index a345bf0d78..42db7e386b 100644
                                        NULL);
      }
  
                                        NULL);
      }
  
-@@ -3879,12 +4007,17 @@ void vnc_display_open(const char *id, Error **errp)
+@@ -4032,12 +4160,17 @@ void vnc_display_open(const char *id, Error **errp)
          }
      } else {
          const char *path;
          }
      } else {
          const char *path;
@@ -625,7 +622,7 @@ index a345bf0d78..42db7e386b 100644
              } else {
                  path = qemu_opt_get(opts, "x509verify");
                  if (path) {
              } else {
                  path = qemu_opt_get(opts, "x509verify");
                  if (path) {
-@@ -3896,6 +4029,7 @@ void vnc_display_open(const char *id, Error **errp)
+@@ -4049,6 +4182,7 @@ void vnc_display_open(const char *id, Error **errp)
                                                      x509verify,
                                                      path,
                                                      vd->id,
                                                      x509verify,
                                                      path,
                                                      vd->id,
@@ -634,10 +631,10 @@ index a345bf0d78..42db7e386b 100644
              if (!vd->tlscreds) {
                  goto fail;
 diff --git a/ui/vnc.h b/ui/vnc.h
              if (!vd->tlscreds) {
                  goto fail;
 diff --git a/ui/vnc.h b/ui/vnc.h
-index 694cf32ca9..78d622ab84 100644
+index bbda0540a7..8cc6367ed3 100644
 --- a/ui/vnc.h
 +++ b/ui/vnc.h
 --- a/ui/vnc.h
 +++ b/ui/vnc.h
-@@ -284,6 +284,8 @@ struct VncState
+@@ -290,6 +290,8 @@ struct VncState
      int auth;
      int subauth; /* Used by VeNCrypt */
      char challenge[VNC_AUTH_CHALLENGE_SIZE];
      int auth;
      int subauth; /* Used by VeNCrypt */
      char challenge[VNC_AUTH_CHALLENGE_SIZE];
@@ -646,7 +643,7 @@ index 694cf32ca9..78d622ab84 100644
      QCryptoTLSSession *tls; /* Borrowed pointer from channel, don't free */
  #ifdef CONFIG_VNC_SASL
      VncStateSASL sasl;
      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);
+@@ -595,4 +597,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 vnc_zywrle_send_framebuffer_update(VncState *vs, int x, int y, int w, int h);
  void vnc_zrle_clear(VncState *vs);
  
@@ -654,10 +651,10 @@ index 694cf32ca9..78d622ab84 100644
 +
  #endif /* QEMU_VNC_H */
 diff --git a/vl.c b/vl.c
 +
  #endif /* QEMU_VNC_H */
 diff --git a/vl.c b/vl.c
-index 5d888cd179..1000a4a259 100644
+index 75fde82180..255d989009 100644
 --- a/vl.c
 +++ b/vl.c
 --- a/vl.c
 +++ b/vl.c
-@@ -2947,6 +2947,7 @@ static int qemu_read_default_config_file(void)
+@@ -3096,6 +3096,7 @@ static void register_global_properties(MachineState *ms)
  int main(int argc, char **argv, char **envp)
  {
      int i;
  int main(int argc, char **argv, char **envp)
  {
      int i;
@@ -665,7 +662,7 @@ index 5d888cd179..1000a4a259 100644
      int snapshot, linux_boot;
      const char *initrd_filename;
      const char *kernel_filename, *kernel_cmdline;
      int snapshot, linux_boot;
      const char *initrd_filename;
      const char *kernel_filename, *kernel_cmdline;
-@@ -3778,6 +3779,14 @@ int main(int argc, char **argv, char **envp)
+@@ -3922,6 +3923,14 @@ int main(int argc, char **argv, char **envp)
                      exit(1);
                  }
                  break;
                      exit(1);
                  }
                  break;
diff --git a/debian/patches/pve/0018-block-rbd-disable-rbd_cache_writethrough_until_flush.patch b/debian/patches/pve/0018-block-rbd-disable-rbd_cache_writethrough_until_flush.patch
new file mode 100644 (file)
index 0000000..e75da3c
--- /dev/null
@@ -0,0 +1,29 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Alexandre Derumier <aderumier@odiso.com>
+Date: Tue, 26 Jul 2016 16:51:00 +0200
+Subject: [PATCH] block: 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 a76a5e8755..a33738a254 100644
+--- a/block/rbd.c
++++ b/block/rbd.c
+@@ -642,6 +642,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.11.0
+
diff --git a/debian/patches/pve/0018-migrate-fix-possible-unitialised-return-value.patch b/debian/patches/pve/0018-migrate-fix-possible-unitialised-return-value.patch
deleted file mode 100644 (file)
index 6fd3e19..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-From 0000000000000000000000000000000000000000 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] migrate: 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 feb0dc6834..d2615f4620 100644
---- a/migration/savevm.c
-+++ b/migration/savevm.c
-@@ -1111,7 +1111,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.11.0
-
diff --git a/debian/patches/pve/0019-block-rbd-disable-rbd_cache_writethrough_until_flush.patch b/debian/patches/pve/0019-block-rbd-disable-rbd_cache_writethrough_until_flush.patch
deleted file mode 100644 (file)
index 1cca949..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Alexandre Derumier <aderumier@odiso.com>
-Date: Tue, 26 Jul 2016 16:51:00 +0200
-Subject: [PATCH] block: 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 2354ffcc64..b7700648ff 100644
---- a/block/rbd.c
-+++ b/block/rbd.c
-@@ -623,6 +623,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.11.0
-
diff --git a/debian/patches/pve/0019-block-snapshot-qmp_snapshot_drive-add-aiocontext.patch b/debian/patches/pve/0019-block-snapshot-qmp_snapshot_drive-add-aiocontext.patch
new file mode 100644 (file)
index 0000000..a3a5828
--- /dev/null
@@ -0,0 +1,65 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Alexandre Derumier <aderumier@odiso.com>
+Date: Tue, 13 Sep 2016 01:57:56 +0200
+Subject: [PATCH] block: snapshot: 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 96523c88ae..06dcd29fc7 100644
+--- a/savevm-async.c
++++ b/savevm-async.c
+@@ -377,6 +377,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;
+@@ -403,20 +404,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;
+@@ -441,8 +445,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.11.0
+
diff --git a/debian/patches/pve/0020-block-snapshot-qmp_delete_drive_snapshot-add-aiocont.patch b/debian/patches/pve/0020-block-snapshot-qmp_delete_drive_snapshot-add-aiocont.patch
new file mode 100644 (file)
index 0000000..8cbef36
--- /dev/null
@@ -0,0 +1,59 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Alexandre Derumier <aderumier@odiso.com>
+Date: Mon, 7 Nov 2016 11:47:50 +0100
+Subject: [PATCH] block: snapshot: qmp_delete_drive_snapshot : add aiocontext
+
+this fix snapshot delete of qcow2 with iothread enabled
+
+Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
+---
+ savevm-async.c | 13 ++++++++++---
+ 1 file changed, 10 insertions(+), 3 deletions(-)
+
+diff --git a/savevm-async.c b/savevm-async.c
+index 06dcd29fc7..e6f86ef865 100644
+--- a/savevm-async.c
++++ b/savevm-async.c
+@@ -459,6 +459,7 @@ void qmp_delete_drive_snapshot(const char *device, const char *name,
+     BlockDriverState *bs;
+     QEMUSnapshotInfo sn1, *sn = &sn1;
+     Error *local_err = NULL;
++    AioContext *aio_context;
+     int ret;
+@@ -475,22 +476,28 @@ void qmp_delete_drive_snapshot(const char *device, const char *name,
+         return;
+     }
++    aio_context = bdrv_get_aio_context(bs);
++    aio_context_acquire(aio_context);
++
+     if (!bdrv_can_snapshot(bs)) {
+         error_setg(errp, QERR_UNSUPPORTED);
+-        return;
++        goto out;
+     }
+     if (bdrv_snapshot_find(bs, sn, name) < 0) {
+         /* return success if snapshot does not exists */
+-        return;
++        goto out;
+     }
+     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;
++        goto out;
+     }
++
++out:
++    aio_context_release(aio_context);
+ }
+ static ssize_t loadstate_get_buffer(void *opaque, uint8_t *buf, int64_t pos,
+-- 
+2.11.0
+
diff --git a/debian/patches/pve/0020-block-snapshot-qmp_snapshot_drive-add-aiocontext.patch b/debian/patches/pve/0020-block-snapshot-qmp_snapshot_drive-add-aiocontext.patch
deleted file mode 100644 (file)
index 05b172c..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Alexandre Derumier <aderumier@odiso.com>
-Date: Tue, 13 Sep 2016 01:57:56 +0200
-Subject: [PATCH] block: snapshot: 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 97d51d3edd..ece193a065 100644
---- a/savevm-async.c
-+++ b/savevm-async.c
-@@ -377,6 +377,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;
-@@ -403,20 +404,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;
-@@ -441,8 +445,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.11.0
-
diff --git a/debian/patches/pve/0021-block-snapshot-qmp_delete_drive_snapshot-add-aiocont.patch b/debian/patches/pve/0021-block-snapshot-qmp_delete_drive_snapshot-add-aiocont.patch
deleted file mode 100644 (file)
index bf98d48..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Alexandre Derumier <aderumier@odiso.com>
-Date: Mon, 7 Nov 2016 11:47:50 +0100
-Subject: [PATCH] block: snapshot: qmp_delete_drive_snapshot : add aiocontext
-
-this fix snapshot delete of qcow2 with iothread enabled
-
-Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
----
- savevm-async.c | 13 ++++++++++---
- 1 file changed, 10 insertions(+), 3 deletions(-)
-
-diff --git a/savevm-async.c b/savevm-async.c
-index ece193a065..716dd2d7a1 100644
---- a/savevm-async.c
-+++ b/savevm-async.c
-@@ -459,6 +459,7 @@ void qmp_delete_drive_snapshot(const char *device, const char *name,
-     BlockDriverState *bs;
-     QEMUSnapshotInfo sn1, *sn = &sn1;
-     Error *local_err = NULL;
-+    AioContext *aio_context;
-     int ret;
-@@ -475,22 +476,28 @@ void qmp_delete_drive_snapshot(const char *device, const char *name,
-         return;
-     }
-+    aio_context = bdrv_get_aio_context(bs);
-+    aio_context_acquire(aio_context);
-+
-     if (!bdrv_can_snapshot(bs)) {
-         error_setg(errp, QERR_UNSUPPORTED);
--        return;
-+        goto out;
-     }
-     if (bdrv_snapshot_find(bs, sn, name) < 0) {
-         /* return success if snapshot does not exists */
--        return;
-+        goto out;
-     }
-     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;
-+        goto out;
-     }
-+
-+out:
-+    aio_context_release(aio_context);
- }
- static ssize_t loadstate_get_buffer(void *opaque, uint8_t *buf, int64_t pos,
--- 
-2.11.0
-
diff --git a/debian/patches/pve/0021-glusterfs-no-default-logfile-if-daemonized.patch b/debian/patches/pve/0021-glusterfs-no-default-logfile-if-daemonized.patch
new file mode 100644 (file)
index 0000000..8725559
--- /dev/null
@@ -0,0 +1,52 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Wolfgang Bumiller <w.bumiller@proxmox.com>
+Date: Mon, 24 Oct 2016 09:32:36 +0200
+Subject: [PATCH] glusterfs: no default logfile if daemonized
+
+---
+ block/gluster.c | 15 +++++++++++----
+ 1 file changed, 11 insertions(+), 4 deletions(-)
+
+diff --git a/block/gluster.c b/block/gluster.c
+index 0f4265a3a4..8fab26481c 100644
+--- a/block/gluster.c
++++ b/block/gluster.c
+@@ -32,7 +32,7 @@
+ #define GLUSTER_DEBUG_DEFAULT       4
+ #define GLUSTER_DEBUG_MAX           9
+ #define GLUSTER_OPT_LOGFILE         "logfile"
+-#define GLUSTER_LOGFILE_DEFAULT     "-" /* handled in libgfapi as /dev/stderr */
++#define GLUSTER_LOGFILE_DEFAULT     NULL
+ #define GERR_INDEX_HINT "hint: check in 'server' array index '%d'\n"
+@@ -396,6 +396,7 @@ static struct glfs *qemu_gluster_glfs_init(BlockdevOptionsGluster *gconf,
+     int old_errno;
+     SocketAddressList *server;
+     unsigned long long port;
++    const char *logfile;
+     glfs = glfs_find_preopened(gconf->volume);
+     if (glfs) {
+@@ -438,9 +439,15 @@ static struct glfs *qemu_gluster_glfs_init(BlockdevOptionsGluster *gconf,
+         }
+     }
+-    ret = glfs_set_logging(glfs, gconf->logfile, gconf->debug);
+-    if (ret < 0) {
+-        goto out;
++    logfile = gconf->logfile;
++    if (!logfile && !is_daemonized()) {
++        logfile = "-";
++    }
++    if (logfile) {
++        ret = glfs_set_logging(glfs, logfile, gconf->debug);
++        if (ret < 0) {
++            goto out;
++        }
+     }
+     ret = glfs_init(glfs);
+-- 
+2.11.0
+
diff --git a/debian/patches/pve/0022-glusterfs-allow-partial-reads.patch b/debian/patches/pve/0022-glusterfs-allow-partial-reads.patch
new file mode 100644 (file)
index 0000000..ec2bc4d
--- /dev/null
@@ -0,0 +1,78 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Wolfgang Bumiller <w.bumiller@proxmox.com>
+Date: Wed, 30 Nov 2016 10:27:47 +0100
+Subject: [PATCH] glusterfs: allow partial reads
+
+This should deal with qemu bug #1644754 until upstream
+decides which way to go. The general direction seems to be
+away from sector based block APIs and with that in mind, and
+when comparing to other network block backends (eg. nfs)
+treating partial reads as errors doesn't seem to make much
+sense.
+---
+ block/gluster.c | 10 +++++++++-
+ 1 file changed, 9 insertions(+), 1 deletion(-)
+
+diff --git a/block/gluster.c b/block/gluster.c
+index 8fab26481c..24296a39b3 100644
+--- a/block/gluster.c
++++ b/block/gluster.c
+@@ -41,6 +41,7 @@ typedef struct GlusterAIOCB {
+     int ret;
+     Coroutine *coroutine;
+     AioContext *aio_context;
++    bool is_write;
+ } GlusterAIOCB;
+ typedef struct BDRVGlusterState {
+@@ -709,8 +710,10 @@ static void gluster_finish_aiocb(struct glfs_fd *fd, ssize_t ret, void *arg)
+         acb->ret = 0; /* Success */
+     } else if (ret < 0) {
+         acb->ret = -errno; /* Read/Write failed */
++    } else if (acb->is_write) {
++        acb->ret = -EIO; /* Partial write - fail it */
+     } else {
+-        acb->ret = -EIO; /* Partial read/write - fail it */
++        acb->ret = 0; /* Success */
+     }
+     aio_co_schedule(acb->aio_context, acb->coroutine);
+@@ -958,6 +961,7 @@ static coroutine_fn int qemu_gluster_co_pwrite_zeroes(BlockDriverState *bs,
+     acb.ret = 0;
+     acb.coroutine = qemu_coroutine_self();
+     acb.aio_context = bdrv_get_aio_context(bs);
++    acb.is_write = true;
+     ret = glfs_zerofill_async(s->fd, offset, size, gluster_finish_aiocb, &acb);
+     if (ret < 0) {
+@@ -1083,9 +1087,11 @@ static coroutine_fn int qemu_gluster_co_rw(BlockDriverState *bs,
+     acb.aio_context = bdrv_get_aio_context(bs);
+     if (write) {
++        acb.is_write = true;
+         ret = glfs_pwritev_async(s->fd, qiov->iov, qiov->niov, offset, 0,
+                                  gluster_finish_aiocb, &acb);
+     } else {
++        acb.is_write = false;
+         ret = glfs_preadv_async(s->fd, qiov->iov, qiov->niov, offset, 0,
+                                 gluster_finish_aiocb, &acb);
+     }
+@@ -1158,6 +1164,7 @@ static coroutine_fn int qemu_gluster_co_flush_to_disk(BlockDriverState *bs)
+     acb.ret = 0;
+     acb.coroutine = qemu_coroutine_self();
+     acb.aio_context = bdrv_get_aio_context(bs);
++    acb.is_write = true;
+     ret = glfs_fsync_async(s->fd, gluster_finish_aiocb, &acb);
+     if (ret < 0) {
+@@ -1204,6 +1211,7 @@ static coroutine_fn int qemu_gluster_co_pdiscard(BlockDriverState *bs,
+     acb.ret = 0;
+     acb.coroutine = qemu_coroutine_self();
+     acb.aio_context = bdrv_get_aio_context(bs);
++    acb.is_write = true;
+     ret = glfs_discard_async(s->fd, offset, size, gluster_finish_aiocb, &acb);
+     if (ret < 0) {
+-- 
+2.11.0
+
diff --git a/debian/patches/pve/0022-glusterfs-no-default-logfile-if-daemonized.patch b/debian/patches/pve/0022-glusterfs-no-default-logfile-if-daemonized.patch
deleted file mode 100644 (file)
index ac2046b..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Wolfgang Bumiller <w.bumiller@proxmox.com>
-Date: Mon, 24 Oct 2016 09:32:36 +0200
-Subject: [PATCH] glusterfs: no default logfile if daemonized
-
----
- block/gluster.c | 15 +++++++++++----
- 1 file changed, 11 insertions(+), 4 deletions(-)
-
-diff --git a/block/gluster.c b/block/gluster.c
-index cf29b5f9a4..bc44c50db0 100644
---- a/block/gluster.c
-+++ b/block/gluster.c
-@@ -33,7 +33,7 @@
- #define GLUSTER_DEBUG_DEFAULT       4
- #define GLUSTER_DEBUG_MAX           9
- #define GLUSTER_OPT_LOGFILE         "logfile"
--#define GLUSTER_LOGFILE_DEFAULT     "-" /* handled in libgfapi as /dev/stderr */
-+#define GLUSTER_LOGFILE_DEFAULT     NULL
- #define GERR_INDEX_HINT "hint: check in 'server' array index '%d'\n"
-@@ -398,6 +398,7 @@ static struct glfs *qemu_gluster_glfs_init(BlockdevOptionsGluster *gconf,
-     int old_errno;
-     SocketAddressFlatList *server;
-     unsigned long long port;
-+    const char *logfile;
-     glfs = glfs_find_preopened(gconf->volume);
-     if (glfs) {
-@@ -440,9 +441,15 @@ static struct glfs *qemu_gluster_glfs_init(BlockdevOptionsGluster *gconf,
-         }
-     }
--    ret = glfs_set_logging(glfs, gconf->logfile, gconf->debug);
--    if (ret < 0) {
--        goto out;
-+    logfile = gconf->logfile;
-+    if (!logfile && !is_daemonized()) {
-+        logfile = "-";
-+    }
-+    if (logfile) {
-+        ret = glfs_set_logging(glfs, logfile, gconf->debug);
-+        if (ret < 0) {
-+            goto out;
-+        }
-     }
-     ret = glfs_init(glfs);
--- 
-2.11.0
-
diff --git a/debian/patches/pve/0023-block-add-the-zeroinit-block-driver-filter.patch b/debian/patches/pve/0023-block-add-the-zeroinit-block-driver-filter.patch
new file mode 100644 (file)
index 0000000..1a0847d
--- /dev/null
@@ -0,0 +1,234 @@
+From 0000000000000000000000000000000000000000 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] block: add the zeroinit block driver filter
+
+---
+ block/Makefile.objs |   1 +
+ block/zeroinit.c    | 202 ++++++++++++++++++++++++++++++++++++++++++++++++++++
+ 2 files changed, 203 insertions(+)
+ create mode 100644 block/zeroinit.c
+
+diff --git a/block/Makefile.objs b/block/Makefile.objs
+index 6eaf78a046..823e60cda6 100644
+--- a/block/Makefile.objs
++++ b/block/Makefile.objs
+@@ -4,6 +4,7 @@ block-obj-y += qed.o qed-l2-cache.o qed-table.o qed-cluster.o
+ block-obj-y += qed-check.o
+ block-obj-y += 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) += file-win32.o win32-aio.o
+diff --git a/block/zeroinit.c b/block/zeroinit.c
+new file mode 100644
+index 0000000000..37f588f75c
+--- /dev/null
++++ b/block/zeroinit.c
+@@ -0,0 +1,202 @@
++/*
++ * 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 int coroutine_fn zeroinit_co_preadv(BlockDriverState *bs,
++    uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags)
++{
++    return bdrv_co_preadv(bs->file, offset, bytes, qiov, flags);
++}
++
++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 int coroutine_fn zeroinit_co_pwritev(BlockDriverState *bs,
++    uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags)
++{
++    BDRVZeroinitState *s = bs->opaque;
++    int64_t extents = offset + bytes;
++    if (extents > s->extents)
++        s->extents = extents;
++    return bdrv_co_pwritev(bs->file, offset, bytes, qiov, flags);
++}
++
++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 int coroutine_fn zeroinit_co_pdiscard(BlockDriverState *bs,
++                                             int64_t offset, int count)
++{
++    return bdrv_co_pdiscard(bs->file->bs, offset, count);
++}
++
++static int zeroinit_truncate(BlockDriverState *bs, int64_t offset,
++                             PreallocMode prealloc, Error **errp)
++{
++    return bdrv_truncate(bs->file, offset, prealloc, errp);
++}
++
++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_child_perm                  = bdrv_filter_default_perms,
++    .bdrv_co_flush_to_disk            = zeroinit_co_flush,
++
++    .bdrv_co_pwrite_zeroes            = zeroinit_co_pwrite_zeroes,
++    .bdrv_co_pwritev                  = zeroinit_co_pwritev,
++    .bdrv_co_preadv                   = zeroinit_co_preadv,
++    .bdrv_co_flush                    = zeroinit_co_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 = bdrv_co_get_block_status_from_file,
++
++    .bdrv_co_pdiscard = zeroinit_co_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.11.0
+
diff --git a/debian/patches/pve/0023-glusterfs-allow-partial-reads.patch b/debian/patches/pve/0023-glusterfs-allow-partial-reads.patch
deleted file mode 100644 (file)
index 6648a2b..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Wolfgang Bumiller <w.bumiller@proxmox.com>
-Date: Wed, 30 Nov 2016 10:27:47 +0100
-Subject: [PATCH] glusterfs: allow partial reads
-
-This should deal with qemu bug #1644754 until upstream
-decides which way to go. The general direction seems to be
-away from sector based block APIs and with that in mind, and
-when comparing to other network block backends (eg. nfs)
-treating partial reads as errors doesn't seem to make much
-sense.
----
- block/gluster.c | 10 +++++++++-
- 1 file changed, 9 insertions(+), 1 deletion(-)
-
-diff --git a/block/gluster.c b/block/gluster.c
-index bc44c50db0..4fdf68f1fc 100644
---- a/block/gluster.c
-+++ b/block/gluster.c
-@@ -42,6 +42,7 @@ typedef struct GlusterAIOCB {
-     int ret;
-     Coroutine *coroutine;
-     AioContext *aio_context;
-+    bool is_write;
- } GlusterAIOCB;
- typedef struct BDRVGlusterState {
-@@ -713,8 +714,10 @@ static void gluster_finish_aiocb(struct glfs_fd *fd, ssize_t ret, void *arg)
-         acb->ret = 0; /* Success */
-     } else if (ret < 0) {
-         acb->ret = -errno; /* Read/Write failed */
-+    } else if (acb->is_write) {
-+        acb->ret = -EIO; /* Partial write - fail it */
-     } else {
--        acb->ret = -EIO; /* Partial read/write - fail it */
-+        acb->ret = 0; /* Success */
-     }
-     aio_co_schedule(acb->aio_context, acb->coroutine);
-@@ -962,6 +965,7 @@ static coroutine_fn int qemu_gluster_co_pwrite_zeroes(BlockDriverState *bs,
-     acb.ret = 0;
-     acb.coroutine = qemu_coroutine_self();
-     acb.aio_context = bdrv_get_aio_context(bs);
-+    acb.is_write = true;
-     ret = glfs_zerofill_async(s->fd, offset, size, gluster_finish_aiocb, &acb);
-     if (ret < 0) {
-@@ -1084,9 +1088,11 @@ static coroutine_fn int qemu_gluster_co_rw(BlockDriverState *bs,
-     acb.aio_context = bdrv_get_aio_context(bs);
-     if (write) {
-+        acb.is_write = true;
-         ret = glfs_pwritev_async(s->fd, qiov->iov, qiov->niov, offset, 0,
-                                  gluster_finish_aiocb, &acb);
-     } else {
-+        acb.is_write = false;
-         ret = glfs_preadv_async(s->fd, qiov->iov, qiov->niov, offset, 0,
-                                 gluster_finish_aiocb, &acb);
-     }
-@@ -1150,6 +1156,7 @@ static coroutine_fn int qemu_gluster_co_flush_to_disk(BlockDriverState *bs)
-     acb.ret = 0;
-     acb.coroutine = qemu_coroutine_self();
-     acb.aio_context = bdrv_get_aio_context(bs);
-+    acb.is_write = true;
-     ret = glfs_fsync_async(s->fd, gluster_finish_aiocb, &acb);
-     if (ret < 0) {
-@@ -1196,6 +1203,7 @@ static coroutine_fn int qemu_gluster_co_pdiscard(BlockDriverState *bs,
-     acb.ret = 0;
-     acb.coroutine = qemu_coroutine_self();
-     acb.aio_context = bdrv_get_aio_context(bs);
-+    acb.is_write = true;
-     ret = glfs_discard_async(s->fd, offset, size, gluster_finish_aiocb, &acb);
-     if (ret < 0) {
--- 
-2.11.0
-
diff --git a/debian/patches/pve/0024-block-add-the-zeroinit-block-driver-filter.patch b/debian/patches/pve/0024-block-add-the-zeroinit-block-driver-filter.patch
deleted file mode 100644 (file)
index e3f9fa5..0000000
+++ /dev/null
@@ -1,252 +0,0 @@
-From 0000000000000000000000000000000000000000 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] 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 de96f8ee80..8cdac08db5 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-y += 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) += file-win32.o win32-aio.o
-diff --git a/block/zeroinit.c b/block/zeroinit.c
-new file mode 100644
-index 0000000000..305185512e
---- /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 int coroutine_fn zeroinit_co_pdiscard(BlockDriverState *bs,
-+                                             int64_t offset, int count)
-+{
-+    return bdrv_co_pdiscard(bs->file->bs, offset, count);
-+}
-+
-+static int zeroinit_truncate(BlockDriverState *bs, int64_t offset)
-+{
-+    return bdrv_truncate(bs->file, offset, NULL);
-+}
-+
-+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_child_perm                  = bdrv_filter_default_perms,
-+    .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_co_pdiscard = zeroinit_co_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.11.0
-
diff --git a/debian/patches/pve/0024-qemu-img-dd-add-osize-and-read-from-to-stdin-stdout.patch b/debian/patches/pve/0024-qemu-img-dd-add-osize-and-read-from-to-stdin-stdout.patch
new file mode 100644 (file)
index 0000000..db04e8d
--- /dev/null
@@ -0,0 +1,321 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Wolfgang Bumiller <w.bumiller@proxmox.com>
+Date: Fri, 23 Jun 2017 12:01:43 +0200
+Subject: [PATCH] qemu-img dd: add osize and read from/to stdin/stdout
+
+Neither convert nor dd were previously able to write to or
+read from a pipe. Particularly serializing an image file
+into a raw stream or vice versa can be useful, but using
+`qemu-img convert -f qcow2 -O raw foo.qcow2 /dev/stdout` in
+a pipe will fail trying to seek.
+
+While dd and convert have overlapping use cases, `dd` is a
+simple read/write loop while convert is much more
+sophisticated and has ways to dealing with holes and blocks
+of zeroes.
+Since these typically can't be detected in pipes via
+SEEK_DATA/HOLE or skipped while writing, dd seems to be the
+better choice for implementing stdin/stdout streams.
+
+This patch causes "if" and "of" to default to stdin and
+stdout respectively, allowing only the "raw" format to be
+used in these cases.
+Since the input can now be a pipe we have no way of
+detecting the size of the output image to create. Since we
+also want to support images with a size not matching the
+dd command's "bs" parameter (which, together with "count"
+could be used to calculate the desired size, and is already
+used to limit it), the "osize" option is added to explicitly
+override the output file's size.
+
+Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
+---
+ qemu-img-cmds.hx |   4 +-
+ qemu-img.c       | 192 ++++++++++++++++++++++++++++++++++---------------------
+ 2 files changed, 122 insertions(+), 74 deletions(-)
+
+diff --git a/qemu-img-cmds.hx b/qemu-img-cmds.hx
+index 2fe31893cf..52042f2773 100644
+--- a/qemu-img-cmds.hx
++++ b/qemu-img-cmds.hx
+@@ -53,9 +53,9 @@ STEXI
+ ETEXI
+ DEF("dd", img_dd,
+-    "dd [--image-opts] [-U] [-f fmt] [-O output_fmt] [bs=block_size] [count=blocks] [skip=blocks] if=input of=output")
++    "dd [--image-opts] [-U] [-f fmt] [-O output_fmt] [bs=block_size] [count=blocks] [skip=blocks] [osize=output_size] if=input of=output")
+ STEXI
+-@item dd [--image-opts] [-U] [-f @var{fmt}] [-O @var{output_fmt}] [bs=@var{block_size}] [count=@var{blocks}] [skip=@var{blocks}] if=@var{input} of=@var{output}
++@item dd [--image-opts] [-U] [-f @var{fmt}] [-O @var{output_fmt}] [bs=@var{block_size}] [count=@var{blocks}] [skip=@var{blocks}] [osize=output_size] if=@var{input} of=@var{output}
+ ETEXI
+ DEF("info", img_info,
+diff --git a/qemu-img.c b/qemu-img.c
+index f8a39dd195..876a3623a7 100644
+--- a/qemu-img.c
++++ b/qemu-img.c
+@@ -4088,10 +4088,12 @@ out:
+ #define C_IF      04
+ #define C_OF      010
+ #define C_SKIP    020
++#define C_OSIZE   040
+ struct DdInfo {
+     unsigned int flags;
+     int64_t count;
++    int64_t osize;
+ };
+ struct DdIo {
+@@ -4170,6 +4172,20 @@ static int img_dd_skip(const char *arg,
+     return 0;
+ }
++static int img_dd_osize(const char *arg,
++                        struct DdIo *in, struct DdIo *out,
++                        struct DdInfo *dd)
++{
++    dd->osize = cvtnum(arg);
++
++    if (dd->osize < 0) {
++        error_report("invalid number: '%s'", arg);
++        return 1;
++    }
++
++    return 0;
++}
++
+ static int img_dd(int argc, char **argv)
+ {
+     int ret = 0;
+@@ -4210,6 +4226,7 @@ static int img_dd(int argc, char **argv)
+         { "if", img_dd_if, C_IF },
+         { "of", img_dd_of, C_OF },
+         { "skip", img_dd_skip, C_SKIP },
++        { "osize", img_dd_osize, C_OSIZE },
+         { NULL, NULL, 0 }
+     };
+     const struct option long_options[] = {
+@@ -4288,8 +4305,13 @@ static int img_dd(int argc, char **argv)
+         arg = NULL;
+     }
+-    if (!(dd.flags & C_IF && dd.flags & C_OF)) {
+-        error_report("Must specify both input and output files");
++    if (!(dd.flags & C_IF) && (!fmt || strcmp(fmt, "raw") != 0)) {
++        error_report("Input format must be raw when readin from stdin");
++        ret = -1;
++        goto out;
++    }
++    if (!(dd.flags & C_OF) && strcmp(out_fmt, "raw") != 0) {
++        error_report("Output format must be raw when writing to stdout");
+         ret = -1;
+         goto out;
+     }
+@@ -4301,85 +4323,101 @@ static int img_dd(int argc, char **argv)
+         goto out;
+     }
+-    blk1 = img_open(image_opts, in.filename, fmt, 0, false, false,
+-                    force_share);
++    if (dd.flags & C_IF) {
++        blk1 = img_open(image_opts, in.filename, fmt, 0, false, false,
++                        force_share);
+-    if (!blk1) {
+-        ret = -1;
+-        goto out;
++        if (!blk1) {
++            ret = -1;
++            goto out;
++        }
+     }
+-    drv = bdrv_find_format(out_fmt);
+-    if (!drv) {
+-        error_report("Unknown file format");
++    if (dd.flags & C_OSIZE) {
++        size = dd.osize;
++    } else if (dd.flags & C_IF) {
++        size = blk_getlength(blk1);
++        if (size < 0) {
++            error_report("Failed to get size for '%s'", in.filename);
++            ret = -1;
++            goto out;
++        }
++    } else if (dd.flags & C_COUNT) {
++        size = dd.count * in.bsz;
++    } else {
++        error_report("Output size must be known when reading from stdin");
+         ret = -1;
+         goto out;
+     }
+-    proto_drv = bdrv_find_protocol(out.filename, true, &local_err);
+-    if (!proto_drv) {
+-        error_report_err(local_err);
+-        ret = -1;
+-        goto out;
+-    }
+-    if (!drv->create_opts) {
+-        error_report("Format driver '%s' does not support image creation",
+-                     drv->format_name);
+-        ret = -1;
+-        goto out;
+-    }
+-    if (!proto_drv->create_opts) {
+-        error_report("Protocol driver '%s' does not support image creation",
+-                     proto_drv->format_name);
+-        ret = -1;
+-        goto out;
++    if (!(dd.flags & C_OSIZE) && dd.flags & C_COUNT && dd.count <= INT64_MAX / in.bsz &&
++        dd.count * in.bsz < size) {
++        size = dd.count * in.bsz;
+     }
+-    create_opts = qemu_opts_append(create_opts, drv->create_opts);
+-    create_opts = qemu_opts_append(create_opts, proto_drv->create_opts);
+-    opts = qemu_opts_create(create_opts, NULL, 0, &error_abort);
++    if (dd.flags & C_OF) {
++        drv = bdrv_find_format(out_fmt);
++        if (!drv) {
++            error_report("Unknown file format");
++            ret = -1;
++            goto out;
++        }
++        proto_drv = bdrv_find_protocol(out.filename, true, &local_err);
+-    size = blk_getlength(blk1);
+-    if (size < 0) {
+-        error_report("Failed to get size for '%s'", in.filename);
+-        ret = -1;
+-        goto out;
+-    }
++        if (!proto_drv) {
++            error_report_err(local_err);
++            ret = -1;
++            goto out;
++        }
++        if (!drv->create_opts) {
++            error_report("Format driver '%s' does not support image creation",
++                         drv->format_name);
++            ret = -1;
++            goto out;
++        }
++        if (!proto_drv->create_opts) {
++            error_report("Protocol driver '%s' does not support image creation",
++                         proto_drv->format_name);
++            ret = -1;
++            goto out;
++        }
++        create_opts = qemu_opts_append(create_opts, drv->create_opts);
++        create_opts = qemu_opts_append(create_opts, proto_drv->create_opts);
+-    if (dd.flags & C_COUNT && dd.count <= INT64_MAX / in.bsz &&
+-        dd.count * in.bsz < size) {
+-        size = dd.count * in.bsz;
+-    }
++        opts = qemu_opts_create(create_opts, NULL, 0, &error_abort);
+-    /* Overflow means the specified offset is beyond input image's size */
+-    if (dd.flags & C_SKIP && (in.offset > INT64_MAX / in.bsz ||
+-                              size < in.bsz * in.offset)) {
+-        qemu_opt_set_number(opts, BLOCK_OPT_SIZE, 0, &error_abort);
+-    } else {
+-        qemu_opt_set_number(opts, BLOCK_OPT_SIZE,
+-                            size - in.bsz * in.offset, &error_abort);
+-    }
++        /* Overflow means the specified offset is beyond input image's size */
++        if (dd.flags & C_OSIZE) {
++            qemu_opt_set_number(opts, BLOCK_OPT_SIZE, size, &error_abort);
++        } else if (dd.flags & C_SKIP && (in.offset > INT64_MAX / in.bsz ||
++                                  size < in.bsz * in.offset)) {
++            qemu_opt_set_number(opts, BLOCK_OPT_SIZE, 0, &error_abort);
++        } else {
++            qemu_opt_set_number(opts, BLOCK_OPT_SIZE,
++                                size - in.bsz * in.offset, &error_abort);
++        }
+-    ret = bdrv_create(drv, out.filename, opts, &local_err);
+-    if (ret < 0) {
+-        error_reportf_err(local_err,
+-                          "%s: error while creating output image: ",
+-                          out.filename);
+-        ret = -1;
+-        goto out;
+-    }
++        ret = bdrv_create(drv, out.filename, opts, &local_err);
++        if (ret < 0) {
++            error_reportf_err(local_err,
++                              "%s: error while creating output image: ",
++                              out.filename);
++            ret = -1;
++            goto out;
++        }
+-    /* TODO, we can't honour --image-opts for the target,
+-     * since it needs to be given in a format compatible
+-     * with the bdrv_create() call above which does not
+-     * support image-opts style.
+-     */
+-    blk2 = img_open_file(out.filename, NULL, out_fmt, BDRV_O_RDWR,
+-                         false, false, false);
++        /* TODO, we can't honour --image-opts for the target,
++         * since it needs to be given in a format compatible
++         * with the bdrv_create() call above which does not
++         * support image-opts style.
++         */
++        blk2 = img_open_file(out.filename, NULL, out_fmt, BDRV_O_RDWR,
++                             false, false, false);
+-    if (!blk2) {
+-        ret = -1;
+-        goto out;
++        if (!blk2) {
++            ret = -1;
++            goto out;
++        }
+     }
+     if (dd.flags & C_SKIP && (in.offset > INT64_MAX / in.bsz ||
+@@ -4397,11 +4435,17 @@ static int img_dd(int argc, char **argv)
+     for (out_pos = 0; in_pos < size; block_count++) {
+         int in_ret, out_ret;
+-
+-        if (in_pos + in.bsz > size) {
+-            in_ret = blk_pread(blk1, in_pos, in.buf, size - in_pos);
++        size_t in_bsz = in_pos + in.bsz > size ? size - in_pos : in.bsz;
++        if (blk1) {
++            in_ret = blk_pread(blk1, in_pos, in.buf, in_bsz);
+         } else {
+-            in_ret = blk_pread(blk1, in_pos, in.buf, in.bsz);
++            in_ret = read(STDIN_FILENO, in.buf, in_bsz);
++            if (in_ret == 0) {
++                /* early EOF is considered an error */
++                error_report("Input ended unexpectedly");
++                ret = -1;
++                goto out;
++            }
+         }
+         if (in_ret < 0) {
+             error_report("error while reading from input image file: %s",
+@@ -4411,9 +4455,13 @@ static int img_dd(int argc, char **argv)
+         }
+         in_pos += in_ret;
+-        out_ret = blk_pwrite(blk2, out_pos, in.buf, in_ret, 0);
++        if (blk2) {
++            out_ret = blk_pwrite(blk2, out_pos, in.buf, in_ret, 0);
++        } else {
++            out_ret = write(STDOUT_FILENO, in.buf, in_ret);
++        }
+-        if (out_ret < 0) {
++        if (out_ret != in_ret) {
+             error_report("error while writing to output image file: %s",
+                          strerror(-out_ret));
+             ret = -1;
+-- 
+2.11.0
+
diff --git a/debian/patches/pve/0025-backup-modify-job-api.patch b/debian/patches/pve/0025-backup-modify-job-api.patch
new file mode 100644 (file)
index 0000000..bb03935
--- /dev/null
@@ -0,0 +1,100 @@
+From 0000000000000000000000000000000000000000 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] backup: modify job api
+
+Introduce a pause_count parameter to start a backup in
+paused mode. This way backups of multiple drives can be
+started up sequentially via the completion callback while
+having been started at the same point in time.
+---
+ block/backup.c            | 2 ++
+ block/replication.c       | 2 +-
+ blockdev.c                | 4 ++--
+ blockjob.c                | 2 +-
+ include/block/block_int.h | 1 +
+ 5 files changed, 7 insertions(+), 4 deletions(-)
+
+diff --git a/block/backup.c b/block/backup.c
+index 99e6bcc748..8c2967a8cb 100644
+--- a/block/backup.c
++++ b/block/backup.c
+@@ -539,6 +539,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
+                   BlockdevOnError on_target_error,
+                   int creation_flags,
+                   BlockCompletionFunc *cb, void *opaque,
++                  int pause_count,
+                   BlockJobTxn *txn, Error **errp)
+ {
+     int64_t len;
+@@ -663,6 +664,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
+     block_job_add_bdrv(&job->common, "target", target, 0, BLK_PERM_ALL,
+                        &error_abort);
+     job->common.len = len;
++    job->common.pause_count = pause_count;
+     block_job_txn_add_job(txn, &job->common);
+     return &job->common;
+diff --git a/block/replication.c b/block/replication.c
+index e41e293d2b..1b08b242eb 100644
+--- a/block/replication.c
++++ b/block/replication.c
+@@ -561,7 +561,7 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode,
+                                 0, MIRROR_SYNC_MODE_NONE, NULL, false,
+                                 BLOCKDEV_ON_ERROR_REPORT,
+                                 BLOCKDEV_ON_ERROR_REPORT, BLOCK_JOB_INTERNAL,
+-                                backup_job_completed, bs, NULL, &local_err);
++                                backup_job_completed, bs, 0, NULL, &local_err);
+         if (local_err) {
+             error_propagate(errp, local_err);
+             backup_job_cleanup(bs);
+diff --git a/blockdev.c b/blockdev.c
+index 56a6b24a0b..a9ed9034b5 100644
+--- a/blockdev.c
++++ b/blockdev.c
+@@ -3293,7 +3293,7 @@ static BlockJob *do_drive_backup(DriveBackup *backup, BlockJobTxn *txn,
+     job = backup_job_create(backup->job_id, bs, target_bs, backup->speed,
+                             backup->sync, bmap, backup->compress,
+                             backup->on_source_error, backup->on_target_error,
+-                            BLOCK_JOB_DEFAULT, NULL, NULL, txn, &local_err);
++                            BLOCK_JOB_DEFAULT, NULL, NULL, 0, txn, &local_err);
+     bdrv_unref(target_bs);
+     if (local_err != NULL) {
+         error_propagate(errp, local_err);
+@@ -3372,7 +3372,7 @@ BlockJob *do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn,
+     job = backup_job_create(backup->job_id, bs, target_bs, backup->speed,
+                             backup->sync, NULL, backup->compress,
+                             backup->on_source_error, backup->on_target_error,
+-                            BLOCK_JOB_DEFAULT, NULL, NULL, txn, &local_err);
++                            BLOCK_JOB_DEFAULT, NULL, NULL, 0, txn, &local_err);
+     if (local_err != NULL) {
+         error_propagate(errp, local_err);
+     }
+diff --git a/blockjob.c b/blockjob.c
+index 715c2c2680..c1b6b6a810 100644
+--- a/blockjob.c
++++ b/blockjob.c
+@@ -322,7 +322,7 @@ void block_job_start(BlockJob *job)
+     job->co = qemu_coroutine_create(block_job_co_entry, job);
+     job->pause_count--;
+     job->busy = true;
+-    job->paused = false;
++    job->paused = job->pause_count > 0;
+     bdrv_coroutine_enter(blk_bs(job->blk), job->co);
+ }
+diff --git a/include/block/block_int.h b/include/block/block_int.h
+index a5482775ec..1dbbdafd31 100644
+--- a/include/block/block_int.h
++++ b/include/block/block_int.h
+@@ -985,6 +985,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
+                             BlockdevOnError on_target_error,
+                             int creation_flags,
+                             BlockCompletionFunc *cb, void *opaque,
++                            int pause_count,
+                             BlockJobTxn *txn, Error **errp);
+ void hmp_drive_add_node(Monitor *mon, const char *optstr);
+-- 
+2.11.0
+
diff --git a/debian/patches/pve/0025-qemu-img-dd-add-osize-and-read-from-to-stdin-stdout.patch b/debian/patches/pve/0025-qemu-img-dd-add-osize-and-read-from-to-stdin-stdout.patch
deleted file mode 100644 (file)
index b730dbe..0000000
+++ /dev/null
@@ -1,304 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Wolfgang Bumiller <w.bumiller@proxmox.com>
-Date: Fri, 23 Jun 2017 12:01:43 +0200
-Subject: [PATCH] qemu-img dd: add osize and read from/to stdin/stdout
-
-Neither convert nor dd were previously able to write to or
-read from a pipe. Particularly serializing an image file
-into a raw stream or vice versa can be useful, but using
-`qemu-img convert -f qcow2 -O raw foo.qcow2 /dev/stdout` in
-a pipe will fail trying to seek.
-
-While dd and convert have overlapping use cases, `dd` is a
-simple read/write loop while convert is much more
-sophisticated and has ways to dealing with holes and blocks
-of zeroes.
-Since these typically can't be detected in pipes via
-SEEK_DATA/HOLE or skipped while writing, dd seems to be the
-better choice for implementing stdin/stdout streams.
-
-This patch causes "if" and "of" to default to stdin and
-stdout respectively, allowing only the "raw" format to be
-used in these cases.
-Since the input can now be a pipe we have no way of
-detecting the size of the output image to create. Since we
-also want to support images with a size not matching the
-dd command's "bs" parameter (which, together with "count"
-could be used to calculate the desired size, and is already
-used to limit it), the "osize" option is added to explicitly
-override the output file's size.
-
-Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
----
- qemu-img-cmds.hx |   4 +-
- qemu-img.c       | 176 +++++++++++++++++++++++++++++++++++--------------------
- 2 files changed, 115 insertions(+), 65 deletions(-)
-
-diff --git a/qemu-img-cmds.hx b/qemu-img-cmds.hx
-index 8ac78222af..16bee83987 100644
---- a/qemu-img-cmds.hx
-+++ b/qemu-img-cmds.hx
-@@ -46,9 +46,9 @@ STEXI
- ETEXI
- DEF("dd", img_dd,
--    "dd [--image-opts] [-f fmt] [-O output_fmt] [bs=block_size] [count=blocks] [skip=blocks] if=input of=output")
-+    "dd [--image-opts] [-f fmt] [-O output_fmt] [bs=block_size] [count=blocks] [skip=blocks] [osize=output_size] [if=input] [of=output]")
- STEXI
--@item dd [--image-opts] [-f @var{fmt}] [-O @var{output_fmt}] [bs=@var{block_size}] [count=@var{blocks}] [skip=@var{blocks}] if=@var{input} of=@var{output}
-+@item dd [--image-opts] [-f @var{fmt}] [-O @var{output_fmt}] [bs=@var{block_size}] [count=@var{blocks}] [skip=@var{blocks}] [osize=output_size] [if=@var{input}] [of=@var{output}]
- ETEXI
- DEF("info", img_info,
-diff --git a/qemu-img.c b/qemu-img.c
-index c7804d63ee..ee7816e727 100644
---- a/qemu-img.c
-+++ b/qemu-img.c
-@@ -4026,10 +4026,12 @@ out:
- #define C_IF      04
- #define C_OF      010
- #define C_SKIP    020
-+#define C_OSIZE   040
- struct DdInfo {
-     unsigned int flags;
-     int64_t count;
-+    int64_t osize;
- };
- struct DdIo {
-@@ -4108,6 +4110,20 @@ static int img_dd_skip(const char *arg,
-     return 0;
- }
-+static int img_dd_osize(const char *arg,
-+                        struct DdIo *in, struct DdIo *out,
-+                        struct DdInfo *dd)
-+{
-+    dd->osize = cvtnum(arg);
-+
-+    if (dd->osize < 0) {
-+        error_report("invalid number: '%s'", arg);
-+        return 1;
-+    }
-+
-+    return 0;
-+}
-+
- static int img_dd(int argc, char **argv)
- {
-     int ret = 0;
-@@ -4147,6 +4163,7 @@ static int img_dd(int argc, char **argv)
-         { "if", img_dd_if, C_IF },
-         { "of", img_dd_of, C_OF },
-         { "skip", img_dd_skip, C_SKIP },
-+        { "osize", img_dd_osize, C_OSIZE },
-         { NULL, NULL, 0 }
-     };
-     const struct option long_options[] = {
-@@ -4214,84 +4231,106 @@ static int img_dd(int argc, char **argv)
-         arg = NULL;
-     }
--    if (!(dd.flags & C_IF && dd.flags & C_OF)) {
--        error_report("Must specify both input and output files");
-+    if (!(dd.flags & C_IF) && (!fmt || strcmp(fmt, "raw") != 0)) {
-+        error_report("Input format must be raw when readin from stdin");
-         ret = -1;
-         goto out;
-     }
--    blk1 = img_open(image_opts, in.filename, fmt, 0, false, false);
--
--    if (!blk1) {
-+    if (!(dd.flags & C_OF) && strcmp(out_fmt, "raw") != 0) {
-+        error_report("Output format must be raw when writing to stdout");
-         ret = -1;
-         goto out;
-     }
-+    if (dd.flags & C_IF) {
-+        blk1 = img_open(image_opts, in.filename, fmt, 0, false, false);
--    drv = bdrv_find_format(out_fmt);
--    if (!drv) {
--        error_report("Unknown file format");
--        ret = -1;
--        goto out;
--    }
--    proto_drv = bdrv_find_protocol(out.filename, true, &local_err);
--
--    if (!proto_drv) {
--        error_report_err(local_err);
--        ret = -1;
--        goto out;
--    }
--    if (!drv->create_opts) {
--        error_report("Format driver '%s' does not support image creation",
--                     drv->format_name);
--        ret = -1;
--        goto out;
--    }
--    if (!proto_drv->create_opts) {
--        error_report("Protocol driver '%s' does not support image creation",
--                     proto_drv->format_name);
--        ret = -1;
--        goto out;
-+        if (!blk1) {
-+            ret = -1;
-+            goto out;
-+        }
-     }
--    create_opts = qemu_opts_append(create_opts, drv->create_opts);
--    create_opts = qemu_opts_append(create_opts, proto_drv->create_opts);
--
--    opts = qemu_opts_create(create_opts, NULL, 0, &error_abort);
--    size = blk_getlength(blk1);
--    if (size < 0) {
--        error_report("Failed to get size for '%s'", in.filename);
-+    if (dd.flags & C_OSIZE) {
-+        size = dd.osize;
-+    } else if (dd.flags & C_IF) {
-+        size = blk_getlength(blk1);
-+        if (size < 0) {
-+            error_report("Failed to get size for '%s'", in.filename);
-+            ret = -1;
-+            goto out;
-+        }
-+    } else if (dd.flags & C_COUNT) {
-+        size = dd.count * in.bsz;
-+    } else {
-+        error_report("Output size must be known when reading from stdin");
-         ret = -1;
-         goto out;
-     }
--    if (dd.flags & C_COUNT && dd.count <= INT64_MAX / in.bsz &&
-+    if (!(dd.flags & C_OSIZE) && dd.flags & C_COUNT && dd.count <= INT64_MAX / in.bsz &&
-         dd.count * in.bsz < size) {
-         size = dd.count * in.bsz;
-     }
--    /* Overflow means the specified offset is beyond input image's size */
--    if (dd.flags & C_SKIP && (in.offset > INT64_MAX / in.bsz ||
--                              size < in.bsz * in.offset)) {
--        qemu_opt_set_number(opts, BLOCK_OPT_SIZE, 0, &error_abort);
--    } else {
--        qemu_opt_set_number(opts, BLOCK_OPT_SIZE,
--                            size - in.bsz * in.offset, &error_abort);
--    }
--    ret = bdrv_create(drv, out.filename, opts, &local_err);
--    if (ret < 0) {
--        error_reportf_err(local_err,
--                          "%s: error while creating output image: ",
--                          out.filename);
--        ret = -1;
--        goto out;
--    }
-+    if (dd.flags & C_OF) {
-+        drv = bdrv_find_format(out_fmt);
-+        if (!drv) {
-+            error_report("Unknown file format");
-+            ret = -1;
-+            goto out;
-+        }
-+        proto_drv = bdrv_find_protocol(out.filename, true, &local_err);
-+
-+        if (!proto_drv) {
-+            error_report_err(local_err);
-+            ret = -1;
-+            goto out;
-+        }
-+        if (!drv->create_opts) {
-+            error_report("Format driver '%s' does not support image creation",
-+                         drv->format_name);
-+            ret = -1;
-+            goto out;
-+        }
-+        if (!proto_drv->create_opts) {
-+            error_report("Protocol driver '%s' does not support image creation",
-+                         proto_drv->format_name);
-+            ret = -1;
-+            goto out;
-+        }
-+        create_opts = qemu_opts_append(create_opts, drv->create_opts);
-+        create_opts = qemu_opts_append(create_opts, proto_drv->create_opts);
--    blk2 = img_open(image_opts, out.filename, out_fmt, BDRV_O_RDWR,
--                    false, false);
-+        opts = qemu_opts_create(create_opts, NULL, 0, &error_abort);
--    if (!blk2) {
--        ret = -1;
--        goto out;
-+        /* Overflow means the specified offset is beyond input image's size */
-+        if (dd.flags & C_OSIZE) {
-+            qemu_opt_set_number(opts, BLOCK_OPT_SIZE, size, &error_abort);
-+        } else if (dd.flags & C_SKIP && (in.offset > INT64_MAX / in.bsz ||
-+                                         size < in.bsz * in.offset)) {
-+            qemu_opt_set_number(opts, BLOCK_OPT_SIZE, 0, &error_abort);
-+        } else {
-+            qemu_opt_set_number(opts, BLOCK_OPT_SIZE,
-+                                size - in.bsz * in.offset, &error_abort);
-+        }
-+
-+        ret = bdrv_create(drv, out.filename, opts, &local_err);
-+        if (ret < 0) {
-+            error_reportf_err(local_err,
-+                              "%s: error while creating output image: ",
-+                              out.filename);
-+            ret = -1;
-+            goto out;
-+        }
-+
-+        blk2 = img_open(image_opts, out.filename, out_fmt, BDRV_O_RDWR,
-+                        false, false);
-+
-+        if (!blk2) {
-+            ret = -1;
-+            goto out;
-+        }
-     }
-     if (dd.flags & C_SKIP && (in.offset > INT64_MAX / in.bsz ||
-@@ -4309,11 +4348,18 @@ static int img_dd(int argc, char **argv)
-     for (out_pos = 0; in_pos < size; block_count++) {
-         int in_ret, out_ret;
-+        size_t in_bsz = in_pos + in.bsz > size ? size - in_pos : in.bsz;
--        if (in_pos + in.bsz > size) {
--            in_ret = blk_pread(blk1, in_pos, in.buf, size - in_pos);
-+        if (blk1) {
-+            in_ret = blk_pread(blk1, in_pos, in.buf, in_bsz);
-         } else {
--            in_ret = blk_pread(blk1, in_pos, in.buf, in.bsz);
-+            in_ret = read(STDIN_FILENO, in.buf, in_bsz);
-+            if (in_ret == 0) {
-+                /* early EOF is considered an error */
-+                error_report("Input ended unexpectedly");
-+                ret = -1;
-+                goto out;
-+            }
-         }
-         if (in_ret < 0) {
-             error_report("error while reading from input image file: %s",
-@@ -4323,9 +4369,13 @@ static int img_dd(int argc, char **argv)
-         }
-         in_pos += in_ret;
--        out_ret = blk_pwrite(blk2, out_pos, in.buf, in_ret, 0);
-+        if (blk2) {
-+            out_ret = blk_pwrite(blk2, out_pos, in.buf, in_ret, 0);
-+        } else {
-+            out_ret = write(STDOUT_FILENO, in.buf, in_ret);
-+        }
--        if (out_ret < 0) {
-+        if (out_ret != in_ret) {
-             error_report("error while writing to output image file: %s",
-                          strerror(-out_ret));
-             ret = -1;
--- 
-2.11.0
-
diff --git a/debian/patches/pve/0026-backup-introduce-vma-archive-format.patch b/debian/patches/pve/0026-backup-introduce-vma-archive-format.patch
new file mode 100644 (file)
index 0000000..3f9d588
--- /dev/null
@@ -0,0 +1,1445 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Wolfgang Bumiller <w.bumiller@proxmox.com>
+Date: Wed, 2 Aug 2017 13:51:02 +0200
+Subject: [PATCH] backup: introduce vma archive format
+
+---
+ MAINTAINERS          |   6 +
+ block/Makefile.objs  |   3 +
+ block/vma.c          | 424 ++++++++++++++++++++++++++++++++++++++++
+ blockdev.c           | 536 +++++++++++++++++++++++++++++++++++++++++++++++++++
+ blockjob.c           |   3 +-
+ configure            |  30 +++
+ hmp-commands-info.hx |  13 ++
+ hmp-commands.hx      |  31 +++
+ hmp.c                |  63 ++++++
+ hmp.h                |   3 +
+ qapi/block-core.json | 109 ++++++++++-
+ 11 files changed, 1219 insertions(+), 2 deletions(-)
+ create mode 100644 block/vma.c
+
+diff --git a/MAINTAINERS b/MAINTAINERS
+index 0255113470..581d80d144 100644
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -1950,6 +1950,12 @@ L: qemu-block@nongnu.org
+ S: Supported
+ F: block/vvfat.c
++VMA
++M: Wolfgang Bumiller <w.bumiller@proxmox.com>.
++L: pve-devel@proxmox.com
++S: Supported
++F: block/vma.c
++
+ Image format fuzzer
+ M: Stefan Hajnoczi <stefanha@redhat.com>
+ L: qemu-block@nongnu.org
+diff --git a/block/Makefile.objs b/block/Makefile.objs
+index 823e60cda6..d74140e413 100644
+--- a/block/Makefile.objs
++++ b/block/Makefile.objs
+@@ -22,6 +22,7 @@ block-obj-$(CONFIG_RBD) += rbd.o
+ block-obj-$(CONFIG_GLUSTERFS) += gluster.o
+ block-obj-$(CONFIG_VXHS) += vxhs.o
+ block-obj-$(CONFIG_LIBSSH2) += ssh.o
++block-obj-$(CONFIG_VMA) += vma.o
+ block-obj-y += accounting.o dirty-bitmap.o
+ block-obj-y += write-threshold.o
+ block-obj-y += backup.o
+@@ -48,3 +49,5 @@ block-obj-$(if $(CONFIG_BZIP2),m,n) += dmg-bz2.o
+ dmg-bz2.o-libs     := $(BZIP2_LIBS)
+ qcow.o-libs        := -lz
+ linux-aio.o-libs   := -laio
++vma.o-cflags       := $(VMA_CFLAGS)
++vma.o-libs         := $(VMA_LIBS)
+diff --git a/block/vma.c b/block/vma.c
+new file mode 100644
+index 0000000000..7151514f94
+--- /dev/null
++++ b/block/vma.c
+@@ -0,0 +1,424 @@
++/*
++ * VMA archive backend for QEMU, container object
++ *
++ * Copyright (C) 2017 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 <vma/vma.h>
++
++#include "qemu/osdep.h"
++#include "qemu/uuid.h"
++#include "qemu-common.h"
++#include "qapi/error.h"
++#include "qapi/qmp/qerror.h"
++#include "qapi/qmp/qstring.h"
++#include "qom/object.h"
++#include "qom/object_interfaces.h"
++#include "block/block_int.h"
++
++/* exported interface */
++void vma_object_add_config_file(Object *obj, const char *name,
++                                const char *contents, size_t len,
++                                Error **errp);
++
++#define TYPE_VMA_OBJECT "vma"
++#define VMA_OBJECT(obj) \
++    OBJECT_CHECK(VMAObjectState, (obj), TYPE_VMA_OBJECT)
++#define VMA_OBJECT_GET_CLASS(obj) \
++    OBJECT_GET_CLASS(VMAObjectClass, (obj), TYPE_VMA_OBJECT)
++
++typedef struct VMAObjectClass {
++    ObjectClass parent_class;
++} VMAObjectClass;
++
++typedef struct VMAObjectState {
++    Object parent;
++
++    char        *filename;
++
++    QemuUUID     uuid;
++    bool         blocked;
++    VMAWriter   *vma;
++    QemuMutex    mutex;
++} VMAObjectState;
++
++static VMAObjectState *vma_by_id(const char *name)
++{
++    Object *container;
++    Object *obj;
++
++    container = object_get_objects_root();
++    obj = object_resolve_path_component(container, name);
++
++    return VMA_OBJECT(obj);
++}
++
++static void vma_object_class_complete(UserCreatable *uc, Error **errp)
++{
++    int rc;
++    VMAObjectState *vo = VMA_OBJECT(uc);
++    VMAObjectClass *voc = VMA_OBJECT_GET_CLASS(uc);
++    (void)!vo;
++    (void)!voc;
++
++    if (!vo->filename) {
++        error_setg(errp, "Parameter 'filename' is required");
++        return;
++    }
++
++    rc = VMAWriter_fopen(vo->filename, &vo->vma);
++    if (rc < 0) {
++        error_setg_errno(errp, -rc, "failed to create VMA archive");
++        return;
++    }
++
++    rc = VMAWriter_set_uuid(vo->vma, vo->uuid.data, sizeof(vo->uuid.data));
++    if (rc < 0) {
++        error_setg_errno(errp, -rc, "failed to set UUID of VMA archive");
++        return;
++    }
++
++    qemu_mutex_init(&vo->mutex);
++}
++
++static bool vma_object_can_be_deleted(UserCreatable *uc, Error **errp)
++{
++    //VMAObjectState *vo = VMA_OBJECT(uc);
++    //if (!vo->vma) {
++    //    return true;
++    //}
++    //return false;
++    return true;
++}
++
++static void vma_object_class_init(ObjectClass *oc, void *data)
++{
++    UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
++
++    ucc->can_be_deleted = vma_object_can_be_deleted;
++    ucc->complete = vma_object_class_complete;
++}
++
++static char *vma_object_get_filename(Object *obj, Error **errp)
++{
++    VMAObjectState *vo = VMA_OBJECT(obj);
++
++    return g_strdup(vo->filename);
++}
++
++static void vma_object_set_filename(Object *obj, const char *str, Error **errp)
++{
++    VMAObjectState *vo = VMA_OBJECT(obj);
++
++    if (vo->vma) {
++        error_setg(errp, "filename cannot be changed after creation");
++        return;
++    }
++
++    g_free(vo->filename);
++    vo->filename = g_strdup(str);
++}
++
++static char *vma_object_get_uuid(Object *obj, Error **errp)
++{
++    VMAObjectState *vo = VMA_OBJECT(obj);
++
++    return qemu_uuid_unparse_strdup(&vo->uuid);
++}
++
++static void vma_object_set_uuid(Object *obj, const char *str, Error **errp)
++{
++    VMAObjectState *vo = VMA_OBJECT(obj);
++
++    if (vo->vma) {
++        error_setg(errp, "uuid cannot be changed after creation");
++        return;
++    }
++
++    qemu_uuid_parse(str, &vo->uuid);
++}
++
++static bool vma_object_get_blocked(Object *obj, Error **errp)
++{
++    VMAObjectState *vo = VMA_OBJECT(obj);
++
++    return vo->blocked;
++}
++
++static void vma_object_set_blocked(Object *obj, bool blocked, Error **errp)
++{
++    VMAObjectState *vo = VMA_OBJECT(obj);
++
++    (void)errp;
++
++    vo->blocked = blocked;
++}
++
++void vma_object_add_config_file(Object *obj, const char *name,
++                                const char *contents, size_t len,
++                                Error **errp)
++{
++    int rc;
++    VMAObjectState *vo = VMA_OBJECT(obj);
++
++    if (!vo || !vo->vma) {
++        error_setg(errp, "not a valid vma object to add config files to");
++        return;
++    }
++
++    rc = VMAWriter_addConfigFile(vo->vma, name, contents, len);
++    if (rc < 0) {
++        error_setg_errno(errp, -rc, "failed to add config file to VMA");
++        return;
++    }
++}
++
++static void vma_object_init(Object *obj)
++{
++    VMAObjectState *vo = VMA_OBJECT(obj);
++    (void)!vo;
++
++    object_property_add_str(obj, "filename",
++                            vma_object_get_filename, vma_object_set_filename,
++                            NULL);
++    object_property_add_str(obj, "uuid",
++                            vma_object_get_uuid, vma_object_set_uuid,
++                            NULL);
++    object_property_add_bool(obj, "blocked",
++                            vma_object_get_blocked, vma_object_set_blocked,
++                            NULL);
++}
++
++static void vma_object_finalize(Object *obj)
++{
++    VMAObjectState *vo = VMA_OBJECT(obj);
++    VMAObjectClass *voc = VMA_OBJECT_GET_CLASS(obj);
++    (void)!voc;
++
++    qemu_mutex_destroy(&vo->mutex);
++
++    VMAWriter_destroy(vo->vma, true);
++    g_free(vo->filename);
++}
++
++static const TypeInfo vma_object_info = {
++    .name = TYPE_VMA_OBJECT,
++    .parent = TYPE_OBJECT,
++    .class_size = sizeof(VMAObjectClass),
++    .class_init = vma_object_class_init,
++    .instance_size = sizeof(VMAObjectState),
++    .instance_init = vma_object_init,
++    .instance_finalize = vma_object_finalize,
++    .interfaces = (InterfaceInfo[]) {
++        { TYPE_USER_CREATABLE },
++        { }
++    }
++};
++
++static void register_types(void)
++{
++    type_register_static(&vma_object_info);
++}
++
++type_init(register_types);
++
++typedef struct {
++    VMAObjectState *vma_obj;
++    char           *name;
++    size_t          device_id;
++    uint64_t        byte_size;
++} BDRVVMAState;
++
++static void qemu_vma_parse_filename(const char *filename, QDict *options,
++                                    Error **errp)
++{
++    char *sep;
++
++    sep = strchr(filename, '/');
++    if (!sep || sep == filename) {
++        error_setg(errp, "VMA filename should be <vma-object>/<device-name>");
++        return;
++    }
++
++    qdict_put(options, "vma", qstring_from_substr(filename, 0, sep-filename-1));
++
++    while (*sep && *sep == '/')
++        ++sep;
++    if (!*sep) {
++        error_setg(errp, "missing device name\n");
++        return;
++    }
++
++    qdict_put(options, "name", qstring_from_str(sep));
++}
++
++static QemuOptsList runtime_opts = {
++    .name = "vma-drive",
++    .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
++    .desc = {
++        {
++            .name = "vma",
++            .type = QEMU_OPT_STRING,
++            .help = "VMA Object name",
++        },
++        {
++            .name = "name",
++            .type = QEMU_OPT_STRING,
++            .help = "VMA device name",
++        },
++        {
++            .name = BLOCK_OPT_SIZE,
++            .type = QEMU_OPT_SIZE,
++            .help = "Virtual disk size"
++        },
++        { /* end of list */ }
++    },
++};
++static int qemu_vma_open(BlockDriverState *bs, QDict *options, int flags,
++                         Error **errp)
++{
++    Error *local_err = NULL;
++    BDRVVMAState *s = bs->opaque;
++    QemuOpts *opts;
++    const char *vma_id, *device_name;
++    ssize_t dev_id;
++    int64_t bytes = 0;
++    int ret;
++
++    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 failed_opts;
++    }
++
++    bytes = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
++                     BDRV_SECTOR_SIZE);
++
++    vma_id = qemu_opt_get(opts, "vma");
++    device_name = qemu_opt_get(opts, "name");
++
++    VMAObjectState *vma = vma_by_id(vma_id);
++    if (!vma) {
++        ret = -EINVAL;
++        error_setg(errp, "no such VMA object: %s", vma_id);
++        goto failed_opts;
++    }
++
++    dev_id = VMAWriter_findDevice(vma->vma, device_name);
++    if (dev_id >= 0) {
++        error_setg(errp, "drive already exists in VMA object");
++        ret = -EIO;
++        goto failed_opts;
++    }
++
++    dev_id = VMAWriter_addDevice(vma->vma, device_name, (uint64_t)bytes);
++    if (dev_id < 0) {
++        error_setg_errno(errp, -dev_id, "failed to add VMA device");
++        ret = -EIO;
++        goto failed_opts;
++    }
++
++    object_ref(OBJECT(vma));
++    s->vma_obj = vma;
++    s->name = g_strdup(device_name);
++    s->device_id = (size_t)dev_id;
++    s->byte_size = bytes;
++
++    ret = 0;
++
++failed_opts:
++    qemu_opts_del(opts);
++    return ret;
++}
++
++static void qemu_vma_close(BlockDriverState *bs)
++{
++    BDRVVMAState *s = bs->opaque;
++
++    (void)VMAWriter_finishDevice(s->vma_obj->vma, s->device_id);
++    object_unref(OBJECT(s->vma_obj));
++
++    g_free(s->name);
++}
++
++static int64_t qemu_vma_getlength(BlockDriverState *bs)
++{
++    BDRVVMAState *s = bs->opaque;
++
++    return s->byte_size;
++}
++
++static coroutine_fn int qemu_vma_co_writev(BlockDriverState *bs,
++                                           int64_t sector_num,
++                                           int nb_sectors,
++                                           QEMUIOVector *qiov)
++{
++    size_t i;
++    ssize_t rc;
++    BDRVVMAState *s = bs->opaque;
++    VMAObjectState *vo = s->vma_obj;
++    off_t offset = sector_num * BDRV_SECTOR_SIZE;
++
++    qemu_mutex_lock(&vo->mutex);
++    if (vo->blocked) {
++        return -EPERM;
++    }
++    for (i = 0; i != qiov->niov; ++i) {
++        const struct iovec *v = &qiov->iov[i];
++        size_t blocks = v->iov_len / VMA_BLOCK_SIZE;
++        if (blocks * VMA_BLOCK_SIZE != v->iov_len) {
++            return -EIO;
++        }
++        rc = VMAWriter_writeBlocks(vo->vma, s->device_id,
++                                   v->iov_base, blocks, offset);
++        if (errno) {
++            return -errno;
++        }
++        if (rc != blocks) {
++            return -EIO;
++        }
++        offset += v->iov_len;
++    }
++    qemu_mutex_unlock(&vo->mutex);
++    return 0;
++}
++
++static int qemu_vma_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
++{
++    bdi->cluster_size = VMA_CLUSTER_SIZE;
++    bdi->unallocated_blocks_are_zero = true;
++    bdi->can_write_zeroes_with_unmap = false;
++    return 0;
++}
++
++static BlockDriver bdrv_vma_drive = {
++    .format_name                  = "vma-drive",
++    .instance_size                = sizeof(BDRVVMAState),
++
++#if 0
++    .bdrv_create                  = qemu_vma_create,
++    .create_opts                  = &qemu_vma_create_opts,
++#endif
++
++    .bdrv_parse_filename          = qemu_vma_parse_filename,
++    .bdrv_file_open               = qemu_vma_open,
++
++    .bdrv_close                   = qemu_vma_close,
++    .bdrv_has_zero_init           = bdrv_has_zero_init_1,
++    .bdrv_getlength               = qemu_vma_getlength,
++    .bdrv_get_info                = qemu_vma_get_info,
++
++    .bdrv_co_writev               = qemu_vma_co_writev,
++};
++
++static void bdrv_vma_init(void)
++{
++    bdrv_register(&bdrv_vma_drive);
++}
++
++block_init(bdrv_vma_init);
+diff --git a/blockdev.c b/blockdev.c
+index a9ed9034b5..3ffd064c48 100644
+--- a/blockdev.c
++++ b/blockdev.c
+@@ -31,10 +31,12 @@
+  */
+ #include "qemu/osdep.h"
++#include "qemu/uuid.h"
+ #include "sysemu/block-backend.h"
+ #include "sysemu/blockdev.h"
+ #include "hw/block/block.h"
+ #include "block/blockjob.h"
++#include "block/blockjob_int.h"
+ #include "block/throttle-groups.h"
+ #include "monitor/monitor.h"
+ #include "qemu/error-report.h"
+@@ -2963,6 +2965,540 @@ out:
+     aio_context_release(aio_context);
+ }
++/* PVE backup related function */
++
++static struct PVEBackupState {
++    Error *error;
++    bool cancel;
++    QemuUUID uuid;
++    char uuid_str[37];
++    int64_t speed;
++    time_t start_time;
++    time_t end_time;
++    char *backup_file;
++    Object *vmaobj;
++    GList *di_list;
++    size_t next_job;
++    size_t total;
++    size_t transferred;
++    size_t zero_bytes;
++    QemuMutex backup_mutex;
++    bool      backup_mutex_initialized;
++} backup_state;
++
++typedef struct PVEBackupDevInfo {
++    BlockDriverState *bs;
++    size_t size;
++    uint8_t dev_id;
++    bool completed;
++    char targetfile[PATH_MAX];
++    BlockDriverState *target;
++} PVEBackupDevInfo;
++
++static void pvebackup_run_next_job(void);
++
++static void pvebackup_cleanup(void)
++{
++    qemu_mutex_lock(&backup_state.backup_mutex);
++    // Avoid race between block jobs and backup-cancel command:
++    if (!backup_state.vmaw) {
++        qemu_mutex_unlock(&backup_state.backup_mutex);
++        return;
++    }
++
++    backup_state.end_time = time(NULL);
++
++    if (backup_state.vmaobj) {
++        object_unparent(backup_state.vmaobj);
++        backup_state.vmaobj = NULL;
++    }
++
++    g_list_free(backup_state.di_list);
++    backup_state.di_list = NULL;
++    qemu_mutex_unlock(&backup_state.backup_mutex);
++}
++
++static void pvebackup_complete_cb(void *opaque, int ret)
++{
++    // This always runs in the main loop
++
++    PVEBackupDevInfo *di = opaque;
++
++    di->completed = true;
++
++    if (ret < 0 && !backup_state.error) {
++        error_setg(&backup_state.error, "job failed with err %d - %s",
++                   ret, strerror(-ret));
++    }
++
++    di->bs = NULL;
++    di->target = NULL;
++
++    if (backup_state.vmaobj) {
++        object_unparent(backup_state.vmaobj);
++        backup_state.vmaobj = NULL;
++    }
++
++    // remove self from job queue
++    qemu_mutex_lock(&backup_state.backup_mutex);
++    backup_state.di_list = g_list_remove(backup_state.di_list, di);
++    g_free(di);
++    qemu_mutex_unlock(&backup_state.backup_mutex);
++
++    if (!backup_state.cancel) {
++        pvebackup_run_next_job();
++    }
++}
++
++static void pvebackup_cancel(void *opaque)
++{
++    backup_state.cancel = true;
++    qemu_mutex_lock(&backup_state.backup_mutex);
++    // Avoid race between block jobs and backup-cancel command:
++    if (!backup_state.vmaw) {
++        qemu_mutex_unlock(&backup_state.backup_mutex);
++        return;
++    }
++
++    if (!backup_state.error) {
++        error_setg(&backup_state.error, "backup cancelled");
++    }
++
++    if (backup_state.vmaobj) {
++        Error *err;
++        /* make sure vma writer does not block anymore */
++        if (!object_set_props(backup_state.vmaobj, &err, "blocked", "yes", NULL)) {
++            if (err) {
++                error_report_err(err);
++            }
++        }
++    }
++
++    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) {
++                AioContext *aio_context = blk_get_aio_context(job->blk);
++                aio_context_acquire(aio_context);
++                if (!di->completed) {
++                    block_job_cancel(job);
++                }
++                aio_context_release(aio_context);
++            }
++        }
++    }
++
++    qemu_mutex_unlock(&backup_state.backup_mutex);
++    pvebackup_cleanup();
++}
++
++void qmp_backup_cancel(Error **errp)
++{
++    if (!backup_state.backup_mutex_initialized)
++        return;
++    Coroutine *co = qemu_coroutine_create(pvebackup_cancel, NULL);
++    qemu_coroutine_enter(co);
++
++    while (backup_state.vmaobj) {
++        /* FIXME: Find something better for this */
++        aio_poll(qemu_get_aio_context(), true);
++    }
++}
++
++void vma_object_add_config_file(Object *obj, const char *name, 
++                                const char *contents, size_t len,
++                                Error **errp);
++static int config_to_vma(const char *file, BackupFormat format,
++                         Object *vmaobj,
++                         const char *backup_dir,
++                         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) {
++        vma_object_add_config_file(vmaobj, basename, cdata, clen, errp);
++    } 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;
++}
++
++void block_job_resume(BlockJob *job);
++static void pvebackup_run_next_job(void)
++{
++    qemu_mutex_lock(&backup_state.backup_mutex);
++
++    GList *next = g_list_nth(backup_state.di_list, backup_state.next_job);
++    while (next) {
++        PVEBackupDevInfo *di = (PVEBackupDevInfo *)next->data;
++        backup_state.next_job++;
++        if (!di->completed && di->bs && di->bs->job) {
++            BlockJob *job = di->bs->job;
++            AioContext *aio_context = blk_get_aio_context(job->blk);
++            aio_context_acquire(aio_context);
++            qemu_mutex_unlock(&backup_state.backup_mutex);
++            if (backup_state.error || backup_state.cancel) {
++                block_job_cancel_sync(job);
++            } else {
++                block_job_resume(job);
++            }
++            aio_context_release(aio_context);
++            return;
++        }
++        next = g_list_next(next);
++    }
++    qemu_mutex_unlock(&backup_state.backup_mutex);
++
++    // no more jobs, run the cleanup
++    pvebackup_cleanup();
++}
++
++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)
++{
++    BlockBackend *blk;
++    BlockDriverState *bs = NULL;
++    const char *backup_dir = NULL;
++    Error *local_err = NULL;
++    QemuUUID uuid;
++    gchar **devs = NULL;
++    GList *di_list = NULL;
++    GList *l;
++    UuidInfo *uuid_info;
++    BlockJob *job;
++
++    if (!backup_state.backup_mutex_initialized) {
++        qemu_mutex_init(&backup_state.backup_mutex);
++        backup_state.backup_mutex_initialized = true;
++    }
++
++    if (backup_state.di_list || backup_state.vmaobj) {
++        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 (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;
++    }
++
++    qemu_uuid_generate(&uuid);
++
++    if (format == BACKUP_FORMAT_VMA) {
++        char uuidstr[UUID_FMT_LEN+1];
++        qemu_uuid_unparse(&uuid, uuidstr);
++        uuidstr[UUID_FMT_LEN] = 0;
++        backup_state.vmaobj =
++            object_new_with_props("vma", object_get_objects_root(),
++                                  "vma-backup-obj", &local_err,
++                                  "filename", backup_file,
++                                  "uuid", uuidstr,
++                                  NULL);
++        if (!backup_state.vmaobj) {
++            if (local_err) {
++                error_propagate(errp, local_err);
++            }
++            goto err;
++        }
++
++        l = di_list;
++        while (l) {
++            QDict *options = qdict_new();
++
++            PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data;
++            l = g_list_next(l);
++
++            const char *devname = bdrv_get_device_name(di->bs);
++            snprintf(di->targetfile, PATH_MAX, "vma-backup-obj/%s.raw", devname);
++
++            qdict_put(options, "driver", qstring_from_str("vma-drive"));
++            qdict_put(options, "size", qint_from_int(di->size));
++            di->target = bdrv_open(di->targetfile, NULL, options, BDRV_O_RDWR, &local_err);
++            if (!di->target) {
++                error_propagate(errp, local_err);
++                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, false, &local_err);
++            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 */
++    if (has_config_file) {
++        if(config_to_vma(config_file, format, backup_state.vmaobj, backup_dir, errp) != 0) {
++            goto err;
++        }
++    }
++
++    /* add firewall file to archive */
++    if (has_firewall_file) {
++        if(config_to_vma(firewall_file, format, backup_state.vmaobj, backup_dir, errp) != 0) {
++            goto err;
++        }
++    }
++    /* 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);
++
++    memcpy(&backup_state.uuid, &uuid, sizeof(uuid));
++    qemu_uuid_unparse(&uuid, backup_state.uuid_str);
++
++    qemu_mutex_lock(&backup_state.backup_mutex);
++    backup_state.di_list = di_list;
++    backup_state.next_job = 0;
++
++    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);
++
++        job = backup_job_create(NULL, di->bs, di->target, speed, MIRROR_SYNC_MODE_FULL, NULL,
++                                false, BLOCKDEV_ON_ERROR_REPORT, BLOCKDEV_ON_ERROR_REPORT,
++                                BLOCK_JOB_DEFAULT,
++                                pvebackup_complete_cb, di, 2, NULL, &local_err);
++        if (di->target) {
++            bdrv_unref(di->target);
++            di->target = NULL;
++        }
++        if (!job || local_err != NULL) {
++            error_setg(&backup_state.error, "backup_job_create failed");
++            pvebackup_cancel(NULL);
++        } else {
++            block_job_start(job);
++        }
++    }
++
++    qemu_mutex_unlock(&backup_state.backup_mutex);
++
++    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) {
++        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);
++
++    if (devs) {
++        g_strfreev(devs);
++    }
++
++    if (backup_state.vmaobj) {
++        object_unparent(backup_state.vmaobj);
++        backup_state.vmaobj = NULL;
++    }
++
++    if (backup_dir) {
++        rmdir(backup_dir);
++    }
++
++    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_base_node, const char *base_node,
+diff --git a/blockjob.c b/blockjob.c
+index c1b6b6a810..2de9f8f4dd 100644
+--- a/blockjob.c
++++ b/blockjob.c
+@@ -149,7 +149,8 @@ static void block_job_pause(BlockJob *job)
+     job->pause_count++;
+ }
+-static void block_job_resume(BlockJob *job)
++void block_job_resume(BlockJob *job);
++void block_job_resume(BlockJob *job)
+ {
+     assert(job->pause_count > 0);
+     job->pause_count--;
+diff --git a/configure b/configure
+index 0c6e7572db..3a28a0a092 100755
+--- a/configure
++++ b/configure
+@@ -422,6 +422,7 @@ tcmalloc="no"
+ jemalloc="no"
+ replication="yes"
+ vxhs=""
++vma=""
+ supported_cpu="no"
+ supported_os="no"
+@@ -1313,6 +1314,10 @@ for opt do
+   ;;
+   --disable-git-update) git_update=no
+   ;;
++  --disable-vma) vma="no"
++  ;;
++  --enable-vma) vma="yes"
++  ;;
+   *)
+       echo "ERROR: unknown option $opt"
+       echo "Try '$0 --help' for more information"
+@@ -1561,6 +1566,7 @@ disabled with --disable-FEATURE, default is enabled if available:
+   crypto-afalg    Linux AF_ALG crypto backend driver
+   vhost-user      vhost-user support
+   capstone        capstone disassembler support
++  vma             VMA archive backend
+ NOTE: The object files are built at the place where configure is launched
+ EOF
+@@ -3890,6 +3896,23 @@ EOF
+ fi
+ ##########################################
++# vma probe
++if test "$vma" != "no" ; then
++  if $pkg_config --exact-version=0.1.0 vma; then
++    vma="yes"
++    vma_cflags=$($pkg_config --cflags vma)
++    vma_libs=$($pkg_config --libs vma)
++  else
++    if test "$vma" = "yes" ; then
++      feature_not_found "VMA Archive backend support" \
++          "Install libvma devel"
++    fi
++    vma="no"
++  fi
++fi
++
++
++##########################################
+ # signalfd probe
+ signalfd="no"
+ cat > $TMPC << EOF
+@@ -5555,6 +5578,7 @@ echo "avx2 optimization $avx2_opt"
+ echo "replication support $replication"
+ echo "VxHS block device $vxhs"
+ echo "capstone          $capstone"
++echo "VMA support       $vma"
+ if test "$sdl_too_old" = "yes"; then
+ echo "-> Your SDL version is too old - please upgrade to have SDL support"
+@@ -5998,6 +6022,12 @@ if test "$libusb" = "yes" ; then
+   echo "LIBUSB_LIBS=$libusb_libs" >> $config_host_mak
+ fi
++if test "$vma" = "yes" ; then
++  echo "CONFIG_VMA=y" >> $config_host_mak
++  echo "VMA_CFLAGS=$vma_cflags" >> $config_host_mak
++  echo "VMA_LIBS=$vma_libs" >> $config_host_mak
++fi
++
+ if test "$usb_redir" = "yes" ; then
+   echo "CONFIG_USB_REDIR=y" >> $config_host_mak
+   echo "USB_REDIR_CFLAGS=$usb_redir_cflags" >> $config_host_mak
+diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx
+index 3bf69a193c..7beeb29e99 100644
+--- a/hmp-commands-info.hx
++++ b/hmp-commands-info.hx
+@@ -493,6 +493,19 @@ STEXI
+ Show CPU statistics.
+ ETEXI
++    {
++        .name       = "backup",
++        .args_type  = "",
++        .params     = "",
++        .help       = "show backup status",
++        .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 b35bc6ab6c..9e50947845 100644
+--- a/hmp-commands.hx
++++ b/hmp-commands.hx
+@@ -87,6 +87,37 @@ STEXI
+ Copy data from a backing file into a block device.
+ ETEXI
++   {
++        .name       = "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.",
++        .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",
++        .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 b9ade681f0..241c2543ec 100644
+--- a/hmp.c
++++ b/hmp.c
+@@ -156,6 +156,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;
+@@ -1848,6 +1886,31 @@ 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;
++
++    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, dir ? BACKUP_FORMAT_DIR : BACKUP_FORMAT_VMA,
++               false, NULL, 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 45ada581b6..635b9b4218 100644
+--- a/hmp.h
++++ b/hmp.h
+@@ -31,6 +31,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);
+@@ -85,6 +86,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/block-core.json b/qapi/block-core.json
+index dd763dcf87..461fca9a3d 100644
+--- a/qapi/block-core.json
++++ b/qapi/block-core.json
+@@ -615,6 +615,97 @@
+ ##
++# @BackupStatus:
++#
++# Detailed backup status.
++#
++# @status: 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: error message (only returned if status is 'error')
++#
++# @total: total amount of bytes involved in the backup process
++#
++# @transferred: amount of bytes already backed up.
++#
++# @zero-bytes: amount of 'zero' bytes detected.
++#
++# @start-time: time (epoch) when backup job started.
++#
++# @end-time: time (epoch) when backup job finished.
++#
++# @backup-file: backup file name
++#
++# @uuid: 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', 'dir' ] }
++
++##
++# @backup:
++#
++# Starts a VM backup.
++#
++# @backup-file: the backup file name
++#
++# @format: format of the backup file
++#
++# @config-file: a configuration file to include into
++# the backup archive.
++#
++# @speed: the maximum speed, in bytes per second
++#
++# @devlist: 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',
++                                    '*firewall-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' }
++
++##
+ # @BlockDeviceTimedStats:
+ #
+ # Statistics of a block device during a given interval of time.
+@@ -2239,7 +2330,7 @@
+             'host_device', 'http', 'https', 'iscsi', 'luks', 'nbd', 'nfs',
+             'null-aio', 'null-co', 'parallels', 'qcow', 'qcow2', 'qed',
+             'quorum', 'raw', 'rbd', 'replication', 'sheepdog', 'ssh',
+-            'throttle', 'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat', 'vxhs' ] }
++            'throttle', 'vdi', 'vhdx', 'vma-drive', 'vmdk', 'vpc', 'vvfat', 'vxhs' ] }
+ ##
+ # @BlockdevOptionsFile:
+@@ -3116,6 +3207,21 @@
+             '*tls-creds': 'str' } }
+ ##
++# @BlockdevOptionsVMADrive:
++#
++# Driver specific block device options for VMA Drives
++#
++# @filename: vma-drive path
++#
++# @size: drive size in bytes
++#
++# Since: 2.9
++##
++{ 'struct': 'BlockdevOptionsVMADrive',
++  'data': { 'filename': 'str',
++            'size': 'int' } }
++
++##
+ # @BlockdevOptionsThrottle:
+ #
+ # Driver specific block device options for the throttle driver
+@@ -3196,6 +3302,7 @@
+       'throttle':   'BlockdevOptionsThrottle',
+       'vdi':        'BlockdevOptionsGenericFormat',
+       'vhdx':       'BlockdevOptionsGenericFormat',
++      'vma-drive':  'BlockdevOptionsVMADrive',
+       'vmdk':       'BlockdevOptionsGenericCOWFormat',
+       'vpc':        'BlockdevOptionsGenericFormat',
+       'vvfat':      'BlockdevOptionsVVFAT',
+-- 
+2.11.0
+
diff --git a/debian/patches/pve/0026-backup-modify-job-api.patch b/debian/patches/pve/0026-backup-modify-job-api.patch
deleted file mode 100644 (file)
index 3b73e9f..0000000
+++ /dev/null
@@ -1,100 +0,0 @@
-From 0000000000000000000000000000000000000000 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] backup: modify job api
-
-Introduce a pause_count parameter to start a backup in
-paused mode. This way backups of multiple drives can be
-started up sequentially via the completion callback while
-having been started at the same point in time.
----
- block/backup.c            | 2 ++
- block/replication.c       | 2 +-
- blockdev.c                | 4 ++--
- blockjob.c                | 2 +-
- include/block/block_int.h | 1 +
- 5 files changed, 7 insertions(+), 4 deletions(-)
-
-diff --git a/block/backup.c b/block/backup.c
-index a4fb2884f9..1ede70c061 100644
---- a/block/backup.c
-+++ b/block/backup.c
-@@ -558,6 +558,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
-                   BlockdevOnError on_target_error,
-                   int creation_flags,
-                   BlockCompletionFunc *cb, void *opaque,
-+                  int pause_count,
-                   BlockJobTxn *txn, Error **errp)
- {
-     int64_t len;
-@@ -682,6 +683,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
-     block_job_add_bdrv(&job->common, "target", target, 0, BLK_PERM_ALL,
-                        &error_abort);
-     job->common.len = len;
-+    job->common.pause_count = pause_count;
-     block_job_txn_add_job(txn, &job->common);
-     return &job->common;
-diff --git a/block/replication.c b/block/replication.c
-index bf3c395eb4..1c41d9e6bf 100644
---- a/block/replication.c
-+++ b/block/replication.c
-@@ -531,7 +531,7 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode,
-                                 0, MIRROR_SYNC_MODE_NONE, NULL, false,
-                                 BLOCKDEV_ON_ERROR_REPORT,
-                                 BLOCKDEV_ON_ERROR_REPORT, BLOCK_JOB_INTERNAL,
--                                backup_job_completed, bs, NULL, &local_err);
-+                                backup_job_completed, bs, 0, NULL, &local_err);
-         if (local_err) {
-             error_propagate(errp, local_err);
-             backup_job_cleanup(bs);
-diff --git a/blockdev.c b/blockdev.c
-index e8a9a65167..9b6cfafd33 100644
---- a/blockdev.c
-+++ b/blockdev.c
-@@ -3262,7 +3262,7 @@ static BlockJob *do_drive_backup(DriveBackup *backup, BlockJobTxn *txn,
-     job = backup_job_create(backup->job_id, bs, target_bs, backup->speed,
-                             backup->sync, bmap, backup->compress,
-                             backup->on_source_error, backup->on_target_error,
--                            BLOCK_JOB_DEFAULT, NULL, NULL, txn, &local_err);
-+                            BLOCK_JOB_DEFAULT, NULL, NULL, 0, txn, &local_err);
-     bdrv_unref(target_bs);
-     if (local_err != NULL) {
-         error_propagate(errp, local_err);
-@@ -3341,7 +3341,7 @@ BlockJob *do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn,
-     job = backup_job_create(backup->job_id, bs, target_bs, backup->speed,
-                             backup->sync, NULL, backup->compress,
-                             backup->on_source_error, backup->on_target_error,
--                            BLOCK_JOB_DEFAULT, NULL, NULL, txn, &local_err);
-+                            BLOCK_JOB_DEFAULT, NULL, NULL, 0, txn, &local_err);
-     if (local_err != NULL) {
-         error_propagate(errp, local_err);
-     }
-diff --git a/blockjob.c b/blockjob.c
-index 6e489327ff..764d41863e 100644
---- a/blockjob.c
-+++ b/blockjob.c
-@@ -289,7 +289,7 @@ void block_job_start(BlockJob *job)
-     job->co = qemu_coroutine_create(block_job_co_entry, job);
-     job->pause_count--;
-     job->busy = true;
--    job->paused = false;
-+    job->paused = job->pause_count > 0;
-     bdrv_coroutine_enter(blk_bs(job->blk), job->co);
- }
-diff --git a/include/block/block_int.h b/include/block/block_int.h
-index 89d7b458e7..19b84b027f 100644
---- a/include/block/block_int.h
-+++ b/include/block/block_int.h
-@@ -879,6 +879,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
-                             BlockdevOnError on_target_error,
-                             int creation_flags,
-                             BlockCompletionFunc *cb, void *opaque,
-+                            int pause_count,
-                             BlockJobTxn *txn, Error **errp);
- void hmp_drive_add_node(Monitor *mon, const char *optstr);
--- 
-2.11.0
-
diff --git a/debian/patches/pve/0027-adding-old-vma-files.patch b/debian/patches/pve/0027-adding-old-vma-files.patch
new file mode 100644 (file)
index 0000000..92a5b54
--- /dev/null
@@ -0,0 +1,3303 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Wolfgang Bumiller <w.bumiller@proxmox.com>
+Date: Mon, 7 Aug 2017 08:51:16 +0200
+Subject: [PATCH] adding old vma files
+
+---
+ Makefile                  |   3 +-
+ Makefile.objs             |   1 +
+ block/backup.c            | 130 ++++---
+ block/replication.c       |   1 +
+ blockdev.c                | 207 +++++++----
+ blockjob.c                |   3 +-
+ include/block/block_int.h |   4 +
+ vma-reader.c              | 857 ++++++++++++++++++++++++++++++++++++++++++++++
+ vma-writer.c              | 771 +++++++++++++++++++++++++++++++++++++++++
+ vma.c                     | 756 ++++++++++++++++++++++++++++++++++++++++
+ vma.h                     | 150 ++++++++
+ 11 files changed, 2760 insertions(+), 123 deletions(-)
+ 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 ab0354c153..ad28227b6c 100644
+--- a/Makefile
++++ b/Makefile
+@@ -340,7 +340,7 @@ dummy := $(call unnest-vars,, \
+ include $(SRC_PATH)/tests/Makefile.include
+-all: $(DOCS) $(TOOLS) $(HELPERS-y) recurse-all modules
++all: $(DOCS) $(TOOLS) vma$(EXESUF) $(HELPERS-y) recurse-all modules
+ qemu-version.h: FORCE
+       $(call quiet-command, \
+@@ -439,6 +439,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) $(COMMON_LDADDS)
+ qemu-nbd$(EXESUF): qemu-nbd.o $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS)
+ qemu-io$(EXESUF): qemu-io.o $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS)
++vma$(EXESUF): vma.o vma-reader.o $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS)
+ qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o $(COMMON_LDADDS)
+diff --git a/Makefile.objs b/Makefile.objs
+index 686247b556..34e62547d8 100644
+--- a/Makefile.objs
++++ b/Makefile.objs
+@@ -14,6 +14,7 @@ block-obj-y += block.o blockjob.o
+ block-obj-y += block/ scsi/
+ block-obj-y += qemu-io-cmds.o
+ block-obj-$(CONFIG_REPLICATION) += replication.o
++block-obj-y += vma-writer.o
+ block-obj-m = block/
+diff --git a/block/backup.c b/block/backup.c
+index 8c2967a8cb..0870acdae7 100644
+--- a/block/backup.c
++++ b/block/backup.c
+@@ -36,6 +36,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;
+@@ -135,13 +136,24 @@ static int coroutine_fn backup_do_cow(BackupBlockJob *job,
+             goto out;
+         }
++
+         if (buffer_is_zero(iov.iov_base, iov.iov_len)) {
+-            ret = blk_co_pwrite_zeroes(job->target, start,
+-                                       bounce_qiov.size, BDRV_REQ_MAY_UNMAP);
++            if (job->dump_cb) {
++                ret = job->dump_cb(job->common.opaque, job->target, start, bounce_qiov.size, NULL);
++            }
++            if (job->target) {
++                ret = blk_co_pwrite_zeroes(job->target, start,
++                                           bounce_qiov.size, BDRV_REQ_MAY_UNMAP);
++            }
+         } else {
+-            ret = blk_co_pwritev(job->target, start,
+-                                 bounce_qiov.size, &bounce_qiov,
+-                                 job->compress ? BDRV_REQ_WRITE_COMPRESSED : 0);
++            if (job->dump_cb) {
++                ret = job->dump_cb(job->common.opaque, job->target, start, bounce_qiov.size, bounce_buffer);
++            }
++            if (job->target) {
++                ret = blk_co_pwritev(job->target, start,
++                                     bounce_qiov.size, &bounce_qiov,
++                                     job->compress ? BDRV_REQ_WRITE_COMPRESSED : 0);
++            }
+         }
+         if (ret < 0) {
+             trace_backup_do_cow_write_fail(job, start, ret);
+@@ -234,7 +246,9 @@ static void backup_abort(BlockJob *job)
+ static void backup_clean(BlockJob *job)
+ {
+     BackupBlockJob *s = container_of(job, BackupBlockJob, common);
+-    assert(s->target);
++    if (!s->target) {
++        return;
++    }
+     blk_unref(s->target);
+     s->target = NULL;
+ }
+@@ -243,7 +257,9 @@ static void backup_attached_aio_context(BlockJob *job, AioContext *aio_context)
+ {
+     BackupBlockJob *s = container_of(job, BackupBlockJob, common);
+-    blk_set_aio_context(s->target, aio_context);
++    if (s->target) {
++        blk_set_aio_context(s->target, aio_context);
++    }
+ }
+ void backup_do_checkpoint(BlockJob *job, Error **errp)
+@@ -315,9 +331,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;
+     }
+ }
+@@ -538,6 +556,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
+                   BlockdevOnError on_source_error,
+                   BlockdevOnError on_target_error,
+                   int creation_flags,
++                  BackupDumpFunc *dump_cb,
+                   BlockCompletionFunc *cb, void *opaque,
+                   int pause_count,
+                   BlockJobTxn *txn, Error **errp)
+@@ -548,7 +567,7 @@ BlockJob *backup_job_create(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");
+@@ -561,13 +580,13 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
+         return NULL;
+     }
+-    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 NULL;
+     }
+-    if (compress && target->drv->bdrv_co_pwritev_compressed == NULL) {
++    if (target && compress && target->drv->bdrv_co_pwritev_compressed == NULL) {
+         error_setg(errp, "Compression is not supported for this drive %s",
+                    bdrv_get_device_name(target));
+         return NULL;
+@@ -577,7 +596,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
+         return NULL;
+     }
+-    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 NULL;
+     }
+@@ -617,15 +636,18 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
+         goto error;
+     }
+-    /* The target must match the source in size, so no resize here either */
+-    job->target = blk_new(BLK_PERM_WRITE,
+-                          BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE |
+-                          BLK_PERM_WRITE_UNCHANGED | BLK_PERM_GRAPH_MOD);
+-    ret = blk_insert_bs(job->target, target, errp);
+-    if (ret < 0) {
+-        goto error;
++    if (target) {
++        /* The target must match the source in size, so no resize here either */
++        job->target = blk_new(BLK_PERM_WRITE,
++                              BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE |
++                              BLK_PERM_WRITE_UNCHANGED | BLK_PERM_GRAPH_MOD);
++        ret = blk_insert_bs(job->target, target, errp);
++        if (ret < 0) {
++            goto error;
++        }
+     }
++    job->dump_cb = dump_cb;
+     job->on_source_error = on_source_error;
+     job->on_target_error = on_target_error;
+     job->sync_mode = sync_mode;
+@@ -633,36 +655,52 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
+                        sync_bitmap : NULL;
+     job->compress = compress;
+-    /* 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 == -ENOTSUP && !target->backing) {
+-        /* Cluster size is not defined */
+-        warn_report("The target block device doesn't provide "
+-                    "information about the block size and it doesn't have a "
+-                    "backing file. The default block size of %u bytes is "
+-                    "used. If the actual block size of the target exceeds "
+-                    "this default, the backup may be unusable",
+-                    BACKUP_CLUSTER_SIZE_DEFAULT);
+-        job->cluster_size = BACKUP_CLUSTER_SIZE_DEFAULT;
+-    } else 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 == -ENOTSUP && !target->backing) {
++            /* Cluster size is not defined */
++            warn_report("The target block device doesn't provide "
++                        "information about the block size and it doesn't have a "
++                        "backing file. The default block size of %u bytes is "
++                        "used. If the actual block size of the target exceeds "
++                        "this default, the backup may be unusable",
++                        BACKUP_CLUSTER_SIZE_DEFAULT);
++            job->cluster_size = BACKUP_CLUSTER_SIZE_DEFAULT;
++        } else 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);
++        }
+     } else {
+-        job->cluster_size = MAX(BACKUP_CLUSTER_SIZE_DEFAULT, bdi.cluster_size);
++        ret = bdrv_get_info(bs, &bdi);
++        if (ret < 0) {
++            job->cluster_size = BACKUP_CLUSTER_SIZE_DEFAULT;
++        } else {
++            /* round down to nearest BACKUP_CLUSTER_SIZE_DEFAULT */
++            job->cluster_size = (bdi.cluster_size / BACKUP_CLUSTER_SIZE_DEFAULT) * BACKUP_CLUSTER_SIZE_DEFAULT;
++            if (job->cluster_size == 0) {
++                /* but we can't go below it */
++                job->cluster_size = BACKUP_CLUSTER_SIZE_DEFAULT;
++            }
++        }
+     }
+-    /* Required permissions are already taken with target's blk_new() */
+-    block_job_add_bdrv(&job->common, "target", target, 0, BLK_PERM_ALL,
+-                       &error_abort);
++    if (target) {
++        /* Required permissions are already taken with target's blk_new() */
++        block_job_add_bdrv(&job->common, "target", target, 0, BLK_PERM_ALL,
++                           &error_abort);
++    }
+     job->common.len = len;
+     job->common.pause_count = pause_count;
+     block_job_txn_add_job(txn, &job->common);
+diff --git a/block/replication.c b/block/replication.c
+index 1b08b242eb..3d101ce6e6 100644
+--- a/block/replication.c
++++ b/block/replication.c
+@@ -561,6 +561,7 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode,
+                                 0, MIRROR_SYNC_MODE_NONE, NULL, false,
+                                 BLOCKDEV_ON_ERROR_REPORT,
+                                 BLOCKDEV_ON_ERROR_REPORT, BLOCK_JOB_INTERNAL,
++                                NULL,
+                                 backup_job_completed, bs, 0, NULL, &local_err);
+         if (local_err) {
+             error_propagate(errp, local_err);
+diff --git a/blockdev.c b/blockdev.c
+index 3ffd064c48..4b6091afc6 100644
+--- a/blockdev.c
++++ b/blockdev.c
+@@ -31,7 +31,6 @@
+  */
+ #include "qemu/osdep.h"
+-#include "qemu/uuid.h"
+ #include "sysemu/block-backend.h"
+ #include "sysemu/blockdev.h"
+ #include "hw/block/block.h"
+@@ -55,6 +54,7 @@
+ #include "qemu/cutils.h"
+ #include "qemu/help_option.h"
+ #include "qemu/throttle-options.h"
++#include "vma.h"
+ static QTAILQ_HEAD(, BlockDriverState) monitor_bdrv_states =
+     QTAILQ_HEAD_INITIALIZER(monitor_bdrv_states);
+@@ -2970,15 +2970,14 @@ out:
+ static struct PVEBackupState {
+     Error *error;
+     bool cancel;
+-    QemuUUID uuid;
++    uuid_t uuid;
+     char uuid_str[37];
+     int64_t speed;
+     time_t start_time;
+     time_t end_time;
+     char *backup_file;
+-    Object *vmaobj;
++    VmaWriter *vmaw;
+     GList *di_list;
+-    size_t next_job;
+     size_t total;
+     size_t transferred;
+     size_t zero_bytes;
+@@ -2997,6 +2996,71 @@ typedef struct PVEBackupDevInfo {
+ static void pvebackup_run_next_job(void);
++static int pvebackup_dump_cb(void *opaque, BlockBackend *target,
++                             uint64_t start, uint64_t bytes,
++                             const void *pbuf)
++{
++    const uint64_t size = bytes;
++    const unsigned char *buf = pbuf;
++    PVEBackupDevInfo *di = opaque;
++
++    if (backup_state.cancel) {
++        return size; // return success
++    }
++
++    uint64_t cluster_num = start / VMA_CLUSTER_SIZE;
++    if ((cluster_num * VMA_CLUSTER_SIZE) != start) {
++        if (!backup_state.error) {
++            error_setg(&backup_state.error,
++                       "got unaligned write inside backup dump "
++                       "callback (sector %ld)", start);
++        }
++        return -1; // not aligned to cluster size
++    }
++
++    int ret = -1;
++
++    if (backup_state.vmaw) {
++        size_t zero_bytes = 0;
++        uint64_t remaining = size;
++        while (remaining > 0) {
++            ret = vma_writer_write(backup_state.vmaw, di->dev_id, cluster_num,
++                                   buf, &zero_bytes);
++            ++cluster_num;
++            if (buf) {
++                buf += VMA_CLUSTER_SIZE;
++            }
++            if (ret < 0) {
++                if (!backup_state.error) {
++                    vma_writer_error_propagate(backup_state.vmaw, &backup_state.error);
++                }
++                if (di->bs && di->bs->job) {
++                    block_job_cancel(di->bs->job);
++                }
++                break;
++            } else {
++                backup_state.zero_bytes += zero_bytes;
++                if (remaining >= VMA_CLUSTER_SIZE) {
++                    backup_state.transferred += VMA_CLUSTER_SIZE;
++                    remaining -= VMA_CLUSTER_SIZE;
++                } else {
++                    backup_state.transferred += remaining;
++                    remaining = 0;
++                }
++            }
++        }
++    } else {
++        if (!buf) {
++            backup_state.zero_bytes += size;
++        }
++        backup_state.transferred += size;
++    }
++
++    // Note: always return success, because we want that writes succeed anyways.
++
++    return size;
++}
++
+ static void pvebackup_cleanup(void)
+ {
+     qemu_mutex_lock(&backup_state.backup_mutex);
+@@ -3008,9 +3072,11 @@ static void pvebackup_cleanup(void)
+     backup_state.end_time = time(NULL);
+-    if (backup_state.vmaobj) {
+-        object_unparent(backup_state.vmaobj);
+-        backup_state.vmaobj = 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;
+     }
+     g_list_free(backup_state.di_list);
+@@ -3018,6 +3084,13 @@ static void pvebackup_cleanup(void)
+     qemu_mutex_unlock(&backup_state.backup_mutex);
+ }
++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)
+ {
+     // This always runs in the main loop
+@@ -3034,9 +3107,9 @@ static void pvebackup_complete_cb(void *opaque, int ret)
+     di->bs = NULL;
+     di->target = NULL;
+-    if (backup_state.vmaobj) {
+-        object_unparent(backup_state.vmaobj);
+-        backup_state.vmaobj = NULL;
++    if (backup_state.vmaw) {
++        Coroutine *co = qemu_coroutine_create(backup_close_vma_stream, di);
++        qemu_coroutine_enter(co);
+     }
+     // remove self from job queue
+@@ -3064,14 +3137,9 @@ static void pvebackup_cancel(void *opaque)
+         error_setg(&backup_state.error, "backup cancelled");
+     }
+-    if (backup_state.vmaobj) {
+-        Error *err;
++    if (backup_state.vmaw) {
+         /* make sure vma writer does not block anymore */
+-        if (!object_set_props(backup_state.vmaobj, &err, "blocked", "yes", NULL)) {
+-            if (err) {
+-                error_report_err(err);
+-            }
+-        }
++        vma_writer_set_error(backup_state.vmaw, "backup cancelled");
+     }
+     GList *l = backup_state.di_list;
+@@ -3102,18 +3170,14 @@ void qmp_backup_cancel(Error **errp)
+     Coroutine *co = qemu_coroutine_create(pvebackup_cancel, NULL);
+     qemu_coroutine_enter(co);
+-    while (backup_state.vmaobj) {
+-        /* FIXME: Find something better for this */
++    while (backup_state.vmaw) {
++        /* vma writer use main aio context */
+         aio_poll(qemu_get_aio_context(), true);
+     }
+ }
+-void vma_object_add_config_file(Object *obj, const char *name, 
+-                                const char *contents, size_t len,
+-                                Error **errp);
+ static int config_to_vma(const char *file, BackupFormat format,
+-                         Object *vmaobj,
+-                         const char *backup_dir,
++                         const char *backup_dir, VmaWriter *vmaw,
+                          Error **errp)
+ {
+     char *cdata = NULL;
+@@ -3127,7 +3191,12 @@ static int config_to_vma(const char *file, BackupFormat format,
+     char *basename = g_path_get_basename(file);
+     if (format == BACKUP_FORMAT_VMA) {
+-        vma_object_add_config_file(vmaobj, basename, cdata, clen, errp);
++        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);
+@@ -3145,28 +3214,30 @@ static int config_to_vma(const char *file, BackupFormat format,
+ }
+ void block_job_resume(BlockJob *job);
++bool block_job_should_pause(BlockJob *job);
+ static void pvebackup_run_next_job(void)
+ {
+     qemu_mutex_lock(&backup_state.backup_mutex);
+-    GList *next = g_list_nth(backup_state.di_list, backup_state.next_job);
+-    while (next) {
+-        PVEBackupDevInfo *di = (PVEBackupDevInfo *)next->data;
+-        backup_state.next_job++;
++    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;
+             AioContext *aio_context = blk_get_aio_context(job->blk);
+             aio_context_acquire(aio_context);
+             qemu_mutex_unlock(&backup_state.backup_mutex);
+-            if (backup_state.error || backup_state.cancel) {
+-                block_job_cancel_sync(job);
+-            } else {
+-                block_job_resume(job);
++            if (block_job_should_pause(job)) {
++                if (backup_state.error || backup_state.cancel) {
++                    block_job_cancel_sync(job);
++                } else {
++                    block_job_resume(job);
++                }
+             }
+             aio_context_release(aio_context);
+             return;
+         }
+-        next = g_list_next(next);
+     }
+     qemu_mutex_unlock(&backup_state.backup_mutex);
+@@ -3177,7 +3248,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_firewall_file, const char *firewall_file,
+                     bool has_devlist, const char *devlist,
+                     bool has_speed, int64_t speed, Error **errp)
+ {
+@@ -3185,7 +3256,8 @@ UuidInfo *qmp_backup(const char *backup_file, bool has_format,
+     BlockDriverState *bs = NULL;
+     const char *backup_dir = NULL;
+     Error *local_err = NULL;
+-    QemuUUID uuid;
++    uuid_t uuid;
++    VmaWriter *vmaw = NULL;
+     gchar **devs = NULL;
+     GList *di_list = NULL;
+     GList *l;
+@@ -3197,7 +3269,7 @@ UuidInfo *qmp_backup(const char *backup_file, bool has_format,
+         backup_state.backup_mutex_initialized = true;
+     }
+-    if (backup_state.di_list || backup_state.vmaobj) {
++    if (backup_state.di_list) {
+         error_set(errp, ERROR_CLASS_GENERIC_ERROR,
+                   "previous backup not finished");
+         return NULL;
+@@ -3272,40 +3344,28 @@ UuidInfo *qmp_backup(const char *backup_file, bool has_format,
+         total += size;
+     }
+-    qemu_uuid_generate(&uuid);
++    uuid_generate(uuid);
+     if (format == BACKUP_FORMAT_VMA) {
+-        char uuidstr[UUID_FMT_LEN+1];
+-        qemu_uuid_unparse(&uuid, uuidstr);
+-        uuidstr[UUID_FMT_LEN] = 0;
+-        backup_state.vmaobj =
+-            object_new_with_props("vma", object_get_objects_root(),
+-                                  "vma-backup-obj", &local_err,
+-                                  "filename", backup_file,
+-                                  "uuid", uuidstr,
+-                                  NULL);
+-        if (!backup_state.vmaobj) {
++        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) {
+-            QDict *options = qdict_new();
+-
+             PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data;
+             l = g_list_next(l);
+             const char *devname = bdrv_get_device_name(di->bs);
+-            snprintf(di->targetfile, PATH_MAX, "vma-backup-obj/%s.raw", devname);
+-
+-            qdict_put(options, "driver", qstring_from_str("vma-drive"));
+-            qdict_put(options, "size", qint_from_int(di->size));
+-            di->target = bdrv_open(di->targetfile, NULL, options, BDRV_O_RDWR, &local_err);
+-            if (!di->target) {
+-                error_propagate(errp, local_err);
++            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;
+             }
+         }
+@@ -3346,14 +3406,14 @@ UuidInfo *qmp_backup(const char *backup_file, bool has_format,
+     /* add configuration file to archive */
+     if (has_config_file) {
+-        if(config_to_vma(config_file, format, backup_state.vmaobj, backup_dir, errp) != 0) {
++        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_state.vmaobj, backup_dir, errp) != 0) {
++        if (config_to_vma(firewall_file, format, backup_dir, vmaw, errp) != 0) {
+             goto err;
+         }
+     }
+@@ -3376,12 +3436,13 @@ UuidInfo *qmp_backup(const char *backup_file, bool has_format,
+     }
+     backup_state.backup_file = g_strdup(backup_file);
+-    memcpy(&backup_state.uuid, &uuid, sizeof(uuid));
+-    qemu_uuid_unparse(&uuid, backup_state.uuid_str);
++    backup_state.vmaw = vmaw;
++
++    uuid_copy(backup_state.uuid, uuid);
++    uuid_unparse_lower(uuid, backup_state.uuid_str);
+     qemu_mutex_lock(&backup_state.backup_mutex);
+     backup_state.di_list = di_list;
+-    backup_state.next_job = 0;
+     backup_state.total = total;
+     backup_state.transferred = 0;
+@@ -3392,21 +3453,16 @@ UuidInfo *qmp_backup(const char *backup_file, bool has_format,
+     while (l) {
+         PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data;
+         l = g_list_next(l);
+-
+         job = backup_job_create(NULL, di->bs, di->target, speed, MIRROR_SYNC_MODE_FULL, NULL,
+                                 false, BLOCKDEV_ON_ERROR_REPORT, BLOCKDEV_ON_ERROR_REPORT,
+                                 BLOCK_JOB_DEFAULT,
+-                                pvebackup_complete_cb, di, 2, NULL, &local_err);
+-        if (di->target) {
+-            bdrv_unref(di->target);
+-            di->target = NULL;
+-        }
++                                pvebackup_dump_cb, pvebackup_complete_cb, di,
++                                2, NULL, &local_err);
+         if (!job || local_err != NULL) {
+             error_setg(&backup_state.error, "backup_job_create failed");
+             pvebackup_cancel(NULL);
+-        } else {
+-            block_job_start(job);
+         }
++        block_job_start(job);
+     }
+     qemu_mutex_unlock(&backup_state.backup_mutex);
+@@ -3442,9 +3498,10 @@ err:
+         g_strfreev(devs);
+     }
+-    if (backup_state.vmaobj) {
+-        object_unparent(backup_state.vmaobj);
+-        backup_state.vmaobj = NULL;
++    if (vmaw) {
++        Error *err = NULL;
++        vma_writer_close(vmaw, &err);
++        unlink(backup_file);
+     }
+     if (backup_dir) {
+@@ -3829,7 +3886,7 @@ static BlockJob *do_drive_backup(DriveBackup *backup, BlockJobTxn *txn,
+     job = backup_job_create(backup->job_id, bs, target_bs, backup->speed,
+                             backup->sync, bmap, backup->compress,
+                             backup->on_source_error, backup->on_target_error,
+-                            BLOCK_JOB_DEFAULT, NULL, NULL, 0, txn, &local_err);
++                            BLOCK_JOB_DEFAULT, NULL, NULL, NULL, 0, txn, &local_err);
+     bdrv_unref(target_bs);
+     if (local_err != NULL) {
+         error_propagate(errp, local_err);
+@@ -3908,7 +3965,7 @@ BlockJob *do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn,
+     job = backup_job_create(backup->job_id, bs, target_bs, backup->speed,
+                             backup->sync, NULL, backup->compress,
+                             backup->on_source_error, backup->on_target_error,
+-                            BLOCK_JOB_DEFAULT, NULL, NULL, 0, txn, &local_err);
++                            BLOCK_JOB_DEFAULT, NULL, NULL, NULL, 0, txn, &local_err);
+     if (local_err != NULL) {
+         error_propagate(errp, local_err);
+     }
+diff --git a/blockjob.c b/blockjob.c
+index 2de9f8f4dd..1df33bd194 100644
+--- a/blockjob.c
++++ b/blockjob.c
+@@ -757,7 +757,8 @@ void block_job_completed(BlockJob *job, int ret)
+     }
+ }
+-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/include/block/block_int.h b/include/block/block_int.h
+index 1dbbdafd31..2ed3e41437 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, BlockBackend *be,
++                           uint64_t offset, uint64_t bytes, const void *buf);
++
+ enum BdrvTrackedRequestType {
+     BDRV_TRACKED_READ,
+     BDRV_TRACKED_WRITE,
+@@ -984,6 +987,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
+                             BlockdevOnError on_source_error,
+                             BlockdevOnError on_target_error,
+                             int creation_flags,
++                            BackupDumpFunc *dump_cb,
+                             BlockCompletionFunc *cb, void *opaque,
+                             int pause_count,
+                             BlockJobTxn *txn, Error **errp);
+diff --git a/vma-reader.c b/vma-reader.c
+new file mode 100644
+index 0000000000..2000889bd3
+--- /dev/null
++++ b/vma-reader.c
+@@ -0,0 +1,857 @@
++/*
++ * VMA: Virtual Machine Archive
++ *
++ * Copyright (C) 2012 Proxmox Server Solutions
++ *
++ * Authors:
++ *  Dietmar Maurer (dietmar@proxmox.com)
++ *
++ * 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 <glib.h>
++#include <uuid/uuid.h>
++
++#include "qemu-common.h"
++#include "qemu/timer.h"
++#include "qemu/ratelimit.h"
++#include "vma.h"
++#include "block/block.h"
++#include "sysemu/block-backend.h"
++
++static unsigned char zero_vma_block[VMA_BLOCK_SIZE];
++
++typedef struct VmaRestoreState {
++    BlockBackend *target;
++    bool write_zeroes;
++    unsigned long *bitmap;
++    int bitmap_size;
++}  VmaRestoreState;
++
++struct VmaReader {
++    int fd;
++    GChecksum *md5csum;
++    GHashTable *blob_hash;
++    unsigned char *head_data;
++    VmaDeviceInfo devinfo[256];
++    VmaRestoreState rstate[256];
++    GList *cdata_list;
++    guint8 vmstate_stream;
++    uint32_t vmstate_clusters;
++    /* to show restore percentage if run with -v */
++    time_t start_time;
++    int64_t cluster_count;
++    int64_t clusters_read;
++    int64_t zero_cluster_data;
++    int64_t partial_zero_cluster_data;
++    int clusters_read_per;
++};
++
++static guint
++g_int32_hash(gconstpointer v)
++{
++    return *(const uint32_t *)v;
++}
++
++static gboolean
++g_int32_equal(gconstpointer v1, gconstpointer v2)
++{
++    return *((const uint32_t *)v1) == *((const uint32_t *)v2);
++}
++
++static int vma_reader_get_bitmap(VmaRestoreState *rstate, int64_t cluster_num)
++{
++    assert(rstate);
++    assert(rstate->bitmap);
++
++    unsigned long val, idx, bit;
++
++    idx = cluster_num / BITS_PER_LONG;
++
++    assert(rstate->bitmap_size > idx);
++
++    bit = cluster_num % BITS_PER_LONG;
++    val = rstate->bitmap[idx];
++
++    return !!(val & (1UL << bit));
++}
++
++static void vma_reader_set_bitmap(VmaRestoreState *rstate, int64_t cluster_num,
++                                  int dirty)
++{
++    assert(rstate);
++    assert(rstate->bitmap);
++
++    unsigned long val, idx, bit;
++
++    idx = cluster_num / BITS_PER_LONG;
++
++    assert(rstate->bitmap_size > idx);
++
++    bit = cluster_num % BITS_PER_LONG;
++    val = rstate->bitmap[idx];
++    if (dirty) {
++        if (!(val & (1UL << bit))) {
++            val |= 1UL << bit;
++        }
++    } else {
++        if (val & (1UL << bit)) {
++            val &= ~(1UL << bit);
++        }
++    }
++    rstate->bitmap[idx] = val;
++}
++
++typedef struct VmaBlob {
++    uint32_t start;
++    uint32_t len;
++    void *data;
++} VmaBlob;
++
++static const VmaBlob *get_header_blob(VmaReader *vmar, uint32_t pos)
++{
++    assert(vmar);
++    assert(vmar->blob_hash);
++
++    return g_hash_table_lookup(vmar->blob_hash, &pos);
++}
++
++static const char *get_header_str(VmaReader *vmar, uint32_t pos)
++{
++    const VmaBlob *blob = get_header_blob(vmar, pos);
++    if (!blob) {
++        return NULL;
++    }
++    const char *res = (char *)blob->data;
++    if (res[blob->len-1] != '\0') {
++        return NULL;
++    }
++    return res;
++}
++
++static ssize_t
++safe_read(int fd, unsigned char *buf, size_t count)
++{
++    ssize_t n;
++
++    do {
++        n = read(fd, buf, count);
++    } while (n < 0 && errno == EINTR);
++
++    return n;
++}
++
++static ssize_t
++full_read(int fd, unsigned char *buf, size_t len)
++{
++    ssize_t n;
++    size_t total;
++
++    total = 0;
++
++    while (len > 0) {
++        n = safe_read(fd, buf, len);
++
++        if (n == 0) {
++            return total;
++        }
++
++        if (n <= 0) {
++            break;
++        }
++
++        buf += n;
++        total += n;
++        len -= n;
++    }
++
++    if (len) {
++        return -1;
++    }
++
++    return total;
++}
++
++void vma_reader_destroy(VmaReader *vmar)
++{
++    assert(vmar);
++
++    if (vmar->fd >= 0) {
++        close(vmar->fd);
++    }
++
++    if (vmar->cdata_list) {
++        g_list_free(vmar->cdata_list);
++    }
++
++    int i;
++    for (i = 1; i < 256; i++) {
++        if (vmar->rstate[i].bitmap) {
++            g_free(vmar->rstate[i].bitmap);
++        }
++    }
++
++    if (vmar->md5csum) {
++        g_checksum_free(vmar->md5csum);
++    }
++
++    if (vmar->blob_hash) {
++        g_hash_table_destroy(vmar->blob_hash);
++    }
++
++    if (vmar->head_data) {
++        g_free(vmar->head_data);
++    }
++
++    g_free(vmar);
++
++};
++
++static int vma_reader_read_head(VmaReader *vmar, Error **errp)
++{
++    assert(vmar);
++    assert(errp);
++    assert(*errp == NULL);
++
++    unsigned char md5sum[16];
++    int i;
++    int ret = 0;
++
++    vmar->head_data = g_malloc(sizeof(VmaHeader));
++
++    if (full_read(vmar->fd, vmar->head_data, sizeof(VmaHeader)) !=
++        sizeof(VmaHeader)) {
++        error_setg(errp, "can't read vma header - %s",
++                   errno ? g_strerror(errno) : "got EOF");
++        return -1;
++    }
++
++    VmaHeader *h = (VmaHeader *)vmar->head_data;
++
++    if (h->magic != VMA_MAGIC) {
++        error_setg(errp, "not a vma file - wrong magic number");
++        return -1;
++    }
++
++    uint32_t header_size = GUINT32_FROM_BE(h->header_size);
++    int need = header_size - sizeof(VmaHeader);
++    if (need <= 0) {
++        error_setg(errp, "wrong vma header size %d", header_size);
++        return -1;
++    }
++
++    vmar->head_data = g_realloc(vmar->head_data, header_size);
++    h = (VmaHeader *)vmar->head_data;
++
++    if (full_read(vmar->fd, vmar->head_data + sizeof(VmaHeader), need) !=
++        need) {
++        error_setg(errp, "can't read vma header data - %s",
++                   errno ? g_strerror(errno) : "got EOF");
++        return -1;
++    }
++
++    memcpy(md5sum, h->md5sum, 16);
++    memset(h->md5sum, 0, 16);
++
++    g_checksum_reset(vmar->md5csum);
++    g_checksum_update(vmar->md5csum, vmar->head_data, header_size);
++    gsize csize = 16;
++    g_checksum_get_digest(vmar->md5csum, (guint8 *)(h->md5sum), &csize);
++
++    if (memcmp(md5sum, h->md5sum, 16) != 0) {
++        error_setg(errp, "wrong vma header chechsum");
++        return -1;
++    }
++
++    /* we can modify header data after checksum verify */
++    h->header_size = header_size;
++
++    h->version = GUINT32_FROM_BE(h->version);
++    if (h->version != 1) {
++        error_setg(errp, "wrong vma version %d", h->version);
++        return -1;
++    }
++
++    h->ctime = GUINT64_FROM_BE(h->ctime);
++    h->blob_buffer_offset = GUINT32_FROM_BE(h->blob_buffer_offset);
++    h->blob_buffer_size = GUINT32_FROM_BE(h->blob_buffer_size);
++
++    uint32_t bstart = h->blob_buffer_offset + 1;
++    uint32_t bend = h->blob_buffer_offset + h->blob_buffer_size;
++
++    if (bstart <= sizeof(VmaHeader)) {
++        error_setg(errp, "wrong vma blob buffer offset %d",
++                   h->blob_buffer_offset);
++        return -1;
++    }
++
++    if (bend > header_size) {
++        error_setg(errp, "wrong vma blob buffer size %d/%d",
++                   h->blob_buffer_offset, h->blob_buffer_size);
++        return -1;
++    }
++
++    while ((bstart + 2) <= bend) {
++        uint32_t size = vmar->head_data[bstart] +
++            (vmar->head_data[bstart+1] << 8);
++        if ((bstart + size + 2) <= bend) {
++            VmaBlob *blob = g_new0(VmaBlob, 1);
++            blob->start = bstart - h->blob_buffer_offset;
++            blob->len = size;
++            blob->data = vmar->head_data + bstart + 2;
++            g_hash_table_insert(vmar->blob_hash, &blob->start, blob);
++        }
++        bstart += size + 2;
++    }
++
++
++    int count = 0;
++    for (i = 1; i < 256; i++) {
++        VmaDeviceInfoHeader *dih = &h->dev_info[i];
++        uint32_t devname_ptr = GUINT32_FROM_BE(dih->devname_ptr);
++        uint64_t size = GUINT64_FROM_BE(dih->size);
++        const char *devname =  get_header_str(vmar, devname_ptr);
++
++        if (size && devname) {
++            count++;
++            vmar->devinfo[i].size = size;
++            vmar->devinfo[i].devname = devname;
++
++            if (strcmp(devname, "vmstate") == 0) {
++                vmar->vmstate_stream = i;
++            }
++        }
++    }
++
++    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]);
++
++        if (!(name_ptr && data_ptr)) {
++            continue;
++        }
++        const char *name =  get_header_str(vmar, name_ptr);
++        const VmaBlob *blob = get_header_blob(vmar, data_ptr);
++
++        if (!(name && blob)) {
++            error_setg(errp, "vma contains invalid data pointers");
++            return -1;
++        }
++
++        VmaConfigData *cdata = g_new0(VmaConfigData, 1);
++        cdata->name = name;
++        cdata->data = blob->data;
++        cdata->len = blob->len;
++
++        vmar->cdata_list = g_list_append(vmar->cdata_list, cdata);
++    }
++
++    return ret;
++};
++
++VmaReader *vma_reader_create(const char *filename, Error **errp)
++{
++    assert(filename);
++    assert(errp);
++
++    VmaReader *vmar = g_new0(VmaReader, 1);
++
++    if (strcmp(filename, "-") == 0) {
++        vmar->fd = dup(0);
++    } else {
++        vmar->fd = open(filename, O_RDONLY);
++    }
++
++    if (vmar->fd < 0) {
++        error_setg(errp, "can't open file %s - %s\n", filename,
++                   g_strerror(errno));
++        goto err;
++    }
++
++    vmar->md5csum = g_checksum_new(G_CHECKSUM_MD5);
++    if (!vmar->md5csum) {
++        error_setg(errp, "can't allocate cmsum\n");
++        goto err;
++    }
++
++    vmar->blob_hash = g_hash_table_new_full(g_int32_hash, g_int32_equal,
++                                            NULL, g_free);
++
++    if (vma_reader_read_head(vmar, errp) < 0) {
++        goto err;
++    }
++
++    return vmar;
++
++err:
++    if (vmar) {
++        vma_reader_destroy(vmar);
++    }
++
++    return NULL;
++}
++
++VmaHeader *vma_reader_get_header(VmaReader *vmar)
++{
++    assert(vmar);
++    assert(vmar->head_data);
++
++    return (VmaHeader *)(vmar->head_data);
++}
++
++GList *vma_reader_get_config_data(VmaReader *vmar)
++{
++    assert(vmar);
++    assert(vmar->head_data);
++
++    return vmar->cdata_list;
++}
++
++VmaDeviceInfo *vma_reader_get_device_info(VmaReader *vmar, guint8 dev_id)
++{
++    assert(vmar);
++    assert(dev_id);
++
++    if (vmar->devinfo[dev_id].size && vmar->devinfo[dev_id].devname) {
++        return &vmar->devinfo[dev_id];
++    }
++
++    return NULL;
++}
++
++static void allocate_rstate(VmaReader *vmar,  guint8 dev_id,
++                            BlockBackend *target, bool write_zeroes)
++{
++    assert(vmar);
++    assert(dev_id);
++
++    vmar->rstate[dev_id].target = target;
++    vmar->rstate[dev_id].write_zeroes = write_zeroes;
++
++    int64_t size = vmar->devinfo[dev_id].size;
++
++    int64_t bitmap_size = (size/BDRV_SECTOR_SIZE) +
++        (VMA_CLUSTER_SIZE/BDRV_SECTOR_SIZE) * BITS_PER_LONG - 1;
++    bitmap_size /= (VMA_CLUSTER_SIZE/BDRV_SECTOR_SIZE) * BITS_PER_LONG;
++
++    vmar->rstate[dev_id].bitmap_size = bitmap_size;
++    vmar->rstate[dev_id].bitmap = g_new0(unsigned long, bitmap_size);
++
++    vmar->cluster_count += size/VMA_CLUSTER_SIZE;
++}
++
++int vma_reader_register_bs(VmaReader *vmar, guint8 dev_id, BlockBackend *target,
++                           bool write_zeroes, Error **errp)
++{
++    assert(vmar);
++    assert(target != NULL);
++    assert(dev_id);
++    assert(vmar->rstate[dev_id].target == NULL);
++
++    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
++     * 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);
++        return -1;
++    }
++
++    allocate_rstate(vmar, dev_id, target, write_zeroes);
++
++    return 0;
++}
++
++static ssize_t safe_write(int fd, void *buf, size_t count)
++{
++    ssize_t n;
++
++    do {
++        n = write(fd, buf, count);
++    } while (n < 0 && errno == EINTR);
++
++    return n;
++}
++
++static size_t full_write(int fd, void *buf, size_t len)
++{
++    ssize_t n;
++    size_t total;
++
++    total = 0;
++
++    while (len > 0) {
++        n = safe_write(fd, buf, len);
++        if (n < 0) {
++            return n;
++        }
++        buf += n;
++        total += n;
++        len -= n;
++    }
++
++    if (len) {
++        /* incomplete write ? */
++        return -1;
++    }
++
++    return total;
++}
++
++static int restore_write_data(VmaReader *vmar, guint8 dev_id,
++                              BlockBackend *target, int vmstate_fd,
++                              unsigned char *buf, int64_t sector_num,
++                              int nb_sectors, Error **errp)
++{
++    assert(vmar);
++
++    if (dev_id == vmar->vmstate_stream) {
++        if (vmstate_fd >= 0) {
++            int len = nb_sectors * BDRV_SECTOR_SIZE;
++            int res = full_write(vmstate_fd, buf, len);
++            if (res < 0) {
++                error_setg(errp, "write vmstate failed %d", res);
++                return -1;
++            }
++        }
++    } else {
++        int res = blk_pwrite(target, sector_num * BDRV_SECTOR_SIZE, buf, nb_sectors * BDRV_SECTOR_SIZE, 0);
++        if (res < 0) {
++            error_setg(errp, "blk_pwrite to %s failed (%d)",
++                       bdrv_get_device_name(blk_bs(target)), res);
++            return -1;
++        }
++    }
++    return 0;
++}
++
++static int restore_extent(VmaReader *vmar, unsigned char *buf,
++                          int extent_size, int vmstate_fd,
++                          bool verbose, bool verify, Error **errp)
++{
++    assert(vmar);
++    assert(buf);
++
++    VmaExtentHeader *ehead = (VmaExtentHeader *)buf;
++    int start = VMA_EXTENT_HEADER_SIZE;
++    int i;
++
++    for (i = 0; i < VMA_BLOCKS_PER_EXTENT; i++) {
++        uint64_t block_info = GUINT64_FROM_BE(ehead->blockinfo[i]);
++        uint64_t cluster_num = block_info & 0xffffffff;
++        uint8_t dev_id = (block_info >> 32) & 0xff;
++        uint16_t mask = block_info >> (32+16);
++        int64_t max_sector;
++
++        if (!dev_id) {
++            continue;
++        }
++
++        VmaRestoreState *rstate = &vmar->rstate[dev_id];
++        BlockBackend *target = NULL;
++
++        if (dev_id != vmar->vmstate_stream) {
++            target = rstate->target;
++            if (!verify && !target) {
++                error_setg(errp, "got wrong dev id %d", dev_id);
++                return -1;
++            }
++
++            if (vma_reader_get_bitmap(rstate, cluster_num)) {
++                error_setg(errp, "found duplicated cluster %zd for stream %s",
++                          cluster_num, vmar->devinfo[dev_id].devname);
++                return -1;
++            }
++            vma_reader_set_bitmap(rstate, cluster_num, 1);
++
++            max_sector = vmar->devinfo[dev_id].size/BDRV_SECTOR_SIZE;
++        } else {
++            max_sector = G_MAXINT64;
++            if (cluster_num != vmar->vmstate_clusters) {
++                error_setg(errp, "found out of order vmstate data");
++                return -1;
++            }
++            vmar->vmstate_clusters++;
++        }
++
++        vmar->clusters_read++;
++
++        if (verbose) {
++            time_t duration = time(NULL) - vmar->start_time;
++            int percent = (vmar->clusters_read*100)/vmar->cluster_count;
++            if (percent != vmar->clusters_read_per) {
++                printf("progress %d%% (read %zd bytes, duration %zd sec)\n",
++                       percent, vmar->clusters_read*VMA_CLUSTER_SIZE,
++                       duration);
++                fflush(stdout);
++                vmar->clusters_read_per = percent;
++            }
++        }
++
++        /* try to write whole clusters to speedup restore */
++        if (mask == 0xffff) {
++            if ((start + VMA_CLUSTER_SIZE) > extent_size) {
++                error_setg(errp, "short vma extent - too many blocks");
++                return -1;
++            }
++            int64_t sector_num = (cluster_num * VMA_CLUSTER_SIZE) /
++                BDRV_SECTOR_SIZE;
++            int64_t end_sector = sector_num +
++                VMA_CLUSTER_SIZE/BDRV_SECTOR_SIZE;
++
++            if (end_sector > max_sector) {
++                end_sector = max_sector;
++            }
++
++            if (end_sector <= sector_num) {
++                error_setg(errp, "got wrong block address - write bejond end");
++                return -1;
++            }
++
++            if (!verify) {
++                int nb_sectors = end_sector - sector_num;
++                if (restore_write_data(vmar, dev_id, target, vmstate_fd,
++                                       buf + start, sector_num, nb_sectors,
++                                       errp) < 0) {
++                    return -1;
++                }
++            }
++
++            start += VMA_CLUSTER_SIZE;
++        } else {
++            int j;
++            int bit = 1;
++
++            for (j = 0; j < 16; j++) {
++                int64_t sector_num = (cluster_num*VMA_CLUSTER_SIZE +
++                                      j*VMA_BLOCK_SIZE)/BDRV_SECTOR_SIZE;
++
++                int64_t end_sector = sector_num +
++                    VMA_BLOCK_SIZE/BDRV_SECTOR_SIZE;
++                if (end_sector > max_sector) {
++                    end_sector = max_sector;
++                }
++
++                if (mask & bit) {
++                    if ((start + VMA_BLOCK_SIZE) > extent_size) {
++                        error_setg(errp, "short vma extent - too many blocks");
++                        return -1;
++                    }
++
++                    if (end_sector <= sector_num) {
++                        error_setg(errp, "got wrong block address - "
++                                   "write bejond end");
++                        return -1;
++                    }
++
++                    if (!verify) {
++                        int nb_sectors = end_sector - sector_num;
++                        if (restore_write_data(vmar, dev_id, target, vmstate_fd,
++                                               buf + start, sector_num,
++                                               nb_sectors, errp) < 0) {
++                            return -1;
++                        }
++                    }
++
++                    start += VMA_BLOCK_SIZE;
++
++                } else {
++
++
++                    if (end_sector > sector_num) {
++                        /* Todo: use bdrv_co_write_zeroes (but that need to
++                         * be run inside coroutine?)
++                         */
++                        int nb_sectors = end_sector - sector_num;
++                        int zero_size = BDRV_SECTOR_SIZE*nb_sectors;
++                        vmar->zero_cluster_data += zero_size;
++                        if (mask != 0) {
++                            vmar->partial_zero_cluster_data += zero_size;
++                        }
++
++                        if (rstate->write_zeroes && !verify) {
++                            if (restore_write_data(vmar, dev_id, target, vmstate_fd,
++                                                   zero_vma_block, sector_num,
++                                                   nb_sectors, errp) < 0) {
++                                return -1;
++                            }
++                        }
++                    }
++                }
++
++                bit = bit << 1;
++            }
++        }
++    }
++
++    if (start != extent_size) {
++        error_setg(errp, "vma extent error - missing blocks");
++        return -1;
++    }
++
++    return 0;
++}
++
++static int vma_reader_restore_full(VmaReader *vmar, int vmstate_fd,
++                                   bool verbose, bool verify,
++                                   Error **errp)
++{
++    assert(vmar);
++    assert(vmar->head_data);
++
++    int ret = 0;
++    unsigned char buf[VMA_MAX_EXTENT_SIZE];
++    int buf_pos = 0;
++    unsigned char md5sum[16];
++    VmaHeader *h = (VmaHeader *)vmar->head_data;
++
++    vmar->start_time = time(NULL);
++
++    while (1) {
++        int bytes = full_read(vmar->fd, buf + buf_pos, sizeof(buf) - buf_pos);
++        if (bytes < 0) {
++            error_setg(errp, "read failed - %s", g_strerror(errno));
++            return -1;
++        }
++
++        buf_pos += bytes;
++
++        if (!buf_pos) {
++            break; /* EOF */
++        }
++
++        if (buf_pos < VMA_EXTENT_HEADER_SIZE) {
++            error_setg(errp, "read short extent (%d bytes)", buf_pos);
++            return -1;
++        }
++
++        VmaExtentHeader *ehead = (VmaExtentHeader *)buf;
++
++        /* extract md5sum */
++        memcpy(md5sum, ehead->md5sum, sizeof(ehead->md5sum));
++        memset(ehead->md5sum, 0, sizeof(ehead->md5sum));
++
++        g_checksum_reset(vmar->md5csum);
++        g_checksum_update(vmar->md5csum, buf, VMA_EXTENT_HEADER_SIZE);
++        gsize csize = 16;
++        g_checksum_get_digest(vmar->md5csum, ehead->md5sum, &csize);
++
++        if (memcmp(md5sum, ehead->md5sum, 16) != 0) {
++            error_setg(errp, "wrong vma extent header chechsum");
++            return -1;
++        }
++
++        if (memcmp(h->uuid, ehead->uuid, sizeof(ehead->uuid)) != 0) {
++            error_setg(errp, "wrong vma extent uuid");
++            return -1;
++        }
++
++        if (ehead->magic != VMA_EXTENT_MAGIC || ehead->reserved1 != 0) {
++            error_setg(errp, "wrong vma extent header magic");
++            return -1;
++        }
++
++        int block_count = GUINT16_FROM_BE(ehead->block_count);
++        int extent_size = VMA_EXTENT_HEADER_SIZE + block_count*VMA_BLOCK_SIZE;
++
++        if (buf_pos < extent_size) {
++            error_setg(errp, "short vma extent (%d < %d)", buf_pos,
++                       extent_size);
++            return -1;
++        }
++
++        if (restore_extent(vmar, buf, extent_size, vmstate_fd, verbose,
++                           verify, errp) < 0) {
++            return -1;
++        }
++
++        if (buf_pos > extent_size) {
++            memmove(buf, buf + extent_size, buf_pos - extent_size);
++            buf_pos = buf_pos - extent_size;
++        } else {
++            buf_pos = 0;
++        }
++    }
++
++    bdrv_drain_all();
++
++    int i;
++    for (i = 1; i < 256; i++) {
++        VmaRestoreState *rstate = &vmar->rstate[i];
++        if (!rstate->target) {
++            continue;
++        }
++
++        if (blk_flush(rstate->target) < 0) {
++            error_setg(errp, "vma blk_flush %s failed",
++                       vmar->devinfo[i].devname);
++            return -1;
++        }
++
++        if (vmar->devinfo[i].size &&
++            (strcmp(vmar->devinfo[i].devname, "vmstate") != 0)) {
++            assert(rstate->bitmap);
++
++            int64_t cluster_num, end;
++
++            end = (vmar->devinfo[i].size + VMA_CLUSTER_SIZE - 1) /
++                VMA_CLUSTER_SIZE;
++
++            for (cluster_num = 0; cluster_num < end; cluster_num++) {
++                if (!vma_reader_get_bitmap(rstate, cluster_num)) {
++                    error_setg(errp, "detected missing cluster %zd "
++                               "for stream %s", cluster_num,
++                               vmar->devinfo[i].devname);
++                    return -1;
++                }
++            }
++        }
++    }
++
++    if (verbose) {
++        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;
++}
++
++int vma_reader_restore(VmaReader *vmar, int vmstate_fd, bool verbose,
++                       Error **errp)
++{
++    return vma_reader_restore_full(vmar, vmstate_fd, verbose, false, errp);
++}
++
++int vma_reader_verify(VmaReader *vmar, bool verbose, Error **errp)
++{
++    guint8 dev_id;
++
++    for (dev_id = 1; dev_id < 255; dev_id++) {
++        if (vma_reader_get_device_info(vmar, dev_id)) {
++            allocate_rstate(vmar, dev_id, NULL, false);
++        }
++    }
++
++    return vma_reader_restore_full(vmar, -1, verbose, true, errp);
++}
++
+diff --git a/vma-writer.c b/vma-writer.c
+new file mode 100644
+index 0000000000..fd9567634d
+--- /dev/null
++++ b/vma-writer.c
+@@ -0,0 +1,771 @@
++/*
++ * VMA: Virtual Machine Archive
++ *
++ * Copyright (C) 2012 Proxmox Server Solutions
++ *
++ * Authors:
++ *  Dietmar Maurer (dietmar@proxmox.com)
++ *
++ * 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 <glib.h>
++#include <uuid/uuid.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
++
++#define DPRINTF(fmt, ...)\
++    do { if (DEBUG_VMA) { printf("vma: " fmt, ## __VA_ARGS__); } } while (0)
++
++#define WRITE_BUFFERS 5
++#define HEADER_CLUSTERS 8
++#define HEADERBUF_SIZE (VMA_CLUSTER_SIZE*HEADER_CLUSTERS)
++
++struct VmaWriter {
++    int fd;
++    FILE *cmd;
++    int status;
++    char errmsg[8192];
++    uuid_t uuid;
++    bool header_written;
++    bool closed;
++
++    /* we always write extents */
++    unsigned char *outbuf;
++    int outbuf_pos; /* in bytes */
++    int outbuf_count; /* in VMA_BLOCKS */
++    uint64_t outbuf_block_info[VMA_BLOCKS_PER_EXTENT];
++
++    unsigned char *headerbuf;
++
++    GChecksum *md5csum;
++    CoMutex flush_lock;
++    Coroutine *co_writer;
++
++    /* drive informations */
++    VmaStreamInfo stream_info[256];
++    guint stream_count;
++
++    guint8 vmstate_stream;
++    uint32_t vmstate_clusters;
++
++    /* header blob table */
++    char *header_blob_table;
++    uint32_t header_blob_table_size;
++    uint32_t header_blob_table_pos;
++
++    /* store for config blobs */
++    uint32_t config_names[VMA_MAX_CONFIGS]; /* offset into blob_buffer table */
++    uint32_t config_data[VMA_MAX_CONFIGS];  /* offset into blob_buffer table */
++    uint32_t config_count;
++};
++
++void vma_writer_set_error(VmaWriter *vmaw, const char *fmt, ...)
++{
++    va_list ap;
++
++    if (vmaw->status < 0) {
++        return;
++    }
++
++    vmaw->status = -1;
++
++    va_start(ap, fmt);
++    g_vsnprintf(vmaw->errmsg, sizeof(vmaw->errmsg), fmt, ap);
++    va_end(ap);
++
++    DPRINTF("vma_writer_set_error: %s\n", vmaw->errmsg);
++}
++
++static uint32_t allocate_header_blob(VmaWriter *vmaw, const char *data,
++                                     size_t len)
++{
++    if (len > 65535) {
++        return 0;
++    }
++
++    if (!vmaw->header_blob_table ||
++        (vmaw->header_blob_table_size <
++         (vmaw->header_blob_table_pos + len + 2))) {
++        int newsize = vmaw->header_blob_table_size + ((len + 2 + 511)/512)*512;
++
++        vmaw->header_blob_table = g_realloc(vmaw->header_blob_table, newsize);
++        memset(vmaw->header_blob_table + vmaw->header_blob_table_size,
++               0, newsize - vmaw->header_blob_table_size);
++        vmaw->header_blob_table_size = newsize;
++    }
++
++    uint32_t cpos = vmaw->header_blob_table_pos;
++    vmaw->header_blob_table[cpos] = len & 255;
++    vmaw->header_blob_table[cpos+1] = (len >> 8) & 255;
++    memcpy(vmaw->header_blob_table + cpos + 2, data, len);
++    vmaw->header_blob_table_pos += len + 2;
++    return cpos;
++}
++
++static uint32_t allocate_header_string(VmaWriter *vmaw, const char *str)
++{
++    assert(vmaw);
++
++    size_t len = strlen(str) + 1;
++
++    return allocate_header_blob(vmaw, str, len);
++}
++
++int vma_writer_add_config(VmaWriter *vmaw, const char *name, gpointer data,
++                          gsize len)
++{
++    assert(vmaw);
++    assert(!vmaw->header_written);
++    assert(vmaw->config_count < VMA_MAX_CONFIGS);
++    assert(name);
++    assert(data);
++
++    gchar *basename = g_path_get_basename(name);
++    uint32_t name_ptr = allocate_header_string(vmaw, basename);
++    g_free(basename);
++
++    if (!name_ptr) {
++        return -1;
++    }
++
++    uint32_t data_ptr = allocate_header_blob(vmaw, data, len);
++    if (!data_ptr) {
++        return -1;
++    }
++
++    vmaw->config_names[vmaw->config_count] = name_ptr;
++    vmaw->config_data[vmaw->config_count] = data_ptr;
++
++    vmaw->config_count++;
++
++    return 0;
++}
++
++int vma_writer_register_stream(VmaWriter *vmaw, const char *devname,
++                               size_t size)
++{
++    assert(vmaw);
++    assert(devname);
++    assert(!vmaw->status);
++
++    if (vmaw->header_written) {
++        vma_writer_set_error(vmaw, "vma_writer_register_stream: header "
++                             "already written");
++        return -1;
++    }
++
++    guint n = vmaw->stream_count + 1;
++
++    /* we can have dev_ids form 1 to 255 (0 reserved)
++     * 255(-1) reseverd for safety
++     */
++    if (n > 254) {
++        vma_writer_set_error(vmaw, "vma_writer_register_stream: "
++                             "too many drives");
++        return -1;
++    }
++
++    if (size <= 0) {
++        vma_writer_set_error(vmaw, "vma_writer_register_stream: "
++                             "got strange size %zd", size);
++        return -1;
++    }
++
++    DPRINTF("vma_writer_register_stream %s %zu %d\n", devname, size, n);
++
++    vmaw->stream_info[n].devname = g_strdup(devname);
++    vmaw->stream_info[n].size = size;
++
++    vmaw->stream_info[n].cluster_count = (size + VMA_CLUSTER_SIZE - 1) /
++        VMA_CLUSTER_SIZE;
++
++    vmaw->stream_count = n;
++
++    if (strcmp(devname, "vmstate") == 0) {
++        vmaw->vmstate_stream = n;
++    }
++
++    return n;
++}
++
++static void vma_co_continue_write(void *opaque)
++{
++    VmaWriter *vmaw = opaque;
++
++    DPRINTF("vma_co_continue_write\n");
++    qemu_coroutine_enter(vmaw->co_writer);
++}
++
++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);
++
++    size_t done = 0;
++    ssize_t ret;
++
++    assert(vmaw->co_writer == NULL);
++
++    vmaw->co_writer = qemu_coroutine_self();
++
++    while (done < bytes) {
++        aio_set_fd_handler(qemu_get_aio_context(), vmaw->fd, false, NULL, vma_co_continue_write, NULL, vmaw);
++        qemu_coroutine_yield();
++        aio_set_fd_handler(qemu_get_aio_context(), vmaw->fd, false, NULL, 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_queue_write written %zd %zd\n", done, ret);
++        } else if (ret < 0) {
++            if (errno == EAGAIN || errno == EWOULDBLOCK) {
++                /* 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;
++            }
++        } else if (ret == 0) {
++            /* should not happen - simply try again */
++        }
++    }
++
++    vmaw->co_writer = NULL;
++
++    return (done == bytes) ? bytes : -1;
++}
++
++VmaWriter *vma_writer_create(const char *filename, uuid_t uuid, Error **errp)
++{
++    const char *p;
++
++    assert(sizeof(VmaHeader) == (4096 + 8192));
++    assert(G_STRUCT_OFFSET(VmaHeader, config_names) == 2044);
++    assert(G_STRUCT_OFFSET(VmaHeader, config_data) == 3068);
++    assert(G_STRUCT_OFFSET(VmaHeader, dev_info) == 4096);
++    assert(sizeof(VmaExtentHeader) == 512);
++
++    VmaWriter *vmaw = g_new0(VmaWriter, 1);
++    vmaw->fd = -1;
++
++    vmaw->md5csum = g_checksum_new(G_CHECKSUM_MD5);
++    if (!vmaw->md5csum) {
++        error_setg(errp, "can't allocate cmsum\n");
++        goto err;
++    }
++
++    if (strstart(filename, "exec:", &p)) {
++        vmaw->cmd = popen(p, "w");
++        if (vmaw->cmd == NULL) {
++            error_setg(errp, "can't popen command '%s' - %s\n", p,
++                       g_strerror(errno));
++            goto err;
++        }
++        vmaw->fd = fileno(vmaw->cmd);
++
++        /* try to use O_NONBLOCK */
++        fcntl(vmaw->fd, F_SETFL, fcntl(vmaw->fd, F_GETFL)|O_NONBLOCK);
++
++    } else {
++        struct stat st;
++        int oflags;
++        const char *tmp_id_str;
++
++        if ((stat(filename, &st) == 0) && S_ISFIFO(st.st_mode)) {
++            oflags = O_NONBLOCK|O_WRONLY;
++            vmaw->fd = qemu_open(filename, oflags, 0644);
++        } else if (strstart(filename, "/dev/fdset/", &tmp_id_str)) {
++            oflags = O_NONBLOCK|O_WRONLY;
++            vmaw->fd = qemu_open(filename, oflags, 0644);
++        } else if (strstart(filename, "/dev/fdname/", &tmp_id_str)) {
++            vmaw->fd = monitor_get_fd(cur_mon, tmp_id_str, errp);
++            if (vmaw->fd < 0) {
++                goto err;
++            }
++            /* try to use O_NONBLOCK */
++            fcntl(vmaw->fd, F_SETFL, fcntl(vmaw->fd, F_GETFL)|O_NONBLOCK);
++        } else  {
++            oflags = O_NONBLOCK|O_DIRECT|O_WRONLY|O_CREAT|O_EXCL;
++            vmaw->fd = qemu_open(filename, oflags, 0644);
++        }
++
++        if (vmaw->fd < 0) {
++            error_setg(errp, "can't open file %s - %s\n", filename,
++                       g_strerror(errno));
++            goto err;
++        }
++    }
++
++    /* we use O_DIRECT, so we need to align IO buffers */
++
++    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->flush_lock);
++
++    uuid_copy(vmaw->uuid, uuid);
++
++    return vmaw;
++
++err:
++    if (vmaw) {
++        if (vmaw->cmd) {
++            pclose(vmaw->cmd);
++        } else if (vmaw->fd >= 0) {
++            close(vmaw->fd);
++        }
++
++        if (vmaw->md5csum) {
++            g_checksum_free(vmaw->md5csum);
++        }
++
++        g_free(vmaw);
++    }
++
++    return NULL;
++}
++
++static int coroutine_fn vma_write_header(VmaWriter *vmaw)
++{
++    assert(vmaw);
++    unsigned char *buf = vmaw->headerbuf;
++    VmaHeader *head = (VmaHeader *)buf;
++
++    int i;
++
++    DPRINTF("VMA WRITE HEADER\n");
++
++    if (vmaw->status < 0) {
++        return vmaw->status;
++    }
++
++    memset(buf, 0, HEADERBUF_SIZE);
++
++    head->magic = VMA_MAGIC;
++    head->version = GUINT32_TO_BE(1); /* v1 */
++    memcpy(head->uuid, vmaw->uuid, 16);
++
++    time_t ctime = time(NULL);
++    head->ctime = GUINT64_TO_BE(ctime);
++
++    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]);
++    }
++
++    /* 32 bytes per device (12 used currently) = 8192 bytes max */
++    for (i = 1; i <= 254; i++) {
++        VmaStreamInfo *si = &vmaw->stream_info[i];
++        if (si->size) {
++            assert(si->devname);
++            uint32_t devname_ptr = allocate_header_string(vmaw, si->devname);
++            if (!devname_ptr) {
++                return -1;
++            }
++            head->dev_info[i].devname_ptr = GUINT32_TO_BE(devname_ptr);
++            head->dev_info[i].size = GUINT64_TO_BE(si->size);
++        }
++    }
++
++    uint32_t header_size = sizeof(VmaHeader) + vmaw->header_blob_table_size;
++    head->header_size = GUINT32_TO_BE(header_size);
++
++    if (header_size > HEADERBUF_SIZE) {
++        return -1; /* just to be sure */
++    }
++
++    uint32_t blob_buffer_offset = sizeof(VmaHeader);
++    memcpy(buf + blob_buffer_offset, vmaw->header_blob_table,
++           vmaw->header_blob_table_size);
++    head->blob_buffer_offset = GUINT32_TO_BE(blob_buffer_offset);
++    head->blob_buffer_size = GUINT32_TO_BE(vmaw->header_blob_table_pos);
++
++    g_checksum_reset(vmaw->md5csum);
++    g_checksum_update(vmaw->md5csum, (const guchar *)buf, header_size);
++    gsize csize = 16;
++    g_checksum_get_digest(vmaw->md5csum, (guint8 *)(head->md5sum), &csize);
++
++    return vma_queue_write(vmaw, buf, header_size);
++}
++
++static int coroutine_fn vma_writer_flush(VmaWriter *vmaw)
++{
++    assert(vmaw);
++
++    int ret;
++    int i;
++
++    if (vmaw->status < 0) {
++        return vmaw->status;
++    }
++
++    if (!vmaw->header_written) {
++        vmaw->header_written = true;
++        ret = vma_write_header(vmaw);
++        if (ret < 0) {
++            vma_writer_set_error(vmaw, "vma_writer_flush: write header failed");
++            return ret;
++        }
++    }
++
++    DPRINTF("VMA WRITE FLUSH %d %d\n", vmaw->outbuf_count, vmaw->outbuf_pos);
++
++
++    VmaExtentHeader *ehead = (VmaExtentHeader *)vmaw->outbuf;
++
++    ehead->magic = VMA_EXTENT_MAGIC;
++    ehead->reserved1 = 0;
++
++    for (i = 0; i < VMA_BLOCKS_PER_EXTENT; i++) {
++        ehead->blockinfo[i] = GUINT64_TO_BE(vmaw->outbuf_block_info[i]);
++    }
++
++    guint16 block_count = (vmaw->outbuf_pos - VMA_EXTENT_HEADER_SIZE) /
++        VMA_BLOCK_SIZE;
++
++    ehead->block_count = GUINT16_TO_BE(block_count);
++
++    memcpy(ehead->uuid, vmaw->uuid, sizeof(ehead->uuid));
++    memset(ehead->md5sum, 0, sizeof(ehead->md5sum));
++
++    g_checksum_reset(vmaw->md5csum);
++    g_checksum_update(vmaw->md5csum, vmaw->outbuf, VMA_EXTENT_HEADER_SIZE);
++    gsize csize = 16;
++    g_checksum_get_digest(vmaw->md5csum, ehead->md5sum, &csize);
++
++    int bytes = vmaw->outbuf_pos;
++    ret = vma_queue_write(vmaw, vmaw->outbuf, bytes);
++    if (ret != bytes) {
++        vma_writer_set_error(vmaw, "vma_writer_flush: failed write");
++    }
++
++    vmaw->outbuf_count = 0;
++    vmaw->outbuf_pos = VMA_EXTENT_HEADER_SIZE;
++
++    for (i = 0; i < VMA_BLOCKS_PER_EXTENT; i++) {
++        vmaw->outbuf_block_info[i] = 0;
++    }
++
++    return vmaw->status;
++}
++
++static int vma_count_open_streams(VmaWriter *vmaw)
++{
++    g_assert(vmaw != NULL);
++
++    int i;
++    int open_drives = 0;
++    for (i = 0; i <= 255; i++) {
++        if (vmaw->stream_info[i].size && !vmaw->stream_info[i].finished) {
++            open_drives++;
++        }
++    }
++
++    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)
++ */
++int coroutine_fn
++vma_writer_close_stream(VmaWriter *vmaw, uint8_t dev_id)
++{
++    g_assert(vmaw != NULL);
++
++    DPRINTF("vma_writer_set_status %d\n", dev_id);
++    if (!vmaw->stream_info[dev_id].size) {
++        vma_writer_set_error(vmaw, "vma_writer_close_stream: "
++                             "no such stream %d", dev_id);
++        return -1;
++    }
++    if (vmaw->stream_info[dev_id].finished) {
++        vma_writer_set_error(vmaw, "vma_writer_close_stream: "
++                             "stream already closed %d", dev_id);
++        return -1;
++    }
++
++    vmaw->stream_info[dev_id].finished = true;
++
++    int open_drives = vma_count_open_streams(vmaw);
++
++    if (open_drives <= 0) {
++        DPRINTF("vma_writer_set_status all drives completed\n");
++        vma_writer_flush_output(vmaw);
++    }
++
++    return open_drives;
++}
++
++int vma_writer_get_status(VmaWriter *vmaw, VmaStatus *status)
++{
++    int i;
++
++    g_assert(vmaw != NULL);
++
++    if (status) {
++        status->status = vmaw->status;
++        g_strlcpy(status->errmsg, vmaw->errmsg, sizeof(status->errmsg));
++        for (i = 0; i <= 255; i++) {
++            status->stream_info[i] = vmaw->stream_info[i];
++        }
++
++        uuid_unparse_lower(vmaw->uuid, status->uuid_str);
++    }
++
++    status->closed = vmaw->closed;
++
++    return vmaw->status;
++}
++
++static int vma_writer_get_buffer(VmaWriter *vmaw)
++{
++    int ret = 0;
++
++    qemu_co_mutex_lock(&vmaw->flush_lock);
++
++    /* wait until buffer is available */
++    while (vmaw->outbuf_count >= (VMA_BLOCKS_PER_EXTENT - 1)) {
++        ret = vma_writer_flush(vmaw);
++        if (ret < 0) {
++            vma_writer_set_error(vmaw, "vma_writer_get_buffer: flush failed");
++            break;
++        }
++    }
++
++    qemu_co_mutex_unlock(&vmaw->flush_lock);
++
++    return ret;
++}
++
++
++int64_t coroutine_fn
++vma_writer_write(VmaWriter *vmaw, uint8_t dev_id, int64_t cluster_num,
++                 const unsigned char *buf, size_t *zero_bytes)
++{
++    g_assert(vmaw != NULL);
++    g_assert(zero_bytes != NULL);
++
++    *zero_bytes = 0;
++
++    if (vmaw->status < 0) {
++        return vmaw->status;
++    }
++
++    if (!dev_id || !vmaw->stream_info[dev_id].size) {
++        vma_writer_set_error(vmaw, "vma_writer_write: "
++                             "no such stream %d", dev_id);
++        return -1;
++    }
++
++    if (vmaw->stream_info[dev_id].finished) {
++        vma_writer_set_error(vmaw, "vma_writer_write: "
++                             "stream already closed %d", dev_id);
++        return -1;
++    }
++
++
++    if (cluster_num >= (((uint64_t)1)<<32)) {
++        vma_writer_set_error(vmaw, "vma_writer_write: "
++                             "cluster number out of range");
++        return -1;
++    }
++
++    if (dev_id == vmaw->vmstate_stream) {
++        if (cluster_num != vmaw->vmstate_clusters) {
++            vma_writer_set_error(vmaw, "vma_writer_write: "
++                                 "non sequential vmstate write");
++        }
++        vmaw->vmstate_clusters++;
++    } else if (cluster_num >= vmaw->stream_info[dev_id].cluster_count) {
++        vma_writer_set_error(vmaw, "vma_writer_write: cluster number too big");
++        return -1;
++    }
++
++    /* wait until buffer is available */
++    if (vma_writer_get_buffer(vmaw) < 0) {
++        vma_writer_set_error(vmaw, "vma_writer_write: "
++                             "vma_writer_get_buffer failed");
++        return -1;
++    }
++
++    DPRINTF("VMA WRITE %d %zd\n", dev_id, cluster_num);
++
++    uint16_t mask = 0;
++
++    if (buf) {
++        int i;
++        int bit = 1;
++        for (i = 0; i < 16; i++) {
++            const unsigned char *vmablock = buf + (i*VMA_BLOCK_SIZE);
++            if (!buffer_is_zero(vmablock, VMA_BLOCK_SIZE)) {
++                mask |= bit;
++                memcpy(vmaw->outbuf + vmaw->outbuf_pos, vmablock,
++                       VMA_BLOCK_SIZE);
++                vmaw->outbuf_pos += VMA_BLOCK_SIZE;
++            } else {
++                DPRINTF("VMA WRITE %zd ZERO BLOCK %d\n", cluster_num, i);
++                vmaw->stream_info[dev_id].zero_bytes += VMA_BLOCK_SIZE;
++                *zero_bytes += VMA_BLOCK_SIZE;
++            }
++
++            bit = bit << 1;
++        }
++    } else {
++        DPRINTF("VMA WRITE %zd ZERO CLUSTER\n", cluster_num);
++        vmaw->stream_info[dev_id].zero_bytes += VMA_CLUSTER_SIZE;
++        *zero_bytes += VMA_CLUSTER_SIZE;
++    }
++
++    uint64_t block_info = ((uint64_t)mask) << (32+16);
++    block_info |= ((uint64_t)dev_id) << 32;
++    block_info |= (cluster_num & 0xffffffff);
++    vmaw->outbuf_block_info[vmaw->outbuf_count] = block_info;
++
++    DPRINTF("VMA WRITE MASK %zd %zx\n", cluster_num, block_info);
++
++    vmaw->outbuf_count++;
++
++    /** NOTE: We allways write whole clusters, but we correctly set
++     * transferred bytes. So transferred == size when when everything
++     * went OK.
++     */
++    size_t transferred = VMA_CLUSTER_SIZE;
++
++    if (dev_id != vmaw->vmstate_stream) {
++        uint64_t last = (cluster_num + 1) * VMA_CLUSTER_SIZE;
++        if (last > vmaw->stream_info[dev_id].size) {
++            uint64_t diff = last - vmaw->stream_info[dev_id].size;
++            if (diff >= VMA_CLUSTER_SIZE) {
++                vma_writer_set_error(vmaw, "vma_writer_write: "
++                                     "read after last cluster");
++                return -1;
++            }
++            transferred -= diff;
++        }
++    }
++
++    vmaw->stream_info[dev_id].transferred += transferred;
++
++    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);
++
++    int i;
++
++    while (vmaw->co_writer) {
++        aio_poll(qemu_get_aio_context(), true);
++    }
++
++    assert(vmaw->co_writer == NULL);
++
++    if (vmaw->cmd) {
++        if (pclose(vmaw->cmd) < 0) {
++            vma_writer_set_error(vmaw, "vma_writer_close: "
++                                 "pclose failed - %s", g_strerror(errno));
++        }
++    } else {
++        if (close(vmaw->fd) < 0) {
++            vma_writer_set_error(vmaw, "vma_writer_close: "
++                                 "close failed - %s", g_strerror(errno));
++        }
++    }
++
++    for (i = 0; i <= 255; i++) {
++        VmaStreamInfo *si = &vmaw->stream_info[i];
++        if (si->size) {
++            if (!si->finished) {
++                vma_writer_set_error(vmaw, "vma_writer_close: "
++                                     "detected open stream '%s'", si->devname);
++            } else if ((si->transferred != si->size) &&
++                       (i != vmaw->vmstate_stream)) {
++                vma_writer_set_error(vmaw, "vma_writer_close: "
++                                     "incomplete stream '%s' (%zd != %zd)",
++                                     si->devname, si->transferred, si->size);
++            }
++        }
++    }
++
++    for (i = 0; i <= 255; i++) {
++        vmaw->stream_info[i].finished = 1; /* mark as closed */
++    }
++
++    vmaw->closed = 1;
++
++    if (vmaw->status < 0 && *errp == NULL) {
++        error_setg(errp, "%s", vmaw->errmsg);
++    }
++
++    return vmaw->status;
++}
++
++void vma_writer_destroy(VmaWriter *vmaw)
++{
++    assert(vmaw);
++
++    int i;
++
++    for (i = 0; i <= 255; i++) {
++        if (vmaw->stream_info[i].devname) {
++            g_free(vmaw->stream_info[i].devname);
++        }
++    }
++
++    if (vmaw->md5csum) {
++        g_checksum_free(vmaw->md5csum);
++    }
++
++    g_free(vmaw);
++}
+diff --git a/vma.c b/vma.c
+new file mode 100644
+index 0000000000..1b59fd1555
+--- /dev/null
++++ b/vma.c
+@@ -0,0 +1,756 @@
++/*
++ * VMA: Virtual Machine Archive
++ *
++ * Copyright (C) 2012-2013 Proxmox Server Solutions
++ *
++ * Authors:
++ *  Dietmar Maurer (dietmar@proxmox.com)
++ *
++ * 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 <glib.h>
++
++#include "vma.h"
++#include "qemu-common.h"
++#include "qemu/error-report.h"
++#include "qemu/main-loop.h"
++#include "qapi/qmp/qstring.h"
++#include "sysemu/block-backend.h"
++
++static void help(void)
++{
++    const char *help_msg =
++        "usage: vma command [command options]\n"
++        "\n"
++        "vma list <filename>\n"
++        "vma config <filename> [-c config]\n"
++        "vma create <filename> [-c config] pathname ...\n"
++        "vma extract <filename> [-r <fifo>] <targetdir>\n"
++        "vma verify <filename> [-v]\n"
++        ;
++
++    printf("%s", help_msg);
++    exit(1);
++}
++
++static const char *extract_devname(const char *path, char **devname, int index)
++{
++    assert(path);
++
++    const char *sep = strchr(path, '=');
++
++    if (sep) {
++        *devname = g_strndup(path, sep - path);
++        path = sep + 1;
++    } else {
++        if (index >= 0) {
++            *devname = g_strdup_printf("disk%d", index);
++        } else {
++            *devname = NULL;
++        }
++    }
++
++    return path;
++}
++
++static void print_content(VmaReader *vmar)
++{
++    assert(vmar);
++
++    VmaHeader *head = vma_reader_get_header(vmar);
++
++    GList *l = vma_reader_get_config_data(vmar);
++    while (l && l->data) {
++        VmaConfigData *cdata = (VmaConfigData *)l->data;
++        l = g_list_next(l);
++        printf("CFG: size: %d name: %s\n", cdata->len, cdata->name);
++    }
++
++    int i;
++    VmaDeviceInfo *di;
++    for (i = 1; i < 255; i++) {
++        di = vma_reader_get_device_info(vmar, i);
++        if (di) {
++            if (strcmp(di->devname, "vmstate") == 0) {
++                printf("VMSTATE: dev_id=%d memory: %zd\n", i, di->size);
++            } else {
++                printf("DEV: dev_id=%d size: %zd devname: %s\n",
++                       i, di->size, di->devname);
++            }
++        }
++    }
++    /* ctime is the last entry we print */
++    printf("CTIME: %s", ctime(&head->ctime));
++    fflush(stdout);
++}
++
++static int list_content(int argc, char **argv)
++{
++    int c, ret = 0;
++    const char *filename;
++
++    for (;;) {
++        c = getopt(argc, argv, "h");
++        if (c == -1) {
++            break;
++        }
++        switch (c) {
++        case '?':
++        case 'h':
++            help();
++            break;
++        default:
++            g_assert_not_reached();
++        }
++    }
++
++    /* Get the filename */
++    if ((optind + 1) != argc) {
++        help();
++    }
++    filename = argv[optind++];
++
++    Error *errp = NULL;
++    VmaReader *vmar = vma_reader_create(filename, &errp);
++
++    if (!vmar) {
++        g_error("%s", error_get_pretty(errp));
++    }
++
++    print_content(vmar);
++
++    vma_reader_destroy(vmar);
++
++    return ret;
++}
++
++typedef struct RestoreMap {
++    char *devname;
++    char *path;
++    char *format;
++    bool write_zero;
++} RestoreMap;
++
++static int extract_content(int argc, char **argv)
++{
++    int c, ret = 0;
++    int verbose = 0;
++    const char *filename;
++    const char *dirname;
++    const char *readmap = NULL;
++
++    for (;;) {
++        c = getopt(argc, argv, "hvr:");
++        if (c == -1) {
++            break;
++        }
++        switch (c) {
++        case '?':
++        case 'h':
++            help();
++            break;
++        case 'r':
++            readmap = optarg;
++            break;
++        case 'v':
++            verbose = 1;
++            break;
++        default:
++            help();
++        }
++    }
++
++    /* Get the filename */
++    if ((optind + 2) != argc) {
++        help();
++    }
++    filename = argv[optind++];
++    dirname = argv[optind++];
++
++    Error *errp = NULL;
++    VmaReader *vmar = vma_reader_create(filename, &errp);
++
++    if (!vmar) {
++        g_error("%s", error_get_pretty(errp));
++    }
++
++    if (mkdir(dirname, 0777) < 0) {
++        g_error("unable to create target directory %s - %s",
++                dirname, g_strerror(errno));
++    }
++
++    GList *l = vma_reader_get_config_data(vmar);
++    while (l && l->data) {
++        VmaConfigData *cdata = (VmaConfigData *)l->data;
++        l = g_list_next(l);
++        char *cfgfn = g_strdup_printf("%s/%s", dirname, cdata->name);
++        GError *err = NULL;
++        if (!g_file_set_contents(cfgfn, (gchar *)cdata->data, cdata->len,
++                                 &err)) {
++            g_error("unable to write file: %s", err->message);
++        }
++    }
++
++    GHashTable *devmap = g_hash_table_new(g_str_hash, g_str_equal);
++
++    if (readmap) {
++        print_content(vmar);
++
++        FILE *map = fopen(readmap, "r");
++        if (!map) {
++            g_error("unable to open fifo %s - %s", readmap, g_strerror(errno));
++        }
++
++        while (1) {
++            char inbuf[8192];
++            char *line = fgets(inbuf, sizeof(inbuf), map);
++            if (!line || line[0] == '\0' || !strcmp(line, "done\n")) {
++                break;
++            }
++            int len = strlen(line);
++            if (line[len - 1] == '\n') {
++                line[len - 1] = '\0';
++                if (len == 1) {
++                    break;
++                }
++            }
++
++            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 = line + 2;
++                write_zero = false;
++            } else if (line[0] == '1' && line[1] == ':') {
++                path = line + 2;
++                write_zero = true;
++            } else {
++                g_error("read map failed - parse error ('%s')", inbuf);
++            }
++
++            char *devname = NULL;
++            path = extract_devname(path, &devname, -1);
++            if (!devname) {
++                g_error("read map failed - no dev name specified ('%s')",
++                        inbuf);
++            }
++
++            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);
++
++        };
++    }
++
++    int i;
++    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)) {
++            vmstate_stream = i;
++            char *statefn = g_strdup_printf("%s/vmstate.bin", dirname);
++            vmstate_fd = open(statefn, O_WRONLY|O_CREAT|O_EXCL, 0644);
++            if (vmstate_fd < 0) {
++                g_error("create vmstate file '%s' failed - %s", statefn,
++                        g_strerror(errno));
++            }
++            g_free(statefn);
++        } else if (di) {
++            char *devfn = NULL;
++            const char *format = NULL;
++            int flags = BDRV_O_RDWR | BDRV_O_NO_FLUSH;
++            bool write_zero = true;
++
++            if (readmap) {
++                RestoreMap *map;
++                map = (RestoreMap *)g_hash_table_lookup(devmap, di->devname);
++                if (map == NULL) {
++                    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",
++                                        dirname, di->devname);
++                printf("DEVINFO %s %zd\n", devfn, di->size);
++
++                bdrv_img_create(devfn, "raw", NULL, NULL, NULL, di->size,
++                                flags, true, &errp);
++                if (errp) {
++                    g_error("can't create file %s: %s", devfn,
++                            error_get_pretty(errp));
++                }
++
++                /* Note: we created an empty file above, so there is no
++                 * need to write zeroes (so we generate a sparse file)
++                 */
++                write_zero = false;
++            }
++
++          size_t devlen = strlen(devfn);
++          QDict *options = NULL;
++            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"));
++          }
++
++
++          if (errp || !(blk = blk_new_open(devfn, NULL, options, flags, &errp))) {
++                g_error("can't open file %s - %s", devfn,
++                        error_get_pretty(errp));
++            }
++
++            if (vma_reader_register_bs(vmar, i, blk, write_zero, &errp) < 0) {
++                g_error("%s", error_get_pretty(errp));
++            }
++
++            if (!readmap) {
++                g_free(devfn);
++            }
++        }
++    }
++
++    if (vma_reader_restore(vmar, vmstate_fd, verbose, &errp) < 0) {
++        g_error("restore failed - %s", error_get_pretty(errp));
++    }
++
++    if (!readmap) {
++        for (i = 1; i < 255; i++) {
++            VmaDeviceInfo *di = vma_reader_get_device_info(vmar, i);
++            if (di && (i != vmstate_stream)) {
++                char *tmpfn = g_strdup_printf("%s/tmp-disk-%s.raw",
++                                              dirname, di->devname);
++                char *fn = g_strdup_printf("%s/disk-%s.raw",
++                                           dirname, di->devname);
++                if (rename(tmpfn, fn) != 0) {
++                    g_error("rename %s to %s failed - %s",
++                            tmpfn, fn, g_strerror(errno));
++                }
++            }
++        }
++    }
++
++    vma_reader_destroy(vmar);
++
++    blk_unref(blk);
++
++    bdrv_close_all();
++
++    return ret;
++}
++
++static int verify_content(int argc, char **argv)
++{
++    int c, ret = 0;
++    int verbose = 0;
++    const char *filename;
++
++    for (;;) {
++        c = getopt(argc, argv, "hv");
++        if (c == -1) {
++            break;
++        }
++        switch (c) {
++        case '?':
++        case 'h':
++            help();
++            break;
++        case 'v':
++            verbose = 1;
++            break;
++        default:
++            help();
++        }
++    }
++
++    /* Get the filename */
++    if ((optind + 1) != argc) {
++        help();
++    }
++    filename = argv[optind++];
++
++    Error *errp = NULL;
++    VmaReader *vmar = vma_reader_create(filename, &errp);
++
++    if (!vmar) {
++        g_error("%s", error_get_pretty(errp));
++    }
++
++    if (verbose) {
++        print_content(vmar);
++    }
++
++    if (vma_reader_verify(vmar, verbose, &errp) < 0) {
++        g_error("verify failed - %s", error_get_pretty(errp));
++    }
++
++    vma_reader_destroy(vmar);
++
++    bdrv_close_all();
++
++    return ret;
++}
++
++typedef struct BackupJob {
++    BlockBackend *target;
++    int64_t len;
++    VmaWriter *vmaw;
++    uint8_t dev_id;
++} 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;
++    struct iovec iov;
++    QEMUIOVector qiov;
++
++    int64_t start, end;
++    int ret = 0;
++
++    unsigned char *buf = blk_blockalign(job->target, VMA_CLUSTER_SIZE);
++
++    start = 0;
++    end = DIV_ROUND_UP(job->len / BDRV_SECTOR_SIZE,
++                       BACKUP_SECTORS_PER_CLUSTER);
++
++    for (; start < end; start++) {
++        iov.iov_base = buf;
++        iov.iov_len = VMA_CLUSTER_SIZE;
++        qemu_iovec_init_external(&qiov, &iov, 1);
++
++        ret = blk_co_preadv(job->target, start * VMA_CLUSTER_SIZE,
++                            VMA_CLUSTER_SIZE, &qiov, 0);
++        if (ret < 0) {
++            vma_writer_set_error(job->vmaw, "read error", -1);
++            goto out;
++        }
++
++        size_t zb = 0;
++        if (vma_writer_write(job->vmaw, job->dev_id, start, buf, &zb) < 0) {
++            vma_writer_set_error(job->vmaw, "backup_dump_cb vma_writer_write failed", -1);
++            goto out;
++        }
++    }
++
++
++out:
++    if (vma_writer_close_stream(job->vmaw, job->dev_id) <= 0) {
++        Error *err = NULL;
++        if (vma_writer_close(job->vmaw, &err) != 0) {
++            g_warning("vma_writer_close failed %s", error_get_pretty(err));
++        }
++    }
++}
++
++static int create_archive(int argc, char **argv)
++{
++    int i, c;
++    int verbose = 0;
++    const char *archivename;
++    GList *config_files = NULL;
++
++    for (;;) {
++        c = getopt(argc, argv, "hvc:");
++        if (c == -1) {
++            break;
++        }
++        switch (c) {
++        case '?':
++        case 'h':
++            help();
++            break;
++        case 'c':
++            config_files = g_list_append(config_files, optarg);
++            break;
++        case 'v':
++            verbose = 1;
++            break;
++        default:
++            g_assert_not_reached();
++        }
++    }
++
++
++    /* make sure we an archive name */
++    if ((optind + 1) > argc) {
++        help();
++    }
++
++    archivename = argv[optind++];
++
++    uuid_t uuid;
++    uuid_generate(uuid);
++
++    Error *local_err = NULL;
++    VmaWriter *vmaw = vma_writer_create(archivename, uuid, &local_err);
++
++    if (vmaw == NULL) {
++        g_error("%s", error_get_pretty(local_err));
++    }
++
++    GList *l = config_files;
++    while (l && l->data) {
++        char *name = l->data;
++        char *cdata = NULL;
++        gsize clen = 0;
++        GError *err = NULL;
++        if (!g_file_get_contents(name, &cdata, &clen, &err)) {
++            unlink(archivename);
++            g_error("Unable to read file: %s", err->message);
++        }
++
++        if (vma_writer_add_config(vmaw, name, cdata, clen) != 0) {
++            unlink(archivename);
++            g_error("Unable to append config data %s (len = %zd)",
++                    name, clen);
++        }
++        l = g_list_next(l);
++    }
++
++    int devcount = 0;
++    while (optind < argc) {
++        const char *path = argv[optind++];
++        char *devname = NULL;
++        path = extract_devname(path, &devname, devcount++);
++
++        Error *errp = NULL;
++        BlockBackend *target;
++
++        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 = blk_getlength(target);
++        int dev_id = vma_writer_register_stream(vmaw, devname, size);
++        if (dev_id <= 0) {
++            unlink(archivename);
++            g_error("vma_writer_register_stream '%s' failed", devname);
++        }
++
++        BackupJob *job = g_new0(BackupJob, 1);
++        job->len = size;
++        job->target = target;
++        job->vmaw = vmaw;
++        job->dev_id = dev_id;
++
++        Coroutine *co = qemu_coroutine_create(backup_run, job);
++        qemu_coroutine_enter(co);
++    }
++
++    VmaStatus vmastat;
++    int percent = 0;
++    int last_percent = -1;
++
++    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);
++
++                    last_percent = percent;
++                }
++            }
++
++            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();
++
++    vma_writer_get_status(vmaw, &vmastat);
++
++    if (verbose) {
++        for (i = 0; i < 256; i++) {
++            VmaStreamInfo *si = &vmastat.stream_info[i];
++            if (si->size) {
++                fprintf(stderr, "image %s: size=%zd zeros=%zd saved=%zd\n",
++                        si->devname, si->size, si->zero_bytes,
++                        si->size - si->zero_bytes);
++            }
++        }
++    }
++
++    if (vmastat.status < 0) {
++        unlink(archivename);
++        g_error("creating vma archive failed");
++    }
++
++    return 0;
++}
++
++static int dump_config(int argc, char **argv)
++{
++    int c, ret = 0;
++    const char *filename;
++    const char *config_name = "qemu-server.conf";
++
++    for (;;) {
++        c = getopt(argc, argv, "hc:");
++        if (c == -1) {
++            break;
++        }
++        switch (c) {
++        case '?':
++        case 'h':
++            help();
++            break;
++        case 'c':
++            config_name = optarg;
++            break;
++        default:
++            help();
++        }
++    }
++
++    /* Get the filename */
++    if ((optind + 1) != argc) {
++        help();
++    }
++    filename = argv[optind++];
++
++    Error *errp = NULL;
++    VmaReader *vmar = vma_reader_create(filename, &errp);
++
++    if (!vmar) {
++        g_error("%s", error_get_pretty(errp));
++    }
++
++    int found = 0;
++    GList *l = vma_reader_get_config_data(vmar);
++    while (l && l->data) {
++        VmaConfigData *cdata = (VmaConfigData *)l->data;
++        l = g_list_next(l);
++        if (strcmp(cdata->name, config_name) == 0) {
++            found = 1;
++            fwrite(cdata->data,  cdata->len, 1, stdout);
++            break;
++        }
++    }
++
++    vma_reader_destroy(vmar);
++
++    bdrv_close_all();
++
++    if (!found) {
++        fprintf(stderr, "unable to find configuration data '%s'\n", config_name);
++        return -1;
++    }
++
++    return ret;
++}
++
++int main(int argc, char **argv)
++{
++    const char *cmdname;
++    Error *main_loop_err = NULL;
++
++    error_set_progname(argv[0]);
++
++    if (qemu_init_main_loop(&main_loop_err)) {
++        g_error("%s", error_get_pretty(main_loop_err));
++    }
++
++    bdrv_init();
++
++    if (argc < 2) {
++        help();
++    }
++
++    cmdname = argv[1];
++    argc--; argv++;
++
++
++    if (!strcmp(cmdname, "list")) {
++        return list_content(argc, argv);
++    } else if (!strcmp(cmdname, "create")) {
++        return create_archive(argc, argv);
++    } else if (!strcmp(cmdname, "extract")) {
++        return extract_content(argc, argv);
++    } else if (!strcmp(cmdname, "verify")) {
++        return verify_content(argc, argv);
++    } else if (!strcmp(cmdname, "config")) {
++        return dump_config(argc, argv);
++    }
++
++    help();
++    return 0;
++}
+diff --git a/vma.h b/vma.h
+new file mode 100644
+index 0000000000..c895c97f6d
+--- /dev/null
++++ b/vma.h
+@@ -0,0 +1,150 @@
++/*
++ * VMA: Virtual Machine Archive
++ *
++ * Copyright (C) Proxmox Server Solutions
++ *
++ * Authors:
++ *  Dietmar Maurer (dietmar@proxmox.com)
++ *
++ * This work is licensed under the terms of the GNU GPL, version 2 or later.
++ * See the COPYING file in the top-level directory.
++ *
++ */
++
++#ifndef BACKUP_VMA_H
++#define BACKUP_VMA_H
++
++#include <uuid/uuid.h>
++#include "qapi/error.h"
++#include "block/block.h"
++
++#define VMA_BLOCK_BITS 12
++#define VMA_BLOCK_SIZE (1<<VMA_BLOCK_BITS)
++#define VMA_CLUSTER_BITS (VMA_BLOCK_BITS+4)
++#define VMA_CLUSTER_SIZE (1<<VMA_CLUSTER_BITS)
++
++#if VMA_CLUSTER_SIZE != 65536
++#error unexpected cluster size
++#endif
++
++#define VMA_EXTENT_HEADER_SIZE 512
++#define VMA_BLOCKS_PER_EXTENT 59
++#define VMA_MAX_CONFIGS 256
++
++#define VMA_MAX_EXTENT_SIZE \
++    (VMA_EXTENT_HEADER_SIZE+VMA_CLUSTER_SIZE*VMA_BLOCKS_PER_EXTENT)
++#if VMA_MAX_EXTENT_SIZE != 3867136
++#error unexpected VMA_EXTENT_SIZE
++#endif
++
++/* File Format Definitions */
++
++#define VMA_MAGIC (GUINT32_TO_BE(('V'<<24)|('M'<<16)|('A'<<8)|0x00))
++#define VMA_EXTENT_MAGIC (GUINT32_TO_BE(('V'<<24)|('M'<<16)|('A'<<8)|'E'))
++
++typedef struct VmaDeviceInfoHeader {
++    uint32_t devname_ptr; /* offset into blob_buffer table */
++    uint32_t reserved0;
++    uint64_t size; /* device size in bytes */
++    uint64_t reserved1;
++    uint64_t reserved2;
++} VmaDeviceInfoHeader;
++
++typedef struct VmaHeader {
++    uint32_t magic;
++    uint32_t version;
++    unsigned char uuid[16];
++    int64_t ctime;
++    unsigned char md5sum[16];
++
++    uint32_t blob_buffer_offset;
++    uint32_t blob_buffer_size;
++    uint32_t header_size;
++
++    unsigned char reserved[1984];
++
++    uint32_t config_names[VMA_MAX_CONFIGS]; /* offset into blob_buffer table */
++    uint32_t config_data[VMA_MAX_CONFIGS];  /* offset into blob_buffer table */
++
++    uint32_t reserved1;
++
++    VmaDeviceInfoHeader dev_info[256];
++} VmaHeader;
++
++typedef struct VmaExtentHeader {
++    uint32_t magic;
++    uint16_t reserved1;
++    uint16_t block_count;
++    unsigned char uuid[16];
++    unsigned char md5sum[16];
++    uint64_t blockinfo[VMA_BLOCKS_PER_EXTENT];
++} VmaExtentHeader;
++
++/* functions/definitions to read/write vma files */
++
++typedef struct VmaReader VmaReader;
++
++typedef struct VmaWriter VmaWriter;
++
++typedef struct VmaConfigData {
++    const char *name;
++    const void *data;
++    uint32_t len;
++} VmaConfigData;
++
++typedef struct VmaStreamInfo {
++    uint64_t size;
++    uint64_t cluster_count;
++    uint64_t transferred;
++    uint64_t zero_bytes;
++    int finished;
++    char *devname;
++} VmaStreamInfo;
++
++typedef struct VmaStatus {
++    int status;
++    bool closed;
++    char errmsg[8192];
++    char uuid_str[37];
++    VmaStreamInfo stream_info[256];
++} VmaStatus;
++
++typedef struct VmaDeviceInfo {
++    uint64_t size; /* device size in bytes */
++    const char *devname;
++} 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);
++int vma_writer_register_stream(VmaWriter *vmaw, const char *devname,
++                               size_t size);
++
++int64_t coroutine_fn vma_writer_write(VmaWriter *vmaw, uint8_t dev_id,
++                                      int64_t cluster_num,
++                                      const unsigned char *buf,
++                                      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, ...);
++
++
++VmaReader *vma_reader_create(const char *filename, Error **errp);
++void vma_reader_destroy(VmaReader *vmar);
++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,
++                           BlockBackend *target, bool write_zeroes,
++                           Error **errp);
++int vma_reader_restore(VmaReader *vmar, int vmstate_fd, bool verbose,
++                       Error **errp);
++int vma_reader_verify(VmaReader *vmar, bool verbose, Error **errp);
++
++#endif /* BACKUP_VMA_H */
+-- 
+2.11.0
+
diff --git a/debian/patches/pve/0027-backup-introduce-vma-archive-format.patch b/debian/patches/pve/0027-backup-introduce-vma-archive-format.patch
deleted file mode 100644 (file)
index b09ac39..0000000
+++ /dev/null
@@ -1,1399 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Wolfgang Bumiller <w.bumiller@proxmox.com>
-Date: Wed, 2 Aug 2017 13:51:02 +0200
-Subject: [PATCH] backup: introduce vma archive format
-
----
- MAINTAINERS          |   6 +
- block/Makefile.objs  |   3 +
- block/vma.c          | 424 +++++++++++++++++++++++++++++++++++++++++++
- blockdev.c           | 498 +++++++++++++++++++++++++++++++++++++++++++++++++++
- configure            |  30 ++++
- hmp-commands-info.hx |  13 ++
- hmp-commands.hx      |  31 ++++
- hmp.c                |  63 +++++++
- hmp.h                |   3 +
- qapi-schema.json     |  91 ++++++++++
- qapi/block-core.json |  20 ++-
- 11 files changed, 1180 insertions(+), 2 deletions(-)
- create mode 100644 block/vma.c
-
-diff --git a/MAINTAINERS b/MAINTAINERS
-index 430efb0ab7..6a7d338aad 100644
---- a/MAINTAINERS
-+++ b/MAINTAINERS
-@@ -1811,6 +1811,12 @@ L: qemu-block@nongnu.org
- S: Supported
- F: block/vvfat.c
-+VMA
-+M: Wolfgang Bumiller <w.bumiller@proxmox.com>.
-+L: pve-devel@proxmox.com
-+S: Supported
-+F: block/vma.c
-+
- Image format fuzzer
- M: Stefan Hajnoczi <stefanha@redhat.com>
- L: qemu-block@nongnu.org
-diff --git a/block/Makefile.objs b/block/Makefile.objs
-index 8cdac08db5..6df5567dd3 100644
---- a/block/Makefile.objs
-+++ b/block/Makefile.objs
-@@ -21,6 +21,7 @@ block-obj-$(CONFIG_CURL) += curl.o
- block-obj-$(CONFIG_RBD) += rbd.o
- block-obj-$(CONFIG_GLUSTERFS) += gluster.o
- block-obj-$(CONFIG_LIBSSH2) += ssh.o
-+block-obj-$(CONFIG_VMA) += vma.o
- block-obj-y += accounting.o dirty-bitmap.o
- block-obj-y += write-threshold.o
- block-obj-y += backup.o
-@@ -45,3 +46,5 @@ block-obj-$(if $(CONFIG_BZIP2),m,n) += dmg-bz2.o
- dmg-bz2.o-libs     := $(BZIP2_LIBS)
- qcow.o-libs        := -lz
- linux-aio.o-libs   := -laio
-+vma.o-cflags       := $(VMA_CFLAGS)
-+vma.o-libs         := $(VMA_LIBS)
-diff --git a/block/vma.c b/block/vma.c
-new file mode 100644
-index 0000000000..7151514f94
---- /dev/null
-+++ b/block/vma.c
-@@ -0,0 +1,424 @@
-+/*
-+ * VMA archive backend for QEMU, container object
-+ *
-+ * Copyright (C) 2017 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 <vma/vma.h>
-+
-+#include "qemu/osdep.h"
-+#include "qemu/uuid.h"
-+#include "qemu-common.h"
-+#include "qapi/error.h"
-+#include "qapi/qmp/qerror.h"
-+#include "qapi/qmp/qstring.h"
-+#include "qom/object.h"
-+#include "qom/object_interfaces.h"
-+#include "block/block_int.h"
-+
-+/* exported interface */
-+void vma_object_add_config_file(Object *obj, const char *name,
-+                                const char *contents, size_t len,
-+                                Error **errp);
-+
-+#define TYPE_VMA_OBJECT "vma"
-+#define VMA_OBJECT(obj) \
-+    OBJECT_CHECK(VMAObjectState, (obj), TYPE_VMA_OBJECT)
-+#define VMA_OBJECT_GET_CLASS(obj) \
-+    OBJECT_GET_CLASS(VMAObjectClass, (obj), TYPE_VMA_OBJECT)
-+
-+typedef struct VMAObjectClass {
-+    ObjectClass parent_class;
-+} VMAObjectClass;
-+
-+typedef struct VMAObjectState {
-+    Object parent;
-+
-+    char        *filename;
-+
-+    QemuUUID     uuid;
-+    bool         blocked;
-+    VMAWriter   *vma;
-+    QemuMutex    mutex;
-+} VMAObjectState;
-+
-+static VMAObjectState *vma_by_id(const char *name)
-+{
-+    Object *container;
-+    Object *obj;
-+
-+    container = object_get_objects_root();
-+    obj = object_resolve_path_component(container, name);
-+
-+    return VMA_OBJECT(obj);
-+}
-+
-+static void vma_object_class_complete(UserCreatable *uc, Error **errp)
-+{
-+    int rc;
-+    VMAObjectState *vo = VMA_OBJECT(uc);
-+    VMAObjectClass *voc = VMA_OBJECT_GET_CLASS(uc);
-+    (void)!vo;
-+    (void)!voc;
-+
-+    if (!vo->filename) {
-+        error_setg(errp, "Parameter 'filename' is required");
-+        return;
-+    }
-+
-+    rc = VMAWriter_fopen(vo->filename, &vo->vma);
-+    if (rc < 0) {
-+        error_setg_errno(errp, -rc, "failed to create VMA archive");
-+        return;
-+    }
-+
-+    rc = VMAWriter_set_uuid(vo->vma, vo->uuid.data, sizeof(vo->uuid.data));
-+    if (rc < 0) {
-+        error_setg_errno(errp, -rc, "failed to set UUID of VMA archive");
-+        return;
-+    }
-+
-+    qemu_mutex_init(&vo->mutex);
-+}
-+
-+static bool vma_object_can_be_deleted(UserCreatable *uc, Error **errp)
-+{
-+    //VMAObjectState *vo = VMA_OBJECT(uc);
-+    //if (!vo->vma) {
-+    //    return true;
-+    //}
-+    //return false;
-+    return true;
-+}
-+
-+static void vma_object_class_init(ObjectClass *oc, void *data)
-+{
-+    UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
-+
-+    ucc->can_be_deleted = vma_object_can_be_deleted;
-+    ucc->complete = vma_object_class_complete;
-+}
-+
-+static char *vma_object_get_filename(Object *obj, Error **errp)
-+{
-+    VMAObjectState *vo = VMA_OBJECT(obj);
-+
-+    return g_strdup(vo->filename);
-+}
-+
-+static void vma_object_set_filename(Object *obj, const char *str, Error **errp)
-+{
-+    VMAObjectState *vo = VMA_OBJECT(obj);
-+
-+    if (vo->vma) {
-+        error_setg(errp, "filename cannot be changed after creation");
-+        return;
-+    }
-+
-+    g_free(vo->filename);
-+    vo->filename = g_strdup(str);
-+}
-+
-+static char *vma_object_get_uuid(Object *obj, Error **errp)
-+{
-+    VMAObjectState *vo = VMA_OBJECT(obj);
-+
-+    return qemu_uuid_unparse_strdup(&vo->uuid);
-+}
-+
-+static void vma_object_set_uuid(Object *obj, const char *str, Error **errp)
-+{
-+    VMAObjectState *vo = VMA_OBJECT(obj);
-+
-+    if (vo->vma) {
-+        error_setg(errp, "uuid cannot be changed after creation");
-+        return;
-+    }
-+
-+    qemu_uuid_parse(str, &vo->uuid);
-+}
-+
-+static bool vma_object_get_blocked(Object *obj, Error **errp)
-+{
-+    VMAObjectState *vo = VMA_OBJECT(obj);
-+
-+    return vo->blocked;
-+}
-+
-+static void vma_object_set_blocked(Object *obj, bool blocked, Error **errp)
-+{
-+    VMAObjectState *vo = VMA_OBJECT(obj);
-+
-+    (void)errp;
-+
-+    vo->blocked = blocked;
-+}
-+
-+void vma_object_add_config_file(Object *obj, const char *name,
-+                                const char *contents, size_t len,
-+                                Error **errp)
-+{
-+    int rc;
-+    VMAObjectState *vo = VMA_OBJECT(obj);
-+
-+    if (!vo || !vo->vma) {
-+        error_setg(errp, "not a valid vma object to add config files to");
-+        return;
-+    }
-+
-+    rc = VMAWriter_addConfigFile(vo->vma, name, contents, len);
-+    if (rc < 0) {
-+        error_setg_errno(errp, -rc, "failed to add config file to VMA");
-+        return;
-+    }
-+}
-+
-+static void vma_object_init(Object *obj)
-+{
-+    VMAObjectState *vo = VMA_OBJECT(obj);
-+    (void)!vo;
-+
-+    object_property_add_str(obj, "filename",
-+                            vma_object_get_filename, vma_object_set_filename,
-+                            NULL);
-+    object_property_add_str(obj, "uuid",
-+                            vma_object_get_uuid, vma_object_set_uuid,
-+                            NULL);
-+    object_property_add_bool(obj, "blocked",
-+                            vma_object_get_blocked, vma_object_set_blocked,
-+                            NULL);
-+}
-+
-+static void vma_object_finalize(Object *obj)
-+{
-+    VMAObjectState *vo = VMA_OBJECT(obj);
-+    VMAObjectClass *voc = VMA_OBJECT_GET_CLASS(obj);
-+    (void)!voc;
-+
-+    qemu_mutex_destroy(&vo->mutex);
-+
-+    VMAWriter_destroy(vo->vma, true);
-+    g_free(vo->filename);
-+}
-+
-+static const TypeInfo vma_object_info = {
-+    .name = TYPE_VMA_OBJECT,
-+    .parent = TYPE_OBJECT,
-+    .class_size = sizeof(VMAObjectClass),
-+    .class_init = vma_object_class_init,
-+    .instance_size = sizeof(VMAObjectState),
-+    .instance_init = vma_object_init,
-+    .instance_finalize = vma_object_finalize,
-+    .interfaces = (InterfaceInfo[]) {
-+        { TYPE_USER_CREATABLE },
-+        { }
-+    }
-+};
-+
-+static void register_types(void)
-+{
-+    type_register_static(&vma_object_info);
-+}
-+
-+type_init(register_types);
-+
-+typedef struct {
-+    VMAObjectState *vma_obj;
-+    char           *name;
-+    size_t          device_id;
-+    uint64_t        byte_size;
-+} BDRVVMAState;
-+
-+static void qemu_vma_parse_filename(const char *filename, QDict *options,
-+                                    Error **errp)
-+{
-+    char *sep;
-+
-+    sep = strchr(filename, '/');
-+    if (!sep || sep == filename) {
-+        error_setg(errp, "VMA filename should be <vma-object>/<device-name>");
-+        return;
-+    }
-+
-+    qdict_put(options, "vma", qstring_from_substr(filename, 0, sep-filename-1));
-+
-+    while (*sep && *sep == '/')
-+        ++sep;
-+    if (!*sep) {
-+        error_setg(errp, "missing device name\n");
-+        return;
-+    }
-+
-+    qdict_put(options, "name", qstring_from_str(sep));
-+}
-+
-+static QemuOptsList runtime_opts = {
-+    .name = "vma-drive",
-+    .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
-+    .desc = {
-+        {
-+            .name = "vma",
-+            .type = QEMU_OPT_STRING,
-+            .help = "VMA Object name",
-+        },
-+        {
-+            .name = "name",
-+            .type = QEMU_OPT_STRING,
-+            .help = "VMA device name",
-+        },
-+        {
-+            .name = BLOCK_OPT_SIZE,
-+            .type = QEMU_OPT_SIZE,
-+            .help = "Virtual disk size"
-+        },
-+        { /* end of list */ }
-+    },
-+};
-+static int qemu_vma_open(BlockDriverState *bs, QDict *options, int flags,
-+                         Error **errp)
-+{
-+    Error *local_err = NULL;
-+    BDRVVMAState *s = bs->opaque;
-+    QemuOpts *opts;
-+    const char *vma_id, *device_name;
-+    ssize_t dev_id;
-+    int64_t bytes = 0;
-+    int ret;
-+
-+    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 failed_opts;
-+    }
-+
-+    bytes = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
-+                     BDRV_SECTOR_SIZE);
-+
-+    vma_id = qemu_opt_get(opts, "vma");
-+    device_name = qemu_opt_get(opts, "name");
-+
-+    VMAObjectState *vma = vma_by_id(vma_id);
-+    if (!vma) {
-+        ret = -EINVAL;
-+        error_setg(errp, "no such VMA object: %s", vma_id);
-+        goto failed_opts;
-+    }
-+
-+    dev_id = VMAWriter_findDevice(vma->vma, device_name);
-+    if (dev_id >= 0) {
-+        error_setg(errp, "drive already exists in VMA object");
-+        ret = -EIO;
-+        goto failed_opts;
-+    }
-+
-+    dev_id = VMAWriter_addDevice(vma->vma, device_name, (uint64_t)bytes);
-+    if (dev_id < 0) {
-+        error_setg_errno(errp, -dev_id, "failed to add VMA device");
-+        ret = -EIO;
-+        goto failed_opts;
-+    }
-+
-+    object_ref(OBJECT(vma));
-+    s->vma_obj = vma;
-+    s->name = g_strdup(device_name);
-+    s->device_id = (size_t)dev_id;
-+    s->byte_size = bytes;
-+
-+    ret = 0;
-+
-+failed_opts:
-+    qemu_opts_del(opts);
-+    return ret;
-+}
-+
-+static void qemu_vma_close(BlockDriverState *bs)
-+{
-+    BDRVVMAState *s = bs->opaque;
-+
-+    (void)VMAWriter_finishDevice(s->vma_obj->vma, s->device_id);
-+    object_unref(OBJECT(s->vma_obj));
-+
-+    g_free(s->name);
-+}
-+
-+static int64_t qemu_vma_getlength(BlockDriverState *bs)
-+{
-+    BDRVVMAState *s = bs->opaque;
-+
-+    return s->byte_size;
-+}
-+
-+static coroutine_fn int qemu_vma_co_writev(BlockDriverState *bs,
-+                                           int64_t sector_num,
-+                                           int nb_sectors,
-+                                           QEMUIOVector *qiov)
-+{
-+    size_t i;
-+    ssize_t rc;
-+    BDRVVMAState *s = bs->opaque;
-+    VMAObjectState *vo = s->vma_obj;
-+    off_t offset = sector_num * BDRV_SECTOR_SIZE;
-+
-+    qemu_mutex_lock(&vo->mutex);
-+    if (vo->blocked) {
-+        return -EPERM;
-+    }
-+    for (i = 0; i != qiov->niov; ++i) {
-+        const struct iovec *v = &qiov->iov[i];
-+        size_t blocks = v->iov_len / VMA_BLOCK_SIZE;
-+        if (blocks * VMA_BLOCK_SIZE != v->iov_len) {
-+            return -EIO;
-+        }
-+        rc = VMAWriter_writeBlocks(vo->vma, s->device_id,
-+                                   v->iov_base, blocks, offset);
-+        if (errno) {
-+            return -errno;
-+        }
-+        if (rc != blocks) {
-+            return -EIO;
-+        }
-+        offset += v->iov_len;
-+    }
-+    qemu_mutex_unlock(&vo->mutex);
-+    return 0;
-+}
-+
-+static int qemu_vma_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
-+{
-+    bdi->cluster_size = VMA_CLUSTER_SIZE;
-+    bdi->unallocated_blocks_are_zero = true;
-+    bdi->can_write_zeroes_with_unmap = false;
-+    return 0;
-+}
-+
-+static BlockDriver bdrv_vma_drive = {
-+    .format_name                  = "vma-drive",
-+    .instance_size                = sizeof(BDRVVMAState),
-+
-+#if 0
-+    .bdrv_create                  = qemu_vma_create,
-+    .create_opts                  = &qemu_vma_create_opts,
-+#endif
-+
-+    .bdrv_parse_filename          = qemu_vma_parse_filename,
-+    .bdrv_file_open               = qemu_vma_open,
-+
-+    .bdrv_close                   = qemu_vma_close,
-+    .bdrv_has_zero_init           = bdrv_has_zero_init_1,
-+    .bdrv_getlength               = qemu_vma_getlength,
-+    .bdrv_get_info                = qemu_vma_get_info,
-+
-+    .bdrv_co_writev               = qemu_vma_co_writev,
-+};
-+
-+static void bdrv_vma_init(void)
-+{
-+    bdrv_register(&bdrv_vma_drive);
-+}
-+
-+block_init(bdrv_vma_init);
-diff --git a/blockdev.c b/blockdev.c
-index 9b6cfafd33..534c00f5da 100644
---- a/blockdev.c
-+++ b/blockdev.c
-@@ -31,10 +31,12 @@
-  */
- #include "qemu/osdep.h"
-+#include "qemu/uuid.h"
- #include "sysemu/block-backend.h"
- #include "sysemu/blockdev.h"
- #include "hw/block/block.h"
- #include "block/blockjob.h"
-+#include "block/blockjob_int.h"
- #include "block/throttle-groups.h"
- #include "monitor/monitor.h"
- #include "qemu/error-report.h"
-@@ -2932,6 +2934,502 @@ out:
-     aio_context_release(aio_context);
- }
-+/* PVE backup related function */
-+
-+static struct PVEBackupState {
-+    Error *error;
-+    bool cancel;
-+    QemuUUID uuid;
-+    char uuid_str[37];
-+    int64_t speed;
-+    time_t start_time;
-+    time_t end_time;
-+    char *backup_file;
-+    Object *vmaobj;
-+    GList *di_list;
-+    size_t next_job;
-+    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 completed;
-+    char targetfile[PATH_MAX];
-+    BlockDriverState *target;
-+} PVEBackupDevInfo;
-+
-+static void pvebackup_run_next_job(void);
-+
-+static void pvebackup_cleanup(void)
-+{
-+    backup_state.end_time = time(NULL);
-+
-+    if (backup_state.vmaobj) {
-+        object_unparent(backup_state.vmaobj);
-+        backup_state.vmaobj = 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;
-+
-+    di->completed = true;
-+
-+    if (ret < 0 && !backup_state.error) {
-+        error_setg(&backup_state.error, "job failed with err %d - %s",
-+                   ret, strerror(-ret));
-+    }
-+
-+    di->bs = NULL;
-+    di->target = NULL;
-+
-+    if (backup_state.vmaobj) {
-+        object_unparent(backup_state.vmaobj);
-+        backup_state.vmaobj = NULL;
-+    }
-+
-+    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");
-+    }
-+
-+    if (backup_state.vmaobj) {
-+        Error *err;
-+        /* make sure vma writer does not block anymore */
-+        if (!object_set_props(backup_state.vmaobj, &err, "blocked", "yes", NULL)) {
-+            if (err) {
-+                error_report_err(err);
-+            }
-+        }
-+    }
-+
-+    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.vmaobj) {
-+        /* FIXME: Find something better for this */
-+        aio_poll(qemu_get_aio_context(), true);
-+    }
-+}
-+
-+void vma_object_add_config_file(Object *obj, const char *name, 
-+                                const char *contents, size_t len,
-+                                Error **errp);
-+static int config_to_vma(const char *file, BackupFormat format,
-+                         Object *vmaobj,
-+                         const char *backup_dir,
-+                         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) {
-+            vma_object_add_config_file(vmaobj, basename, cdata, clen, errp);
-+        } 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)
-+{
-+    bool cancel = backup_state.error || backup_state.cancel;
-+    GList *next = g_list_nth(backup_state.di_list, backup_state.next_job);
-+    while (next) {
-+        PVEBackupDevInfo *di = (PVEBackupDevInfo *)next->data;
-+        backup_state.next_job++;
-+        if (!di->completed && di->bs && di->bs->job) {
-+            BlockJob *job = di->bs->job;
-+            if (cancel) {
-+                block_job_cancel(job);
-+            } else {
-+                block_job_resume(job);
-+            }
-+            return;
-+        }
-+        next = g_list_next(next);
-+    }
-+    pvebackup_cleanup();
-+}
-+
-+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)
-+{
-+    BlockBackend *blk;
-+    BlockDriverState *bs = NULL;
-+    const char *backup_dir = NULL;
-+    Error *local_err = NULL;
-+    QemuUUID uuid;
-+    gchar **devs = NULL;
-+    GList *di_list = NULL;
-+    GList *l;
-+    UuidInfo *uuid_info;
-+    BlockJob *job;
-+
-+    if (backup_state.di_list || backup_state.vmaobj) {
-+        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 (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;
-+    }
-+
-+    qemu_uuid_generate(&uuid);
-+
-+    if (format == BACKUP_FORMAT_VMA) {
-+        char uuidstr[UUID_FMT_LEN+1];
-+        qemu_uuid_unparse(&uuid, uuidstr);
-+        uuidstr[UUID_FMT_LEN] = 0;
-+        backup_state.vmaobj =
-+            object_new_with_props("vma", object_get_objects_root(),
-+                                  "vma-backup-obj", &local_err,
-+                                  "filename", backup_file,
-+                                  "uuid", uuidstr,
-+                                  NULL);
-+        if (!backup_state.vmaobj) {
-+            if (local_err) {
-+                error_propagate(errp, local_err);
-+            }
-+            goto err;
-+        }
-+
-+        l = di_list;
-+        while (l) {
-+            QDict *options = qdict_new();
-+
-+            PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data;
-+            l = g_list_next(l);
-+
-+            const char *devname = bdrv_get_device_name(di->bs);
-+            snprintf(di->targetfile, PATH_MAX, "vma-backup-obj/%s.raw", devname);
-+
-+            qdict_put(options, "driver", qstring_from_str("vma-drive"));
-+            qdict_put(options, "size", qint_from_int(di->size));
-+            di->target = bdrv_open(di->targetfile, NULL, options, BDRV_O_RDWR, &local_err);
-+            if (!di->target) {
-+                error_propagate(errp, local_err);
-+                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 */
-+    if (has_config_file) {
-+      if(config_to_vma(config_file, format, backup_state.vmaobj, backup_dir, errp) != 0) {
-+        goto err;
-+      }
-+    }
-+
-+    /* add firewall file to archive */
-+    if (has_firewall_file) {
-+      if(config_to_vma(firewall_file, format, backup_state.vmaobj, backup_dir, errp) != 0) {
-+        goto err;
-+      }
-+    }
-+    /* 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);
-+
-+    memcpy(&backup_state.uuid, &uuid, sizeof(uuid));
-+    qemu_uuid_unparse(&uuid, backup_state.uuid_str);
-+
-+    backup_state.di_list = di_list;
-+    backup_state.next_job = 0;
-+
-+    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);
-+
-+        job = backup_job_create(NULL, di->bs, di->target, speed, MIRROR_SYNC_MODE_FULL, NULL,
-+                                false, BLOCKDEV_ON_ERROR_REPORT, BLOCKDEV_ON_ERROR_REPORT,
-+                                BLOCK_JOB_DEFAULT,
-+                                pvebackup_complete_cb, di, 2, NULL, &local_err);
-+        if (di->target) {
-+            bdrv_unref(di->target);
-+            di->target = NULL;
-+        }
-+        if (!job || local_err != NULL) {
-+            error_setg(&backup_state.error, "backup_job_create failed");
-+            pvebackup_cancel(NULL);
-+        } else {
-+            block_job_start(job);
-+        }
-+    }
-+
-+    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) {
-+        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);
-+
-+    if (devs) {
-+        g_strfreev(devs);
-+    }
-+
-+    if (backup_state.vmaobj) {
-+        object_unparent(backup_state.vmaobj);
-+        backup_state.vmaobj = NULL;
-+    }
-+
-+    if (backup_dir) {
-+        rmdir(backup_dir);
-+    }
-+
-+    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_base_node, const char *base_node,
-diff --git a/configure b/configure
-index be4d326ae0..841f7a8fae 100755
---- a/configure
-+++ b/configure
-@@ -320,6 +320,7 @@ numa=""
- tcmalloc="no"
- jemalloc="no"
- replication="yes"
-+vma=""
- supported_cpu="no"
- supported_os="no"
-@@ -1183,6 +1184,10 @@ for opt do
-   ;;
-   --enable-replication) replication="yes"
-   ;;
-+  --disable-vma) vma="no"
-+  ;;
-+  --enable-vma) vma="yes"
-+  ;;
-   *)
-       echo "ERROR: unknown option $opt"
-       echo "Try '$0 --help' for more information"
-@@ -1427,6 +1432,7 @@ disabled with --disable-FEATURE, default is enabled if available:
-   xfsctl          xfsctl support
-   qom-cast-debug  cast debugging support
-   tools           build qemu-io, qemu-nbd and qemu-image tools
-+  vma             VMA archive backend
- NOTE: The object files are built at the place where configure is launched
- EOF
-@@ -3705,6 +3711,23 @@ EOF
- fi
- ##########################################
-+# vma probe
-+if test "$vma" != "no" ; then
-+  if $pkg_config --exact-version=0.1.0 vma; then
-+    vma="yes"
-+    vma_cflags=$($pkg_config --cflags vma)
-+    vma_libs=$($pkg_config --libs vma)
-+  else
-+    if test "$vma" = "yes" ; then
-+      feature_not_found "VMA Archive backend support" \
-+          "Install libvma devel"
-+    fi
-+    vma="no"
-+  fi
-+fi
-+
-+
-+##########################################
- # signalfd probe
- signalfd="no"
- cat > $TMPC << EOF
-@@ -5146,6 +5169,7 @@ echo "tcmalloc support  $tcmalloc"
- echo "jemalloc support  $jemalloc"
- echo "avx2 optimization $avx2_opt"
- echo "replication support $replication"
-+echo "VMA support       $vma"
- if test "$sdl_too_old" = "yes"; then
- echo "-> Your SDL version is too old - please upgrade to have SDL support"
-@@ -5703,6 +5727,12 @@ if test "$libssh2" = "yes" ; then
-   echo "LIBSSH2_LIBS=$libssh2_libs" >> $config_host_mak
- fi
-+if test "$vma" = "yes" ; then
-+  echo "CONFIG_VMA=y" >> $config_host_mak
-+  echo "VMA_CFLAGS=$vma_cflags" >> $config_host_mak
-+  echo "VMA_LIBS=$vma_libs" >> $config_host_mak
-+fi
-+
- # USB host support
- if test "$libusb" = "yes"; then
-   echo "HOST_USB=libusb legacy" >> $config_host_mak
-diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx
-index 5fc57a2210..3b5a0f95e4 100644
---- a/hmp-commands-info.hx
-+++ b/hmp-commands-info.hx
-@@ -487,6 +487,19 @@ STEXI
- Show CPU statistics.
- ETEXI
-+    {
-+        .name       = "backup",
-+        .args_type  = "",
-+        .params     = "",
-+        .help       = "show backup status",
-+        .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 58940a762b..a2867b56f2 100644
---- a/hmp-commands.hx
-+++ b/hmp-commands.hx
-@@ -87,6 +87,37 @@ STEXI
- Copy data from a backing file into a block device.
- ETEXI
-+   {
-+        .name       = "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.",
-+        .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",
-+        .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 f725d061e6..12f1f46125 100644
---- a/hmp.c
-+++ b/hmp.c
-@@ -151,6 +151,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;
-@@ -1613,6 +1651,31 @@ 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;
-+
-+    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, dir ? BACKUP_FORMAT_DIR : BACKUP_FORMAT_VMA,
-+               false, NULL, 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 0497afbf65..8c1b4846b3 100644
---- a/hmp.h
-+++ b/hmp.h
-@@ -31,6 +31,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);
-@@ -80,6 +81,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 5e82933ca1..b20020a054 100644
---- a/qapi-schema.json
-+++ b/qapi-schema.json
-@@ -571,6 +571,97 @@
- { 'command': 'query-events', 'returns': ['EventInfo'] }
- ##
-+# @BackupStatus:
-+#
-+# Detailed backup status.
-+#
-+# @status: 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: error message (only returned if status is 'error')
-+#
-+# @total: total amount of bytes involved in the backup process
-+#
-+# @transferred: amount of bytes already backed up.
-+#
-+# @zero-bytes: amount of 'zero' bytes detected.
-+#
-+# @start-time: time (epoch) when backup job started.
-+#
-+# @end-time: time (epoch) when backup job finished.
-+#
-+# @backup-file: backup file name
-+#
-+# @uuid: 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', 'dir' ] }
-+
-+##
-+# @backup:
-+#
-+# Starts a VM backup.
-+#
-+# @backup-file: the backup file name
-+#
-+# @format: format of the backup file
-+#
-+# @config-file: a configuration file to include into
-+# the backup archive.
-+#
-+# @speed: the maximum speed, in bytes per second
-+#
-+# @devlist: 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',
-+                                    '*firewall-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:
- #
- # Detailed migration status.
-diff --git a/qapi/block-core.json b/qapi/block-core.json
-index 7ce90ec940..b0ffd3de4d 100644
---- a/qapi/block-core.json
-+++ b/qapi/block-core.json
-@@ -2118,7 +2118,7 @@
-             'host_device', 'http', 'https', 'iscsi', 'luks', 'nbd', 'nfs',
-             'null-aio', 'null-co', 'parallels', 'qcow', 'qcow2', 'qed',
-             'quorum', 'raw', 'rbd', 'replication', 'sheepdog', 'ssh',
--            'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat' ] }
-+            'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat', 'vma-drive' ] }
- ##
- # @BlockdevOptionsFile:
-@@ -2895,6 +2895,21 @@
-   'data': { '*offset': 'int', '*size': 'int' } }
- ##
-+# @BlockdevOptionsVMADrive:
-+#
-+# Driver specific block device options for VMA Drives
-+#
-+# @filename: vma-drive path
-+#
-+# @size: drive size in bytes
-+#
-+# Since: 2.9
-+##
-+{ 'struct': 'BlockdevOptionsVMADrive',
-+  'data': { 'filename': 'str',
-+            'size': 'int' } }
-+
-+##
- # @BlockdevOptions:
- #
- # Options for creating a block device.  Many options are available for all
-@@ -2956,7 +2971,8 @@
-       'vhdx':       'BlockdevOptionsGenericFormat',
-       'vmdk':       'BlockdevOptionsGenericCOWFormat',
-       'vpc':        'BlockdevOptionsGenericFormat',
--      'vvfat':      'BlockdevOptionsVVFAT'
-+      'vvfat':      'BlockdevOptionsVVFAT',
-+      'vma-drive':  'BlockdevOptionsVMADrive'
-   } }
- ##
--- 
-2.11.0
-
diff --git a/debian/patches/pve/0028-adding-old-vma-files.patch b/debian/patches/pve/0028-adding-old-vma-files.patch
deleted file mode 100644 (file)
index 8025136..0000000
+++ /dev/null
@@ -1,3388 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Wolfgang Bumiller <w.bumiller@proxmox.com>
-Date: Mon, 7 Aug 2017 08:51:16 +0200
-Subject: [PATCH] adding old vma files
-
----
- Makefile                  |   3 +-
- Makefile.objs             |   1 +
- block/backup.c            | 132 ++++---
- block/replication.c       |   1 +
- blockdev.c                | 249 +++++++++-----
- blockjob.c                |  11 +-
- include/block/block_int.h |   4 +
- vma-reader.c              | 857 ++++++++++++++++++++++++++++++++++++++++++++++
- vma-writer.c              | 771 +++++++++++++++++++++++++++++++++++++++++
- vma.c                     | 757 ++++++++++++++++++++++++++++++++++++++++
- vma.h                     | 149 ++++++++
- 11 files changed, 2802 insertions(+), 133 deletions(-)
- 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 6c359b2f86..edbc8b50f0 100644
---- a/Makefile
-+++ b/Makefile
-@@ -284,7 +284,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
- qemu-version.h: FORCE
-       $(call quiet-command, \
-@@ -377,6 +377,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) $(COMMON_LDADDS)
- qemu-nbd$(EXESUF): qemu-nbd.o $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS)
- qemu-io$(EXESUF): qemu-io.o $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS)
-+vma$(EXESUF): vma.o vma-reader.o $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS)
- qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o $(COMMON_LDADDS)
-diff --git a/Makefile.objs b/Makefile.objs
-index fbfbbb7f70..f5f8dbab3b 100644
---- a/Makefile.objs
-+++ b/Makefile.objs
-@@ -14,6 +14,7 @@ block-obj-y += block.o blockjob.o
- block-obj-y += block/
- block-obj-y += qemu-io-cmds.o
- block-obj-$(CONFIG_REPLICATION) += replication.o
-+block-obj-y += vma-writer.o
- block-obj-m = block/
-diff --git a/block/backup.c b/block/backup.c
-index 1ede70c061..7c5febc434 100644
---- a/block/backup.c
-+++ b/block/backup.c
-@@ -36,6 +36,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;
-@@ -145,13 +146,24 @@ 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,
--                                 job->compress ? BDRV_REQ_WRITE_COMPRESSED : 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,
-+                                     job->compress ? BDRV_REQ_WRITE_COMPRESSED : 0);
-+            }
-         }
-         if (ret < 0) {
-             trace_backup_do_cow_write_fail(job, start, ret);
-@@ -246,6 +258,9 @@ static void backup_abort(BlockJob *job)
- static void backup_clean(BlockJob *job)
- {
-     BackupBlockJob *s = container_of(job, BackupBlockJob, common);
-+    if (!s->target) {
-+        return;
-+    }
-     assert(s->target);
-     blk_unref(s->target);
-     s->target = NULL;
-@@ -255,7 +270,9 @@ static void backup_attached_aio_context(BlockJob *job, AioContext *aio_context)
- {
-     BackupBlockJob *s = container_of(job, BackupBlockJob, common);
--    blk_set_aio_context(s->target, aio_context);
-+    if (s->target) {
-+        blk_set_aio_context(s->target, aio_context);
-+    }
- }
- void backup_do_checkpoint(BlockJob *job, Error **errp)
-@@ -330,9 +347,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;
-     }
- }
-@@ -557,6 +576,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
-                   BlockdevOnError on_source_error,
-                   BlockdevOnError on_target_error,
-                   int creation_flags,
-+                  BackupDumpFunc *dump_cb,
-                   BlockCompletionFunc *cb, void *opaque,
-                   int pause_count,
-                   BlockJobTxn *txn, Error **errp)
-@@ -567,7 +587,7 @@ BlockJob *backup_job_create(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");
-@@ -580,13 +600,13 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
-         return NULL;
-     }
--    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 NULL;
-     }
--    if (compress && target->drv->bdrv_co_pwritev_compressed == NULL) {
-+    if (target && compress && target->drv->bdrv_co_pwritev_compressed == NULL) {
-         error_setg(errp, "Compression is not supported for this drive %s",
-                    bdrv_get_device_name(target));
-         return NULL;
-@@ -596,7 +616,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
-         return NULL;
-     }
--    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 NULL;
-     }
-@@ -636,15 +656,18 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
-         goto error;
-     }
--    /* The target must match the source in size, so no resize here either */
--    job->target = blk_new(BLK_PERM_WRITE,
--                          BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE |
--                          BLK_PERM_WRITE_UNCHANGED | BLK_PERM_GRAPH_MOD);
--    ret = blk_insert_bs(job->target, target, errp);
--    if (ret < 0) {
--        goto error;
-+    if (target) {
-+        /* The target must match the source in size, so no resize here either */
-+        job->target = blk_new(BLK_PERM_WRITE,
-+                              BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE |
-+                              BLK_PERM_WRITE_UNCHANGED | BLK_PERM_GRAPH_MOD);
-+        ret = blk_insert_bs(job->target, target, errp);
-+        if (ret < 0) {
-+            goto error;
-+        }
-     }
-+    job->dump_cb = dump_cb;
-     job->on_source_error = on_source_error;
-     job->on_target_error = on_target_error;
-     job->sync_mode = sync_mode;
-@@ -652,38 +675,55 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
-                        sync_bitmap : NULL;
-     job->compress = compress;
--    /* 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 == -ENOTSUP && !target->backing) {
--        /* Cluster size is not defined */
--        error_report("WARNING: The target block device doesn't provide "
--                     "information about the block size and it doesn't have a "
--                     "backing file. The default block size of %u bytes is "
--                     "used. If the actual block size of the target exceeds "
--                     "this default, the backup may be unusable",
--                     BACKUP_CLUSTER_SIZE_DEFAULT);
--        job->cluster_size = BACKUP_CLUSTER_SIZE_DEFAULT;
--    } else 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 == -ENOTSUP && !target->backing) {
-+            /* Cluster size is not defined */
-+            error_report("WARNING: The target block device doesn't provide "
-+                         "information about the block size and it doesn't have a "
-+                         "backing file. The default block size of %u bytes is "
-+                         "used. If the actual block size of the target exceeds "
-+                         "this default, the backup may be unusable",
-+                         BACKUP_CLUSTER_SIZE_DEFAULT);
-+            job->cluster_size = BACKUP_CLUSTER_SIZE_DEFAULT;
-+        } else 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);
-+        }
-     } else {
--        job->cluster_size = MAX(BACKUP_CLUSTER_SIZE_DEFAULT, bdi.cluster_size);
-+        ret = bdrv_get_info(bs, &bdi);
-+        if (ret < 0) {
-+            job->cluster_size = BACKUP_CLUSTER_SIZE_DEFAULT;
-+        } else {
-+            /* round down to nearest BACKUP_CLUSTER_SIZE_DEFAULT */
-+            job->cluster_size = (bdi.cluster_size / BACKUP_CLUSTER_SIZE_DEFAULT) * BACKUP_CLUSTER_SIZE_DEFAULT;
-+            if (job->cluster_size == 0) {
-+                /* but we can't go below it */
-+                job->cluster_size = BACKUP_CLUSTER_SIZE_DEFAULT;
-+            }
-+        }
-     }
--    /* Required permissions are already taken with target's blk_new() */
--    block_job_add_bdrv(&job->common, "target", target, 0, BLK_PERM_ALL,
--                       &error_abort);
-+    if (target) {
-+        /* Required permissions are already taken with target's blk_new() */
-+        block_job_add_bdrv(&job->common, "target", target, 0, BLK_PERM_ALL,
-+                           &error_abort);
-+    } else {
-+        job->common.pause_count = pause_count;
-+    }
-     job->common.len = len;
--    job->common.pause_count = pause_count;
-     block_job_txn_add_job(txn, &job->common);
-     return &job->common;
-diff --git a/block/replication.c b/block/replication.c
-index 1c41d9e6bf..60c6524417 100644
---- a/block/replication.c
-+++ b/block/replication.c
-@@ -531,6 +531,7 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode,
-                                 0, MIRROR_SYNC_MODE_NONE, NULL, false,
-                                 BLOCKDEV_ON_ERROR_REPORT,
-                                 BLOCKDEV_ON_ERROR_REPORT, BLOCK_JOB_INTERNAL,
-+                                NULL,
-                                 backup_job_completed, bs, 0, NULL, &local_err);
-         if (local_err) {
-             error_propagate(errp, local_err);
-diff --git a/blockdev.c b/blockdev.c
-index 534c00f5da..19a82e8774 100644
---- a/blockdev.c
-+++ b/blockdev.c
-@@ -31,7 +31,6 @@
-  */
- #include "qemu/osdep.h"
--#include "qemu/uuid.h"
- #include "sysemu/block-backend.h"
- #include "sysemu/blockdev.h"
- #include "hw/block/block.h"
-@@ -55,6 +54,7 @@
- #include "qemu/cutils.h"
- #include "qemu/help_option.h"
- #include "qemu/throttle-options.h"
-+#include "vma.h"
- static QTAILQ_HEAD(, BlockDriverState) monitor_bdrv_states =
-     QTAILQ_HEAD_INITIALIZER(monitor_bdrv_states);
-@@ -2934,20 +2934,44 @@ out:
-     aio_context_release(aio_context);
- }
-+void block_job_event_cancelled(BlockJob *job);
-+void block_job_event_completed(BlockJob *job, const char *msg);
-+static void block_job_cb(void *opaque, int ret)
-+{
-+    /* Note that this function may be executed from another AioContext besides
-+     * the QEMU main loop.  If you need to access anything that assumes the
-+     * QEMU global mutex, use a BH or introduce a mutex.
-+     */
-+
-+    BlockDriverState *bs = opaque;
-+    const char *msg = NULL;
-+
-+    assert(bs->job);
-+
-+    if (ret < 0) {
-+        msg = strerror(-ret);
-+    }
-+
-+    if (block_job_is_cancelled(bs->job)) {
-+        block_job_event_cancelled(bs->job);
-+    } else {
-+        block_job_event_completed(bs->job, msg);
-+    }
-+}
-+
- /* PVE backup related function */
- static struct PVEBackupState {
-     Error *error;
-     bool cancel;
--    QemuUUID uuid;
-+    uuid_t uuid;
-     char uuid_str[37];
-     int64_t speed;
-     time_t start_time;
-     time_t end_time;
-     char *backup_file;
--    Object *vmaobj;
-+    VmaWriter *vmaw;
-     GList *di_list;
--    size_t next_job;
-     size_t total;
-     size_t transferred;
-     size_t zero_bytes;
-@@ -2957,6 +2981,7 @@ typedef struct PVEBackupDevInfo {
-     BlockDriverState *bs;
-     size_t size;
-     uint8_t dev_id;
-+    //bool started;
-     bool completed;
-     char targetfile[PATH_MAX];
-     BlockDriverState *target;
-@@ -2964,13 +2989,79 @@ typedef struct 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;
-+
-+    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,
-+                       "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 ret = -1;
-+
-+    if (backup_state.vmaw) {
-+        size_t zero_bytes = 0;
-+        int64_t remaining = n_sectors * BDRV_SECTOR_SIZE;
-+        while (remaining > 0) {
-+            ret = vma_writer_write(backup_state.vmaw, di->dev_id, cluster_num,
-+                                   buf, &zero_bytes);
-+            ++cluster_num;
-+            if (buf) {
-+                buf += VMA_CLUSTER_SIZE;
-+            }
-+            if (ret < 0) {
-+                if (!backup_state.error) {
-+                    vma_writer_error_propagate(backup_state.vmaw, &backup_state.error);
-+                }
-+                if (di->bs && di->bs->job) {
-+                    block_job_cancel(di->bs->job);
-+                }
-+                break;
-+            } else {
-+                backup_state.zero_bytes += zero_bytes;
-+                if (remaining >= VMA_CLUSTER_SIZE) {
-+                    backup_state.transferred += VMA_CLUSTER_SIZE;
-+                } else {
-+                    backup_state.transferred += remaining;
-+                }
-+                remaining -= VMA_CLUSTER_SIZE;
-+            }
-+        }
-+    } else {
-+        if (!buf) {
-+            backup_state.zero_bytes += size;
-+        }
-+        backup_state.transferred += size;
-+    }
-+
-+    // Note: always return success, because we want that writes succeed anyways.
-+
-+    return size;
-+}
-+
- static void pvebackup_cleanup(void)
- {
-     backup_state.end_time = time(NULL);
--    if (backup_state.vmaobj) {
--        object_unparent(backup_state.vmaobj);
--        backup_state.vmaobj = 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) {
-@@ -2985,6 +3076,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;
-@@ -2996,14 +3094,18 @@ static void pvebackup_complete_cb(void *opaque, int ret)
-                    ret, strerror(-ret));
-     }
-+    BlockDriverState *bs = di->bs;
-+
-     di->bs = NULL;
-     di->target = NULL;
--    if (backup_state.vmaobj) {
--        object_unparent(backup_state.vmaobj);
--        backup_state.vmaobj = NULL;
-+    if (backup_state.vmaw) {
-+        Coroutine *co = qemu_coroutine_create(backup_close_vma_stream, di);
-+        qemu_coroutine_enter(co);
-     }
-+    block_job_cb(bs, ret);
-+
-     if (!backup_state.cancel) {
-         pvebackup_run_next_job();
-     }
-@@ -3017,14 +3119,9 @@ static void pvebackup_cancel(void *opaque)
-         error_setg(&backup_state.error, "backup cancelled");
-     }
--    if (backup_state.vmaobj) {
--        Error *err;
-+    if (backup_state.vmaw) {
-         /* make sure vma writer does not block anymore */
--        if (!object_set_props(backup_state.vmaobj, &err, "blocked", "yes", NULL)) {
--            if (err) {
--                error_report_err(err);
--            }
--        }
-+        vma_writer_set_error(backup_state.vmaw, "backup cancelled");
-     }
-     GList *l = backup_state.di_list;
-@@ -3049,19 +3146,15 @@ void qmp_backup_cancel(Error **errp)
-     Coroutine *co = qemu_coroutine_create(pvebackup_cancel, NULL);
-     qemu_coroutine_enter(co);
--    while (backup_state.vmaobj) {
--        /* FIXME: Find something better for this */
-+    while (backup_state.vmaw) {
-+        /* vma writer use main aio context */
-         aio_poll(qemu_get_aio_context(), true);
-     }
- }
--void vma_object_add_config_file(Object *obj, const char *name, 
--                                const char *contents, size_t len,
--                                Error **errp);
- static int config_to_vma(const char *file, BackupFormat format,
--                         Object *vmaobj,
--                         const char *backup_dir,
--                         Error **errp)
-+                       const char *backup_dir, VmaWriter *vmaw,
-+                       Error **errp)
- {
-         char *cdata = NULL;
-         gsize clen = 0;
-@@ -3074,12 +3167,17 @@ static int config_to_vma(const char *file, BackupFormat format,
-         char *basename = g_path_get_basename(file);
-         if (format == BACKUP_FORMAT_VMA) {
--            vma_object_add_config_file(vmaobj, basename, cdata, clen, errp);
-+            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);
-+            error_setg(errp, "unable to write config file '%s'", config_path);
-                 g_free(cdata);
-                 g_free(basename);
-                 return 1;
-@@ -3089,34 +3187,37 @@ static int config_to_vma(const char *file, BackupFormat format,
-         g_free(basename);
-         g_free(cdata);
--        return 0;
-+      return 0;
- }
-+bool block_job_should_pause(BlockJob *job);
- static void pvebackup_run_next_job(void)
- {
--    bool cancel = backup_state.error || backup_state.cancel;
--    GList *next = g_list_nth(backup_state.di_list, backup_state.next_job);
--    while (next) {
--        PVEBackupDevInfo *di = (PVEBackupDevInfo *)next->data;
--        backup_state.next_job++;
-+    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 (cancel) {
--                block_job_cancel(job);
--            } else {
--                block_job_resume(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;
-         }
--        next = g_list_next(next);
-     }
-+
-     pvebackup_cleanup();
- }
- 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_firewall_file, const char *firewall_file,
-                     bool has_devlist, const char *devlist,
-                     bool has_speed, int64_t speed, Error **errp)
- {
-@@ -3124,14 +3225,15 @@ UuidInfo *qmp_backup(const char *backup_file, bool has_format,
-     BlockDriverState *bs = NULL;
-     const char *backup_dir = NULL;
-     Error *local_err = NULL;
--    QemuUUID uuid;
-+    uuid_t uuid;
-+    VmaWriter *vmaw = NULL;
-     gchar **devs = NULL;
-     GList *di_list = NULL;
-     GList *l;
-     UuidInfo *uuid_info;
-     BlockJob *job;
--    if (backup_state.di_list || backup_state.vmaobj) {
-+    if (backup_state.di_list) {
-         error_set(errp, ERROR_CLASS_GENERIC_ERROR,
-                   "previous backup not finished");
-         return NULL;
-@@ -3206,40 +3308,28 @@ UuidInfo *qmp_backup(const char *backup_file, bool has_format,
-         total += size;
-     }
--    qemu_uuid_generate(&uuid);
-+    uuid_generate(uuid);
-     if (format == BACKUP_FORMAT_VMA) {
--        char uuidstr[UUID_FMT_LEN+1];
--        qemu_uuid_unparse(&uuid, uuidstr);
--        uuidstr[UUID_FMT_LEN] = 0;
--        backup_state.vmaobj =
--            object_new_with_props("vma", object_get_objects_root(),
--                                  "vma-backup-obj", &local_err,
--                                  "filename", backup_file,
--                                  "uuid", uuidstr,
--                                  NULL);
--        if (!backup_state.vmaobj) {
-+        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) {
--            QDict *options = qdict_new();
--
-             PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data;
-             l = g_list_next(l);
-             const char *devname = bdrv_get_device_name(di->bs);
--            snprintf(di->targetfile, PATH_MAX, "vma-backup-obj/%s.raw", devname);
--
--            qdict_put(options, "driver", qstring_from_str("vma-drive"));
--            qdict_put(options, "size", qint_from_int(di->size));
--            di->target = bdrv_open(di->targetfile, NULL, options, BDRV_O_RDWR, &local_err);
--            if (!di->target) {
--                error_propagate(errp, local_err);
-+            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;
-             }
-         }
-@@ -3280,15 +3370,15 @@ UuidInfo *qmp_backup(const char *backup_file, bool has_format,
-     /* add configuration file to archive */
-     if (has_config_file) {
--      if(config_to_vma(config_file, format, backup_state.vmaobj, backup_dir, errp) != 0) {
--        goto err;
-+      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_state.vmaobj, backup_dir, errp) != 0) {
--        goto err;
-+      if(config_to_vma(firewall_file, format, backup_dir, vmaw, errp) != 0) {
-+      goto err;
-       }
-     }
-     /* initialize global backup_state now */
-@@ -3310,11 +3400,12 @@ UuidInfo *qmp_backup(const char *backup_file, bool has_format,
-     }
-     backup_state.backup_file = g_strdup(backup_file);
--    memcpy(&backup_state.uuid, &uuid, sizeof(uuid));
--    qemu_uuid_unparse(&uuid, backup_state.uuid_str);
-+    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.next_job = 0;
-     backup_state.total = total;
-     backup_state.transferred = 0;
-@@ -3325,21 +3416,16 @@ UuidInfo *qmp_backup(const char *backup_file, bool has_format,
-     while (l) {
-         PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data;
-         l = g_list_next(l);
--
-         job = backup_job_create(NULL, di->bs, di->target, speed, MIRROR_SYNC_MODE_FULL, NULL,
-                                 false, BLOCKDEV_ON_ERROR_REPORT, BLOCKDEV_ON_ERROR_REPORT,
-                                 BLOCK_JOB_DEFAULT,
--                                pvebackup_complete_cb, di, 2, NULL, &local_err);
--        if (di->target) {
--            bdrv_unref(di->target);
--            di->target = NULL;
--        }
-+                                pvebackup_dump_cb, pvebackup_complete_cb, di,
-+                                2, NULL, &local_err);
-         if (!job || local_err != NULL) {
-             error_setg(&backup_state.error, "backup_job_create failed");
-             pvebackup_cancel(NULL);
--        } else {
--            block_job_start(job);
-         }
-+        block_job_start(job);
-     }
-     if (!backup_state.error) {
-@@ -3373,9 +3459,10 @@ err:
-         g_strfreev(devs);
-     }
--    if (backup_state.vmaobj) {
--        object_unparent(backup_state.vmaobj);
--        backup_state.vmaobj = NULL;
-+    if (vmaw) {
-+        Error *err = NULL;
-+        vma_writer_close(vmaw, &err);
-+        unlink(backup_file);
-     }
-     if (backup_dir) {
-@@ -3760,7 +3847,7 @@ static BlockJob *do_drive_backup(DriveBackup *backup, BlockJobTxn *txn,
-     job = backup_job_create(backup->job_id, bs, target_bs, backup->speed,
-                             backup->sync, bmap, backup->compress,
-                             backup->on_source_error, backup->on_target_error,
--                            BLOCK_JOB_DEFAULT, NULL, NULL, 0, txn, &local_err);
-+                            BLOCK_JOB_DEFAULT, NULL, NULL, NULL, 0, txn, &local_err);
-     bdrv_unref(target_bs);
-     if (local_err != NULL) {
-         error_propagate(errp, local_err);
-@@ -3839,7 +3926,7 @@ BlockJob *do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn,
-     job = backup_job_create(backup->job_id, bs, target_bs, backup->speed,
-                             backup->sync, NULL, backup->compress,
-                             backup->on_source_error, backup->on_target_error,
--                            BLOCK_JOB_DEFAULT, NULL, NULL, 0, txn, &local_err);
-+                            BLOCK_JOB_DEFAULT, NULL, NULL, NULL, 0, txn, &local_err);
-     if (local_err != NULL) {
-         error_propagate(errp, local_err);
-     }
-diff --git a/blockjob.c b/blockjob.c
-index 764d41863e..cb3741f6dd 100644
---- a/blockjob.c
-+++ b/blockjob.c
-@@ -37,8 +37,8 @@
- #include "qemu/timer.h"
- #include "qapi-event.h"
--static void block_job_event_cancelled(BlockJob *job);
--static void block_job_event_completed(BlockJob *job, const char *msg);
-+void block_job_event_cancelled(BlockJob *job);
-+void block_job_event_completed(BlockJob *job, const char *msg);
- /* Transactional group of block jobs */
- struct BlockJobTxn {
-@@ -473,7 +473,8 @@ void block_job_user_pause(BlockJob *job)
-     block_job_pause(job);
- }
--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;
- }
-@@ -687,7 +688,7 @@ static void block_job_iostatus_set_err(BlockJob *job, int error)
-     }
- }
--static void block_job_event_cancelled(BlockJob *job)
-+void block_job_event_cancelled(BlockJob *job)
- {
-     if (block_job_is_internal(job)) {
-         return;
-@@ -701,7 +702,7 @@ static void block_job_event_cancelled(BlockJob *job)
-                                         &error_abort);
- }
--static void block_job_event_completed(BlockJob *job, const char *msg)
-+void block_job_event_completed(BlockJob *job, const char *msg)
- {
-     if (block_job_is_internal(job)) {
-         return;
-diff --git a/include/block/block_int.h b/include/block/block_int.h
-index 19b84b027f..fc1c53a059 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, BlockBackend *be,
-+                           int64_t sector_num, int n_sectors, unsigned char *buf);
-+
- enum BdrvTrackedRequestType {
-     BDRV_TRACKED_READ,
-     BDRV_TRACKED_WRITE,
-@@ -878,6 +881,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
-                             BlockdevOnError on_source_error,
-                             BlockdevOnError on_target_error,
-                             int creation_flags,
-+                            BackupDumpFunc *dump_cb,
-                             BlockCompletionFunc *cb, void *opaque,
-                             int pause_count,
-                             BlockJobTxn *txn, Error **errp);
-diff --git a/vma-reader.c b/vma-reader.c
-new file mode 100644
-index 0000000000..2000889bd3
---- /dev/null
-+++ b/vma-reader.c
-@@ -0,0 +1,857 @@
-+/*
-+ * VMA: Virtual Machine Archive
-+ *
-+ * Copyright (C) 2012 Proxmox Server Solutions
-+ *
-+ * Authors:
-+ *  Dietmar Maurer (dietmar@proxmox.com)
-+ *
-+ * 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 <glib.h>
-+#include <uuid/uuid.h>
-+
-+#include "qemu-common.h"
-+#include "qemu/timer.h"
-+#include "qemu/ratelimit.h"
-+#include "vma.h"
-+#include "block/block.h"
-+#include "sysemu/block-backend.h"
-+
-+static unsigned char zero_vma_block[VMA_BLOCK_SIZE];
-+
-+typedef struct VmaRestoreState {
-+    BlockBackend *target;
-+    bool write_zeroes;
-+    unsigned long *bitmap;
-+    int bitmap_size;
-+}  VmaRestoreState;
-+
-+struct VmaReader {
-+    int fd;
-+    GChecksum *md5csum;
-+    GHashTable *blob_hash;
-+    unsigned char *head_data;
-+    VmaDeviceInfo devinfo[256];
-+    VmaRestoreState rstate[256];
-+    GList *cdata_list;
-+    guint8 vmstate_stream;
-+    uint32_t vmstate_clusters;
-+    /* to show restore percentage if run with -v */
-+    time_t start_time;
-+    int64_t cluster_count;
-+    int64_t clusters_read;
-+    int64_t zero_cluster_data;
-+    int64_t partial_zero_cluster_data;
-+    int clusters_read_per;
-+};
-+
-+static guint
-+g_int32_hash(gconstpointer v)
-+{
-+    return *(const uint32_t *)v;
-+}
-+
-+static gboolean
-+g_int32_equal(gconstpointer v1, gconstpointer v2)
-+{
-+    return *((const uint32_t *)v1) == *((const uint32_t *)v2);
-+}
-+
-+static int vma_reader_get_bitmap(VmaRestoreState *rstate, int64_t cluster_num)
-+{
-+    assert(rstate);
-+    assert(rstate->bitmap);
-+
-+    unsigned long val, idx, bit;
-+
-+    idx = cluster_num / BITS_PER_LONG;
-+
-+    assert(rstate->bitmap_size > idx);
-+
-+    bit = cluster_num % BITS_PER_LONG;
-+    val = rstate->bitmap[idx];
-+
-+    return !!(val & (1UL << bit));
-+}
-+
-+static void vma_reader_set_bitmap(VmaRestoreState *rstate, int64_t cluster_num,
-+                                  int dirty)
-+{
-+    assert(rstate);
-+    assert(rstate->bitmap);
-+
-+    unsigned long val, idx, bit;
-+
-+    idx = cluster_num / BITS_PER_LONG;
-+
-+    assert(rstate->bitmap_size > idx);
-+
-+    bit = cluster_num % BITS_PER_LONG;
-+    val = rstate->bitmap[idx];
-+    if (dirty) {
-+        if (!(val & (1UL << bit))) {
-+            val |= 1UL << bit;
-+        }
-+    } else {
-+        if (val & (1UL << bit)) {
-+            val &= ~(1UL << bit);
-+        }
-+    }
-+    rstate->bitmap[idx] = val;
-+}
-+
-+typedef struct VmaBlob {
-+    uint32_t start;
-+    uint32_t len;
-+    void *data;
-+} VmaBlob;
-+
-+static const VmaBlob *get_header_blob(VmaReader *vmar, uint32_t pos)
-+{
-+    assert(vmar);
-+    assert(vmar->blob_hash);
-+
-+    return g_hash_table_lookup(vmar->blob_hash, &pos);
-+}
-+
-+static const char *get_header_str(VmaReader *vmar, uint32_t pos)
-+{
-+    const VmaBlob *blob = get_header_blob(vmar, pos);
-+    if (!blob) {
-+        return NULL;
-+    }
-+    const char *res = (char *)blob->data;
-+    if (res[blob->len-1] != '\0') {
-+        return NULL;
-+    }
-+    return res;
-+}
-+
-+static ssize_t
-+safe_read(int fd, unsigned char *buf, size_t count)
-+{
-+    ssize_t n;
-+
-+    do {
-+        n = read(fd, buf, count);
-+    } while (n < 0 && errno == EINTR);
-+
-+    return n;
-+}
-+
-+static ssize_t
-+full_read(int fd, unsigned char *buf, size_t len)
-+{
-+    ssize_t n;
-+    size_t total;
-+
-+    total = 0;
-+
-+    while (len > 0) {
-+        n = safe_read(fd, buf, len);
-+
-+        if (n == 0) {
-+            return total;
-+        }
-+
-+        if (n <= 0) {
-+            break;
-+        }
-+
-+        buf += n;
-+        total += n;
-+        len -= n;
-+    }
-+
-+    if (len) {
-+        return -1;
-+    }
-+
-+    return total;
-+}
-+
-+void vma_reader_destroy(VmaReader *vmar)
-+{
-+    assert(vmar);
-+
-+    if (vmar->fd >= 0) {
-+        close(vmar->fd);
-+    }
-+
-+    if (vmar->cdata_list) {
-+        g_list_free(vmar->cdata_list);
-+    }
-+
-+    int i;
-+    for (i = 1; i < 256; i++) {
-+        if (vmar->rstate[i].bitmap) {
-+            g_free(vmar->rstate[i].bitmap);
-+        }
-+    }
-+
-+    if (vmar->md5csum) {
-+        g_checksum_free(vmar->md5csum);
-+    }
-+
-+    if (vmar->blob_hash) {
-+        g_hash_table_destroy(vmar->blob_hash);
-+    }
-+
-+    if (vmar->head_data) {
-+        g_free(vmar->head_data);
-+    }
-+
-+    g_free(vmar);
-+
-+};
-+
-+static int vma_reader_read_head(VmaReader *vmar, Error **errp)
-+{
-+    assert(vmar);
-+    assert(errp);
-+    assert(*errp == NULL);
-+
-+    unsigned char md5sum[16];
-+    int i;
-+    int ret = 0;
-+
-+    vmar->head_data = g_malloc(sizeof(VmaHeader));
-+
-+    if (full_read(vmar->fd, vmar->head_data, sizeof(VmaHeader)) !=
-+        sizeof(VmaHeader)) {
-+        error_setg(errp, "can't read vma header - %s",
-+                   errno ? g_strerror(errno) : "got EOF");
-+        return -1;
-+    }
-+
-+    VmaHeader *h = (VmaHeader *)vmar->head_data;
-+
-+    if (h->magic != VMA_MAGIC) {
-+        error_setg(errp, "not a vma file - wrong magic number");
-+        return -1;
-+    }
-+
-+    uint32_t header_size = GUINT32_FROM_BE(h->header_size);
-+    int need = header_size - sizeof(VmaHeader);
-+    if (need <= 0) {
-+        error_setg(errp, "wrong vma header size %d", header_size);
-+        return -1;
-+    }
-+
-+    vmar->head_data = g_realloc(vmar->head_data, header_size);
-+    h = (VmaHeader *)vmar->head_data;
-+
-+    if (full_read(vmar->fd, vmar->head_data + sizeof(VmaHeader), need) !=
-+        need) {
-+        error_setg(errp, "can't read vma header data - %s",
-+                   errno ? g_strerror(errno) : "got EOF");
-+        return -1;
-+    }
-+
-+    memcpy(md5sum, h->md5sum, 16);
-+    memset(h->md5sum, 0, 16);
-+
-+    g_checksum_reset(vmar->md5csum);
-+    g_checksum_update(vmar->md5csum, vmar->head_data, header_size);
-+    gsize csize = 16;
-+    g_checksum_get_digest(vmar->md5csum, (guint8 *)(h->md5sum), &csize);
-+
-+    if (memcmp(md5sum, h->md5sum, 16) != 0) {
-+        error_setg(errp, "wrong vma header chechsum");
-+        return -1;
-+    }
-+
-+    /* we can modify header data after checksum verify */
-+    h->header_size = header_size;
-+
-+    h->version = GUINT32_FROM_BE(h->version);
-+    if (h->version != 1) {
-+        error_setg(errp, "wrong vma version %d", h->version);
-+        return -1;
-+    }
-+
-+    h->ctime = GUINT64_FROM_BE(h->ctime);
-+    h->blob_buffer_offset = GUINT32_FROM_BE(h->blob_buffer_offset);
-+    h->blob_buffer_size = GUINT32_FROM_BE(h->blob_buffer_size);
-+
-+    uint32_t bstart = h->blob_buffer_offset + 1;
-+    uint32_t bend = h->blob_buffer_offset + h->blob_buffer_size;
-+
-+    if (bstart <= sizeof(VmaHeader)) {
-+        error_setg(errp, "wrong vma blob buffer offset %d",
-+                   h->blob_buffer_offset);
-+        return -1;
-+    }
-+
-+    if (bend > header_size) {
-+        error_setg(errp, "wrong vma blob buffer size %d/%d",
-+                   h->blob_buffer_offset, h->blob_buffer_size);
-+        return -1;
-+    }
-+
-+    while ((bstart + 2) <= bend) {
-+        uint32_t size = vmar->head_data[bstart] +
-+            (vmar->head_data[bstart+1] << 8);
-+        if ((bstart + size + 2) <= bend) {
-+            VmaBlob *blob = g_new0(VmaBlob, 1);
-+            blob->start = bstart - h->blob_buffer_offset;
-+            blob->len = size;
-+            blob->data = vmar->head_data + bstart + 2;
-+            g_hash_table_insert(vmar->blob_hash, &blob->start, blob);
-+        }
-+        bstart += size + 2;
-+    }
-+
-+
-+    int count = 0;
-+    for (i = 1; i < 256; i++) {
-+        VmaDeviceInfoHeader *dih = &h->dev_info[i];
-+        uint32_t devname_ptr = GUINT32_FROM_BE(dih->devname_ptr);
-+        uint64_t size = GUINT64_FROM_BE(dih->size);
-+        const char *devname =  get_header_str(vmar, devname_ptr);
-+
-+        if (size && devname) {
-+            count++;
-+            vmar->devinfo[i].size = size;
-+            vmar->devinfo[i].devname = devname;
-+
-+            if (strcmp(devname, "vmstate") == 0) {
-+                vmar->vmstate_stream = i;
-+            }
-+        }
-+    }
-+
-+    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]);
-+
-+        if (!(name_ptr && data_ptr)) {
-+            continue;
-+        }
-+        const char *name =  get_header_str(vmar, name_ptr);
-+        const VmaBlob *blob = get_header_blob(vmar, data_ptr);
-+
-+        if (!(name && blob)) {
-+            error_setg(errp, "vma contains invalid data pointers");
-+            return -1;
-+        }
-+
-+        VmaConfigData *cdata = g_new0(VmaConfigData, 1);
-+        cdata->name = name;
-+        cdata->data = blob->data;
-+        cdata->len = blob->len;
-+
-+        vmar->cdata_list = g_list_append(vmar->cdata_list, cdata);
-+    }
-+
-+    return ret;
-+};
-+
-+VmaReader *vma_reader_create(const char *filename, Error **errp)
-+{
-+    assert(filename);
-+    assert(errp);
-+
-+    VmaReader *vmar = g_new0(VmaReader, 1);
-+
-+    if (strcmp(filename, "-") == 0) {
-+        vmar->fd = dup(0);
-+    } else {
-+        vmar->fd = open(filename, O_RDONLY);
-+    }
-+
-+    if (vmar->fd < 0) {
-+        error_setg(errp, "can't open file %s - %s\n", filename,
-+                   g_strerror(errno));
-+        goto err;
-+    }
-+
-+    vmar->md5csum = g_checksum_new(G_CHECKSUM_MD5);
-+    if (!vmar->md5csum) {
-+        error_setg(errp, "can't allocate cmsum\n");
-+        goto err;
-+    }
-+
-+    vmar->blob_hash = g_hash_table_new_full(g_int32_hash, g_int32_equal,
-+                                            NULL, g_free);
-+
-+    if (vma_reader_read_head(vmar, errp) < 0) {
-+        goto err;
-+    }
-+
-+    return vmar;
-+
-+err:
-+    if (vmar) {
-+        vma_reader_destroy(vmar);
-+    }
-+
-+    return NULL;
-+}
-+
-+VmaHeader *vma_reader_get_header(VmaReader *vmar)
-+{
-+    assert(vmar);
-+    assert(vmar->head_data);
-+
-+    return (VmaHeader *)(vmar->head_data);
-+}
-+
-+GList *vma_reader_get_config_data(VmaReader *vmar)
-+{
-+    assert(vmar);
-+    assert(vmar->head_data);
-+
-+    return vmar->cdata_list;
-+}
-+
-+VmaDeviceInfo *vma_reader_get_device_info(VmaReader *vmar, guint8 dev_id)
-+{
-+    assert(vmar);
-+    assert(dev_id);
-+
-+    if (vmar->devinfo[dev_id].size && vmar->devinfo[dev_id].devname) {
-+        return &vmar->devinfo[dev_id];
-+    }
-+
-+    return NULL;
-+}
-+
-+static void allocate_rstate(VmaReader *vmar,  guint8 dev_id,
-+                            BlockBackend *target, bool write_zeroes)
-+{
-+    assert(vmar);
-+    assert(dev_id);
-+
-+    vmar->rstate[dev_id].target = target;
-+    vmar->rstate[dev_id].write_zeroes = write_zeroes;
-+
-+    int64_t size = vmar->devinfo[dev_id].size;
-+
-+    int64_t bitmap_size = (size/BDRV_SECTOR_SIZE) +
-+        (VMA_CLUSTER_SIZE/BDRV_SECTOR_SIZE) * BITS_PER_LONG - 1;
-+    bitmap_size /= (VMA_CLUSTER_SIZE/BDRV_SECTOR_SIZE) * BITS_PER_LONG;
-+
-+    vmar->rstate[dev_id].bitmap_size = bitmap_size;
-+    vmar->rstate[dev_id].bitmap = g_new0(unsigned long, bitmap_size);
-+
-+    vmar->cluster_count += size/VMA_CLUSTER_SIZE;
-+}
-+
-+int vma_reader_register_bs(VmaReader *vmar, guint8 dev_id, BlockBackend *target,
-+                           bool write_zeroes, Error **errp)
-+{
-+    assert(vmar);
-+    assert(target != NULL);
-+    assert(dev_id);
-+    assert(vmar->rstate[dev_id].target == NULL);
-+
-+    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
-+     * 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);
-+        return -1;
-+    }
-+
-+    allocate_rstate(vmar, dev_id, target, write_zeroes);
-+
-+    return 0;
-+}
-+
-+static ssize_t safe_write(int fd, void *buf, size_t count)
-+{
-+    ssize_t n;
-+
-+    do {
-+        n = write(fd, buf, count);
-+    } while (n < 0 && errno == EINTR);
-+
-+    return n;
-+}
-+
-+static size_t full_write(int fd, void *buf, size_t len)
-+{
-+    ssize_t n;
-+    size_t total;
-+
-+    total = 0;
-+
-+    while (len > 0) {
-+        n = safe_write(fd, buf, len);
-+        if (n < 0) {
-+            return n;
-+        }
-+        buf += n;
-+        total += n;
-+        len -= n;
-+    }
-+
-+    if (len) {
-+        /* incomplete write ? */
-+        return -1;
-+    }
-+
-+    return total;
-+}
-+
-+static int restore_write_data(VmaReader *vmar, guint8 dev_id,
-+                              BlockBackend *target, int vmstate_fd,
-+                              unsigned char *buf, int64_t sector_num,
-+                              int nb_sectors, Error **errp)
-+{
-+    assert(vmar);
-+
-+    if (dev_id == vmar->vmstate_stream) {
-+        if (vmstate_fd >= 0) {
-+            int len = nb_sectors * BDRV_SECTOR_SIZE;
-+            int res = full_write(vmstate_fd, buf, len);
-+            if (res < 0) {
-+                error_setg(errp, "write vmstate failed %d", res);
-+                return -1;
-+            }
-+        }
-+    } else {
-+        int res = blk_pwrite(target, sector_num * BDRV_SECTOR_SIZE, buf, nb_sectors * BDRV_SECTOR_SIZE, 0);
-+        if (res < 0) {
-+            error_setg(errp, "blk_pwrite to %s failed (%d)",
-+                       bdrv_get_device_name(blk_bs(target)), res);
-+            return -1;
-+        }
-+    }
-+    return 0;
-+}
-+
-+static int restore_extent(VmaReader *vmar, unsigned char *buf,
-+                          int extent_size, int vmstate_fd,
-+                          bool verbose, bool verify, Error **errp)
-+{
-+    assert(vmar);
-+    assert(buf);
-+
-+    VmaExtentHeader *ehead = (VmaExtentHeader *)buf;
-+    int start = VMA_EXTENT_HEADER_SIZE;
-+    int i;
-+
-+    for (i = 0; i < VMA_BLOCKS_PER_EXTENT; i++) {
-+        uint64_t block_info = GUINT64_FROM_BE(ehead->blockinfo[i]);
-+        uint64_t cluster_num = block_info & 0xffffffff;
-+        uint8_t dev_id = (block_info >> 32) & 0xff;
-+        uint16_t mask = block_info >> (32+16);
-+        int64_t max_sector;
-+
-+        if (!dev_id) {
-+            continue;
-+        }
-+
-+        VmaRestoreState *rstate = &vmar->rstate[dev_id];
-+        BlockBackend *target = NULL;
-+
-+        if (dev_id != vmar->vmstate_stream) {
-+            target = rstate->target;
-+            if (!verify && !target) {
-+                error_setg(errp, "got wrong dev id %d", dev_id);
-+                return -1;
-+            }
-+
-+            if (vma_reader_get_bitmap(rstate, cluster_num)) {
-+                error_setg(errp, "found duplicated cluster %zd for stream %s",
-+                          cluster_num, vmar->devinfo[dev_id].devname);
-+                return -1;
-+            }
-+            vma_reader_set_bitmap(rstate, cluster_num, 1);
-+
-+            max_sector = vmar->devinfo[dev_id].size/BDRV_SECTOR_SIZE;
-+        } else {
-+            max_sector = G_MAXINT64;
-+            if (cluster_num != vmar->vmstate_clusters) {
-+                error_setg(errp, "found out of order vmstate data");
-+                return -1;
-+            }
-+            vmar->vmstate_clusters++;
-+        }
-+
-+        vmar->clusters_read++;
-+
-+        if (verbose) {
-+            time_t duration = time(NULL) - vmar->start_time;
-+            int percent = (vmar->clusters_read*100)/vmar->cluster_count;
-+            if (percent != vmar->clusters_read_per) {
-+                printf("progress %d%% (read %zd bytes, duration %zd sec)\n",
-+                       percent, vmar->clusters_read*VMA_CLUSTER_SIZE,
-+                       duration);
-+                fflush(stdout);
-+                vmar->clusters_read_per = percent;
-+            }
-+        }
-+
-+        /* try to write whole clusters to speedup restore */
-+        if (mask == 0xffff) {
-+            if ((start + VMA_CLUSTER_SIZE) > extent_size) {
-+                error_setg(errp, "short vma extent - too many blocks");
-+                return -1;
-+            }
-+            int64_t sector_num = (cluster_num * VMA_CLUSTER_SIZE) /
-+                BDRV_SECTOR_SIZE;
-+            int64_t end_sector = sector_num +
-+                VMA_CLUSTER_SIZE/BDRV_SECTOR_SIZE;
-+
-+            if (end_sector > max_sector) {
-+                end_sector = max_sector;
-+            }
-+
-+            if (end_sector <= sector_num) {
-+                error_setg(errp, "got wrong block address - write bejond end");
-+                return -1;
-+            }
-+
-+            if (!verify) {
-+                int nb_sectors = end_sector - sector_num;
-+                if (restore_write_data(vmar, dev_id, target, vmstate_fd,
-+                                       buf + start, sector_num, nb_sectors,
-+                                       errp) < 0) {
-+                    return -1;
-+                }
-+            }
-+
-+            start += VMA_CLUSTER_SIZE;
-+        } else {
-+            int j;
-+            int bit = 1;
-+
-+            for (j = 0; j < 16; j++) {
-+                int64_t sector_num = (cluster_num*VMA_CLUSTER_SIZE +
-+                                      j*VMA_BLOCK_SIZE)/BDRV_SECTOR_SIZE;
-+
-+                int64_t end_sector = sector_num +
-+                    VMA_BLOCK_SIZE/BDRV_SECTOR_SIZE;
-+                if (end_sector > max_sector) {
-+                    end_sector = max_sector;
-+                }
-+
-+                if (mask & bit) {
-+                    if ((start + VMA_BLOCK_SIZE) > extent_size) {
-+                        error_setg(errp, "short vma extent - too many blocks");
-+                        return -1;
-+                    }
-+
-+                    if (end_sector <= sector_num) {
-+                        error_setg(errp, "got wrong block address - "
-+                                   "write bejond end");
-+                        return -1;
-+                    }
-+
-+                    if (!verify) {
-+                        int nb_sectors = end_sector - sector_num;
-+                        if (restore_write_data(vmar, dev_id, target, vmstate_fd,
-+                                               buf + start, sector_num,
-+                                               nb_sectors, errp) < 0) {
-+                            return -1;
-+                        }
-+                    }
-+
-+                    start += VMA_BLOCK_SIZE;
-+
-+                } else {
-+
-+
-+                    if (end_sector > sector_num) {
-+                        /* Todo: use bdrv_co_write_zeroes (but that need to
-+                         * be run inside coroutine?)
-+                         */
-+                        int nb_sectors = end_sector - sector_num;
-+                        int zero_size = BDRV_SECTOR_SIZE*nb_sectors;
-+                        vmar->zero_cluster_data += zero_size;
-+                        if (mask != 0) {
-+                            vmar->partial_zero_cluster_data += zero_size;
-+                        }
-+
-+                        if (rstate->write_zeroes && !verify) {
-+                            if (restore_write_data(vmar, dev_id, target, vmstate_fd,
-+                                                   zero_vma_block, sector_num,
-+                                                   nb_sectors, errp) < 0) {
-+                                return -1;
-+                            }
-+                        }
-+                    }
-+                }
-+
-+                bit = bit << 1;
-+            }
-+        }
-+    }
-+
-+    if (start != extent_size) {
-+        error_setg(errp, "vma extent error - missing blocks");
-+        return -1;
-+    }
-+
-+    return 0;
-+}
-+
-+static int vma_reader_restore_full(VmaReader *vmar, int vmstate_fd,
-+                                   bool verbose, bool verify,
-+                                   Error **errp)
-+{
-+    assert(vmar);
-+    assert(vmar->head_data);
-+
-+    int ret = 0;
-+    unsigned char buf[VMA_MAX_EXTENT_SIZE];
-+    int buf_pos = 0;
-+    unsigned char md5sum[16];
-+    VmaHeader *h = (VmaHeader *)vmar->head_data;
-+
-+    vmar->start_time = time(NULL);
-+
-+    while (1) {
-+        int bytes = full_read(vmar->fd, buf + buf_pos, sizeof(buf) - buf_pos);
-+        if (bytes < 0) {
-+            error_setg(errp, "read failed - %s", g_strerror(errno));
-+            return -1;
-+        }
-+
-+        buf_pos += bytes;
-+
-+        if (!buf_pos) {
-+            break; /* EOF */
-+        }
-+
-+        if (buf_pos < VMA_EXTENT_HEADER_SIZE) {
-+            error_setg(errp, "read short extent (%d bytes)", buf_pos);
-+            return -1;
-+        }
-+
-+        VmaExtentHeader *ehead = (VmaExtentHeader *)buf;
-+
-+        /* extract md5sum */
-+        memcpy(md5sum, ehead->md5sum, sizeof(ehead->md5sum));
-+        memset(ehead->md5sum, 0, sizeof(ehead->md5sum));
-+
-+        g_checksum_reset(vmar->md5csum);
-+        g_checksum_update(vmar->md5csum, buf, VMA_EXTENT_HEADER_SIZE);
-+        gsize csize = 16;
-+        g_checksum_get_digest(vmar->md5csum, ehead->md5sum, &csize);
-+
-+        if (memcmp(md5sum, ehead->md5sum, 16) != 0) {
-+            error_setg(errp, "wrong vma extent header chechsum");
-+            return -1;
-+        }
-+
-+        if (memcmp(h->uuid, ehead->uuid, sizeof(ehead->uuid)) != 0) {
-+            error_setg(errp, "wrong vma extent uuid");
-+            return -1;
-+        }
-+
-+        if (ehead->magic != VMA_EXTENT_MAGIC || ehead->reserved1 != 0) {
-+            error_setg(errp, "wrong vma extent header magic");
-+            return -1;
-+        }
-+
-+        int block_count = GUINT16_FROM_BE(ehead->block_count);
-+        int extent_size = VMA_EXTENT_HEADER_SIZE + block_count*VMA_BLOCK_SIZE;
-+
-+        if (buf_pos < extent_size) {
-+            error_setg(errp, "short vma extent (%d < %d)", buf_pos,
-+                       extent_size);
-+            return -1;
-+        }
-+
-+        if (restore_extent(vmar, buf, extent_size, vmstate_fd, verbose,
-+                           verify, errp) < 0) {
-+            return -1;
-+        }
-+
-+        if (buf_pos > extent_size) {
-+            memmove(buf, buf + extent_size, buf_pos - extent_size);
-+            buf_pos = buf_pos - extent_size;
-+        } else {
-+            buf_pos = 0;
-+        }
-+    }
-+
-+    bdrv_drain_all();
-+
-+    int i;
-+    for (i = 1; i < 256; i++) {
-+        VmaRestoreState *rstate = &vmar->rstate[i];
-+        if (!rstate->target) {
-+            continue;
-+        }
-+
-+        if (blk_flush(rstate->target) < 0) {
-+            error_setg(errp, "vma blk_flush %s failed",
-+                       vmar->devinfo[i].devname);
-+            return -1;
-+        }
-+
-+        if (vmar->devinfo[i].size &&
-+            (strcmp(vmar->devinfo[i].devname, "vmstate") != 0)) {
-+            assert(rstate->bitmap);
-+
-+            int64_t cluster_num, end;
-+
-+            end = (vmar->devinfo[i].size + VMA_CLUSTER_SIZE - 1) /
-+                VMA_CLUSTER_SIZE;
-+
-+            for (cluster_num = 0; cluster_num < end; cluster_num++) {
-+                if (!vma_reader_get_bitmap(rstate, cluster_num)) {
-+                    error_setg(errp, "detected missing cluster %zd "
-+                               "for stream %s", cluster_num,
-+                               vmar->devinfo[i].devname);
-+                    return -1;
-+                }
-+            }
-+        }
-+    }
-+
-+    if (verbose) {
-+        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;
-+}
-+
-+int vma_reader_restore(VmaReader *vmar, int vmstate_fd, bool verbose,
-+                       Error **errp)
-+{
-+    return vma_reader_restore_full(vmar, vmstate_fd, verbose, false, errp);
-+}
-+
-+int vma_reader_verify(VmaReader *vmar, bool verbose, Error **errp)
-+{
-+    guint8 dev_id;
-+
-+    for (dev_id = 1; dev_id < 255; dev_id++) {
-+        if (vma_reader_get_device_info(vmar, dev_id)) {
-+            allocate_rstate(vmar, dev_id, NULL, false);
-+        }
-+    }
-+
-+    return vma_reader_restore_full(vmar, -1, verbose, true, errp);
-+}
-+
-diff --git a/vma-writer.c b/vma-writer.c
-new file mode 100644
-index 0000000000..9001cbdd2b
---- /dev/null
-+++ b/vma-writer.c
-@@ -0,0 +1,771 @@
-+/*
-+ * VMA: Virtual Machine Archive
-+ *
-+ * Copyright (C) 2012 Proxmox Server Solutions
-+ *
-+ * Authors:
-+ *  Dietmar Maurer (dietmar@proxmox.com)
-+ *
-+ * 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 <glib.h>
-+#include <uuid/uuid.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
-+
-+#define DPRINTF(fmt, ...)\
-+    do { if (DEBUG_VMA) { printf("vma: " fmt, ## __VA_ARGS__); } } while (0)
-+
-+#define WRITE_BUFFERS 5
-+#define HEADER_CLUSTERS 8
-+#define HEADERBUF_SIZE (VMA_CLUSTER_SIZE*HEADER_CLUSTERS)
-+
-+struct VmaWriter {
-+    int fd;
-+    FILE *cmd;
-+    int status;
-+    char errmsg[8192];
-+    uuid_t uuid;
-+    bool header_written;
-+    bool closed;
-+
-+    /* we always write extents */
-+    unsigned char *outbuf;
-+    int outbuf_pos; /* in bytes */
-+    int outbuf_count; /* in VMA_BLOCKS */
-+    uint64_t outbuf_block_info[VMA_BLOCKS_PER_EXTENT];
-+
-+    unsigned char *headerbuf;
-+
-+    GChecksum *md5csum;
-+    CoMutex flush_lock;
-+    Coroutine *co_writer;
-+
-+    /* drive informations */
-+    VmaStreamInfo stream_info[256];
-+    guint stream_count;
-+
-+    guint8 vmstate_stream;
-+    uint32_t vmstate_clusters;
-+
-+    /* header blob table */
-+    char *header_blob_table;
-+    uint32_t header_blob_table_size;
-+    uint32_t header_blob_table_pos;
-+
-+    /* store for config blobs */
-+    uint32_t config_names[VMA_MAX_CONFIGS]; /* offset into blob_buffer table */
-+    uint32_t config_data[VMA_MAX_CONFIGS];  /* offset into blob_buffer table */
-+    uint32_t config_count;
-+};
-+
-+void vma_writer_set_error(VmaWriter *vmaw, const char *fmt, ...)
-+{
-+    va_list ap;
-+
-+    if (vmaw->status < 0) {
-+        return;
-+    }
-+
-+    vmaw->status = -1;
-+
-+    va_start(ap, fmt);
-+    g_vsnprintf(vmaw->errmsg, sizeof(vmaw->errmsg), fmt, ap);
-+    va_end(ap);
-+
-+    DPRINTF("vma_writer_set_error: %s\n", vmaw->errmsg);
-+}
-+
-+static uint32_t allocate_header_blob(VmaWriter *vmaw, const char *data,
-+                                     size_t len)
-+{
-+    if (len > 65535) {
-+        return 0;
-+    }
-+
-+    if (!vmaw->header_blob_table ||
-+        (vmaw->header_blob_table_size <
-+         (vmaw->header_blob_table_pos + len + 2))) {
-+        int newsize = vmaw->header_blob_table_size + ((len + 2 + 511)/512)*512;
-+
-+        vmaw->header_blob_table = g_realloc(vmaw->header_blob_table, newsize);
-+        memset(vmaw->header_blob_table + vmaw->header_blob_table_size,
-+               0, newsize - vmaw->header_blob_table_size);
-+        vmaw->header_blob_table_size = newsize;
-+    }
-+
-+    uint32_t cpos = vmaw->header_blob_table_pos;
-+    vmaw->header_blob_table[cpos] = len & 255;
-+    vmaw->header_blob_table[cpos+1] = (len >> 8) & 255;
-+    memcpy(vmaw->header_blob_table + cpos + 2, data, len);
-+    vmaw->header_blob_table_pos += len + 2;
-+    return cpos;
-+}
-+
-+static uint32_t allocate_header_string(VmaWriter *vmaw, const char *str)
-+{
-+    assert(vmaw);
-+
-+    size_t len = strlen(str) + 1;
-+
-+    return allocate_header_blob(vmaw, str, len);
-+}
-+
-+int vma_writer_add_config(VmaWriter *vmaw, const char *name, gpointer data,
-+                          gsize len)
-+{
-+    assert(vmaw);
-+    assert(!vmaw->header_written);
-+    assert(vmaw->config_count < VMA_MAX_CONFIGS);
-+    assert(name);
-+    assert(data);
-+
-+    gchar *basename = g_path_get_basename(name);
-+    uint32_t name_ptr = allocate_header_string(vmaw, basename);
-+    g_free(basename);
-+
-+    if (!name_ptr) {
-+        return -1;
-+    }
-+
-+    uint32_t data_ptr = allocate_header_blob(vmaw, data, len);
-+    if (!data_ptr) {
-+        return -1;
-+    }
-+
-+    vmaw->config_names[vmaw->config_count] = name_ptr;
-+    vmaw->config_data[vmaw->config_count] = data_ptr;
-+
-+    vmaw->config_count++;
-+
-+    return 0;
-+}
-+
-+int vma_writer_register_stream(VmaWriter *vmaw, const char *devname,
-+                               size_t size)
-+{
-+    assert(vmaw);
-+    assert(devname);
-+    assert(!vmaw->status);
-+
-+    if (vmaw->header_written) {
-+        vma_writer_set_error(vmaw, "vma_writer_register_stream: header "
-+                             "already written");
-+        return -1;
-+    }
-+
-+    guint n = vmaw->stream_count + 1;
-+
-+    /* we can have dev_ids form 1 to 255 (0 reserved)
-+     * 255(-1) reseverd for safety
-+     */
-+    if (n > 254) {
-+        vma_writer_set_error(vmaw, "vma_writer_register_stream: "
-+                             "too many drives");
-+        return -1;
-+    }
-+
-+    if (size <= 0) {
-+        vma_writer_set_error(vmaw, "vma_writer_register_stream: "
-+                             "got strange size %zd", size);
-+        return -1;
-+    }
-+
-+    DPRINTF("vma_writer_register_stream %s %zu %d\n", devname, size, n);
-+
-+    vmaw->stream_info[n].devname = g_strdup(devname);
-+    vmaw->stream_info[n].size = size;
-+
-+    vmaw->stream_info[n].cluster_count = (size + VMA_CLUSTER_SIZE - 1) /
-+        VMA_CLUSTER_SIZE;
-+
-+    vmaw->stream_count = n;
-+
-+    if (strcmp(devname, "vmstate") == 0) {
-+        vmaw->vmstate_stream = n;
-+    }
-+
-+    return n;
-+}
-+
-+static void vma_co_continue_write(void *opaque)
-+{
-+    VmaWriter *vmaw = opaque;
-+
-+    DPRINTF("vma_co_continue_write\n");
-+    qemu_coroutine_enter(vmaw->co_writer);
-+}
-+
-+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);
-+
-+    size_t done = 0;
-+    ssize_t ret;
-+
-+    assert(vmaw->co_writer == NULL);
-+
-+    vmaw->co_writer = qemu_coroutine_self();
-+
-+    while (done < bytes) {
-+        aio_set_fd_handler(qemu_get_aio_context(), vmaw->fd, false, NULL, vma_co_continue_write, NULL, vmaw);
-+        qemu_coroutine_yield();
-+        aio_set_fd_handler(qemu_get_aio_context(), vmaw->fd, false, NULL, 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_queue_write written %zd %zd\n", done, ret);
-+        } else if (ret < 0) {
-+            if (errno == EAGAIN || errno == EWOULDBLOCK) {
-+                /* 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;
-+            }
-+        } else if (ret == 0) {
-+            /* should not happen - simply try again */
-+        }
-+    }
-+
-+    vmaw->co_writer = NULL;
-+
-+    return (done == bytes) ? bytes : -1;
-+}
-+
-+VmaWriter *vma_writer_create(const char *filename, uuid_t uuid, Error **errp)
-+{
-+    const char *p;
-+
-+    assert(sizeof(VmaHeader) == (4096 + 8192));
-+    assert(G_STRUCT_OFFSET(VmaHeader, config_names) == 2044);
-+    assert(G_STRUCT_OFFSET(VmaHeader, config_data) == 3068);
-+    assert(G_STRUCT_OFFSET(VmaHeader, dev_info) == 4096);
-+    assert(sizeof(VmaExtentHeader) == 512);
-+
-+    VmaWriter *vmaw = g_new0(VmaWriter, 1);
-+    vmaw->fd = -1;
-+
-+    vmaw->md5csum = g_checksum_new(G_CHECKSUM_MD5);
-+    if (!vmaw->md5csum) {
-+        error_setg(errp, "can't allocate cmsum\n");
-+        goto err;
-+    }
-+
-+    if (strstart(filename, "exec:", &p)) {
-+        vmaw->cmd = popen(p, "w");
-+        if (vmaw->cmd == NULL) {
-+            error_setg(errp, "can't popen command '%s' - %s\n", p,
-+                       g_strerror(errno));
-+            goto err;
-+        }
-+        vmaw->fd = fileno(vmaw->cmd);
-+
-+        /* try to use O_NONBLOCK */
-+        fcntl(vmaw->fd, F_SETFL, fcntl(vmaw->fd, F_GETFL)|O_NONBLOCK);
-+
-+    } else {
-+        struct stat st;
-+        int oflags;
-+        const char *tmp_id_str;
-+
-+        if ((stat(filename, &st) == 0) && S_ISFIFO(st.st_mode)) {
-+            oflags = O_NONBLOCK|O_WRONLY;
-+            vmaw->fd = qemu_open(filename, oflags, 0644);
-+        } else if (strstart(filename, "/dev/fdset/", &tmp_id_str)) {
-+            oflags = O_NONBLOCK|O_WRONLY;
-+            vmaw->fd = qemu_open(filename, oflags, 0644);
-+        } else if (strstart(filename, "/dev/fdname/", &tmp_id_str)) {
-+            vmaw->fd = monitor_get_fd(cur_mon, tmp_id_str, errp);
-+            if (vmaw->fd < 0) {
-+                goto err;
-+            }
-+            /* try to use O_NONBLOCK */
-+            fcntl(vmaw->fd, F_SETFL, fcntl(vmaw->fd, F_GETFL)|O_NONBLOCK);
-+        } else  {
-+            oflags = O_NONBLOCK|O_DIRECT|O_WRONLY|O_CREAT|O_EXCL;
-+            vmaw->fd = qemu_open(filename, oflags, 0644);
-+        }
-+
-+        if (vmaw->fd < 0) {
-+            error_setg(errp, "can't open file %s - %s\n", filename,
-+                       g_strerror(errno));
-+            goto err;
-+        }
-+    }
-+
-+    /* we use O_DIRECT, so we need to align IO buffers */
-+
-+    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->flush_lock);
-+
-+    uuid_copy(vmaw->uuid, uuid);
-+
-+    return vmaw;
-+
-+err:
-+    if (vmaw) {
-+        if (vmaw->cmd) {
-+            pclose(vmaw->cmd);
-+        } else if (vmaw->fd >= 0) {
-+            close(vmaw->fd);
-+        }
-+
-+        if (vmaw->md5csum) {
-+            g_checksum_free(vmaw->md5csum);
-+        }
-+
-+        g_free(vmaw);
-+    }
-+
-+    return NULL;
-+}
-+
-+static int coroutine_fn vma_write_header(VmaWriter *vmaw)
-+{
-+    assert(vmaw);
-+    unsigned char *buf = vmaw->headerbuf;
-+    VmaHeader *head = (VmaHeader *)buf;
-+
-+    int i;
-+
-+    DPRINTF("VMA WRITE HEADER\n");
-+
-+    if (vmaw->status < 0) {
-+        return vmaw->status;
-+    }
-+
-+    memset(buf, 0, HEADERBUF_SIZE);
-+
-+    head->magic = VMA_MAGIC;
-+    head->version = GUINT32_TO_BE(1); /* v1 */
-+    memcpy(head->uuid, vmaw->uuid, 16);
-+
-+    time_t ctime = time(NULL);
-+    head->ctime = GUINT64_TO_BE(ctime);
-+
-+    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]);
-+    }
-+
-+    /* 32 bytes per device (12 used currently) = 8192 bytes max */
-+    for (i = 1; i <= 254; i++) {
-+        VmaStreamInfo *si = &vmaw->stream_info[i];
-+        if (si->size) {
-+            assert(si->devname);
-+            uint32_t devname_ptr = allocate_header_string(vmaw, si->devname);
-+            if (!devname_ptr) {
-+                return -1;
-+            }
-+            head->dev_info[i].devname_ptr = GUINT32_TO_BE(devname_ptr);
-+            head->dev_info[i].size = GUINT64_TO_BE(si->size);
-+        }
-+    }
-+
-+    uint32_t header_size = sizeof(VmaHeader) + vmaw->header_blob_table_size;
-+    head->header_size = GUINT32_TO_BE(header_size);
-+
-+    if (header_size > HEADERBUF_SIZE) {
-+        return -1; /* just to be sure */
-+    }
-+
-+    uint32_t blob_buffer_offset = sizeof(VmaHeader);
-+    memcpy(buf + blob_buffer_offset, vmaw->header_blob_table,
-+           vmaw->header_blob_table_size);
-+    head->blob_buffer_offset = GUINT32_TO_BE(blob_buffer_offset);
-+    head->blob_buffer_size = GUINT32_TO_BE(vmaw->header_blob_table_pos);
-+
-+    g_checksum_reset(vmaw->md5csum);
-+    g_checksum_update(vmaw->md5csum, (const guchar *)buf, header_size);
-+    gsize csize = 16;
-+    g_checksum_get_digest(vmaw->md5csum, (guint8 *)(head->md5sum), &csize);
-+
-+    return vma_queue_write(vmaw, buf, header_size);
-+}
-+
-+static int coroutine_fn vma_writer_flush(VmaWriter *vmaw)
-+{
-+    assert(vmaw);
-+
-+    int ret;
-+    int i;
-+
-+    if (vmaw->status < 0) {
-+        return vmaw->status;
-+    }
-+
-+    if (!vmaw->header_written) {
-+        vmaw->header_written = true;
-+        ret = vma_write_header(vmaw);
-+        if (ret < 0) {
-+            vma_writer_set_error(vmaw, "vma_writer_flush: write header failed");
-+            return ret;
-+        }
-+    }
-+
-+    DPRINTF("VMA WRITE FLUSH %d %d\n", vmaw->outbuf_count, vmaw->outbuf_pos);
-+
-+
-+    VmaExtentHeader *ehead = (VmaExtentHeader *)vmaw->outbuf;
-+
-+    ehead->magic = VMA_EXTENT_MAGIC;
-+    ehead->reserved1 = 0;
-+
-+    for (i = 0; i < VMA_BLOCKS_PER_EXTENT; i++) {
-+        ehead->blockinfo[i] = GUINT64_TO_BE(vmaw->outbuf_block_info[i]);
-+    }
-+
-+    guint16 block_count = (vmaw->outbuf_pos - VMA_EXTENT_HEADER_SIZE) /
-+        VMA_BLOCK_SIZE;
-+
-+    ehead->block_count = GUINT16_TO_BE(block_count);
-+
-+    memcpy(ehead->uuid, vmaw->uuid, sizeof(ehead->uuid));
-+    memset(ehead->md5sum, 0, sizeof(ehead->md5sum));
-+
-+    g_checksum_reset(vmaw->md5csum);
-+    g_checksum_update(vmaw->md5csum, vmaw->outbuf, VMA_EXTENT_HEADER_SIZE);
-+    gsize csize = 16;
-+    g_checksum_get_digest(vmaw->md5csum, ehead->md5sum, &csize);
-+
-+    int bytes = vmaw->outbuf_pos;
-+    ret = vma_queue_write(vmaw, vmaw->outbuf, bytes);
-+    if (ret != bytes) {
-+        vma_writer_set_error(vmaw, "vma_writer_flush: failed write");
-+    }
-+
-+    vmaw->outbuf_count = 0;
-+    vmaw->outbuf_pos = VMA_EXTENT_HEADER_SIZE;
-+
-+    for (i = 0; i < VMA_BLOCKS_PER_EXTENT; i++) {
-+        vmaw->outbuf_block_info[i] = 0;
-+    }
-+
-+    return vmaw->status;
-+}
-+
-+static int vma_count_open_streams(VmaWriter *vmaw)
-+{
-+    g_assert(vmaw != NULL);
-+
-+    int i;
-+    int open_drives = 0;
-+    for (i = 0; i <= 255; i++) {
-+        if (vmaw->stream_info[i].size && !vmaw->stream_info[i].finished) {
-+            open_drives++;
-+        }
-+    }
-+
-+    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)
-+ */
-+int coroutine_fn
-+vma_writer_close_stream(VmaWriter *vmaw, uint8_t dev_id)
-+{
-+    g_assert(vmaw != NULL);
-+
-+    DPRINTF("vma_writer_set_status %d\n", dev_id);
-+    if (!vmaw->stream_info[dev_id].size) {
-+        vma_writer_set_error(vmaw, "vma_writer_close_stream: "
-+                             "no such stream %d", dev_id);
-+        return -1;
-+    }
-+    if (vmaw->stream_info[dev_id].finished) {
-+        vma_writer_set_error(vmaw, "vma_writer_close_stream: "
-+                             "stream already closed %d", dev_id);
-+        return -1;
-+    }
-+
-+    vmaw->stream_info[dev_id].finished = true;
-+
-+    int open_drives = vma_count_open_streams(vmaw);
-+
-+    if (open_drives <= 0) {
-+        DPRINTF("vma_writer_set_status all drives completed\n");
-+        vma_writer_flush_output(vmaw);
-+    }
-+
-+    return open_drives;
-+}
-+
-+int vma_writer_get_status(VmaWriter *vmaw, VmaStatus *status)
-+{
-+    int i;
-+
-+    g_assert(vmaw != NULL);
-+
-+    if (status) {
-+        status->status = vmaw->status;
-+        g_strlcpy(status->errmsg, vmaw->errmsg, sizeof(status->errmsg));
-+        for (i = 0; i <= 255; i++) {
-+            status->stream_info[i] = vmaw->stream_info[i];
-+        }
-+
-+        uuid_unparse_lower(vmaw->uuid, status->uuid_str);
-+    }
-+
-+    status->closed = vmaw->closed;
-+
-+    return vmaw->status;
-+}
-+
-+static int vma_writer_get_buffer(VmaWriter *vmaw)
-+{
-+    int ret = 0;
-+
-+    qemu_co_mutex_lock(&vmaw->flush_lock);
-+
-+    /* wait until buffer is available */
-+    while (vmaw->outbuf_count >= (VMA_BLOCKS_PER_EXTENT - 1)) {
-+        ret = vma_writer_flush(vmaw);
-+        if (ret < 0) {
-+            vma_writer_set_error(vmaw, "vma_writer_get_buffer: flush failed");
-+            break;
-+        }
-+    }
-+
-+    qemu_co_mutex_unlock(&vmaw->flush_lock);
-+
-+    return ret;
-+}
-+
-+
-+int64_t coroutine_fn
-+vma_writer_write(VmaWriter *vmaw, uint8_t dev_id, int64_t cluster_num,
-+                 unsigned char *buf, size_t *zero_bytes)
-+{
-+    g_assert(vmaw != NULL);
-+    g_assert(zero_bytes != NULL);
-+
-+    *zero_bytes = 0;
-+
-+    if (vmaw->status < 0) {
-+        return vmaw->status;
-+    }
-+
-+    if (!dev_id || !vmaw->stream_info[dev_id].size) {
-+        vma_writer_set_error(vmaw, "vma_writer_write: "
-+                             "no such stream %d", dev_id);
-+        return -1;
-+    }
-+
-+    if (vmaw->stream_info[dev_id].finished) {
-+        vma_writer_set_error(vmaw, "vma_writer_write: "
-+                             "stream already closed %d", dev_id);
-+        return -1;
-+    }
-+
-+
-+    if (cluster_num >= (((uint64_t)1)<<32)) {
-+        vma_writer_set_error(vmaw, "vma_writer_write: "
-+                             "cluster number out of range");
-+        return -1;
-+    }
-+
-+    if (dev_id == vmaw->vmstate_stream) {
-+        if (cluster_num != vmaw->vmstate_clusters) {
-+            vma_writer_set_error(vmaw, "vma_writer_write: "
-+                                 "non sequential vmstate write");
-+        }
-+        vmaw->vmstate_clusters++;
-+    } else if (cluster_num >= vmaw->stream_info[dev_id].cluster_count) {
-+        vma_writer_set_error(vmaw, "vma_writer_write: cluster number too big");
-+        return -1;
-+    }
-+
-+    /* wait until buffer is available */
-+    if (vma_writer_get_buffer(vmaw) < 0) {
-+        vma_writer_set_error(vmaw, "vma_writer_write: "
-+                             "vma_writer_get_buffer failed");
-+        return -1;
-+    }
-+
-+    DPRINTF("VMA WRITE %d %zd\n", dev_id, cluster_num);
-+
-+    uint16_t mask = 0;
-+
-+    if (buf) {
-+        int i;
-+        int bit = 1;
-+        for (i = 0; i < 16; i++) {
-+            unsigned char *vmablock = buf + (i*VMA_BLOCK_SIZE);
-+            if (!buffer_is_zero(vmablock, VMA_BLOCK_SIZE)) {
-+                mask |= bit;
-+                memcpy(vmaw->outbuf + vmaw->outbuf_pos, vmablock,
-+                       VMA_BLOCK_SIZE);
-+                vmaw->outbuf_pos += VMA_BLOCK_SIZE;
-+            } else {
-+                DPRINTF("VMA WRITE %zd ZERO BLOCK %d\n", cluster_num, i);
-+                vmaw->stream_info[dev_id].zero_bytes += VMA_BLOCK_SIZE;
-+                *zero_bytes += VMA_BLOCK_SIZE;
-+            }
-+
-+            bit = bit << 1;
-+        }
-+    } else {
-+        DPRINTF("VMA WRITE %zd ZERO CLUSTER\n", cluster_num);
-+        vmaw->stream_info[dev_id].zero_bytes += VMA_CLUSTER_SIZE;
-+        *zero_bytes += VMA_CLUSTER_SIZE;
-+    }
-+
-+    uint64_t block_info = ((uint64_t)mask) << (32+16);
-+    block_info |= ((uint64_t)dev_id) << 32;
-+    block_info |= (cluster_num & 0xffffffff);
-+    vmaw->outbuf_block_info[vmaw->outbuf_count] = block_info;
-+
-+    DPRINTF("VMA WRITE MASK %zd %zx\n", cluster_num, block_info);
-+
-+    vmaw->outbuf_count++;
-+
-+    /** NOTE: We allways write whole clusters, but we correctly set
-+     * transferred bytes. So transferred == size when when everything
-+     * went OK.
-+     */
-+    size_t transferred = VMA_CLUSTER_SIZE;
-+
-+    if (dev_id != vmaw->vmstate_stream) {
-+        uint64_t last = (cluster_num + 1) * VMA_CLUSTER_SIZE;
-+        if (last > vmaw->stream_info[dev_id].size) {
-+            uint64_t diff = last - vmaw->stream_info[dev_id].size;
-+            if (diff >= VMA_CLUSTER_SIZE) {
-+                vma_writer_set_error(vmaw, "vma_writer_write: "
-+                                     "read after last cluster");
-+                return -1;
-+            }
-+            transferred -= diff;
-+        }
-+    }
-+
-+    vmaw->stream_info[dev_id].transferred += transferred;
-+
-+    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);
-+
-+    int i;
-+
-+    while (vmaw->co_writer) {
-+        aio_poll(qemu_get_aio_context(), true);
-+    }
-+
-+    assert(vmaw->co_writer == NULL);
-+
-+    if (vmaw->cmd) {
-+        if (pclose(vmaw->cmd) < 0) {
-+            vma_writer_set_error(vmaw, "vma_writer_close: "
-+                                 "pclose failed - %s", g_strerror(errno));
-+        }
-+    } else {
-+        if (close(vmaw->fd) < 0) {
-+            vma_writer_set_error(vmaw, "vma_writer_close: "
-+                                 "close failed - %s", g_strerror(errno));
-+        }
-+    }
-+
-+    for (i = 0; i <= 255; i++) {
-+        VmaStreamInfo *si = &vmaw->stream_info[i];
-+        if (si->size) {
-+            if (!si->finished) {
-+                vma_writer_set_error(vmaw, "vma_writer_close: "
-+                                     "detected open stream '%s'", si->devname);
-+            } else if ((si->transferred != si->size) &&
-+                       (i != vmaw->vmstate_stream)) {
-+                vma_writer_set_error(vmaw, "vma_writer_close: "
-+                                     "incomplete stream '%s' (%zd != %zd)",
-+                                     si->devname, si->transferred, si->size);
-+            }
-+        }
-+    }
-+
-+    for (i = 0; i <= 255; i++) {
-+        vmaw->stream_info[i].finished = 1; /* mark as closed */
-+    }
-+
-+    vmaw->closed = 1;
-+
-+    if (vmaw->status < 0 && *errp == NULL) {
-+        error_setg(errp, "%s", vmaw->errmsg);
-+    }
-+
-+    return vmaw->status;
-+}
-+
-+void vma_writer_destroy(VmaWriter *vmaw)
-+{
-+    assert(vmaw);
-+
-+    int i;
-+
-+    for (i = 0; i <= 255; i++) {
-+        if (vmaw->stream_info[i].devname) {
-+            g_free(vmaw->stream_info[i].devname);
-+        }
-+    }
-+
-+    if (vmaw->md5csum) {
-+        g_checksum_free(vmaw->md5csum);
-+    }
-+
-+    g_free(vmaw);
-+}
-diff --git a/vma.c b/vma.c
-new file mode 100644
-index 0000000000..04915427c8
---- /dev/null
-+++ b/vma.c
-@@ -0,0 +1,757 @@
-+/*
-+ * VMA: Virtual Machine Archive
-+ *
-+ * Copyright (C) 2012-2013 Proxmox Server Solutions
-+ *
-+ * Authors:
-+ *  Dietmar Maurer (dietmar@proxmox.com)
-+ *
-+ * 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 <glib.h>
-+
-+#include "vma.h"
-+#include "qemu-common.h"
-+#include "qemu/error-report.h"
-+#include "qemu/main-loop.h"
-+#include "qapi/qmp/qstring.h"
-+#include "sysemu/char.h" /* qstring_from_str */
-+#include "sysemu/block-backend.h"
-+
-+static void help(void)
-+{
-+    const char *help_msg =
-+        "usage: vma command [command options]\n"
-+        "\n"
-+        "vma list <filename>\n"
-+        "vma config <filename> [-c config]\n"
-+        "vma create <filename> [-c config] pathname ...\n"
-+        "vma extract <filename> [-r <fifo>] <targetdir>\n"
-+        "vma verify <filename> [-v]\n"
-+        ;
-+
-+    printf("%s", help_msg);
-+    exit(1);
-+}
-+
-+static const char *extract_devname(const char *path, char **devname, int index)
-+{
-+    assert(path);
-+
-+    const char *sep = strchr(path, '=');
-+
-+    if (sep) {
-+        *devname = g_strndup(path, sep - path);
-+        path = sep + 1;
-+    } else {
-+        if (index >= 0) {
-+            *devname = g_strdup_printf("disk%d", index);
-+        } else {
-+            *devname = NULL;
-+        }
-+    }
-+
-+    return path;
-+}
-+
-+static void print_content(VmaReader *vmar)
-+{
-+    assert(vmar);
-+
-+    VmaHeader *head = vma_reader_get_header(vmar);
-+
-+    GList *l = vma_reader_get_config_data(vmar);
-+    while (l && l->data) {
-+        VmaConfigData *cdata = (VmaConfigData *)l->data;
-+        l = g_list_next(l);
-+        printf("CFG: size: %d name: %s\n", cdata->len, cdata->name);
-+    }
-+
-+    int i;
-+    VmaDeviceInfo *di;
-+    for (i = 1; i < 255; i++) {
-+        di = vma_reader_get_device_info(vmar, i);
-+        if (di) {
-+            if (strcmp(di->devname, "vmstate") == 0) {
-+                printf("VMSTATE: dev_id=%d memory: %zd\n", i, di->size);
-+            } else {
-+                printf("DEV: dev_id=%d size: %zd devname: %s\n",
-+                       i, di->size, di->devname);
-+            }
-+        }
-+    }
-+    /* ctime is the last entry we print */
-+    printf("CTIME: %s", ctime(&head->ctime));
-+    fflush(stdout);
-+}
-+
-+static int list_content(int argc, char **argv)
-+{
-+    int c, ret = 0;
-+    const char *filename;
-+
-+    for (;;) {
-+        c = getopt(argc, argv, "h");
-+        if (c == -1) {
-+            break;
-+        }
-+        switch (c) {
-+        case '?':
-+        case 'h':
-+            help();
-+            break;
-+        default:
-+            g_assert_not_reached();
-+        }
-+    }
-+
-+    /* Get the filename */
-+    if ((optind + 1) != argc) {
-+        help();
-+    }
-+    filename = argv[optind++];
-+
-+    Error *errp = NULL;
-+    VmaReader *vmar = vma_reader_create(filename, &errp);
-+
-+    if (!vmar) {
-+        g_error("%s", error_get_pretty(errp));
-+    }
-+
-+    print_content(vmar);
-+
-+    vma_reader_destroy(vmar);
-+
-+    return ret;
-+}
-+
-+typedef struct RestoreMap {
-+    char *devname;
-+    char *path;
-+    char *format;
-+    bool write_zero;
-+} RestoreMap;
-+
-+static int extract_content(int argc, char **argv)
-+{
-+    int c, ret = 0;
-+    int verbose = 0;
-+    const char *filename;
-+    const char *dirname;
-+    const char *readmap = NULL;
-+
-+    for (;;) {
-+        c = getopt(argc, argv, "hvr:");
-+        if (c == -1) {
-+            break;
-+        }
-+        switch (c) {
-+        case '?':
-+        case 'h':
-+            help();
-+            break;
-+        case 'r':
-+            readmap = optarg;
-+            break;
-+        case 'v':
-+            verbose = 1;
-+            break;
-+        default:
-+            help();
-+        }
-+    }
-+
-+    /* Get the filename */
-+    if ((optind + 2) != argc) {
-+        help();
-+    }
-+    filename = argv[optind++];
-+    dirname = argv[optind++];
-+
-+    Error *errp = NULL;
-+    VmaReader *vmar = vma_reader_create(filename, &errp);
-+
-+    if (!vmar) {
-+        g_error("%s", error_get_pretty(errp));
-+    }
-+
-+    if (mkdir(dirname, 0777) < 0) {
-+        g_error("unable to create target directory %s - %s",
-+                dirname, g_strerror(errno));
-+    }
-+
-+    GList *l = vma_reader_get_config_data(vmar);
-+    while (l && l->data) {
-+        VmaConfigData *cdata = (VmaConfigData *)l->data;
-+        l = g_list_next(l);
-+        char *cfgfn = g_strdup_printf("%s/%s", dirname, cdata->name);
-+        GError *err = NULL;
-+        if (!g_file_set_contents(cfgfn, (gchar *)cdata->data, cdata->len,
-+                                 &err)) {
-+            g_error("unable to write file: %s", err->message);
-+        }
-+    }
-+
-+    GHashTable *devmap = g_hash_table_new(g_str_hash, g_str_equal);
-+
-+    if (readmap) {
-+        print_content(vmar);
-+
-+        FILE *map = fopen(readmap, "r");
-+        if (!map) {
-+            g_error("unable to open fifo %s - %s", readmap, g_strerror(errno));
-+        }
-+
-+        while (1) {
-+            char inbuf[8192];
-+            char *line = fgets(inbuf, sizeof(inbuf), map);
-+            if (!line || line[0] == '\0' || !strcmp(line, "done\n")) {
-+                break;
-+            }
-+            int len = strlen(line);
-+            if (line[len - 1] == '\n') {
-+                line[len - 1] = '\0';
-+                if (len == 1) {
-+                    break;
-+                }
-+            }
-+
-+            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 = line + 2;
-+                write_zero = false;
-+            } else if (line[0] == '1' && line[1] == ':') {
-+                path = line + 2;
-+                write_zero = true;
-+            } else {
-+                g_error("read map failed - parse error ('%s')", inbuf);
-+            }
-+
-+            char *devname = NULL;
-+            path = extract_devname(path, &devname, -1);
-+            if (!devname) {
-+                g_error("read map failed - no dev name specified ('%s')",
-+                        inbuf);
-+            }
-+
-+            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);
-+
-+        };
-+    }
-+
-+    int i;
-+    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)) {
-+            vmstate_stream = i;
-+            char *statefn = g_strdup_printf("%s/vmstate.bin", dirname);
-+            vmstate_fd = open(statefn, O_WRONLY|O_CREAT|O_EXCL, 0644);
-+            if (vmstate_fd < 0) {
-+                g_error("create vmstate file '%s' failed - %s", statefn,
-+                        g_strerror(errno));
-+            }
-+            g_free(statefn);
-+        } else if (di) {
-+            char *devfn = NULL;
-+            const char *format = NULL;
-+            int flags = BDRV_O_RDWR | BDRV_O_NO_FLUSH;
-+            bool write_zero = true;
-+
-+            if (readmap) {
-+                RestoreMap *map;
-+                map = (RestoreMap *)g_hash_table_lookup(devmap, di->devname);
-+                if (map == NULL) {
-+                    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",
-+                                        dirname, di->devname);
-+                printf("DEVINFO %s %zd\n", devfn, di->size);
-+
-+                bdrv_img_create(devfn, "raw", NULL, NULL, NULL, di->size,
-+                                flags, &errp, 0);
-+                if (errp) {
-+                    g_error("can't create file %s: %s", devfn,
-+                            error_get_pretty(errp));
-+                }
-+
-+                /* Note: we created an empty file above, so there is no
-+                 * need to write zeroes (so we generate a sparse file)
-+                 */
-+                write_zero = false;
-+            }
-+
-+          size_t devlen = strlen(devfn);
-+          QDict *options = NULL;
-+            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"));
-+          }
-+
-+
-+          if (errp || !(blk = blk_new_open(devfn, NULL, options, flags, &errp))) {
-+                g_error("can't open file %s - %s", devfn,
-+                        error_get_pretty(errp));
-+            }
-+
-+            if (vma_reader_register_bs(vmar, i, blk, write_zero, &errp) < 0) {
-+                g_error("%s", error_get_pretty(errp));
-+            }
-+
-+            if (!readmap) {
-+                g_free(devfn);
-+            }
-+        }
-+    }
-+
-+    if (vma_reader_restore(vmar, vmstate_fd, verbose, &errp) < 0) {
-+        g_error("restore failed - %s", error_get_pretty(errp));
-+    }
-+
-+    if (!readmap) {
-+        for (i = 1; i < 255; i++) {
-+            VmaDeviceInfo *di = vma_reader_get_device_info(vmar, i);
-+            if (di && (i != vmstate_stream)) {
-+                char *tmpfn = g_strdup_printf("%s/tmp-disk-%s.raw",
-+                                              dirname, di->devname);
-+                char *fn = g_strdup_printf("%s/disk-%s.raw",
-+                                           dirname, di->devname);
-+                if (rename(tmpfn, fn) != 0) {
-+                    g_error("rename %s to %s failed - %s",
-+                            tmpfn, fn, g_strerror(errno));
-+                }
-+            }
-+        }
-+    }
-+
-+    vma_reader_destroy(vmar);
-+
-+    blk_unref(blk);
-+
-+    bdrv_close_all();
-+
-+    return ret;
-+}
-+
-+static int verify_content(int argc, char **argv)
-+{
-+    int c, ret = 0;
-+    int verbose = 0;
-+    const char *filename;
-+
-+    for (;;) {
-+        c = getopt(argc, argv, "hv");
-+        if (c == -1) {
-+            break;
-+        }
-+        switch (c) {
-+        case '?':
-+        case 'h':
-+            help();
-+            break;
-+        case 'v':
-+            verbose = 1;
-+            break;
-+        default:
-+            help();
-+        }
-+    }
-+
-+    /* Get the filename */
-+    if ((optind + 1) != argc) {
-+        help();
-+    }
-+    filename = argv[optind++];
-+
-+    Error *errp = NULL;
-+    VmaReader *vmar = vma_reader_create(filename, &errp);
-+
-+    if (!vmar) {
-+        g_error("%s", error_get_pretty(errp));
-+    }
-+
-+    if (verbose) {
-+        print_content(vmar);
-+    }
-+
-+    if (vma_reader_verify(vmar, verbose, &errp) < 0) {
-+        g_error("verify failed - %s", error_get_pretty(errp));
-+    }
-+
-+    vma_reader_destroy(vmar);
-+
-+    bdrv_close_all();
-+
-+    return ret;
-+}
-+
-+typedef struct BackupJob {
-+    BlockBackend *target;
-+    int64_t len;
-+    VmaWriter *vmaw;
-+    uint8_t dev_id;
-+} 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;
-+    struct iovec iov;
-+    QEMUIOVector qiov;
-+
-+    int64_t start, end;
-+    int ret = 0;
-+
-+    unsigned char *buf = blk_blockalign(job->target, VMA_CLUSTER_SIZE);
-+
-+    start = 0;
-+    end = DIV_ROUND_UP(job->len / BDRV_SECTOR_SIZE,
-+                       BACKUP_SECTORS_PER_CLUSTER);
-+
-+    for (; start < end; start++) {
-+        iov.iov_base = buf;
-+        iov.iov_len = VMA_CLUSTER_SIZE;
-+        qemu_iovec_init_external(&qiov, &iov, 1);
-+
-+        ret = blk_co_preadv(job->target, start * VMA_CLUSTER_SIZE,
-+                            VMA_CLUSTER_SIZE, &qiov, 0);
-+        if (ret < 0) {
-+            vma_writer_set_error(job->vmaw, "read error", -1);
-+            goto out;
-+        }
-+
-+        size_t zb = 0;
-+        if (vma_writer_write(job->vmaw, job->dev_id, start, buf, &zb) < 0) {
-+            vma_writer_set_error(job->vmaw, "backup_dump_cb vma_writer_write failed", -1);
-+            goto out;
-+        }
-+    }
-+
-+
-+out:
-+    if (vma_writer_close_stream(job->vmaw, job->dev_id) <= 0) {
-+        Error *err = NULL;
-+        if (vma_writer_close(job->vmaw, &err) != 0) {
-+            g_warning("vma_writer_close failed %s", error_get_pretty(err));
-+        }
-+    }
-+}
-+
-+static int create_archive(int argc, char **argv)
-+{
-+    int i, c;
-+    int verbose = 0;
-+    const char *archivename;
-+    GList *config_files = NULL;
-+
-+    for (;;) {
-+        c = getopt(argc, argv, "hvc:");
-+        if (c == -1) {
-+            break;
-+        }
-+        switch (c) {
-+        case '?':
-+        case 'h':
-+            help();
-+            break;
-+        case 'c':
-+            config_files = g_list_append(config_files, optarg);
-+            break;
-+        case 'v':
-+            verbose = 1;
-+            break;
-+        default:
-+            g_assert_not_reached();
-+        }
-+    }
-+
-+
-+    /* make sure we an archive name */
-+    if ((optind + 1) > argc) {
-+        help();
-+    }
-+
-+    archivename = argv[optind++];
-+
-+    uuid_t uuid;
-+    uuid_generate(uuid);
-+
-+    Error *local_err = NULL;
-+    VmaWriter *vmaw = vma_writer_create(archivename, uuid, &local_err);
-+
-+    if (vmaw == NULL) {
-+        g_error("%s", error_get_pretty(local_err));
-+    }
-+
-+    GList *l = config_files;
-+    while (l && l->data) {
-+        char *name = l->data;
-+        char *cdata = NULL;
-+        gsize clen = 0;
-+        GError *err = NULL;
-+        if (!g_file_get_contents(name, &cdata, &clen, &err)) {
-+            unlink(archivename);
-+            g_error("Unable to read file: %s", err->message);
-+        }
-+
-+        if (vma_writer_add_config(vmaw, name, cdata, clen) != 0) {
-+            unlink(archivename);
-+            g_error("Unable to append config data %s (len = %zd)",
-+                    name, clen);
-+        }
-+        l = g_list_next(l);
-+    }
-+
-+    int devcount = 0;
-+    while (optind < argc) {
-+        const char *path = argv[optind++];
-+        char *devname = NULL;
-+        path = extract_devname(path, &devname, devcount++);
-+
-+        Error *errp = NULL;
-+        BlockBackend *target;
-+
-+        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 = blk_getlength(target);
-+        int dev_id = vma_writer_register_stream(vmaw, devname, size);
-+        if (dev_id <= 0) {
-+            unlink(archivename);
-+            g_error("vma_writer_register_stream '%s' failed", devname);
-+        }
-+
-+        BackupJob *job = g_new0(BackupJob, 1);
-+        job->len = size;
-+        job->target = target;
-+        job->vmaw = vmaw;
-+        job->dev_id = dev_id;
-+
-+        Coroutine *co = qemu_coroutine_create(backup_run, job);
-+        qemu_coroutine_enter(co);
-+    }
-+
-+    VmaStatus vmastat;
-+    int percent = 0;
-+    int last_percent = -1;
-+
-+    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);
-+
-+                    last_percent = percent;
-+                }
-+            }
-+
-+            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();
-+
-+    vma_writer_get_status(vmaw, &vmastat);
-+
-+    if (verbose) {
-+        for (i = 0; i < 256; i++) {
-+            VmaStreamInfo *si = &vmastat.stream_info[i];
-+            if (si->size) {
-+                fprintf(stderr, "image %s: size=%zd zeros=%zd saved=%zd\n",
-+                        si->devname, si->size, si->zero_bytes,
-+                        si->size - si->zero_bytes);
-+            }
-+        }
-+    }
-+
-+    if (vmastat.status < 0) {
-+        unlink(archivename);
-+        g_error("creating vma archive failed");
-+    }
-+
-+    return 0;
-+}
-+
-+static int dump_config(int argc, char **argv)
-+{
-+    int c, ret = 0;
-+    const char *filename;
-+    const char *config_name = "qemu-server.conf";
-+
-+    for (;;) {
-+        c = getopt(argc, argv, "hc:");
-+        if (c == -1) {
-+            break;
-+        }
-+        switch (c) {
-+        case '?':
-+        case 'h':
-+            help();
-+            break;
-+        case 'c':
-+            config_name = optarg;
-+            break;
-+        default:
-+            help();
-+        }
-+    }
-+
-+    /* Get the filename */
-+    if ((optind + 1) != argc) {
-+        help();
-+    }
-+    filename = argv[optind++];
-+
-+    Error *errp = NULL;
-+    VmaReader *vmar = vma_reader_create(filename, &errp);
-+
-+    if (!vmar) {
-+        g_error("%s", error_get_pretty(errp));
-+    }
-+
-+    int found = 0;
-+    GList *l = vma_reader_get_config_data(vmar);
-+    while (l && l->data) {
-+        VmaConfigData *cdata = (VmaConfigData *)l->data;
-+        l = g_list_next(l);
-+        if (strcmp(cdata->name, config_name) == 0) {
-+            found = 1;
-+            fwrite(cdata->data,  cdata->len, 1, stdout);
-+            break;
-+        }
-+    }
-+
-+    vma_reader_destroy(vmar);
-+
-+    bdrv_close_all();
-+
-+    if (!found) {
-+        fprintf(stderr, "unable to find configuration data '%s'\n", config_name);
-+        return -1;
-+    }
-+
-+    return ret;
-+}
-+
-+int main(int argc, char **argv)
-+{
-+    const char *cmdname;
-+    Error *main_loop_err = NULL;
-+
-+    error_set_progname(argv[0]);
-+
-+    if (qemu_init_main_loop(&main_loop_err)) {
-+        g_error("%s", error_get_pretty(main_loop_err));
-+    }
-+
-+    bdrv_init();
-+
-+    if (argc < 2) {
-+        help();
-+    }
-+
-+    cmdname = argv[1];
-+    argc--; argv++;
-+
-+
-+    if (!strcmp(cmdname, "list")) {
-+        return list_content(argc, argv);
-+    } else if (!strcmp(cmdname, "create")) {
-+        return create_archive(argc, argv);
-+    } else if (!strcmp(cmdname, "extract")) {
-+        return extract_content(argc, argv);
-+    } else if (!strcmp(cmdname, "verify")) {
-+        return verify_content(argc, argv);
-+    } else if (!strcmp(cmdname, "config")) {
-+        return dump_config(argc, argv);
-+    }
-+
-+    help();
-+    return 0;
-+}
-diff --git a/vma.h b/vma.h
-new file mode 100644
-index 0000000000..fa6f4df7e7
---- /dev/null
-+++ b/vma.h
-@@ -0,0 +1,149 @@
-+/*
-+ * VMA: Virtual Machine Archive
-+ *
-+ * Copyright (C) Proxmox Server Solutions
-+ *
-+ * Authors:
-+ *  Dietmar Maurer (dietmar@proxmox.com)
-+ *
-+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
-+ * See the COPYING file in the top-level directory.
-+ *
-+ */
-+
-+#ifndef BACKUP_VMA_H
-+#define BACKUP_VMA_H
-+
-+#include <uuid/uuid.h>
-+#include "qapi/error.h"
-+#include "block/block.h"
-+
-+#define VMA_BLOCK_BITS 12
-+#define VMA_BLOCK_SIZE (1<<VMA_BLOCK_BITS)
-+#define VMA_CLUSTER_BITS (VMA_BLOCK_BITS+4)
-+#define VMA_CLUSTER_SIZE (1<<VMA_CLUSTER_BITS)
-+
-+#if VMA_CLUSTER_SIZE != 65536
-+#error unexpected cluster size
-+#endif
-+
-+#define VMA_EXTENT_HEADER_SIZE 512
-+#define VMA_BLOCKS_PER_EXTENT 59
-+#define VMA_MAX_CONFIGS 256
-+
-+#define VMA_MAX_EXTENT_SIZE \
-+    (VMA_EXTENT_HEADER_SIZE+VMA_CLUSTER_SIZE*VMA_BLOCKS_PER_EXTENT)
-+#if VMA_MAX_EXTENT_SIZE != 3867136
-+#error unexpected VMA_EXTENT_SIZE
-+#endif
-+
-+/* File Format Definitions */
-+
-+#define VMA_MAGIC (GUINT32_TO_BE(('V'<<24)|('M'<<16)|('A'<<8)|0x00))
-+#define VMA_EXTENT_MAGIC (GUINT32_TO_BE(('V'<<24)|('M'<<16)|('A'<<8)|'E'))
-+
-+typedef struct VmaDeviceInfoHeader {
-+    uint32_t devname_ptr; /* offset into blob_buffer table */
-+    uint32_t reserved0;
-+    uint64_t size; /* device size in bytes */
-+    uint64_t reserved1;
-+    uint64_t reserved2;
-+} VmaDeviceInfoHeader;
-+
-+typedef struct VmaHeader {
-+    uint32_t magic;
-+    uint32_t version;
-+    unsigned char uuid[16];
-+    int64_t ctime;
-+    unsigned char md5sum[16];
-+
-+    uint32_t blob_buffer_offset;
-+    uint32_t blob_buffer_size;
-+    uint32_t header_size;
-+
-+    unsigned char reserved[1984];
-+
-+    uint32_t config_names[VMA_MAX_CONFIGS]; /* offset into blob_buffer table */
-+    uint32_t config_data[VMA_MAX_CONFIGS];  /* offset into blob_buffer table */
-+
-+    uint32_t reserved1;
-+
-+    VmaDeviceInfoHeader dev_info[256];
-+} VmaHeader;
-+
-+typedef struct VmaExtentHeader {
-+    uint32_t magic;
-+    uint16_t reserved1;
-+    uint16_t block_count;
-+    unsigned char uuid[16];
-+    unsigned char md5sum[16];
-+    uint64_t blockinfo[VMA_BLOCKS_PER_EXTENT];
-+} VmaExtentHeader;
-+
-+/* functions/definitions to read/write vma files */
-+
-+typedef struct VmaReader VmaReader;
-+
-+typedef struct VmaWriter VmaWriter;
-+
-+typedef struct VmaConfigData {
-+    const char *name;
-+    const void *data;
-+    uint32_t len;
-+} VmaConfigData;
-+
-+typedef struct VmaStreamInfo {
-+    uint64_t size;
-+    uint64_t cluster_count;
-+    uint64_t transferred;
-+    uint64_t zero_bytes;
-+    int finished;
-+    char *devname;
-+} VmaStreamInfo;
-+
-+typedef struct VmaStatus {
-+    int status;
-+    bool closed;
-+    char errmsg[8192];
-+    char uuid_str[37];
-+    VmaStreamInfo stream_info[256];
-+} VmaStatus;
-+
-+typedef struct VmaDeviceInfo {
-+    uint64_t size; /* device size in bytes */
-+    const char *devname;
-+} 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);
-+int vma_writer_register_stream(VmaWriter *vmaw, const char *devname,
-+                               size_t size);
-+
-+int64_t coroutine_fn vma_writer_write(VmaWriter *vmaw, uint8_t dev_id,
-+                                      int64_t cluster_num, unsigned char *buf,
-+                                      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, ...);
-+
-+
-+VmaReader *vma_reader_create(const char *filename, Error **errp);
-+void vma_reader_destroy(VmaReader *vmar);
-+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,
-+                           BlockBackend *target, bool write_zeroes,
-+                           Error **errp);
-+int vma_reader_restore(VmaReader *vmar, int vmstate_fd, bool verbose,
-+                       Error **errp);
-+int vma_reader_verify(VmaReader *vmar, bool verbose, Error **errp);
-+
-+#endif /* BACKUP_VMA_H */
--- 
-2.11.0
-
diff --git a/debian/patches/pve/0029-backup-fix-race-in-backup-stop-command.patch b/debian/patches/pve/0029-backup-fix-race-in-backup-stop-command.patch
deleted file mode 100644 (file)
index 0aa7271..0000000
+++ /dev/null
@@ -1,253 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Wolfgang Bumiller <w.bumiller@proxmox.com>
-Date: Tue, 5 Dec 2017 12:12:15 +0100
-Subject: [PATCH] backup: fix race in backup-stop command
-
-Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
----
- blockdev.c | 90 +++++++++++++++++++++++++++++++++-----------------------------
- blockjob.c |  8 +++---
- 2 files changed, 52 insertions(+), 46 deletions(-)
-
-diff --git a/blockdev.c b/blockdev.c
-index 19a82e8774..d3490936d8 100644
---- a/blockdev.c
-+++ b/blockdev.c
-@@ -2934,31 +2934,6 @@ out:
-     aio_context_release(aio_context);
- }
--void block_job_event_cancelled(BlockJob *job);
--void block_job_event_completed(BlockJob *job, const char *msg);
--static void block_job_cb(void *opaque, int ret)
--{
--    /* Note that this function may be executed from another AioContext besides
--     * the QEMU main loop.  If you need to access anything that assumes the
--     * QEMU global mutex, use a BH or introduce a mutex.
--     */
--
--    BlockDriverState *bs = opaque;
--    const char *msg = NULL;
--
--    assert(bs->job);
--
--    if (ret < 0) {
--        msg = strerror(-ret);
--    }
--
--    if (block_job_is_cancelled(bs->job)) {
--        block_job_event_cancelled(bs->job);
--    } else {
--        block_job_event_completed(bs->job, msg);
--    }
--}
--
- /* PVE backup related function */
- static struct PVEBackupState {
-@@ -2975,6 +2950,8 @@ static struct PVEBackupState {
-     size_t total;
-     size_t transferred;
-     size_t zero_bytes;
-+    QemuMutex backup_mutex;
-+    bool      backup_mutex_initialized;
- } backup_state;
- typedef struct PVEBackupDevInfo {
-@@ -3055,6 +3032,13 @@ static int pvebackup_dump_cb(void *opaque, BlockBackend *target,
- static void pvebackup_cleanup(void)
- {
-+    qemu_mutex_lock(&backup_state.backup_mutex);
-+    // Avoid race between block jobs and backup-cancel command:
-+    if (!backup_state.vmaw) {
-+        qemu_mutex_unlock(&backup_state.backup_mutex);
-+        return;
-+    }
-+
-     backup_state.end_time = time(NULL);
-     if (backup_state.vmaw) {
-@@ -3064,16 +3048,9 @@ static void pvebackup_cleanup(void)
-         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;
--    }
-+    g_list_free(backup_state.di_list);
-+    backup_state.di_list = NULL;
-+    qemu_mutex_unlock(&backup_state.backup_mutex);
- }
- static void coroutine_fn backup_close_vma_stream(void *opaque)
-@@ -3085,6 +3062,8 @@ static void coroutine_fn backup_close_vma_stream(void *opaque)
- static void pvebackup_complete_cb(void *opaque, int ret)
- {
-+    // This always runs in the main loop
-+
-     PVEBackupDevInfo *di = opaque;
-     di->completed = true;
-@@ -3094,8 +3073,6 @@ static void pvebackup_complete_cb(void *opaque, int ret)
-                    ret, strerror(-ret));
-     }
--    BlockDriverState *bs = di->bs;
--
-     di->bs = NULL;
-     di->target = NULL;
-@@ -3104,7 +3081,11 @@ static void pvebackup_complete_cb(void *opaque, int ret)
-         qemu_coroutine_enter(co);
-     }
--    block_job_cb(bs, ret);
-+    // remove self from job queue
-+    qemu_mutex_lock(&backup_state.backup_mutex);
-+    backup_state.di_list = g_list_remove(backup_state.di_list, di);
-+    g_free(di);
-+    qemu_mutex_unlock(&backup_state.backup_mutex);
-     if (!backup_state.cancel) {
-         pvebackup_run_next_job();
-@@ -3114,6 +3095,12 @@ static void pvebackup_complete_cb(void *opaque, int ret)
- static void pvebackup_cancel(void *opaque)
- {
-     backup_state.cancel = true;
-+    qemu_mutex_lock(&backup_state.backup_mutex);
-+    // Avoid race between block jobs and backup-cancel command:
-+    if (!backup_state.vmaw) {
-+        qemu_mutex_unlock(&backup_state.backup_mutex);
-+        return;
-+    }
-     if (!backup_state.error) {
-         error_setg(&backup_state.error, "backup cancelled");
-@@ -3131,13 +3118,17 @@ static void pvebackup_cancel(void *opaque)
-         if (!di->completed && di->bs) {
-             BlockJob *job = di->bs->job;
-             if (job) {
-+                AioContext *aio_context = blk_get_aio_context(job->blk);
-+                aio_context_acquire(aio_context);
-                 if (!di->completed) {
--                    block_job_cancel_sync(job);
-+                    block_job_cancel(job);
-                 }
-+                aio_context_release(aio_context);
-             }
-         }
-     }
-+    qemu_mutex_unlock(&backup_state.backup_mutex);
-     pvebackup_cleanup();
- }
-@@ -3193,24 +3184,31 @@ static int config_to_vma(const char *file, BackupFormat format,
- bool block_job_should_pause(BlockJob *job);
- static void pvebackup_run_next_job(void)
- {
-+    qemu_mutex_lock(&backup_state.backup_mutex);
-+
-     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;
-+            AioContext *aio_context = blk_get_aio_context(job->blk);
-+            aio_context_acquire(aio_context);
-+            qemu_mutex_unlock(&backup_state.backup_mutex);
-             if (block_job_should_pause(job)) {
--                bool cancel = backup_state.error || backup_state.cancel;
--                if (cancel) {
--                    block_job_cancel(job);
-+                if (backup_state.error || backup_state.cancel) {
-+                    block_job_cancel_sync(job);
-                 } else {
-                     block_job_resume(job);
-                 }
-             }
-+            aio_context_release(aio_context);
-             return;
-         }
-     }
-+    qemu_mutex_unlock(&backup_state.backup_mutex);
-+    // no more jobs, run the cleanup
-     pvebackup_cleanup();
- }
-@@ -3233,6 +3231,11 @@ UuidInfo *qmp_backup(const char *backup_file, bool has_format,
-     UuidInfo *uuid_info;
-     BlockJob *job;
-+    if (!backup_state.backup_mutex_initialized) {
-+        qemu_mutex_init(&backup_state.backup_mutex);
-+        backup_state.backup_mutex_initialized = true;
-+    }
-+
-     if (backup_state.di_list) {
-         error_set(errp, ERROR_CLASS_GENERIC_ERROR,
-                   "previous backup not finished");
-@@ -3405,6 +3408,7 @@ UuidInfo *qmp_backup(const char *backup_file, bool has_format,
-     uuid_copy(backup_state.uuid, uuid);
-     uuid_unparse_lower(uuid, backup_state.uuid_str);
-+    qemu_mutex_lock(&backup_state.backup_mutex);
-     backup_state.di_list = di_list;
-     backup_state.total = total;
-@@ -3428,6 +3432,8 @@ UuidInfo *qmp_backup(const char *backup_file, bool has_format,
-         block_job_start(job);
-     }
-+    qemu_mutex_unlock(&backup_state.backup_mutex);
-+
-     if (!backup_state.error) {
-         pvebackup_run_next_job(); // run one job
-     }
-diff --git a/blockjob.c b/blockjob.c
-index cb3741f6dd..199d5852db 100644
---- a/blockjob.c
-+++ b/blockjob.c
-@@ -37,8 +37,8 @@
- #include "qemu/timer.h"
- #include "qapi-event.h"
--void block_job_event_cancelled(BlockJob *job);
--void block_job_event_completed(BlockJob *job, const char *msg);
-+static void block_job_event_cancelled(BlockJob *job);
-+static void block_job_event_completed(BlockJob *job, const char *msg);
- /* Transactional group of block jobs */
- struct BlockJobTxn {
-@@ -688,7 +688,7 @@ static void block_job_iostatus_set_err(BlockJob *job, int error)
-     }
- }
--void block_job_event_cancelled(BlockJob *job)
-+static void block_job_event_cancelled(BlockJob *job)
- {
-     if (block_job_is_internal(job)) {
-         return;
-@@ -702,7 +702,7 @@ void block_job_event_cancelled(BlockJob *job)
-                                         &error_abort);
- }
--void block_job_event_completed(BlockJob *job, const char *msg)
-+static void block_job_event_completed(BlockJob *job, const char *msg)
- {
-     if (block_job_is_internal(job)) {
-         return;
--- 
-2.11.0
-
diff --git a/debian/patches/pve/0030-vma-add-throttling-options-to-drive-mapping-fifo-pro.patch b/debian/patches/pve/0030-vma-add-throttling-options-to-drive-mapping-fifo-pro.patch
deleted file mode 100644 (file)
index d75114d..0000000
+++ /dev/null
@@ -1,165 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Wolfgang Bumiller <w.bumiller@proxmox.com>
-Date: Thu, 15 Feb 2018 11:07:56 +0100
-Subject: [PATCH] vma: add throttling options to drive mapping fifo protocol
-
-Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
----
- vma.c | 82 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
- 1 file changed, 73 insertions(+), 9 deletions(-)
-
-diff --git a/vma.c b/vma.c
-index 04915427c8..91ae14cdc3 100644
---- a/vma.c
-+++ b/vma.c
-@@ -18,7 +18,9 @@
- #include "qemu-common.h"
- #include "qemu/error-report.h"
- #include "qemu/main-loop.h"
-+#include "qemu/cutils.h"
- #include "qapi/qmp/qstring.h"
-+#include "qapi/qmp/qint.h"
- #include "sysemu/char.h" /* qstring_from_str */
- #include "sysemu/block-backend.h"
-@@ -133,9 +135,39 @@ typedef struct RestoreMap {
-     char *devname;
-     char *path;
-     char *format;
-+    uint64_t throttling_bps;
-+    char *throttling_group;
-     bool write_zero;
- } RestoreMap;
-+static bool try_parse_option(char **line, const char *optname, char **out, const char *inbuf) {
-+    size_t optlen = strlen(optname);
-+    if (strncmp(*line, optname, optlen) != 0 || (*line)[optlen] != '=') {
-+        return false;
-+    }
-+    if (*out) {
-+        g_error("read map failed - duplicate value for option '%s'", optname);
-+    }
-+    char *value = (*line) + optlen + 1; /* including a '=' */
-+    char *colon = strchr(value, ':');
-+    if (!colon) {
-+        g_error("read map failed - option '%s' not terminated ('%s')",
-+                optname, inbuf);
-+    }
-+    *line = colon+1;
-+    *out = g_strndup(value, colon - value);
-+    return true;
-+}
-+
-+static uint64_t verify_u64(const char *text) {
-+    uint64_t value;
-+    const char *endptr = NULL;
-+    if (qemu_strtou64(text, &endptr, 0, &value) != 0 || !endptr || *endptr) {
-+        g_error("read map failed - not a number: %s", text);
-+    }
-+    return value;
-+}
-+
- static int extract_content(int argc, char **argv)
- {
-     int c, ret = 0;
-@@ -209,6 +241,9 @@ static int extract_content(int argc, char **argv)
-         while (1) {
-             char inbuf[8192];
-             char *line = fgets(inbuf, sizeof(inbuf), map);
-+            char *format = NULL;
-+            char *bps = NULL;
-+            char *group = NULL;
-             if (!line || line[0] == '\0' || !strcmp(line, "done\n")) {
-                 break;
-             }
-@@ -220,15 +255,19 @@ 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);
-+            while (1) {
-+                if (!try_parse_option(&line, "format", &format, inbuf) &&
-+                    !try_parse_option(&line, "throttling.bps", &bps, inbuf) &&
-+                    !try_parse_option(&line, "throttling.group", &group, inbuf))
-+                {
-+                    break;
-                 }
--                format = g_strndup(format, colon - format);
--                line = colon+1;
-+            }
-+
-+            uint64_t bps_value = 0;
-+            if (bps) {
-+                bps_value = verify_u64(bps);
-+                g_free(bps);
-             }
-             const char *path;
-@@ -254,6 +293,8 @@ static int extract_content(int argc, char **argv)
-             map->devname = g_strdup(devname);
-             map->path = g_strdup(path);
-             map->format = format;
-+            map->throttling_bps = bps_value;
-+            map->throttling_group = group;
-             map->write_zero = write_zero;
-             g_hash_table_insert(devmap, map->devname, map);
-@@ -281,6 +322,8 @@ static int extract_content(int argc, char **argv)
-         } else if (di) {
-             char *devfn = NULL;
-             const char *format = NULL;
-+            uint64_t throttling_bps = 0;
-+            const char *throttling_group = NULL;
-             int flags = BDRV_O_RDWR | BDRV_O_NO_FLUSH;
-             bool write_zero = true;
-@@ -292,6 +335,8 @@ static int extract_content(int argc, char **argv)
-                 }
-                 devfn = map->path;
-                 format = map->format;
-+                throttling_bps = map->throttling_bps;
-+                throttling_group = map->throttling_group;
-                 write_zero = map->write_zero;
-             } else {
-                 devfn = g_strdup_printf("%s/tmp-disk-%s.raw",
-@@ -328,12 +373,31 @@ static int extract_content(int argc, char **argv)
-               qdict_put(options, "driver", qstring_from_str("raw"));
-           }
--
-           if (errp || !(blk = blk_new_open(devfn, NULL, options, flags, &errp))) {
-                 g_error("can't open file %s - %s", devfn,
-                         error_get_pretty(errp));
-             }
-+            if (throttling_group) {
-+                blk_io_limits_enable(blk, throttling_group);
-+            }
-+
-+            if (throttling_bps) {
-+                if (!throttling_group) {
-+                    blk_io_limits_enable(blk, devfn);
-+                }
-+
-+                ThrottleConfig cfg;
-+                throttle_config_init(&cfg);
-+                cfg.buckets[THROTTLE_BPS_WRITE].avg = throttling_bps;
-+                Error *err = NULL;
-+                if (!throttle_is_valid(&cfg, &err)) {
-+                    error_report_err(err);
-+                    g_error("failed to apply throttling");
-+                }
-+                blk_set_io_limits(blk, &cfg);
-+            }
-+
-             if (vma_reader_register_bs(vmar, i, blk, write_zero, &errp) < 0) {
-                 g_error("%s", error_get_pretty(errp));
-             }
--- 
-2.11.0
-
index c49ca9647c886f04e7525ff342d93bdda24adf28..954ea119ffbf6c05f048bbeb13007245e3c49ac0 100644 (file)
@@ -1,4 +1,4 @@
-pve/0001-fr-ca-keymap-corrections.patch
+pve/0001-block-file-change-locking-default-to-off.patch
 pve/0002-Adjust-network-script-path-to-etc-kvm.patch
 pve/0003-qemu-img-return-success-on-info-without-snapshots.patch
 pve/0004-use-kvm-by-default.patch
 pve/0002-Adjust-network-script-path-to-etc-kvm.patch
 pve/0003-qemu-img-return-success-on-info-without-snapshots.patch
 pve/0004-use-kvm-by-default.patch
@@ -15,65 +15,15 @@ pve/0014-use-whitespace-between-VERSION-and-PKGVERSION.patch
 pve/0015-vnc-altgr-emulation.patch
 pve/0016-vnc-make-x509-imply-tls-again.patch
 pve/0017-vnc-PVE-VNC-authentication.patch
 pve/0015-vnc-altgr-emulation.patch
 pve/0016-vnc-make-x509-imply-tls-again.patch
 pve/0017-vnc-PVE-VNC-authentication.patch
-pve/0018-migrate-fix-possible-unitialised-return-value.patch
-pve/0019-block-rbd-disable-rbd_cache_writethrough_until_flush.patch
-pve/0020-block-snapshot-qmp_snapshot_drive-add-aiocontext.patch
-pve/0021-block-snapshot-qmp_delete_drive_snapshot-add-aiocont.patch
-pve/0022-glusterfs-no-default-logfile-if-daemonized.patch
-pve/0023-glusterfs-allow-partial-reads.patch
-pve/0024-block-add-the-zeroinit-block-driver-filter.patch
-pve/0025-qemu-img-dd-add-osize-and-read-from-to-stdin-stdout.patch
-pve/0026-backup-modify-job-api.patch
-pve/0027-backup-introduce-vma-archive-format.patch
-pve/0028-adding-old-vma-files.patch
-pve/0029-backup-fix-race-in-backup-stop-command.patch
-pve/0030-vma-add-throttling-options-to-drive-mapping-fifo-pro.patch
+pve/0018-block-rbd-disable-rbd_cache_writethrough_until_flush.patch
+pve/0019-block-snapshot-qmp_snapshot_drive-add-aiocontext.patch
+pve/0020-block-snapshot-qmp_delete_drive_snapshot-add-aiocont.patch
+pve/0021-glusterfs-no-default-logfile-if-daemonized.patch
+pve/0022-glusterfs-allow-partial-reads.patch
+pve/0023-block-add-the-zeroinit-block-driver-filter.patch
+pve/0024-qemu-img-dd-add-osize-and-read-from-to-stdin-stdout.patch
+pve/0025-backup-modify-job-api.patch
+pve/0026-backup-introduce-vma-archive-format.patch
+pve/0027-adding-old-vma-files.patch
 extra/0001-Revert-target-i386-disable-LINT0-after-reset.patch
 extra/0001-Revert-target-i386-disable-LINT0-after-reset.patch
-extra/0002-virtio-serial-fix-segfault-on-disconnect.patch
-extra/0003-megasas-always-store-SCSIRequest-into-MegasasCmd.patch
-extra/0004-slirp-check-len-against-dhcp-options-array-end.patch
-extra/0005-IDE-Do-not-flush-empty-CDROM-drives.patch
-extra/0006-bitmap-add-bitmap_copy_and_clear_atomic.patch
-extra/0007-memory-add-support-getting-and-using-a-dirty-bitmap-.patch
-extra/0008-vga-add-vga_scanline_invalidated-helper.patch
-extra/0009-vga-make-display-updates-thread-safe.patch
-extra/0010-vga-fix-display-update-region-calculation.patch
-extra/0011-vga-fix-display-update-region-calculation-split-scre.patch
-extra/0012-vga-stop-passing-pointers-to-vga_draw_line-functions.patch
-extra/0013-multiboot-validate-multiboot-header-address-values.patch
-extra/0014-virtio-fix-descriptor-counting-in-virtqueue_pop.patch
-extra/0015-nbd-server-CVE-2017-15119-Reject-options-larger-than.patch
-extra/0016-vga-migration-Update-memory-map-in-post_load.patch
-extra/0017-vga-drop-line_offset-variable.patch
-extra/0018-vga-handle-cirrus-vbe-mode-wraparounds.patch
-extra/0019-vga-add-ram_addr_t-cast.patch
-extra/0020-vga-fix-region-checks-in-wraparound-case.patch
-extra/0021-io-monitor-encoutput-buffer-size-from-websocket-GSou.patch
-extra/0022-9pfs-use-g_malloc0-to-allocate-space-for-xattr.patch
-extra/0023-cirrus-fix-oob-access-in-mode4and5-write-functions.patch
-extra/0024-virtio-check-VirtQueue-Vring-object-is-set.patch
-extra/0025-block-gluster-glfs_lseek-workaround.patch
-extra/0026-gluster-add-support-for-PREALLOC_MODE_FALLOC.patch
-extra/0027-target-i386-Use-host_vendor_fms-in-max_x86_cpu_initf.patch
-extra/0028-target-i386-Define-CPUID_MODEL_ID_SZ-macro.patch
-extra/0029-target-i386-Don-t-use-x86_cpu_load_def-on-max-CPU-mo.patch
-extra/0030-i386-Change-X86CPUDefinition-model_id-to-const-char.patch
-extra/0031-i386-Add-support-for-SPEC_CTRL-MSR.patch
-extra/0032-i386-Add-spec-ctrl-CPUID-bit.patch
-extra/0033-i386-Add-FEAT_8000_0008_EBX-CPUID-feature-word.patch
-extra/0034-i386-Add-new-IBRS-versions-of-Intel-CPU-models.patch
-extra/0035-ratelimit-don-t-align-wait-time-with-slices.patch
-extra/0036-nbd-make-it-thread-safe-fix-qcow2-over-nbd.patch
-extra/0037-nbd-strict-nbd_wr_syncv.patch
-extra/0038-nbd-read_sync-and-friends-return-0-on-success.patch
-extra/0039-nbd-make-nbd_drop-public.patch
-extra/0040-nbd-server-get-rid-of-nbd_negotiate_read-and-friends.patch
-extra/0041-nbd-client-Fix-regression-when-server-sends-garbage.patch
-extra/0042-fix-build-failure-in-nbd_read_reply_entry.patch
-extra/0043-nbd-client-avoid-spurious-qio_channel_yield-re-entry.patch
-extra/0044-nbd-client-avoid-read_reply_co-entry-if-send-failed.patch
-extra/0045-qemu-iotests-improve-nbd-fault-injector.py-startup-p.patch
-extra/0046-qemu-iotests-test-NBD-over-UNIX-domain-sockets-in-08.patch
-extra/0047-block-nbd-client-nbd_co_send_request-fix-return-code.patch
-extra/0048-target-i386-cpu-Add-new-EPYC-CPU-model.patch
-extra/0049-i386-Add-EPYC-IBPB-CPU-model.patch
+extra/0002-ratelimit-don-t-align-wait-time-with-slices.patch
diff --git a/keycodemapdb/LICENSE.BSD b/keycodemapdb/LICENSE.BSD
new file mode 100644 (file)
index 0000000..ec1a29d
--- /dev/null
@@ -0,0 +1,27 @@
+Copyright (c) Individual contributors.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    1. Redistributions of source code must retain the above copyright notice,
+       this list of conditions and the following disclaimer.
+
+    2. Redistributions in binary form must reproduce the above copyright
+       notice, this list of conditions and the following disclaimer in the
+       documentation and/or other materials provided with the distribution.
+
+    3. Neither the name of PyCA Cryptography nor the names of its contributors
+       may be used to endorse or promote products derived from this software
+       without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/keycodemapdb/LICENSE.GPL2 b/keycodemapdb/LICENSE.GPL2
new file mode 100644 (file)
index 0000000..d511905
--- /dev/null
@@ -0,0 +1,339 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+
+           How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/keycodemapdb/README b/keycodemapdb/README
new file mode 100644 (file)
index 0000000..a3c3c9f
--- /dev/null
@@ -0,0 +1,114 @@
+   Key code / scan code / key symbol mapping database
+   ==================================================
+
+This module provides a database that maps between different
+key code / scan code / key symbol sets:
+
+ - Linux evdev
+ - OS-X
+ - AT Set 1
+ - AT Set 2
+ - AT Set 3
+ - XT
+ - Linux XT KBD driver
+ - USB HID
+ - Win32
+ - XWin XT
+ - XKBD XT
+ - Xorg Evdev
+ - Xorg KBD
+ - Xorg OS-X
+ - XOrg Cygwin
+ - RFB
+
+Licensing
+---------
+
+The contents of this package are dual licensed under the terms of:
+
+ - GNU General Public License (version 2 or later)
+ - 3-clause BSD License
+
+The output files generated by keymap-gen may be distributed & used under
+the terms of either of the above licenses.
+
+Data formats
+------------
+
+The following output formats are possible
+
+ - Code map
+
+   An array mapping between key code sets values
+
+   Indexes in the array are values from the source code set.
+   Entries in the array are values from the target code set
+
+
+ - Code table
+
+   An array listing all values in a key code set
+
+   Indexes in the array are simply a numeric counter
+   Entries in the array are values from the key code set
+
+   The size of the array matches the total number of entries in
+   the keycode database.
+
+
+ - Name map
+
+   An array mapping between key code sets values and names
+
+   Indexes in the array are values from the source code set
+   Entries in the array are names from the target code set
+
+
+ - Name table
+
+   An array listing all names in a key code set
+
+   Indexes in the array are simply a numeric counter
+   Entries in the array are values from the key code set
+
+   The size of the array matches the total number of entries in
+   the keycode database.
+
+
+Output languages
+----------------
+
+The tool is capable of generating data tables for the following
+programming languages / environments
+
+ - Standard C
+ - GLib2 (standard C, but with GLib2 data types)
+ - Python
+ - Perl
+
+
+Usage
+-----
+
+Map values from AT Set 1 to USB HID, generating tables for the
+C programming language
+
+ $ keymap-gen --lang stdc code-map data/keymaps.csv atset1 usb
+
+Generate a tables of names for Linux key codes, OS-X key codes,
+in python - equivalent array indexes map between the two sets.
+A variable name override is used
+
+ $ keymap-gen --varname linux_keycodes --lang stdc \
+              code-table data/keymaps.csv linux
+ $ keymap-gen --varname osx_keycodes --lang stdc \
+              code-table data/keymaps.csv os-x
+
+Generate a mapping from XOrg XWin values to Win32 names
+
+ $ keymap-gen --lang perl name-map data/keymaps.csv xorgxwin win32
+
+Generate a table of names for Linux key codes in Perl
+
+ $ keymap-gen --lang perl name-table data/keymaps.csv linux
+
diff --git a/keycodemapdb/data/README b/keycodemapdb/data/README
new file mode 100644 (file)
index 0000000..6b56534
--- /dev/null
@@ -0,0 +1,89 @@
+This directory contains the raw data for mapping between different
+keyboard codes. Naming if often based on the US keyboard layout, but
+does not indicate the symbol actually generated by the key.
+
+The columns currently in this data set are:
+
+Linux
+-----
+
+Name and value of the hardware independent keycodes used by the linux
+kernel and exposed through the input subsystem.
+
+References: linux/input.h
+
+macOS
+-----
+
+Low level key codes as exposed by Mac OS X/macOS.
+
+References: Carbon/HIToolbox/Events.h
+
+PC scan code sets
+-----------------
+
+Scan codes for the three orignal PC keyboard generations:
+
+ Set 1: XT
+ Set 2: AT
+ Set 3: PS/2
+
+The sets include codes for modern keys as well and not just the keys
+present on those original keyboards.
+
+References: linux/drivers/input/keyboard/atkbd.c
+
+USB HID
+-------
+
+Codes as specified by the HID profile in USB.
+
+References: linux/drivers/hid/usbhid/usbkbd.c
+
+Windows Virtual-key codes
+-------------------------
+
+The low level, hardware independent "VKEYs" exposed by Windows.
+
+References: mingw32/winuser.h
+
+XWin XT
+-------
+
+X11 keycodes generated by the XWin server. Based on the XT scan code
+set.
+
+References: xorg-server/hw/xwin/{winkeybd.c,winkeynames.h}
+
+Xfree86 KBD XT
+--------------
+
+X11 keycodes generated by the Xfree86 keyboard drivers. Based on the XT
+scan code set.
+
+References: xf86-input-keyboard/src/at_scancode.c
+
+X11 keysyms
+-----------
+
+Corresponding X11 keysym value(s) for a US keyboard layout.
+
+WARNING: These columns represent symbols, not physical keys, and should
+         be used with extreme care.
+
+References: http://cgit.freedesktop.org/xorg/proto/x11proto/plain/keysymdef.h
+
+HTML KeyboardEvent.code
+-----------------------
+
+Key codes seen in the KeyboardEvent.code attribute as part of the
+UI Events specification.
+
+References: https://www.w3.org/TR/uievents-code/
+
+XKEYBOARD key names
+-------------------
+
+Hardware independent key names as used in the XKEYBOARD extension.
+
+References: /usr/share/X11/xkb/keycodes/
diff --git a/keycodemapdb/data/keymaps.csv b/keycodemapdb/data/keymaps.csv
new file mode 100644 (file)
index 0000000..0ef2ec1
--- /dev/null
@@ -0,0 +1,539 @@
+"Linux Name","Linux Keycode","OS-X Name","OS-X Keycode","AT set1 keycode","AT set2 keycode","AT set3 keycode","USB Keycodes","Win32 Name","Win32 Keycode","Xwin XT","Xfree86 KBD XT","X11 keysym name","X11 keysym","HTML code","XKB key name","QEMU QKeyCode","Sun KBD","Apple ADB"
+KEY_RESERVED,0,,0xff,,,,,,,,,,,,,unmapped,,0xff
+KEY_ESC,1,Escape,0x35,0x01,0x76,0x08,41,VK_ESCAPE,0x1b,1,1,XK_Escape,0xff1b,Escape,ESC,esc,0x1d,0x35
+KEY_1,2,ANSI_1,0x12,0x02,0x16,0x16,30,VK_1,0x31,2,2,XK_1,0x0031,Digit1,AE01,1,0x1e,0x12
+KEY_1,2,ANSI_1,0x12,0x02,0x16,0x16,30,VK_1,0x31,2,2,XK_exclam,0x0021,Digit1,AE01,1,0x1e,0x12
+KEY_2,3,ANSI_2,0x13,0x03,0x1e,0x1e,31,VK_2,0x32,3,3,XK_2,0x0032,Digit2,AE02,2,0x1f,0x13
+KEY_2,3,ANSI_2,0x13,0x03,0x1e,0x1e,31,VK_2,0x32,3,3,XK_at,0x0040,Digit2,AE02,2,0x1f,0x13
+KEY_3,4,ANSI_3,0x14,0x04,0x26,0x26,32,VK_3,0x33,4,4,XK_3,0x0033,Digit3,AE03,3,0x20,0x14
+KEY_3,4,ANSI_3,0x14,0x04,0x26,0x26,32,VK_3,0x33,4,4,XK_numbersign,0x0023,Digit3,AE03,3,0x20,0x14
+KEY_4,5,ANSI_4,0x15,0x05,0x25,0x25,33,VK_4,0x34,5,5,XK_4,0x0034,Digit4,AE04,4,0x21,0x15
+KEY_4,5,ANSI_4,0x15,0x05,0x25,0x25,33,VK_4,0x34,5,5,XK_dollar,0x0024,Digit4,AE04,4,0x21,0x15
+KEY_5,6,ANSI_5,0x17,0x06,0x2e,0x2e,34,VK_5,0x35,6,6,XK_5,0x0035,Digit5,AE05,5,0x22,0x17
+KEY_5,6,ANSI_5,0x17,0x06,0x2e,0x2e,34,VK_5,0x35,6,6,XK_percent,0x0025,Digit5,AE05,5,0x22,0x17
+KEY_6,7,ANSI_6,0x16,0x07,0x36,0x36,35,VK_6,0x36,7,7,XK_6,0x0036,Digit6,AE06,6,0x23,0x16
+KEY_6,7,ANSI_6,0x16,0x07,0x36,0x36,35,VK_6,0x36,7,7,XK_asciicircum,0x005e,Digit6,AE06,6,0x23,0x16
+KEY_7,8,ANSI_7,0x1a,0x08,0x3d,0x3d,36,VK_7,0x37,8,8,XK_7,0x0037,Digit7,AE07,7,0x24,0x1a
+KEY_7,8,ANSI_7,0x1a,0x08,0x3d,0x3d,36,VK_7,0x37,8,8,XK_ampersand,0x0026,Digit7,AE07,7,0x24,0x1a
+KEY_8,9,ANSI_8,0x1c,0x09,0x3e,0x3e,37,VK_8,0x38,9,9,XK_8,0x0038,Digit8,AE08,8,0x25,0x1c
+KEY_8,9,ANSI_8,0x1c,0x09,0x3e,0x3e,37,VK_8,0x38,9,9,XK_asterisk,0x002a,Digit8,AE08,8,0x25,0x1c
+KEY_9,10,ANSI_9,0x19,0x0a,0x46,0x46,38,VK_9,0x39,10,10,XK_9,0x0039,Digit9,AE09,9,0x26,0x19
+KEY_9,10,ANSI_9,0x19,0x0a,0x46,0x46,38,VK_9,0x39,10,10,XK_parenleft,0x0028,Digit9,AE09,9,0x26,0x19
+KEY_0,11,ANSI_0,0x1d,0x0b,0x45,0x45,39,VK_0,0x30,11,11,XK_0,0x0030,Digit0,AE10,0,0x27,0x1d
+KEY_0,11,ANSI_0,0x1d,0x0b,0x45,0x45,39,VK_0,0x30,11,11,XK_parenright,0x0029,Digit0,AE10,0,0x27,0x1d
+KEY_MINUS,12,ANSI_Minus,0x1b,0x0c,0x4e,0x4e,45,VK_OEM_MINUS,0xbd,12,12,XK_minus,0x002d,Minus,AE11,minus,0x28,0x1b
+KEY_MINUS,12,ANSI_Minus,0x1b,0x0c,0x4e,0x4e,45,VK_OEM_MINUS,0xbd,12,12,XK_underscore,0x005f,Minus,AE11,minus,0x28,0x1b
+KEY_EQUAL,13,ANSI_Equal,0x18,0x0d,0x55,0x55,46,VK_OEM_PLUS,0xbb,13,13,XK_equal,0x003d,Equal,AE12,equal,0x29,0x18
+KEY_EQUAL,13,ANSI_Equal,0x18,0x0d,0x55,0x55,46,VK_OEM_PLUS,0xbb,13,13,XK_plus,0x002b,Equal,AE12,equal,0x29,0x18
+KEY_BACKSPACE,14,Delete,0x33,0x0e,0x66,0x66,42,VK_BACK,0x08,14,14,XK_BackSpace,0xff08,Backspace,BKSP,backspace,0x2b,0x33
+KEY_TAB,15,Tab,0x30,0x0f,0x0d,0x0d,43,VK_TAB,0x09,15,15,XK_Tab,0xff09,Tab,TAB,tab,0x35,0x30
+KEY_Q,16,ANSI_Q,0xc,0x10,0x15,0x15,20,VK_Q,0x51,16,16,XK_Q,0x0051,KeyQ,AD01,q,0x36,0xc
+KEY_Q,16,ANSI_Q,0xc,0x10,0x15,0x15,20,VK_Q,0x51,16,16,XK_q,0x0071,KeyQ,AD01,q,0x36,0xc
+KEY_W,17,ANSI_W,0xd,0x11,0x1d,0x1d,26,VK_W,0x57,17,17,XK_W,0x0057,KeyW,AD02,w,0x37,0xd
+KEY_W,17,ANSI_W,0xd,0x11,0x1d,0x1d,26,VK_W,0x57,17,17,XK_w,0x0077,KeyW,AD02,w,0x37,0xd
+KEY_E,18,ANSI_E,0xe,0x12,0x24,0x24,8,VK_E,0x45,18,18,XK_E,0x0045,KeyE,AD03,e,0x38,0xe
+KEY_E,18,ANSI_E,0xe,0x12,0x24,0x24,8,VK_E,0x45,18,18,XK_e,0x0065,KeyE,AD03,e,0x38,0xe
+KEY_R,19,ANSI_R,0xf,0x13,0x2d,0x2d,21,VK_R,0x52,19,19,XK_R,0x0052,KeyR,AD04,r,0x39,0xf
+KEY_R,19,ANSI_R,0xf,0x13,0x2d,0x2d,21,VK_R,0x52,19,19,XK_r,0x0072,KeyR,AD04,r,0x39,0xf
+KEY_T,20,ANSI_T,0x11,0x14,0x2c,0x2c,23,VK_T,0x54,20,20,XK_T,0x0054,KeyT,AD05,t,0x3a,0x11
+KEY_T,20,ANSI_T,0x11,0x14,0x2c,0x2c,23,VK_T,0x54,20,20,XK_t,0x0074,KeyT,AD05,t,0x3a,0x11
+KEY_Y,21,ANSI_Y,0x10,0x15,0x35,0x35,28,VK_Y,0x59,21,21,XK_Y,0x0059,KeyY,AD06,y,0x3b,0x10
+KEY_Y,21,ANSI_Y,0x10,0x15,0x35,0x35,28,VK_Y,0x59,21,21,XK_y,0x0079,KeyY,AD06,y,0x3b,0x10
+KEY_U,22,ANSI_U,0x20,0x16,0x3c,0x3c,24,VK_U,0x55,22,22,XK_U,0x0055,KeyU,AD07,u,0x3c,0x20
+KEY_U,22,ANSI_U,0x20,0x16,0x3c,0x3c,24,VK_U,0x55,22,22,XK_u,0x0075,KeyU,AD07,u,0x3c,0x20
+KEY_I,23,ANSI_I,0x22,0x17,0x43,0x43,12,VK_I,0x49,23,23,XK_I,0x0049,KeyI,AD08,i,0x3d,0x22
+KEY_I,23,ANSI_I,0x22,0x17,0x43,0x43,12,VK_I,0x49,23,23,XK_i,0x0069,KeyI,AD08,i,0x3d,0x22
+KEY_O,24,ANSI_O,0x1f,0x18,0x44,0x44,18,VK_O,0x4f,24,24,XK_O,0x004f,KeyO,AD09,o,0x3e,0x1f
+KEY_O,24,ANSI_O,0x1f,0x18,0x44,0x44,18,VK_O,0x4f,24,24,XK_o,0x006f,KeyO,AD09,o,0x3e,0x1f
+KEY_P,25,ANSI_P,0x23,0x19,0x4d,0x4d,19,VK_P,0x50,25,25,XK_P,0x0050,KeyP,AD10,p,0x3f,0x23
+KEY_P,25,ANSI_P,0x23,0x19,0x4d,0x4d,19,VK_P,0x50,25,25,XK_p,0x0070,KeyP,AD10,p,0x3f,0x23
+KEY_LEFTBRACE,26,ANSI_LeftBracket,0x21,0x1a,0x54,0x54,47,VK_OEM_4,0xdb,26,26,XK_bracketleft,0x005b,BracketLeft,AD11,bracket_left,0x40,0x21
+KEY_LEFTBRACE,26,ANSI_LeftBracket,0x21,0x1a,0x54,0x54,47,VK_OEM_4,0xdb,26,26,XK_braceleft,0x007b,BracketLeft,AD11,bracket_left,0x40,0x21
+KEY_RIGHTBRACE,27,ANSI_RightBracket,0x1e,0x1b,0x5b,0x5b,48,VK_OEM_6,0xdd,27,27,XK_bracketright,0x005d,BracketRight,AD12,bracket_right,0x41,0x1e
+KEY_RIGHTBRACE,27,ANSI_RightBracket,0x1e,0x1b,0x5b,0x5b,48,VK_OEM_6,0xdd,27,27,XK_braceright,0x007d,BracketRight,AD12,bracket_right,0x41,0x1e
+KEY_ENTER,28,Return,0x24,0x1c,0x5a,0x5a,40,VK_RETURN,0x0d,28,28,XK_Return,0xff0d,Enter,RTRN,ret,0x59,0x24
+KEY_LEFTCTRL,29,Control,0x3b,0x1d,0x14,0x11,224,VK_LCONTROL,0xa2,29,29,XK_Control_L,0xffe3,ControlLeft,LCTL,ctrl,0x4c,0x36
+KEY_LEFTCTRL,29,Control,0x3b,0x1d,0x14,0x11,224,VK_CONTROL,0x11,29,29,XK_Control_L,0xffe3,ControlLeft,LCTL,ctrl,0x4c,0x36
+KEY_A,30,ANSI_A,0x0,0x1e,0x1c,0x1c,4,VK_A,0x41,30,30,XK_A,0x0041,KeyA,AC01,a,0x4d,0x0
+KEY_A,30,ANSI_A,0x0,0x1e,0x1c,0x1c,4,VK_A,0x41,30,30,XK_a,0x0061,KeyA,AC01,a,0x4d,0x0
+KEY_S,31,ANSI_S,0x1,0x1f,0x1b,0x1b,22,VK_S,0x53,31,31,XK_S,0x0053,KeyS,AC02,s,0x4e,0x1
+KEY_S,31,ANSI_S,0x1,0x1f,0x1b,0x1b,22,VK_S,0x53,31,31,XK_s,0x0073,KeyS,AC02,s,0x4e,0x1
+KEY_D,32,ANSI_D,0x2,0x20,0x23,0x23,7,VK_D,0x44,32,32,XK_D,0x0044,KeyD,AC03,d,0x4f,0x2
+KEY_D,32,ANSI_D,0x2,0x20,0x23,0x23,7,VK_D,0x44,32,32,XK_d,0x0064,KeyD,AC03,d,0x4f,0x2
+KEY_F,33,ANSI_F,0x3,0x21,0x2b,0x2b,9,VK_F,0x46,33,33,XK_F,0x0046,KeyF,AC04,f,0x50,0x3
+KEY_F,33,ANSI_F,0x3,0x21,0x2b,0x2b,9,VK_F,0x46,33,33,XK_f,0x0066,KeyF,AC04,f,0x50,0x3
+KEY_G,34,ANSI_G,0x5,0x22,0x34,0x34,10,VK_G,0x47,34,34,XK_G,0x0047,KeyG,AC05,g,0x51,0x5
+KEY_G,34,ANSI_G,0x5,0x22,0x34,0x34,10,VK_G,0x47,34,34,XK_g,0x0067,KeyG,AC05,g,0x51,0x5
+KEY_H,35,ANSI_H,0x4,0x23,0x33,0x33,11,VK_H,0x48,35,35,XK_H,0x0048,KeyH,AC06,h,0x52,0x4
+KEY_H,35,ANSI_H,0x4,0x23,0x33,0x33,11,VK_H,0x48,35,35,XK_h,0x0068,KeyH,AC06,h,0x52,0x4
+KEY_J,36,ANSI_J,0x26,0x24,0x3b,0x3b,13,VK_J,0x4a,36,36,XK_J,0x004a,KeyJ,AC07,j,0x53,0x26
+KEY_J,36,ANSI_J,0x26,0x24,0x3b,0x3b,13,VK_J,0x4a,36,36,XK_j,0x006a,KeyJ,AC07,j,0x53,0x26
+KEY_K,37,ANSI_K,0x28,0x25,0x42,0x42,14,VK_K,0x4b,37,37,XK_K,0x004b,KeyK,AC08,k,0x54,0x28
+KEY_K,37,ANSI_K,0x28,0x25,0x42,0x42,14,VK_K,0x4b,37,37,XK_k,0x006b,KeyK,AC08,k,0x54,0x28
+KEY_L,38,ANSI_L,0x25,0x26,0x4b,0x4b,15,VK_L,0x4c,38,38,XK_L,0x004c,KeyL,AC09,l,0x55,0x25
+KEY_L,38,ANSI_L,0x25,0x26,0x4b,0x4b,15,VK_L,0x4c,38,38,XK_l,0x006c,KeyL,AC09,l,0x55,0x25
+KEY_SEMICOLON,39,ANSI_Semicolon,0x29,0x27,0x4c,0x4c,51,VK_OEM_1,0xba,39,39,XK_semicolon,0x003b,Semicolon,AC10,semicolon,0x56,0x29
+KEY_SEMICOLON,39,ANSI_Semicolon,0x29,0x27,0x4c,0x4c,51,VK_OEM_1,0xba,39,39,XK_colon,0x003a,Semicolon,AC10,semicolon,0x56,0x29
+KEY_APOSTROPHE,40,ANSI_Quote,0x27,0x28,0x52,0x52,52,VK_OEM_7,0xde,40,40,XK_apostrophe,0x0027,Quote,AC11,apostrophe,0x57,0x27
+KEY_APOSTROPHE,40,ANSI_Quote,0x27,0x28,0x52,0x52,52,VK_OEM_7,0xde,40,40,XK_quotedbl,0x0022,Quote,AC11,apostrophe,0x57,0x27
+KEY_GRAVE,41,ANSI_Grave,0x32,0x29,0x0e,0x0e,53,VK_OEM_3,0xc0,41,41,XK_grave,0x0060,Backquote,TLDE,grave_accent,0x2a,0x32
+KEY_GRAVE,41,ANSI_Grave,0x32,0x29,0x0e,0x0e,53,VK_OEM_3,0xc0,41,41,XK_grave,0x0060,Backquote,AB00,grave_accent,0x2a,0x32
+KEY_GRAVE,41,ANSI_Grave,0x32,0x29,0x0e,0x0e,53,VK_OEM_3,0xc0,41,41,XK_asciitilde,0x007e,Backquote,TLDE,grave_accent,0x2a,0x32
+KEY_GRAVE,41,ANSI_Grave,0x32,0x29,0x0e,0x0e,53,VK_OEM_3,0xc0,41,41,XK_asciitilde,0x007e,Backquote,AB00,grave_accent,0x2a,0x32
+KEY_SHIFT,42,Shift,0x38,0x2a,0x12,0x12,225,VK_SHIFT,0x10,42,42,XK_Shift_L,0xffe1,ShiftLeft,LFSH,shift,0x63,0x38
+KEY_LEFTSHIFT,42,Shift,0x38,0x2a,0x12,0x12,225,VK_LSHIFT,0xa0,42,42,XK_Shift_L,0xffe1,ShiftLeft,LFSH,shift,0x63,0x38
+KEY_BACKSLASH,43,ANSI_Backslash,0x2a,0x2b,0x5d,0x5c,49,VK_OEM_5,0xdc,43,43,XK_backslash,0x005c,Backslash,BKSL,backslash,0x58,0x2a
+KEY_BACKSLASH,43,ANSI_Backslash,0x2a,0x2b,0x5d,0x5c,49,VK_OEM_5,0xdc,43,43,XK_backslash,0x005c,Backslash,AC12,backslash,0x58,0x2a
+KEY_BACKSLASH,43,ANSI_Backslash,0x2a,0x2b,0x5d,0x5c,49,VK_OEM_5,0xdc,43,43,XK_bar,0x007c,Backslash,BKSL,backslash,0x58,0x2a
+KEY_BACKSLASH,43,ANSI_Backslash,0x2a,0x2b,0x5d,0x5c,49,VK_OEM_5,0xdc,43,43,XK_bar,0x007c,Backslash,AC12,backslash,0x58,0x2a
+KEY_BACKSLASH,43,ANSI_Backslash,0x2a,0x2b,0x5d,0x5c,50,VK_OEM_5,0xdc,43,43,XK_backslash,0x005c,Backslash,BKSL,backslash,0x58,0x2a
+KEY_BACKSLASH,43,ANSI_Backslash,0x2a,0x2b,0x5d,0x5c,50,VK_OEM_5,0xdc,43,43,XK_backslash,0x005c,Backslash,AC12,backslash,0x58,0x2a
+KEY_BACKSLASH,43,ANSI_Backslash,0x2a,0x2b,0x5d,0x5c,50,VK_OEM_5,0xdc,43,43,XK_bar,0x007c,Backslash,BKSL,backslash,0x58,0x2a
+KEY_BACKSLASH,43,ANSI_Backslash,0x2a,0x2b,0x5d,0x5c,50,VK_OEM_5,0xdc,43,43,XK_bar,0x007c,Backslash,AC12,backslash,0x58,0x2a
+KEY_Z,44,ANSI_Z,0x6,0x2c,0x1a,0x1a,29,VK_Z,0x5a,44,44,XK_Z,0x005a,KeyZ,AB01,z,0x64,0x6
+KEY_Z,44,ANSI_Z,0x6,0x2c,0x1a,0x1a,29,VK_Z,0x5a,44,44,XK_z,0x007a,KeyZ,AB01,z,0x64,0x6
+KEY_X,45,ANSI_X,0x7,0x2d,0x22,0x22,27,VK_X,0x58,45,45,XK_X,0x0058,KeyX,AB02,x,0x65,0x7
+KEY_X,45,ANSI_X,0x7,0x2d,0x22,0x22,27,VK_X,0x58,45,45,XK_x,0x0078,KeyX,AB02,x,0x65,0x7
+KEY_C,46,ANSI_C,0x8,0x2e,0x21,0x21,6,VK_C,0x43,46,46,XK_C,0x0043,KeyC,AB03,c,0x66,0x8
+KEY_C,46,ANSI_C,0x8,0x2e,0x21,0x21,6,VK_C,0x43,46,46,XK_c,0x0063,KeyC,AB03,c,0x66,0x8
+KEY_V,47,ANSI_V,0x9,0x2f,0x2a,0x2a,25,VK_V,0x56,47,47,XK_V,0x0056,KeyV,AB04,v,0x67,0x9
+KEY_V,47,ANSI_V,0x9,0x2f,0x2a,0x2a,25,VK_V,0x56,47,47,XK_v,0x0076,KeyV,AB04,v,0x67,0x9
+KEY_B,48,ANSI_B,0xb,0x30,0x32,0x32,5,VK_B,0x42,48,48,XK_B,0x0042,KeyB,AB05,b,0x68,0xb
+KEY_B,48,ANSI_B,0xb,0x30,0x32,0x32,5,VK_B,0x42,48,48,XK_b,0x0062,KeyB,AB05,b,0x68,0xb
+KEY_N,49,ANSI_N,0x2d,0x31,0x31,0x31,17,VK_N,0x4e,49,49,XK_N,0x004e,KeyN,AB06,n,0x69,0x2d
+KEY_N,49,ANSI_N,0x2d,0x31,0x31,0x31,17,VK_N,0x4e,49,49,XK_n,0x006e,KeyN,AB06,n,0x69,0x2d
+KEY_M,50,ANSI_M,0x2e,0x32,0x3a,0x3a,16,VK_M,0x4d,50,50,XK_M,0x004d,KeyM,AB07,m,0x6a,0x2e
+KEY_M,50,ANSI_M,0x2e,0x32,0x3a,0x3a,16,VK_M,0x4d,50,50,XK_m,0x006d,KeyM,AB07,m,0x6a,0x2e
+KEY_COMMA,51,ANSI_Comma,0x2b,0x33,0x41,0x41,54,VK_OEM_COMMA,0xbc,51,51,XK_comma,0x002c,Comma,AB08,comma,0x6b,0x2b
+KEY_COMMA,51,ANSI_Comma,0x2b,0x33,0x41,0x41,54,VK_OEM_COMMA,0xbc,51,51,XK_less,0x003c,Comma,AB08,comma,0x6b,0x2b
+KEY_DOT,52,ANSI_Period,0x2f,0x34,0x49,0x49,55,VK_OEM_PERIOD,0xbe,52,52,XK_period,0x002e,Period,AB09,dot,0x6c,0x2f
+KEY_DOT,52,ANSI_Period,0x2f,0x34,0x49,0x49,55,VK_OEM_PERIOD,0xbe,52,52,XK_greater,0x003e,Period,AB09,dot,0x6c,0x2f
+KEY_SLASH,53,ANSI_Slash,0x2c,0x35,0x4a,0x4a,56,VK_OEM_2,0xbf,53,53,XK_slash,0x002f,Slash,AB10,slash,0x6d,0x2c
+KEY_SLASH,53,ANSI_Slash,0x2c,0x35,0x4a,0x4a,56,VK_OEM_2,0xbf,53,53,XK_question,0x003f,Slash,AB10,slash,0x6d,0x2c
+KEY_RIGHTSHIFT,54,RightShift,0x3c,0x36,0x59,0x59,229,VK_RSHIFT,0xa1,54,54,XK_Shift_R,0xffe2,ShiftRight,RTSH,shift_r,0x6e,0x7b
+KEY_KPASTERISK,55,ANSI_KeypadMultiply,0x43,0x37,0x7c,0x7e,85,VK_MULTIPLY,0x6a,55,55,XK_multiply,0x00d7,NumpadMultiply,KPMU,asterisk,0x2f,0x43
+KEY_KPASTERISK,55,ANSI_KeypadMultiply,0x43,0x37,0x7c,0x7e,85,VK_MULTIPLY,0x6a,55,55,XK_multiply,0x00d7,NumpadMultiply,KPMU,kp_multiply,0x2f,0x43
+KEY_LEFTALT,56,Option,0x3a,0x38,0x11,0x19,226,VK_LMENU,0xa4,56,56,XK_Alt_L,0xffe9,AltLeft,LALT,alt,0x13,0x3a
+KEY_LEFTALT,56,Option,0x3a,0x38,0x11,0x19,226,VK_MENU,0x12,56,56,XK_Alt_L,0xffe9,AltLeft,LALT,alt,0x13,0x3a
+KEY_SPACE,57,Space,0x31,0x39,0x29,0x29,44,VK_SPACE,0x20,57,57,XK_space,0x0020,Space,SPCE,spc,0x79,0x31
+KEY_CAPSLOCK,58,CapsLock,0x39,0x3a,0x58,0x14,57,VK_CAPITAL,0x14,58,58,XK_Caps_Lock,0xffe5,CapsLock,CAPS,caps_lock,0x77,0x39
+KEY_F1,59,F1,0x7a,0x3b,0x05,0x07,58,VK_F1,0x70,59,59,XK_F1,0xffbe,F1,FK01,f1,0x05,0x7a
+KEY_F2,60,F2,0x78,0x3c,0x06,0x0f,59,VK_F2,0x71,60,60,XK_F2,0xffbf,F2,FK02,f2,0x06,0x78
+KEY_F3,61,F3,0x63,0x3d,0x04,0x17,60,VK_F3,0x72,61,61,XK_F3,0xffc0,F3,FK03,f3,0x08,0x63
+KEY_F4,62,F4,0x76,0x3e,0x0c,0x1f,61,VK_F4,0x73,62,62,XK_F4,0xffc1,F4,FK04,f4,0x0a,0x76
+KEY_F5,63,F5,0x60,0x3f,0x03,0x27,62,VK_F5,0x74,63,63,XK_F5,0xffc2,F5,FK05,f5,0x0c,0x60
+KEY_F6,64,F6,0x61,0x40,0x0b,0x2f,63,VK_F6,0x75,64,64,XK_F6,0xffc3,F6,FK06,f6,0x0e,0x61
+KEY_F7,65,F7,0x62,0x41,0x83,0x37,64,VK_F7,0x76,65,65,XK_F7,0xffc4,F7,FK07,f7,0x10,0x62
+KEY_F8,66,F8,0x64,0x42,0x0a,0x3f,65,VK_F8,0x77,66,66,XK_F8,0xffc5,F8,FK08,f8,0x11,0x64
+KEY_F9,67,F9,0x65,0x43,0x01,0x47,66,VK_F9,0x78,67,67,XK_F9,0xffc6,F9,FK09,f9,0x12,0x65
+KEY_F10,68,F10,0x6d,0x44,0x09,0x4f,67,VK_F10,0x79,68,68,XK_F10,0xffc7,F10,FK10,f10,0x07,0x6d
+KEY_NUMLOCK,69,ANSI_KeypadClear,0x47,0x45,0x77,0x76,83,VK_NUMLOCK,0x90,69,69,XK_Num_Lock,0xff7f,NumLock,NMLK,num_lock,0x62,0x47
+KEY_SCROLLLOCK,70,,,0x46,0x7e,0x5f,71,VK_SCROLL,0x91,70,70,XK_Scroll_Lock,0xff14,ScrollLock,SCLK,scroll_lock,0x17,0x6b
+KEY_KP7,71,ANSI_Keypad7,0x59,0x47,0x6c,0x6c,95,VK_NUMPAD7,0x67,71,71,XK_KP_7,0xffb7,Numpad7,KP7,kp_7,0x44,0x59
+KEY_KP8,72,ANSI_Keypad8,0x5b,0x48,0x75,0x75,96,VK_NUMPAD8,0x68,72,72,XK_KP_8,0xffb8,Numpad8,KP8,kp_8,0x45,0x5b
+KEY_KP9,73,ANSI_Keypad9,0x5c,0x49,0x7d,0x7d,97,VK_NUMPAD9,0x69,73,73,XK_KP_9,0xffb9,Numpad9,KP9,kp_9,0x46,0x5c
+KEY_KPMINUS,74,ANSI_KeypadMinus,0x4e,0x4a,0x7b,0x4e,86,VK_SUBTRACT,0x6d,74,74,XK_KP_Subtract,0xffad,NumpadSubtract,KPSU,kp_subtract,0x47,0x4e
+KEY_KP4,75,ANSI_Keypad4,0x56,0x4b,0x6b,0x6b,92,VK_NUMPAD4,0x64,75,75,XK_KP_4,0xffb4,Numpad4,KP4,kp_4,0x5b,0x56
+KEY_KP5,76,ANSI_Keypad5,0x57,0x4c,0x73,0x73,93,VK_NUMPAD5,0x65,76,76,XK_KP_5,0xffb5,Numpad5,KP5,kp_5,0x5c,0x57
+KEY_KP6,77,ANSI_Keypad6,0x58,0x4d,0x74,0x74,94,VK_NUMPAD6,0x66,77,77,XK_KP_6,0xffb6,Numpad6,KP6,kp_6,0x5d,0x58
+KEY_KPPLUS,78,ANSI_KeypadPlus,0x45,0x4e,0x79,0x7c,87,VK_ADD,0x6b,78,78,XK_KP_Add,0xffab,NumpadAdd,KPAD,kp_add,0x7d,0x45
+KEY_KP1,79,ANSI_Keypad1,0x53,0x4f,0x69,0x69,89,VK_NUMPAD1,0x61,79,79,XK_KP_1,0xffb1,Numpad1,KP1,kp_1,0x70,0x53
+KEY_KP2,80,ANSI_Keypad2,0x54,0x50,0x72,0x72,90,VK_NUMPAD2,0x62,80,80,XK_KP_2,0xffb2,Numpad2,KP2,kp_2,0x71,0x54
+KEY_KP3,81,ANSI_Keypad3,0x55,0x51,0x7a,0x7a,91,VK_NUMPAD3,0x63,81,81,XK_KP_3,0xffb3,Numpad3,KP3,kp_3,0x72,0x55
+KEY_KP0,82,ANSI_Keypad0,0x52,0x52,0x70,0x70,98,VK_NUMPAD0,0x60,82,82,XK_KP_0,0xffb0,Numpad0,KP0,kp_0,0x5e,0x52
+KEY_KPDOT,83,ANSI_KeypadDecimal,0x41,0x53,0x71,0x71,99,VK_DECIMAL,0x6e,83,83,XK_KP_Decimal,0xffae,NumpadDecimal,KPDL,kp_decimal,0x32,0x41
+KEY_KPDOT,83,ANSI_KeypadDecimal,0x41,0x53,0x71,0x71,99,VK_DECIMAL,0x6e,83,83,XK_KP_Decimal,0xffae,NumpadDecimal,KPDC,kp_decimal,0x32,0x41
+,84,,,0x54,,,,,,,,,,,,,,
+KEY_ZENKAKUHANKAKU,85,,,0x76,0x5f,,148,,,,,,,Lang5,HZTG,,,
+KEY_102ND,86,,,0x56,0x61,0x13,100,VK_OEM_102,0xe1,86,86,,,IntlBackslash,LSGT,less,0x7c,
+KEY_F11,87,F11,0x67,0x57,0x78,0x56,68,VK_F11,0x7a,87,87,XK_F11,0xffc8,F11,FK11,f11,0x09,0x67
+KEY_F12,88,F12,0x6f,0x58,0x07,0x5e,69,VK_F12,0x7b,88,88,XK_F12,0xffc9,F12,FK12,f12,0x0b,0x6f
+KEY_RO,89,,,0x73,0x51,,135,,,,,,,IntlRo,AB11,ro,,
+KEY_KATAKANA,90,JIS_Kana,0x68,0x78,0x63,,146,VK_KANA,0x15,,,,,Katakana,KATA,,,
+KEY_KATAKANA,90,JIS_Kana,0x68,0x78,0x63,,146,VK_KANA,0x15,,,,,Lang3,KATA,,,
+KEY_HIRAGANA,91,,,0x77,0x62,0x87,147,,,,,,,Hiragana,HIRA,hiragana,,
+KEY_HIRAGANA,91,,,0x77,0x62,0x87,147,,,,,,,Lang4,HIRA,hiragana,,
+KEY_HENKAN,92,,,0x79,0x64,0x86,138,,,,,,,Convert,HENK,henkan,,
+KEY_KATAKANAHIRAGANA,93,,,0x70,0x13,0x87,136,,,0xc8,0xc8,,,KanaMode,HKTG,,,
+KEY_MUHENKAN,94,,,0x7b,0x67,0x85,139,,,,,,,NonConvert,NFER,,,
+KEY_MUHENKAN,94,,,0x7b,0x67,0x85,139,,,,,,,NonConvert,MUHE,,,
+KEY_KPJPCOMMA,95,JIS_KeypadComma,0x5f,0x5c,0x27,,140,,,,,XK_KP_Separator,0xffac,,KPSP,,,
+KEY_KPJPCOMMA,95,JIS_KeypadComma,0x5f,0x5c,0x27,,140,,,,,XK_KP_Separator,0xffac,,JPCM,,,
+KEY_KPENTER,96,ANSI_KeypadEnter,0x4c,0xe01c,0xe05a,0x79,88,,,0x64,0x64,XK_KP_Enter,0xff8d,NumpadEnter,KPEN,kp_enter,0x5a,0x4c
+KEY_RIGHTCTRL,97,RightControl,0x3e,0xe01d,0xe014,0x58,228,VK_RCONTROL,0xa3,0x65,0x65,XK_Control_R,0xffe4,ControlRight,RCTL,ctrl_r,0x4c,0x7d
+KEY_KPSLASH,98,ANSI_KeypadDivide,0x4b,0xe035,0xe04a,0x4a,84,VK_DIVIDE,0x6f,0x68,0x68,XK_KP_Divide,0xffaf,NumpadDivide,KPDV,kp_divide,0x2e,0x4b
+KEY_SYSRQ,99,,,0x54,0x7f,0x57,70,VK_SNAPSHOT,0x2c,0x67,0x67,XK_Sys_Req,0xff15,PrintScreen,PRSC,print,0x16,0x69
+KEY_SYSRQ,99,,,0x54,0x7f,0x57,70,VK_SNAPSHOT,0x2c,0x67,0x67,XK_Sys_Req,0xff15,PrintScreen,SYRQ,sysrq,0x16,0x69
+KEY_RIGHTALT,100,RightOption,0x3d,0xe038,0xe011,0x39,230,VK_RMENU,0xa5,0x69,0x69,XK_Alt_R,0xffea,AltRight,ALGR,alt_r,0x0d,0x7c
+KEY_RIGHTALT,100,RightOption,0x3d,0xe038,0xe011,0x39,230,VK_RMENU,0xa5,0x69,0x69,XK_Alt_R,0xffea,AltRight,RALT,alt_r,0x0d,0x7c
+KEY_LINEFEED,101,,,0x5b,,,,,,,,,,,LNFD,lf,0x6f,
+KEY_HOME,102,Home,0x73,0xe047,0xe06c,0x6e,74,VK_HOME,0x24,0x59,0x59,XK_Home,0xff50,Home,HOME,home,0x34,0x73
+KEY_UP,103,UpArrow,0x7e,0xe048,0xe075,0x63,82,VK_UP,0x26,0x5a,0x5a,XK_Up,0xff52,ArrowUp,UP,up,0x14,0x3e
+KEY_PAGEUP,104,PageUp,0x74,0xe049,0xe07d,0x6f,75,VK_PRIOR,0x21,0x5b,0x5b,XK_Page_Up,0xff55,PageUp,PGUP,pgup,0x60,0x74
+KEY_LEFT,105,LeftArrow,0x7b,0xe04b,0xe06b,0x61,80,VK_LEFT,0x25,0x5c,0x5c,XK_Left,0xff51,ArrowLeft,LEFT,left,0x18,0x3b
+KEY_RIGHT,106,RightArrow,0x7c,0xe04d,0xe074,0x6a,79,VK_RIGHT,0x27,0x5e,0x5e,XK_Right,0xff53,ArrowRight,RGHT,right,0x1c,0x3c
+KEY_END,107,End,0x77,0xe04f,0xe069,0x65,77,VK_END,0x23,0x5f,0x5f,XK_End,0xff57,End,END,end,0x4a,0x77
+KEY_DOWN,108,DownArrow,0x7d,0xe050,0xe072,0x60,81,VK_DOWN,0x28,0x60,0x60,XK_Down,0xff54,ArrowDown,DOWN,down,0x1b,0x3d
+KEY_PAGEDOWN,109,PageDown,0x79,0xe051,0xe07a,0x6d,78,VK_NEXT,0x22,0x61,0x61,XK_Page_Down,0xff56,PageDown,PGDN,pgdn,0x7b,0x79
+KEY_INSERT,110,,,0xe052,0xe070,0x67,73,VK_INSERT,0x2d,0x62,0x62,XK_Insert,0xff63,Insert,INS,insert,0x2c,0x72
+KEY_DELETE,111,ForwardDelete,0x75,0xe053,0xe071,0x64,76,VK_DELETE,0x2e,0x63,0x63,XK_Delete,0xffff,Delete,DEL,delete,0x42,0x75
+KEY_DELETE,111,ForwardDelete,0x75,0xe053,0xe071,0x64,76,VK_DELETE,0x2e,0x63,0x63,XK_Delete,0xffff,Delete,DELE,,0x42,0x75
+KEY_MACRO,112,,,0xe06f,0xe06f,0x8e,,,,,,,,,I120,,,
+KEY_MUTE,113,Mute,0x4a,0xe020,0xe023,0x9c,127,VK_VOLUME_MUTE,0xad,,,,,AudioVolumeMute,MUTE,audiomute,,
+KEY_MUTE,113,Mute,0x4a,0xe020,0xe023,0x9c,239,VK_VOLUME_MUTE,0xad,,,,,AudioVolumeMute,MUTE,audiomute,,
+KEY_VOLUMEDOWN,114,VolumeDown,0x49,0xe02e,0xe021,0x9d,129,VK_VOLUME_DOWN,0xae,,,,,AudioVolumeDown,VOL-,volumedown,,
+KEY_VOLUMEDOWN,114,VolumeDown,0x49,0xe02e,0xe021,0x9d,238,VK_VOLUME_DOWN,0xae,,,,,AudioVolumeDown,VOL-,volumedown,,
+KEY_VOLUMEUP,115,VolumeUp,0x48,0xe030,0xe032,0x95,128,VK_VOLUME_UP,0xaf,,,,,AudioVolumeUp,VOL+,volumeup,,
+KEY_VOLUMEUP,115,VolumeUp,0x48,0xe030,0xe032,0x95,237,VK_VOLUME_UP,0xaf,,,,,AudioVolumeUp,VOL+,volumeup,,
+KEY_POWER,116,,,0xe05e,0xe037,,102,,,,,,,Power,POWR,power,,0x7f7f
+KEY_KPEQUAL,117,ANSI_KeypadEquals,0x51,0x59,0x0f,,103,,,0x76,0x76,XK_KP_Equal,0xffbd,NumpadEqual,KPEQ,kp_equals,0x2d,0x51
+KEY_KPPLUSMINUS,118,,,0xe04e,0xe079,,,,,,,,,,I126,,,
+KEY_PAUSE,119,,,0xe046,0xe077,0x62,72,VK_PAUSE,0x013,0x66,0x66,XK_Pause,0xff13,Pause,PAUS,pause,0x15,0x71
+KEY_SCALE,120,,,0xe00b,,,,,,,,,,,I128,,,
+KEY_KPCOMMA,121,,,0x7e,0x6d,,133,VK_SEPARATOR??,0x6c,,,,,NumpadComma,KPCO,kp_comma,,
+KEY_KPCOMMA,121,,,0x7e,0x6d,,133,VK_SEPARATOR??,0x6c,,,,,NumpadComma,I129,,,
+KEY_HANGEUL,122,,,,,,144,VK_HANGEUL,0x15,,,,,,HNGL,,,
+KEY_HANJA,123,,,0xe00d,,,145,VK_HANJA,0x19,,,,,,HJCV,,,
+KEY_YEN,124,JIS_Yen,0x5d,0x7d,0x6a,0x5d,137,,,0x7d,0x7d,,,IntlYen,AE13,yen,,
+KEY_LEFTMETA,125,Command,0x37,0xe05b,0xe01f,0x8b,227,VK_LWIN,0x5b,0x6b,0x6b,XK_Meta_L,0xffe7,MetaLeft,LMTA,meta_l,0x78,0x37
+KEY_LEFTMETA,125,Command,0x37,0xe05b,0xe01f,0x8b,227,VK_LWIN,0x5b,0x6b,0x6b,XK_Meta_L,0xffe7,MetaLeft,LWIN,meta_l,0x78,0x37
+KEY_RIGHTMETA,126,RightCommand,0x36,0xe05c,0xe027,0x8c,231,VK_RWIN,0x5c,0x6c,0x6c,XK_Meta_R,0xffe8,MetaRight,RMTA,meta_r,0x7a,0x37
+KEY_RIGHTMETA,126,RightCommand,0x36,0xe05c,0xe027,0x8c,231,VK_RWIN,0x5c,0x6c,0x6c,XK_Meta_R,0xffe8,MetaRight,RWIN,meta_r,0x7a,0x37
+KEY_COMPOSE,127,,0x6e,0xe05d,0xe02f,0x8d,101,VK_APPS,0x5d,0x6d,0x6d,,,ContextMenu,MENU,compose,0x43,
+KEY_COMPOSE,127,,0x6e,0xe05d,0xe02f,0x8d,101,VK_APPS,0x5d,0x6d,0x6d,,,ContextMenu,COMP,compose,0x43,
+KEY_STOP,128,,,0xe068,0xe028,0x0a,120,VK_BROWSER_STOP,0xa9,,,,,BrowserStop,STOP,stop,0x01,
+KEY_STOP,128,,,0xe068,0xe028,0x0a,243,VK_BROWSER_STOP,0xa9,,,,,BrowserStop,STOP,stop,0x01,
+KEY_AGAIN,129,,,0xe005,,0x0b,121,,,,,,,Again,AGAI,again,0x03,
+KEY_PROPS,130,,,0xe006,,0x0c,,,,,,,,Props,PROP,props,0x19,
+KEY_UNDO,131,,,0xe007,,0x10,122,,,,,,,Undo,UNDO,undo,0x1a,
+KEY_FRONT,132,,,0xe00c,,,119,,,,,,,,FRNT,front,0x31,
+KEY_COPY,133,,,0xe078,,0x18,124,,,,,,,Copy,COPY,copy,0x33,
+KEY_OPEN,134,,,0x64,,0x20,116,,,,,,,Open,OPEN,open,0x48,
+KEY_PASTE,135,,,0x65,,0x28,125,,,,,,,Paste,PAST,paste,0x49,
+KEY_FIND,136,,,0xe041,,0x30,126,,,,,,,Find,FIND,find,0x5f,
+KEY_FIND,136,,,0xe041,,0x30,244,,,,,,,Find,FIND,find,0x5f,
+KEY_CUT,137,,,0xe03c,,0x38,123,,,,,,,Cut,CUT,cut,0x61,
+KEY_HELP,138,Help,0x72,0xe075,,0x09,117,VK_HELP,0x2f,,,XK_Help,0xff6a,Help,HELP,help,0x76,
+KEY_MENU,139,,,0xe01e,,0x91,118,,,,,,,,I147,menu,,
+KEY_CALC,140,,,0xe021,0xe02b,0xa3,251,,,,,,,LaunchApp2,I148,calculator,,
+KEY_SETUP,141,,,0x66,,,,,,,,,,,I149,,,
+KEY_SLEEP,142,,,0xe05f,0xe03f,,248,VK_SLEEP,0x5f,,,,,Sleep,I150,sleep,,
+KEY_WAKEUP,143,,,0xe063,0xe05e,,,,,,,,,WakeUp,I151,wake,,
+KEY_FILE,144,,,0x67,,,,,,,,,,,I152,,,
+KEY_SENDFILE,145,,,0x68,,,,,,,,,,,I153,,,
+KEY_DELETEFILE,146,,,0x69,,,,,,,,,,,I154,,,
+KEY_XFER,147,,,0xe013,,0xa2,,,,,,,,,XFER,,,
+KEY_XFER,147,,,0xe013,,0xa2,,,,,,,,,I155,,,
+KEY_PROG1,148,,,0xe01f,,0xa0,,,,,,,,,I156,,,
+KEY_PROG2,149,,,0xe017,,0xa1,,,,,,,,,I157,,,
+KEY_WWW,150,,,0xe002,,,240,,,,,,,,I158,,,
+KEY_MSDOS,151,,,0x6a,,,,,,,,,,,I159,,,
+KEY_SCREENLOCK,152,,,0xe012,,0x96,249,,,,,,,,I160,,,
+KEY_DIRECTION,153,,,0x6b,,,,,,,,,,,I161,,,
+KEY_CYCLEWINDOWS,154,,,0xe026,,0x9b,,,,,,,,,I162,,,
+KEY_MAIL,155,,,0xe06c,0xe048,,,,,,,,,LaunchMail,I163,mail,,
+KEY_BOOKMARKS,156,,,0xe066,0xe018,,,,,,,,,BrowserFavorites,I164,ac_bookmarks,,
+KEY_COMPUTER,157,,,0xe06b,0xe040,,,,,,,,,LaunchApp1,I165,computer,,
+KEY_BACK,158,,,0xe06a,0xe038,,241,VK_BROWSER_BACK,0xa6,,,,,BrowserBack,I166,ac_back,,
+KEY_FORWARD,159,,,0xe069,0xe030,,242,VK_BROWSER_FORWARD,0xa7,,,,,BrowserForward,I167,ac_forward,,
+KEY_CLOSECD,160,,,0xe023,,0x9a,,,,,,,,,I168,,,
+KEY_EJECTCD,161,,,0x6c,,,236,,,,,,,,I169,,,
+KEY_EJECTCLOSECD,162,,,0xe07d,,,,,,,,,,Eject,I170,,,
+KEY_NEXTSONG,163,,,0xe019,0xe04d,0x93,235,VK_MEDIA_NEXT_TRACK,0xb0,,,,,MediaTrackNext,I171,audionext,,
+KEY_PLAYPAUSE,164,,,0xe022,0xe034,,232,VK_MEDIA_PLAY_PAUSE,0xb3,,,,,MediaPlayPause,I172,audioplay,,
+KEY_PREVIOUSSONG,165,,,0xe010,0xe015,0x94,234,VK_MEDIA_PREV_TRACK,0xb1,,,,,MediaTrackPrevious,I173,audioprev,,
+KEY_STOPCD,166,,,0xe024,0xe03b,0x98,233,VK_MEDIA_STOP,0xb2,,,,,MediaStop,I174,audiostop,,
+KEY_RECORD,167,,,0xe031,,0x9e,,,,,,,,,I175,,,
+KEY_REWIND,168,,,0xe018,,0x9f,,,,,,,,,I176,,,
+KEY_PHONE,169,,,0x63,,,,,,,,,,,I177,,,
+KEY_ISO,170,ISO_Section,0xa,0x70,,,,,,,,,,,I178,,,
+KEY_CONFIG,171,,,0xe001,,,,,,,,,,,I179,,,
+KEY_HOMEPAGE,172,,,0xe032,0xe03a,0x97,,VK_BROWSER_HOME,0xac,,,,,BrowserHome,I180,ac_home,,
+KEY_REFRESH,173,,,0xe067,0xe020,,250,VK_BROWSER_REFRESH,0xa8,,,,,BrowserRefresh,I181,ac_refresh,,
+KEY_EXIT,174,,,0x71,,,,,,,,,,,I182,,,
+KEY_MOVE,175,,,0x72,,,,,,,,,,,I183,,,
+KEY_EDIT,176,,,0xe008,,,247,,,,,,,,I184,,,
+KEY_SCROLLUP,177,,,0x75,,,245,,,,,,,,I185,,,
+KEY_SCROLLDOWN,178,,,0xe00f,,,246,,,,,,,,I186,,,
+KEY_KPLEFTPAREN,179,,,0xe076,,,182,,,,,,,NumpadParenLeft,I187,,,
+KEY_KPRIGHTPAREN,180,,,0xe07b,,,183,,,,,,,NumpadParenRight,I188,,,
+KEY_NEW,181,,,0xe009,,,,,,,,,,,I189,,,
+KEY_REDO,182,,,0xe00a,,,,,,,,,,,I190,,,
+KEY_F13,183,F13,0x69,0x5d,0x2f,0x7f,104,VK_F13,0x7c,0x6e,0x6e,,,F13,FK13,,,0x69
+KEY_F14,184,F14,0x6b,0x5e,0x37,0x80,105,VK_F14,0x7d,0x6f,0x6f,,,F14,FK14,,,0x6b
+KEY_F15,185,F15,0x71,0x5f,0x3f,0x81,106,VK_F15,0x7e,0x70,0x70,,,F15,FK15,,,0x71
+KEY_F16,186,F16,0x6a,0x55,,0x82,107,VK_F16,0x7f,0x71,0x71,,,F16,FK16,,,
+KEY_F17,187,F17,0x40,0xe003,,0x83,108,VK_F17,0x80,0x72,0x72,,,F17,FK17,,,
+KEY_F18,188,F18,0x4f,0xe077,,,109,VK_F18,0x81,,,,,F18,FK18,,,
+KEY_F19,189,F19,0x50,0xe004,,,110,VK_F19,0x82,,,,,F19,FK19,,,
+KEY_F20,190,F20,0x5a,0x5a,,,111,VK_F20,0x83,,,,,F20,FK20,,,
+KEY_F21,191,,,0x74,,,112,VK_F21,0x84,,,,,F21,FK21,,,
+KEY_F22,192,,,0xe079,,,113,VK_F22,0x85,,,,,F22,FK22,,,
+KEY_F23,193,,,0x6d,,,114,VK_F23,0x86,,,,,F23,FK23,,,
+KEY_F24,194,,,0x6f,,,115,VK_F24,0x87,,,,,F24,FK24,,,
+,195,,,0xe015,,,,,,,,,,,,,,
+,196,,,0xe016,,,,,,,,,,,,,,
+,197,,,0xe01a,,,,,,,,,,,,,,
+,198,,,0xe01b,,,,,,,,,,,,,,
+,199,,,0xe027,,,,,,,,,,,,,,
+KEY_PLAYCD,200,,,0xe028,,,,,,,,,,,I208,,,
+KEY_PAUSECD,201,,,0xe029,,,,,,,,,,,I209,,,
+KEY_PROG3,202,,,0xe02b,,,,,,,,,,,I210,,,
+KEY_PROG4,203,,,0xe02c,,,,,,,,,,,I211,,,
+KEY_DASHBOARD,204,,,0xe02d,,,,,,,,,,,I212,,,
+KEY_SUSPEND,205,,,0xe025,,,,,,,,,,Suspend,I213,,,
+KEY_CLOSE,206,,,0xe02f,,,,,,,,,,,I214,,,
+KEY_PLAY,207,,,0xe033,,,,VK_PLAY,0xfa,,,,,,I215,,,
+KEY_FASTFORWARD,208,,,0xe034,,,,,,,,,,,I216,,,
+KEY_BASSBOOST,209,,,0xe036,,,,,,,,,,,I217,,,
+KEY_PRINT,210,,,0xe039,,,,VK_PRINT,0x2a,,,,,,I218,,,
+KEY_HP,211,,,0xe03a,,,,,,,,,,,I219,,,
+KEY_CAMERA,212,,,0xe03b,,,,,,,,,,,I220,,,
+KEY_SOUND,213,,,0xe03d,,,,,,,,,,,I221,,,
+KEY_QUESTION,214,,,0xe03e,,,,,,,,,,,I222,,,
+KEY_EMAIL,215,,,0xe03f,,,,VK_LAUNCH_MAIL,0xb4,,,,,,I223,,,
+KEY_CHAT,216,,,0xe040,,,,,,,,,,,I224,,,
+KEY_SEARCH,217,,,0xe065,0xe010,,,VK_BROWSER_SEARCH,0xaa,,,,,BrowserSearch,I225,,,
+KEY_CONNECT,218,,,0xe042,,,,,,,,,,,I226,,,
+KEY_FINANCE,219,,,0xe043,,,,,,,,,,,I227,,,
+KEY_SPORT,220,,,0xe044,,,,,,,,,,,I228,,,
+KEY_SHOP,221,,,0xe045,,,,,,,,,,,I229,,,
+KEY_ALTERASE,222,,,0xe014,,,,,,,,,,,I230,,,
+KEY_CANCEL,223,,,0xe04a,,,,,,,,,,,I231,,,
+KEY_BRIGHTNESSDOWN,224,,,0xe04c,,,,,,,,,,,I232,,,
+KEY_BRIGHTNESSUP,225,,,0xe054,,,,,,,,,,,I233,,,
+KEY_MEDIA,226,,,0xe06d,0xe050,,,,,,,,,MediaSelect,I234,mediaselect,,
+KEY_SWITCHVIDEOMODE,227,,,0xe056,,,,,,,,,,,I235,,,
+KEY_KBDILLUMTOGGLE,228,,,0xe057,,,,,,,,,,,I236,,,
+KEY_KBDILLUMDOWN,229,,,0xe058,,,,,,,,,,,I237,,,
+KEY_KBDILLUMUP,230,,,0xe059,,,,,,,,,,,I238,,,
+KEY_SEND,231,,,0xe05a,,,,,,,,,,,I239,,,
+KEY_REPLY,232,,,0xe064,,,,,,,,,,,I240,,,
+KEY_FORWARDMAIL,233,,,0xe00e,,,,,,,,,,,I241,,,
+KEY_SAVE,234,,,0xe055,,,,,,,,,,,I242,,,
+KEY_DOCUMENTS,235,,,0xe070,,,,,,,,,,,I243,,,
+KEY_BATTERY,236,,,0xe071,,,,,,,,,,,I244,,,
+KEY_BLUETOOTH,237,,,0xe072,,,,,,,,,,,I245,,,
+KEY_WLAN,238,,,0xe073,,,,,,,,,,,I246,,,
+KEY_UWB,239,,,0xe074,,,,,,,,,,,I247,,,
+KEY_UNKNOWN,240,,,,,,,,,,,,,,I248,,,
+KEY_VIDEO_NEXT,241,,,,,,,,,,,,,,I249,,,
+KEY_VIDEO_PREV,242,,,,,,,,,,,,,,I250,,,
+KEY_BRIGHTNESS_CYCLE,243,,,,,,,,,,,,,,I251,,,
+KEY_BRIGHTNESS_ZERO,244,,,,,,,,,,,,,,I252,,,
+KEY_DISPLAY_OFF,245,,,,,,,,,,,,,,I253,,,
+KEY_WIMAX,246,,,,,,,,,,,,,,,,,
+,247,,,,,,,,,,,,,,,,,
+,248,,,,,,,,,,,,,,,,,
+,249,,,,,,,,,,,,,,,,,
+,250,,,,,,,,,,,,,,,,,
+,251,,,,,,,,,,,,,,,,,
+,252,,,,,,,,,,,,,,,,,
+,253,,,,,,,,,,,,,,,,,
+,254,,,,,,,,,,,,,,,,,
+,255,,,,0xe012,,,,,,,,,,,,,
+BTN_MISC,0x100,,,,,,,,,,,,,,,,,
+BTN_0,0x100,,,,,,,VK_LBUTTON,0x01,,,,,,,,,
+BTN_1,0x101,,,,,,,VK_RBUTTON,0x02,,,,,,,,,
+BTN_2,0x102,,,,,,,VK_MBUTTON,0x04,,,,,,,,,
+BTN_3,0x103,,,,,,,VK_XBUTTON1,0x05,,,,,,,,,
+BTN_4,0x104,,,,,,,VK_XBUTTON2,0x06,,,,,,,,,
+BTN_5,0x105,,,,,,,,,,,,,,,,,
+BTN_6,0x106,,,,,,,,,,,,,,,,,
+BTN_7,0x107,,,,,,,,,,,,,,,,,
+BTN_8,0x108,,,,,,,,,,,,,,,,,
+BTN_9,0x109,,,,,,,,,,,,,,,,,
+BTN_MOUSE,0x110,,,,,,,,,,,,,,,,,
+BTN_LEFT,0x110,,,,,,,,,,,,,,,,,
+BTN_RIGHT,0x111,,,,,,,,,,,,,,,,,
+BTN_MIDDLE,0x112,,,,,,,,,,,,,,,,,
+BTN_SIDE,0x113,,,,,,,,,,,,,,,,,
+BTN_EXTRA,0x114,,,,,,,,,,,,,,,,,
+BTN_FORWARD,0x115,,,,,,,,,,,,,,,,,
+BTN_BACK,0x116,,,,,,,,,,,,,,,,,
+BTN_TASK,0x117,,,,,,,,,,,,,,,,,
+BTN_JOYSTICK,0x120,,,,,,,,,,,,,,,,,
+BTN_TRIGGER,0x120,,,,,,,,,,,,,,,,,
+BTN_THUMB,0x121,,,,,,,,,,,,,,,,,
+BTN_THUMB2,0x122,,,,,,,,,,,,,,,,,
+BTN_TOP,0x123,,,,,,,,,,,,,,,,,
+BTN_TOP2,0x124,,,,,,,,,,,,,,,,,
+BTN_PINKIE,0x125,,,,,,,,,,,,,,,,,
+BTN_BASE,0x126,,,,,,,,,,,,,,,,,
+BTN_BASE2,0x127,,,,,,,,,,,,,,,,,
+BTN_BASE3,0x128,,,,,,,,,,,,,,,,,
+BTN_BASE4,0x129,,,,,,,,,,,,,,,,,
+BTN_BASE5,0x12a,,,,,,,,,,,,,,,,,
+BTN_BASE6,0x12b,,,,,,,,,,,,,,,,,
+BTN_DEAD,0x12f,,,,,,,,,,,,,,,,,
+BTN_GAMEPAD,0x130,,,,,,,,,,,,,,,,,
+BTN_A,0x130,,,,,,,,,,,,,,,,,
+BTN_B,0x131,,,,,,,,,,,,,,,,,
+BTN_C,0x132,,,,,,,,,,,,,,,,,
+BTN_X,0x133,,,,,,,,,,,,,,,,,
+BTN_Y,0x134,,,,,,,,,,,,,,,,,
+BTN_Z,0x135,,,,,,,,,,,,,,,,,
+BTN_TL,0x136,,,,,,,,,,,,,,,,,
+BTN_TR,0x137,,,,,,,,,,,,,,,,,
+BTN_TL2,0x138,,,,,,,,,,,,,,,,,
+BTN_TR2,0x139,,,,,,,,,,,,,,,,,
+BTN_SELECT,0x13a,,,,,,,,,,,,,,,,,
+BTN_START,0x13b,,,,,,,,,,,,,,,,,
+BTN_MODE,0x13c,,,,,,,,,,,,,,,,,
+BTN_THUMBL,0x13d,,,,,,,,,,,,,,,,,
+BTN_THUMBR,0x13e,,,,,,,,,,,,,,,,,
+BTN_DIGI,0x140,,,,,,,,,,,,,,,,,
+BTN_TOOL_PEN,0x140,,,,,,,,,,,,,,,,,
+BTN_TOOL_RUBBER,0x141,,,,,,,,,,,,,,,,,
+BTN_TOOL_BRUSH,0x142,,,,,,,,,,,,,,,,,
+BTN_TOOL_PENCIL,0x143,,,,,,,,,,,,,,,,,
+BTN_TOOL_AIRBRUSH,0x144,,,,,,,,,,,,,,,,,
+BTN_TOOL_FINGER,0x145,,,,,,,,,,,,,,,,,
+BTN_TOOL_MOUSE,0x146,,,,,,,,,,,,,,,,,
+BTN_TOOL_LENS,0x147,,,,,,,,,,,,,,,,,
+BTN_TOUCH,0x14a,,,,,,,,,,,,,,,,,
+BTN_STYLUS,0x14b,,,,,,,,,,,,,,,,,
+BTN_STYLUS2,0x14c,,,,,,,,,,,,,,,,,
+BTN_TOOL_DOUBLETAP,0x14d,,,,,,,,,,,,,,,,,
+BTN_TOOL_TRIPLETAP,0x14e,,,,,,,,,,,,,,,,,
+BTN_TOOL_QUADTAP,0x14f,,,,,,,,,,,,,,,,,
+BTN_WHEEL,0x150,,,,,,,,,,,,,,,,,
+BTN_GEAR_DOWN,0x150,,,,,,,,,,,,,,,,,
+BTN_GEAR_UP,0x151,,,,,,,,,,,,,,,,,
+KEY_OK,0x160,,,,,,,,,,,,,,,,,
+KEY_SELECT,0x161,,,,,,,VK_SELECT,0x29,,,XK_Select,0xff60,Select,SELE,,,
+KEY_GOTO,0x162,,,,,,,,,,,,,,,,,
+KEY_CLEAR,0x163,,,,,,,,,,,,,NumpadClear,CLR,,,
+KEY_POWER2,0x164,,,,,,,,,,,,,,,,,
+KEY_OPTION,0x165,,,,,,,,,,,,,,,,,
+KEY_INFO,0x166,,,,,,,,,,,,,,,,,
+KEY_TIME,0x167,,,,,,,,,,,,,,,,,
+KEY_VENDOR,0x168,,,,,,,,,,,,,,,,,
+KEY_ARCHIVE,0x169,,,,,,,,,,,,,,,,,
+KEY_PROGRAM,0x16a,,,,,,,,,,,,,,,,,
+KEY_CHANNEL,0x16b,,,,,,,,,,,,,,,,,
+KEY_FAVORITES,0x16c,,,,,,,VK_BROWSER_FAVOURITES,0xab,,,,,,,,,
+KEY_EPG,0x16d,,,,,,,,,,,,,,,,,
+KEY_PVR,0x16e,,,,,,,,,,,,,,,,,
+KEY_MHP,0x16f,,,,,,,,,,,,,,,,,
+KEY_LANGUAGE,0x170,,,,,,,,,,,,,,,,,
+KEY_TITLE,0x171,,,,,,,,,,,,,,,,,
+KEY_SUBTITLE,0x172,,,,,,,,,,,,,,,,,
+KEY_ANGLE,0x173,,,,,,,,,,,,,,,,,
+KEY_ZOOM,0x174,,,,,,,VK_ZOOM,0xfb,,,,,,,,,
+KEY_MODE,0x175,,,,,,,,,,,,,,,,,
+KEY_KEYBOARD,0x176,,,,,,,,,,,,,,,,,
+KEY_SCREEN,0x177,,,,,,,,,,,,,,,,,
+KEY_PC,0x178,,,,,,,,,,,,,,,,,
+KEY_TV,0x179,,,,,,,,,,,,,,,,,
+KEY_TV2,0x17a,,,,,,,,,,,,,,,,,
+KEY_VCR,0x17b,,,,,,,,,,,,,,,,,
+KEY_VCR2,0x17c,,,,,,,,,,,,,,,,,
+KEY_SAT,0x17d,,,,,,,,,,,,,,,,,
+KEY_SAT2,0x17e,,,,,,,,,,,,,,,,,
+KEY_CD,0x17f,,,,,,,,,,,,,,,,,
+KEY_TAPE,0x180,,,,,,,,,,,,,,,,,
+KEY_RADIO,0x181,,,,,,,,,,,,,,,,,
+KEY_TUNER,0x182,,,,,,,,,,,,,,,,,
+KEY_PLAYER,0x183,,,,,,,,,,,,,,,,,
+KEY_TEXT,0x184,,,,,,,,,,,,,,,,,
+KEY_DVD,0x185,,,,,,,,,,,,,,,,,
+KEY_AUX,0x186,,,,,,,,,,,,,,,,,
+KEY_MP3,0x187,,,,,,,,,,,,,,,,,
+KEY_AUDIO,0x188,,,,,,,,,,,,,,,,,
+KEY_VIDEO,0x189,,,,,,,,,,,,,,,,,
+KEY_DIRECTORY,0x18a,,,,,,,,,,,,,,,,,
+KEY_LIST,0x18b,,,,,,,,,,,,,,,,,
+KEY_MEMO,0x18c,,,,,,,,,,,,,,,,,
+KEY_CALENDAR,0x18d,,,,,,,,,,,,,,,,,
+KEY_RED,0x18e,,,,,,,,,,,,,,,,,
+KEY_GREEN,0x18f,,,,,,,,,,,,,,,,,
+KEY_YELLOW,0x190,,,,,,,,,,,,,,,,,
+KEY_BLUE,0x191,,,,,,,,,,,,,,,,,
+KEY_CHANNELUP,0x192,,,,,,,,,,,,,,,,,
+KEY_CHANNELDOWN,0x193,,,,,,,,,,,,,,,,,
+KEY_FIRST,0x194,,,,,,,,,,,,,,,,,
+KEY_LAST,0x195,,,,,,,,,,,,,,,,,
+KEY_AB,0x196,,,,,,,,,,,,,,,,,
+KEY_NEXT,0x197,,,,,,,,,,,,,,,,,
+KEY_RESTART,0x198,,,,,,,,,,,,,,,,,
+KEY_SLOW,0x199,,,,,,,,,,,,,,,,,
+KEY_SHUFFLE,0x19a,,,,,,,,,,,,,,,,,
+KEY_BREAK,0x19b,,,,,,,,,,,,,,BREA,,,
+KEY_BREAK,0x19b,,,,,,,,,,,,,,BRK,,,
+KEY_PREVIOUS,0x19c,,,,,,,,,,,,,,,,,
+KEY_DIGITS,0x19d,,,,,,,,,,,,,,,,,
+KEY_TEEN,0x19e,,,,,,,,,,,,,,,,,
+KEY_TWEN,0x19f,,,,,,,,,,,,,,,,,
+KEY_VIDEOPHONE,0x1a0,,,,,,,,,,,,,,,,,
+KEY_GAMES,0x1a1,,,,,,,,,,,,,,,,,
+KEY_ZOOMIN,0x1a2,,,,,,,,,,,,,,,,,
+KEY_ZOOMOUT,0x1a3,,,,,,,,,,,,,,,,,
+KEY_ZOOMRESET,0x1a4,,,,,,,,,,,,,,,,,
+KEY_WORDPROCESSOR,0x1a5,,,,,,,,,,,,,,,,,
+KEY_EDITOR,0x1a6,,,,,,,,,,,,,,,,,
+KEY_SPREADSHEET,0x1a7,,,,,,,,,,,,,,,,,
+KEY_GRAPHICSEDITOR,0x1a8,,,,,,,,,,,,,,,,,
+KEY_PRESENTATION,0x1a9,,,,,,,,,,,,,,,,,
+KEY_DATABASE,0x1aa,,,,,,,,,,,,,,,,,
+KEY_NEWS,0x1ab,,,,,,,,,,,,,,,,,
+KEY_VOICEMAIL,0x1ac,,,,,,,,,,,,,,,,,
+KEY_ADDRESSBOOK,0x1ad,,,,,,,,,,,,,,,,,
+KEY_MESSENGER,0x1ae,,,,,,,,,,,,,,,,,
+KEY_DISPLAYTOGGLE,0x1af,,,,,,,,,,,,,,,,,
+KEY_SPELLCHECK,0x1b0,,,,,,,,,,,,,,,,,
+KEY_LOGOFF,0x1b1,,,,,,,,,,,,,,,,,
+KEY_DOLLAR,0x1b2,,,,,,,,,,,,,,,,,
+KEY_EURO,0x1b3,,,,,,,,,,,,,,,,,
+KEY_FRAMEBACK,0x1b4,,,,,,,,,,,,,,,,,
+KEY_FRAMEFORWARD,0x1b5,,,,,,,,,,,,,,,,,
+KEY_CONTEXT_MENU,0x1b6,,,,,,,,,,,,,,,,,
+KEY_MEDIA_REPEAT,0x1b7,,,,,,,,,,,,,,,,,
+KEY_DEL_EOL,0x1c0,,,,,,,,,,,,,,,,,
+KEY_DEL_EOS,0x1c1,,,,,,,,,,,,,,,,,
+KEY_INS_LINE,0x1c2,,,,,,,,,,,,,,,,,
+KEY_DEL_LINE,0x1c3,,,,,,,,,,,,,,,,,
+KEY_FN,0x1d0,Function,0x3f,,,,,,,,,,,Fn,,,,
+KEY_FN_ESC,0x1d1,,,,,,,,,,,,,,,,,
+KEY_FN_F1,0x1d2,,,,,,,,,,,,,,,,,
+KEY_FN_F2,0x1d3,,,,,,,,,,,,,,,,,
+KEY_FN_F3,0x1d4,,,,,,,,,,,,,,,,,
+KEY_FN_F4,0x1d5,,,,,,,,,,,,,,,,,
+KEY_FN_F5,0x1d6,,,,,,,,,,,,,,,,,
+KEY_FN_F6,0x1d7,,,,,,,,,,,,,,,,,
+KEY_FN_F7,0x1d8,,,,,,,,,,,,,,,,,
+KEY_FN_F8,0x1d9,,,,,,,,,,,,,,,,,
+KEY_FN_F9,0x1da,,,,,,,,,,,,,,,,,
+KEY_FN_F10,0x1db,,,,,,,,,,,,,,,,,
+KEY_FN_F11,0x1dc,,,,,,,,,,,,,,,,,
+KEY_FN_F12,0x1dd,,,,,,,,,,,,,,,,,
+KEY_FN_1,0x1de,,,,,,,,,,,,,,,,,
+KEY_FN_2,0x1df,,,,,,,,,,,,,,,,,
+KEY_FN_D,0x1e0,,,,,,,,,,,,,,,,,
+KEY_FN_E,0x1e1,,,,,,,,,,,,,,,,,
+KEY_FN_F,0x1e2,,,,,,,,,,,,,,,,,
+KEY_FN_S,0x1e3,,,,,,,,,,,,,,,,,
+KEY_FN_B,0x1e4,,,,,,,,,,,,,,,,,
+KEY_BRL_DOT1,0x1f1,,,,,,,,,,,,,,,,,
+KEY_BRL_DOT2,0x1f2,,,,,,,,,,,,,,,,,
+KEY_BRL_DOT3,0x1f3,,,,,,,,,,,,,,,,,
+KEY_BRL_DOT4,0x1f4,,,,,,,,,,,,,,,,,
+KEY_BRL_DOT5,0x1f5,,,,,,,,,,,,,,,,,
+KEY_BRL_DOT6,0x1f6,,,,,,,,,,,,,,,,,
+KEY_BRL_DOT7,0x1f7,,,,,,,,,,,,,,,,,
+KEY_BRL_DOT8,0x1f8,,,,,,,,,,,,,,,,,
+KEY_BRL_DOT9,0x1f9,,,,,,,,,,,,,,,,,
+KEY_BRL_DOT10,0x1fa,,,,,,,,,,,,,,,,,
+KEY_NUMERIC_0,0x200,,,,,,,,,,,,,,,,,
+KEY_NUMERIC_1,0x201,,,,,,,,,,,,,,,,,
+KEY_NUMERIC_2,0x202,,,,,,,,,,,,,,,,,
+KEY_NUMERIC_3,0x203,,,,,,,,,,,,,,,,,
+KEY_NUMERIC_4,0x204,,,,,,,,,,,,,,,,,
+KEY_NUMERIC_5,0x205,,,,,,,,,,,,,,,,,
+KEY_NUMERIC_6,0x206,,,,,,,,,,,,,,,,,
+KEY_NUMERIC_7,0x207,,,,,,,,,,,,,,,,,
+KEY_NUMERIC_8,0x208,,,,,,,,,,,,,,,,,
+KEY_NUMERIC_9,0x209,,,,,,,,,,,,,,,,,
+KEY_NUMERIC_STAR,0x20a,,,,,,,,,,,,,NumpadStar,,,,
+KEY_NUMERIC_POUND,0x20b,,,,,,,,,,,,,NumpadHash,,,,
+KEY_RFKILL,0x20c,,,,,,,,,,,,,,,,,
diff --git a/keycodemapdb/tests/.gitignore b/keycodemapdb/tests/.gitignore
new file mode 100644 (file)
index 0000000..0562305
--- /dev/null
@@ -0,0 +1,11 @@
+osx2win32.*
+osx2win32_name.*
+osx2xkb.*
+osx2xkb_name.*
+html2win32.*
+html2win32_name.*
+osx.*
+osx_name.*
+stdc
+stdc++
+node_modules/
diff --git a/keycodemapdb/tests/Makefile b/keycodemapdb/tests/Makefile
new file mode 100644 (file)
index 0000000..b25c77c
--- /dev/null
@@ -0,0 +1,118 @@
+TESTS := stdc stdc++ python2 python3 javascript
+
+check: $(TESTS)
+       @set -e; for fn in $(TESTS); do \
+               ./$$fn; \
+               echo $$fn: OK; \
+       done
+       @echo Done.
+
+GEN := ../tools/keymap-gen
+DATA := ../data/keymaps.csv
+SOURCES := $(GEN) $(DATA)
+
+.DELETE_ON_ERROR:
+
+stdc: stdc.c osx2win32.h osx2win32_name.h \
+             osx2xkb.h osx2xkb_name.h \
+             html2win32.h html2win32_name.h \
+             osx.h osx_name.h
+       $(CC) -Wall -o $@ $^
+osx2win32.h: $(SOURCES)
+       $(GEN) --lang stdc code-map $(DATA) osx win32 > $@
+osx2win32_name.h: $(SOURCES)
+       $(GEN) --lang stdc name-map $(DATA) osx win32 > $@
+osx2xkb.h: $(SOURCES)
+       $(GEN) --lang stdc code-map $(DATA) osx xkb > $@
+osx2xkb_name.h: $(SOURCES)
+       $(GEN) --lang stdc name-map $(DATA) osx xkb > $@
+html2win32.h: $(SOURCES)
+       $(GEN) --lang stdc code-map $(DATA) html win32 > $@
+html2win32_name.h: $(SOURCES)
+       $(GEN) --lang stdc name-map $(DATA) html win32 > $@
+osx.h: $(SOURCES)
+       $(GEN) --lang stdc code-table $(DATA) osx > $@
+osx_name.h: $(SOURCES)
+       $(GEN) --lang stdc name-table $(DATA) osx > $@
+
+stdc++: stdc++.cc osx2win32.hh osx2win32_name.hh \
+             osx2xkb.hh osx2xkb_name.hh \
+             html2win32.hh html2win32_name.hh \
+             osx.hh osx_name.hh
+       $(CXX) -Wall -std=c++11 -o $@ $^
+osx2win32.hh: $(SOURCES)
+       $(GEN) --lang stdc++ code-map $(DATA) osx win32 > $@
+osx2win32_name.hh: $(SOURCES)
+       $(GEN) --lang stdc++ name-map $(DATA) osx win32 > $@
+osx2xkb.hh: $(SOURCES)
+       $(GEN) --lang stdc++ code-map $(DATA) osx xkb > $@
+osx2xkb_name.hh: $(SOURCES)
+       $(GEN) --lang stdc++ name-map $(DATA) osx xkb > $@
+html2win32.hh: $(SOURCES)
+       $(GEN) --lang stdc++ code-map $(DATA) html win32 > $@
+html2win32_name.hh: $(SOURCES)
+       $(GEN) --lang stdc++ name-map $(DATA) html win32 > $@
+osx.hh: $(SOURCES)
+       $(GEN) --lang stdc++ code-table $(DATA) osx > $@
+osx_name.hh: $(SOURCES)
+       $(GEN) --lang stdc++ name-table $(DATA) osx > $@
+
+python2: osx2win32.py osx2win32_name.py \
+         osx2xkb.py osx2xkb_name.py \
+         html2win32.py html2win32_name.py \
+         osx.py osx_name.py
+osx2win32.py: $(SOURCES)
+       $(GEN) --lang python2 code-map $(DATA) osx win32 > $@
+osx2win32_name.py: $(SOURCES)
+       $(GEN) --lang python2 name-map $(DATA) osx win32 > $@
+osx2xkb.py: $(SOURCES)
+       $(GEN) --lang python2 code-map $(DATA) osx xkb > $@
+osx2xkb_name.py: $(SOURCES)
+       $(GEN) --lang python2 name-map $(DATA) osx xkb > $@
+html2win32.py: $(SOURCES)
+       $(GEN) --lang python2 code-map $(DATA) html win32 > $@
+html2win32_name.py: $(SOURCES)
+       $(GEN) --lang python2 name-map $(DATA) html win32 > $@
+osx.py: $(SOURCES)
+       $(GEN) --lang python2 code-table $(DATA) osx > $@
+osx_name.py: $(SOURCES)
+       $(GEN) --lang python2 name-table $(DATA) osx > $@
+
+javascript: node_modules/babel-core \
+            node_modules/babel-plugin-transform-es2015-modules-commonjs \
+            osx2win32.js osx2win32_name.js \
+            osx2xkb.js osx2xkb_name.js \
+            html2win32.js html2win32_name.js \
+            osx.js osx_name.js
+node_modules/babel-core:
+       npm install babel-core
+node_modules/babel-plugin-transform-es2015-modules-commonjs:
+       npm install babel-plugin-transform-es2015-modules-commonjs
+osx2win32.js: $(SOURCES)
+       $(GEN) --lang js code-map $(DATA) osx win32 > $@
+osx2win32_name.js: $(SOURCES)
+       $(GEN) --lang js name-map $(DATA) osx win32 > $@
+osx2xkb.js: $(SOURCES)
+       $(GEN) --lang js code-map $(DATA) osx xkb > $@
+osx2xkb_name.js: $(SOURCES)
+       $(GEN) --lang js name-map $(DATA) osx xkb > $@
+html2win32.js: $(SOURCES)
+       $(GEN) --lang js code-map $(DATA) html win32 > $@
+html2win32_name.js: $(SOURCES)
+       $(GEN) --lang js name-map $(DATA) html win32 > $@
+osx.js: $(SOURCES)
+       $(GEN) --lang js code-table $(DATA) osx > $@
+osx_name.js: $(SOURCES)
+       $(GEN) --lang js name-table $(DATA) osx > $@
+
+clean:
+       rm -rf node_modules
+       rm -f osx2win32.*
+       rm -f osx2win32_name.*
+       rm -f osx2xkb.*
+       rm -f osx2xkb_name.*
+       rm -f html2win32.*
+       rm -f html2win32_name.*
+       rm -f osx.*
+       rm -f osx_name.*
+       rm -f stdc stdc++
diff --git a/keycodemapdb/tests/javascript b/keycodemapdb/tests/javascript
new file mode 100755 (executable)
index 0000000..5179db2
--- /dev/null
@@ -0,0 +1,53 @@
+#!/usr/bin/env node
+/*
+ * Keycode Map Generator JavaScript Tests
+ *
+ * Copyright 2017 Pierre Ossman for Cendio AB
+ *
+ * This file is dual license under the terms of the GPLv2 or later
+ * and 3-clause BSD licenses.
+ */
+
+"use strict";
+
+var assert = require('assert');
+var babel = require('babel-core');
+var fs = require('fs');
+
+function include(fn) {
+  var options = {
+    plugins: ["transform-es2015-modules-commonjs"]
+  };
+
+  var code = babel.transformFileSync(fn, options).code;
+  fs.writeFileSync("." + fn + "_nodejs.js", code);
+  var imp = require("./." + fn + "_nodejs.js");
+  fs.unlinkSync("./." + fn + "_nodejs.js");
+
+  return imp
+}
+
+var code_map_osx_to_win32 = include("osx2win32.js").default;
+var name_map_osx_to_win32 = include("osx2win32_name.js").default;
+
+var code_map_osx_to_xkb = include("osx2xkb.js").default;
+var name_map_osx_to_xkb = include("osx2xkb_name.js").default;
+
+var code_map_html_to_win32 = include("html2win32.js").default;
+var name_map_html_to_win32 = include("html2win32_name.js").default;
+
+var code_table_osx = include("osx.js").default;
+var name_table_osx = include("osx_name.js").default;
+
+assert.equal(code_map_osx_to_win32[0x1d], 0x30);
+assert.equal(name_map_osx_to_win32[0x1d], "VK_0");
+
+assert.equal(code_map_osx_to_xkb[0x1d], "AE10");
+assert.equal(name_map_osx_to_xkb[0x1d], "AE10");
+
+assert.equal(code_map_html_to_win32["ControlLeft"], 0x11);
+assert.equal(name_map_html_to_win32["ControlLeft"], "VK_CONTROL");
+
+assert.equal(code_table_osx[0x1d], 0x3b);
+assert.equal(name_table_osx[0x1d], "Control");
+
diff --git a/keycodemapdb/tests/python2 b/keycodemapdb/tests/python2
new file mode 100755 (executable)
index 0000000..28a5b03
--- /dev/null
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+python ./test.py
diff --git a/keycodemapdb/tests/python3 b/keycodemapdb/tests/python3
new file mode 100755 (executable)
index 0000000..ded1f68
--- /dev/null
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+python3 ./test.py
diff --git a/keycodemapdb/tests/stdc++.cc b/keycodemapdb/tests/stdc++.cc
new file mode 100644 (file)
index 0000000..5e3e8f5
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Keycode Map Generator C++ Tests
+ *
+ * Copyright 2017 Pierre Ossman for Cendio AB
+ *
+ * This file is dual license under the terms of the GPLv2 or later
+ * and 3-clause BSD licenses.
+ */
+
+#include <assert.h>
+#include <string.h>
+
+#include "osx2win32.hh"
+#include "osx2win32_name.hh"
+
+#include "osx2xkb.hh"
+#include "osx2xkb_name.hh"
+
+#include "html2win32.hh"
+#include "html2win32_name.hh"
+
+#include "osx.hh"
+#include "osx_name.hh"
+
+int main(int argc, char** argv)
+{
+       assert(code_map_osx_to_win32[0x1d] == 0x30);
+       assert(strcmp(name_map_osx_to_win32[0x1d], "VK_0") == 0);
+
+       assert(strcmp(code_map_osx_to_xkb[0x1d], "AE10") == 0);
+       assert(strcmp(name_map_osx_to_xkb[0x1d], "AE10") == 0);
+
+       assert(code_map_html_to_win32.at("ControlLeft") == 0x11);
+       assert(strcmp(name_map_html_to_win32.at("ControlLeft"), "VK_CONTROL") == 0);
+
+       assert(code_table_osx[0x1d] == 0x3b);
+       assert(strcmp(name_table_osx[0x1d], "Control") == 0);
+
+       return 0;
+}
diff --git a/keycodemapdb/tests/stdc.c b/keycodemapdb/tests/stdc.c
new file mode 100644 (file)
index 0000000..e4946fa
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Keycode Map Generator C Tests
+ *
+ * Copyright 2017 Pierre Ossman for Cendio AB
+ *
+ * This file is dual license under the terms of the GPLv2 or later
+ * and 3-clause BSD licenses.
+ */
+
+#include <assert.h>
+#include <string.h>
+
+#include "osx2win32.h"
+#include "osx2win32_name.h"
+
+#include "osx2xkb.h"
+#include "osx2xkb_name.h"
+
+#include "html2win32.h"
+#include "html2win32_name.h"
+
+#include "osx.h"
+#include "osx_name.h"
+
+#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
+
+int main(int argc, char** argv)
+{
+       unsigned i;
+
+       assert(code_map_osx_to_win32_len == ARRAY_SIZE(code_map_osx_to_win32));
+       assert(code_map_osx_to_win32[0x1d] == 0x30);
+       assert(name_map_osx_to_win32_len == ARRAY_SIZE(name_map_osx_to_win32));
+       assert(strcmp(name_map_osx_to_win32[0x1d], "VK_0") == 0);
+
+       assert(code_map_osx_to_xkb_len == ARRAY_SIZE(code_map_osx_to_xkb));
+       assert(strcmp(code_map_osx_to_xkb[0x1d], "AE10") == 0);
+       assert(name_map_osx_to_xkb_len == ARRAY_SIZE(name_map_osx_to_xkb));
+       assert(strcmp(name_map_osx_to_xkb[0x1d], "AE10") == 0);
+
+       assert(code_map_html_to_win32_len == ARRAY_SIZE(code_map_html_to_win32));
+       for (i = 0;i < code_map_html_to_win32_len;i++) {
+               if (strcmp(code_map_html_to_win32[i].from, "ControlLeft") == 0) {
+                       assert(code_map_html_to_win32[i].to == 0x11);
+                       break;
+               }
+       }
+       assert(i != code_map_html_to_win32_len);
+       assert(name_map_html_to_win32_len == ARRAY_SIZE(name_map_html_to_win32));
+       for (i = 0;i < name_map_html_to_win32_len;i++) {
+               if (strcmp(name_map_html_to_win32[i].from, "ControlLeft") == 0) {
+                       assert(strcmp(name_map_html_to_win32[i].to, "VK_CONTROL") == 0);
+                       break;
+               }
+       }
+       assert(i != name_map_html_to_win32_len);
+
+       assert(code_table_osx_len == ARRAY_SIZE(code_table_osx));
+       assert(code_table_osx[0x1d] == 0x3b);
+       assert(name_table_osx_len == ARRAY_SIZE(name_table_osx));
+       assert(strcmp(name_table_osx[0x1d], "Control") == 0);
+
+       return 0;
+}
diff --git a/keycodemapdb/tests/test.py b/keycodemapdb/tests/test.py
new file mode 100644 (file)
index 0000000..f265145
--- /dev/null
@@ -0,0 +1,30 @@
+# Keycode Map Generator Python Tests
+#
+# Copyright 2017 Pierre Ossman for Cendio AB
+#
+# This file is dual license under the terms of the GPLv2 or later
+# and 3-clause BSD licenses.
+
+import osx2win32
+import osx2win32_name
+
+import osx2xkb
+import osx2xkb_name
+
+import html2win32
+import html2win32_name
+
+import osx
+import osx_name
+
+assert osx2win32.code_map_osx_to_win32[0x1d] == 0x30
+assert osx2win32_name.name_map_osx_to_win32[0x1d] == "VK_0"
+
+assert osx2xkb.code_map_osx_to_xkb[0x1d] == "AE10"
+assert osx2xkb_name.name_map_osx_to_xkb[0x1d] == "AE10"
+
+assert html2win32.code_map_html_to_win32["ControlLeft"] == 0x11
+assert html2win32_name.name_map_html_to_win32["ControlLeft"] == "VK_CONTROL"
+
+assert osx.code_table_osx[0x1d] == 0x3b;
+assert osx_name.name_table_osx[0x1d] == "Control";
diff --git a/keycodemapdb/thirdparty/LICENSE-argparse.txt b/keycodemapdb/thirdparty/LICENSE-argparse.txt
new file mode 100644 (file)
index 0000000..640bc78
--- /dev/null
@@ -0,0 +1,20 @@
+argparse is (c) 2006-2009 Steven J. Bethard <steven.bethard@gmail.com>.
+
+The argparse module was contributed to Python as of Python 2.7 and thus
+was licensed under the Python license. Same license applies to all files in
+the argparse package project.
+
+For details about the Python License, please see doc/Python-License.txt.
+
+History
+-------
+
+Before (and including) argparse 1.1, the argparse package was licensed under
+Apache License v2.0.
+
+After argparse 1.1, all project files from the argparse project were deleted
+due to license compatibility issues between Apache License 2.0 and GNU GPL v2.
+
+The project repository then had a clean start with some files taken from
+Python 2.7.1, so definitely all files are under Python License now.
+
diff --git a/keycodemapdb/thirdparty/__init__.py b/keycodemapdb/thirdparty/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/keycodemapdb/thirdparty/argparse.py b/keycodemapdb/thirdparty/argparse.py
new file mode 100644 (file)
index 0000000..70a77cc
--- /dev/null
@@ -0,0 +1,2392 @@
+# Author: Steven J. Bethard <steven.bethard@gmail.com>.
+# Maintainer: Thomas Waldmann <tw@waldmann-edv.de>
+
+"""Command-line parsing library
+
+This module is an optparse-inspired command-line parsing library that:
+
+    - handles both optional and positional arguments
+    - produces highly informative usage messages
+    - supports parsers that dispatch to sub-parsers
+
+The following is a simple usage example that sums integers from the
+command-line and writes the result to a file::
+
+    parser = argparse.ArgumentParser(
+        description='sum the integers at the command line')
+    parser.add_argument(
+        'integers', metavar='int', nargs='+', type=int,
+        help='an integer to be summed')
+    parser.add_argument(
+        '--log', default=sys.stdout, type=argparse.FileType('w'),
+        help='the file where the sum should be written')
+    args = parser.parse_args()
+    args.log.write('%s' % sum(args.integers))
+    args.log.close()
+
+The module contains the following public classes:
+
+    - ArgumentParser -- The main entry point for command-line parsing. As the
+        example above shows, the add_argument() method is used to populate
+        the parser with actions for optional and positional arguments. Then
+        the parse_args() method is invoked to convert the args at the
+        command-line into an object with attributes.
+
+    - ArgumentError -- The exception raised by ArgumentParser objects when
+        there are errors with the parser's actions. Errors raised while
+        parsing the command-line are caught by ArgumentParser and emitted
+        as command-line messages.
+
+    - FileType -- A factory for defining types of files to be created. As the
+        example above shows, instances of FileType are typically passed as
+        the type= argument of add_argument() calls.
+
+    - Action -- The base class for parser actions. Typically actions are
+        selected by passing strings like 'store_true' or 'append_const' to
+        the action= argument of add_argument(). However, for greater
+        customization of ArgumentParser actions, subclasses of Action may
+        be defined and passed as the action= argument.
+
+    - HelpFormatter, RawDescriptionHelpFormatter, RawTextHelpFormatter,
+        ArgumentDefaultsHelpFormatter -- Formatter classes which
+        may be passed as the formatter_class= argument to the
+        ArgumentParser constructor. HelpFormatter is the default,
+        RawDescriptionHelpFormatter and RawTextHelpFormatter tell the parser
+        not to change the formatting for help text, and
+        ArgumentDefaultsHelpFormatter adds information about argument defaults
+        to the help.
+
+All other classes in this module are considered implementation details.
+(Also note that HelpFormatter and RawDescriptionHelpFormatter are only
+considered public as object names -- the API of the formatter objects is
+still considered an implementation detail.)
+"""
+
+__version__ = '1.4.0'  # we use our own version number independant of the
+                       # one in stdlib and we release this on pypi.
+
+__external_lib__ = True  # to make sure the tests really test THIS lib,
+                         # not the builtin one in Python stdlib
+
+__all__ = [
+    'ArgumentParser',
+    'ArgumentError',
+    'ArgumentTypeError',
+    'FileType',
+    'HelpFormatter',
+    'ArgumentDefaultsHelpFormatter',
+    'RawDescriptionHelpFormatter',
+    'RawTextHelpFormatter',
+    'Namespace',
+    'Action',
+    'ONE_OR_MORE',
+    'OPTIONAL',
+    'PARSER',
+    'REMAINDER',
+    'SUPPRESS',
+    'ZERO_OR_MORE',
+]
+
+
+import copy as _copy
+import os as _os
+import re as _re
+import sys as _sys
+import textwrap as _textwrap
+
+from gettext import gettext as _
+
+try:
+    set
+except NameError:
+    # for python < 2.4 compatibility (sets module is there since 2.3):
+    from sets import Set as set
+
+try:
+    basestring
+except NameError:
+    basestring = str
+
+try:
+    sorted
+except NameError:
+    # for python < 2.4 compatibility:
+    def sorted(iterable, reverse=False):
+        result = list(iterable)
+        result.sort()
+        if reverse:
+            result.reverse()
+        return result
+
+
+def _callable(obj):
+    return hasattr(obj, '__call__') or hasattr(obj, '__bases__')
+
+
+SUPPRESS = '==SUPPRESS=='
+
+OPTIONAL = '?'
+ZERO_OR_MORE = '*'
+ONE_OR_MORE = '+'
+PARSER = 'A...'
+REMAINDER = '...'
+_UNRECOGNIZED_ARGS_ATTR = '_unrecognized_args'
+
+# =============================
+# Utility functions and classes
+# =============================
+
+class _AttributeHolder(object):
+    """Abstract base class that provides __repr__.
+
+    The __repr__ method returns a string in the format::
+        ClassName(attr=name, attr=name, ...)
+    The attributes are determined either by a class-level attribute,
+    '_kwarg_names', or by inspecting the instance __dict__.
+    """
+
+    def __repr__(self):
+        type_name = type(self).__name__
+        arg_strings = []
+        for arg in self._get_args():
+            arg_strings.append(repr(arg))
+        for name, value in self._get_kwargs():
+            arg_strings.append('%s=%r' % (name, value))
+        return '%s(%s)' % (type_name, ', '.join(arg_strings))
+
+    def _get_kwargs(self):
+        return sorted(self.__dict__.items())
+
+    def _get_args(self):
+        return []
+
+
+def _ensure_value(namespace, name, value):
+    if getattr(namespace, name, None) is None:
+        setattr(namespace, name, value)
+    return getattr(namespace, name)
+
+
+# ===============
+# Formatting Help
+# ===============
+
+class HelpFormatter(object):
+    """Formatter for generating usage messages and argument help strings.
+
+    Only the name of this class is considered a public API. All the methods
+    provided by the class are considered an implementation detail.
+    """
+
+    def __init__(self,
+                 prog,
+                 indent_increment=2,
+                 max_help_position=24,
+                 width=None):
+
+        # default setting for width
+        if width is None:
+            try:
+                width = int(_os.environ['COLUMNS'])
+            except (KeyError, ValueError):
+                width = 80
+            width -= 2
+
+        self._prog = prog
+        self._indent_increment = indent_increment
+        self._max_help_position = max_help_position
+        self._width = width
+
+        self._current_indent = 0
+        self._level = 0
+        self._action_max_length = 0
+
+        self._root_section = self._Section(self, None)
+        self._current_section = self._root_section
+
+        self._whitespace_matcher = _re.compile(r'\s+')
+        self._long_break_matcher = _re.compile(r'\n\n\n+')
+
+    # ===============================
+    # Section and indentation methods
+    # ===============================
+    def _indent(self):
+        self._current_indent += self._indent_increment
+        self._level += 1
+
+    def _dedent(self):
+        self._current_indent -= self._indent_increment
+        assert self._current_indent >= 0, 'Indent decreased below 0.'
+        self._level -= 1
+
+    class _Section(object):
+
+        def __init__(self, formatter, parent, heading=None):
+            self.formatter = formatter
+            self.parent = parent
+            self.heading = heading
+            self.items = []
+
+        def format_help(self):
+            # format the indented section
+            if self.parent is not None:
+                self.formatter._indent()
+            join = self.formatter._join_parts
+            for func, args in self.items:
+                func(*args)
+            item_help = join([func(*args) for func, args in self.items])
+            if self.parent is not None:
+                self.formatter._dedent()
+
+            # return nothing if the section was empty
+            if not item_help:
+                return ''
+
+            # add the heading if the section was non-empty
+            if self.heading is not SUPPRESS and self.heading is not None:
+                current_indent = self.formatter._current_indent
+                heading = '%*s%s:\n' % (current_indent, '', self.heading)
+            else:
+                heading = ''
+
+            # join the section-initial newline, the heading and the help
+            return join(['\n', heading, item_help, '\n'])
+
+    def _add_item(self, func, args):
+        self._current_section.items.append((func, args))
+
+    # ========================
+    # Message building methods
+    # ========================
+    def start_section(self, heading):
+        self._indent()
+        section = self._Section(self, self._current_section, heading)
+        self._add_item(section.format_help, [])
+        self._current_section = section
+
+    def end_section(self):
+        self._current_section = self._current_section.parent
+        self._dedent()
+
+    def add_text(self, text):
+        if text is not SUPPRESS and text is not None:
+            self._add_item(self._format_text, [text])
+
+    def add_usage(self, usage, actions, groups, prefix=None):
+        if usage is not SUPPRESS:
+            args = usage, actions, groups, prefix
+            self._add_item(self._format_usage, args)
+
+    def add_argument(self, action):
+        if action.help is not SUPPRESS:
+
+            # find all invocations
+            get_invocation = self._format_action_invocation
+            invocations = [get_invocation(action)]
+            for subaction in self._iter_indented_subactions(action):
+                invocations.append(get_invocation(subaction))
+
+            # update the maximum item length
+            invocation_length = max([len(s) for s in invocations])
+            action_length = invocation_length + self._current_indent
+            self._action_max_length = max(self._action_max_length,
+                                          action_length)
+
+            # add the item to the list
+            self._add_item(self._format_action, [action])
+
+    def add_arguments(self, actions):
+        for action in actions:
+            self.add_argument(action)
+
+    # =======================
+    # Help-formatting methods
+    # =======================
+    def format_help(self):
+        help = self._root_section.format_help()
+        if help:
+            help = self._long_break_matcher.sub('\n\n', help)
+            help = help.strip('\n') + '\n'
+        return help
+
+    def _join_parts(self, part_strings):
+        return ''.join([part
+                        for part in part_strings
+                        if part and part is not SUPPRESS])
+
+    def _format_usage(self, usage, actions, groups, prefix):
+        if prefix is None:
+            prefix = _('usage: ')
+
+        # if usage is specified, use that
+        if usage is not None:
+            usage = usage % dict(prog=self._prog)
+
+        # if no optionals or positionals are available, usage is just prog
+        elif usage is None and not actions:
+            usage = '%(prog)s' % dict(prog=self._prog)
+
+        # if optionals and positionals are available, calculate usage
+        elif usage is None:
+            prog = '%(prog)s' % dict(prog=self._prog)
+
+            # split optionals from positionals
+            optionals = []
+            positionals = []
+            for action in actions:
+                if action.option_strings:
+                    optionals.append(action)
+                else:
+                    positionals.append(action)
+
+            # build full usage string
+            format = self._format_actions_usage
+            action_usage = format(optionals + positionals, groups)
+            usage = ' '.join([s for s in [prog, action_usage] if s])
+
+            # wrap the usage parts if it's too long
+            text_width = self._width - self._current_indent
+            if len(prefix) + len(usage) > text_width:
+
+                # break usage into wrappable parts
+                part_regexp = r'\(.*?\)+|\[.*?\]+|\S+'
+                opt_usage = format(optionals, groups)
+                pos_usage = format(positionals, groups)
+                opt_parts = _re.findall(part_regexp, opt_usage)
+                pos_parts = _re.findall(part_regexp, pos_usage)
+                assert ' '.join(opt_parts) == opt_usage
+                assert ' '.join(pos_parts) == pos_usage
+
+                # helper for wrapping lines
+                def get_lines(parts, indent, prefix=None):
+                    lines = []
+                    line = []
+                    if prefix is not None:
+                        line_len = len(prefix) - 1
+                    else:
+                        line_len = len(indent) - 1
+                    for part in parts:
+                        if line_len + 1 + len(part) > text_width:
+                            lines.append(indent + ' '.join(line))
+                            line = []
+                            line_len = len(indent) - 1
+                        line.append(part)
+                        line_len += len(part) + 1
+                    if line:
+                        lines.append(indent + ' '.join(line))
+                    if prefix is not None:
+                        lines[0] = lines[0][len(indent):]
+                    return lines
+
+                # if prog is short, follow it with optionals or positionals
+                if len(prefix) + len(prog) <= 0.75 * text_width:
+                    indent = ' ' * (len(prefix) + len(prog) + 1)
+                    if opt_parts:
+                        lines = get_lines([prog] + opt_parts, indent, prefix)
+                        lines.extend(get_lines(pos_parts, indent))
+                    elif pos_parts:
+                        lines = get_lines([prog] + pos_parts, indent, prefix)
+                    else:
+                        lines = [prog]
+
+                # if prog is long, put it on its own line
+                else:
+                    indent = ' ' * len(prefix)
+                    parts = opt_parts + pos_parts
+                    lines = get_lines(parts, indent)
+                    if len(lines) > 1:
+                        lines = []
+                        lines.extend(get_lines(opt_parts, indent))
+                        lines.extend(get_lines(pos_parts, indent))
+                    lines = [prog] + lines
+
+                # join lines into usage
+                usage = '\n'.join(lines)
+
+        # prefix with 'usage:'
+        return '%s%s\n\n' % (prefix, usage)
+
+    def _format_actions_usage(self, actions, groups):
+        # find group indices and identify actions in groups
+        group_actions = set()
+        inserts = {}
+        for group in groups:
+            try:
+                start = actions.index(group._group_actions[0])
+            except ValueError:
+                continue
+            else:
+                end = start + len(group._group_actions)
+                if actions[start:end] == group._group_actions:
+                    for action in group._group_actions:
+                        group_actions.add(action)
+                    if not group.required:
+                        if start in inserts:
+                            inserts[start] += ' ['
+                        else:
+                            inserts[start] = '['
+                        inserts[end] = ']'
+                    else:
+                        if start in inserts:
+                            inserts[start] += ' ('
+                        else:
+                            inserts[start] = '('
+                        inserts[end] = ')'
+                    for i in range(start + 1, end):
+                        inserts[i] = '|'
+
+        # collect all actions format strings
+        parts = []
+        for i, action in enumerate(actions):
+
+            # suppressed arguments are marked with None
+            # remove | separators for suppressed arguments
+            if action.help is SUPPRESS:
+                parts.append(None)
+                if inserts.get(i) == '|':
+                    inserts.pop(i)
+                elif inserts.get(i + 1) == '|':
+                    inserts.pop(i + 1)
+
+            # produce all arg strings
+            elif not action.option_strings:
+                part = self._format_args(action, action.dest)
+
+                # if it's in a group, strip the outer []
+                if action in group_actions:
+                    if part[0] == '[' and part[-1] == ']':
+                        part = part[1:-1]
+
+                # add the action string to the list
+                parts.append(part)
+
+            # produce the first way to invoke the option in brackets
+            else:
+                option_string = action.option_strings[0]
+
+                # if the Optional doesn't take a value, format is:
+                #    -s or --long
+                if action.nargs == 0:
+                    part = '%s' % option_string
+
+                # if the Optional takes a value, format is:
+                #    -s ARGS or --long ARGS
+                else:
+                    default = action.dest.upper()
+                    args_string = self._format_args(action, default)
+                    part = '%s %s' % (option_string, args_string)
+
+                # make it look optional if it's not required or in a group
+                if not action.required and action not in group_actions:
+                    part = '[%s]' % part
+
+                # add the action string to the list
+                parts.append(part)
+
+        # insert things at the necessary indices
+        for i in sorted(inserts, reverse=True):
+            parts[i:i] = [inserts[i]]
+
+        # join all the action items with spaces
+        text = ' '.join([item for item in parts if item is not None])
+
+        # clean up separators for mutually exclusive groups
+        open = r'[\[(]'
+        close = r'[\])]'
+        text = _re.sub(r'(%s) ' % open, r'\1', text)
+        text = _re.sub(r' (%s)' % close, r'\1', text)
+        text = _re.sub(r'%s *%s' % (open, close), r'', text)
+        text = _re.sub(r'\(([^|]*)\)', r'\1', text)
+        text = text.strip()
+
+        # return the text
+        return text
+
+    def _format_text(self, text):
+        if '%(prog)' in text:
+            text = text % dict(prog=self._prog)
+        text_width = self._width - self._current_indent
+        indent = ' ' * self._current_indent
+        return self._fill_text(text, text_width, indent) + '\n\n'
+
+    def _format_action(self, action):
+        # determine the required width and the entry label
+        help_position = min(self._action_max_length + 2,
+                            self._max_help_position)
+        help_width = self._width - help_position
+        action_width = help_position - self._current_indent - 2
+        action_header = self._format_action_invocation(action)
+
+        # ho nelp; start on same line and add a final newline
+        if not action.help:
+            tup = self._current_indent, '', action_header
+            action_header = '%*s%s\n' % tup
+
+        # short action name; start on the same line and pad two spaces
+        elif len(action_header) <= action_width:
+            tup = self._current_indent, '', action_width, action_header
+            action_header = '%*s%-*s  ' % tup
+            indent_first = 0
+
+        # long action name; start on the next line
+        else:
+            tup = self._current_indent, '', action_header
+            action_header = '%*s%s\n' % tup
+            indent_first = help_position
+
+        # collect the pieces of the action help
+        parts = [action_header]
+
+        # if there was help for the action, add lines of help text
+        if action.help:
+            help_text = self._expand_help(action)
+            help_lines = self._split_lines(help_text, help_width)
+            parts.append('%*s%s\n' % (indent_first, '', help_lines[0]))
+            for line in help_lines[1:]:
+                parts.append('%*s%s\n' % (help_position, '', line))
+
+        # or add a newline if the description doesn't end with one
+        elif not action_header.endswith('\n'):
+            parts.append('\n')
+
+        # if there are any sub-actions, add their help as well
+        for subaction in self._iter_indented_subactions(action):
+            parts.append(self._format_action(subaction))
+
+        # return a single string
+        return self._join_parts(parts)
+
+    def _format_action_invocation(self, action):
+        if not action.option_strings:
+            metavar, = self._metavar_formatter(action, action.dest)(1)
+            return metavar
+
+        else:
+            parts = []
+
+            # if the Optional doesn't take a value, format is:
+            #    -s, --long
+            if action.nargs == 0:
+                parts.extend(action.option_strings)
+
+            # if the Optional takes a value, format is:
+            #    -s ARGS, --long ARGS
+            else:
+                default = action.dest.upper()
+                args_string = self._format_args(action, default)
+                for option_string in action.option_strings:
+                    parts.append('%s %s' % (option_string, args_string))
+
+            return ', '.join(parts)
+
+    def _metavar_formatter(self, action, default_metavar):
+        if action.metavar is not None:
+            result = action.metavar
+        elif action.choices is not None:
+            choice_strs = [str(choice) for choice in action.choices]
+            result = '{%s}' % ','.join(choice_strs)
+        else:
+            result = default_metavar
+
+        def format(tuple_size):
+            if isinstance(result, tuple):
+                return result
+            else:
+                return (result, ) * tuple_size
+        return format
+
+    def _format_args(self, action, default_metavar):
+        get_metavar = self._metavar_formatter(action, default_metavar)
+        if action.nargs is None:
+            result = '%s' % get_metavar(1)
+        elif action.nargs == OPTIONAL:
+            result = '[%s]' % get_metavar(1)
+        elif action.nargs == ZERO_OR_MORE:
+            result = '[%s [%s ...]]' % get_metavar(2)
+        elif action.nargs == ONE_OR_MORE:
+            result = '%s [%s ...]' % get_metavar(2)
+        elif action.nargs == REMAINDER:
+            result = '...'
+        elif action.nargs == PARSER:
+            result = '%s ...' % get_metavar(1)
+        else:
+            formats = ['%s' for _ in range(action.nargs)]
+            result = ' '.join(formats) % get_metavar(action.nargs)
+        return result
+
+    def _expand_help(self, action):
+        params = dict(vars(action), prog=self._prog)
+        for name in list(params):
+            if params[name] is SUPPRESS:
+                del params[name]
+        for name in list(params):
+            if hasattr(params[name], '__name__'):
+                params[name] = params[name].__name__
+        if params.get('choices') is not None:
+            choices_str = ', '.join([str(c) for c in params['choices']])
+            params['choices'] = choices_str
+        return self._get_help_string(action) % params
+
+    def _iter_indented_subactions(self, action):
+        try:
+            get_subactions = action._get_subactions
+        except AttributeError:
+            pass
+        else:
+            self._indent()
+            for subaction in get_subactions():
+                yield subaction
+            self._dedent()
+
+    def _split_lines(self, text, width):
+        text = self._whitespace_matcher.sub(' ', text).strip()
+        return _textwrap.wrap(text, width)
+
+    def _fill_text(self, text, width, indent):
+        text = self._whitespace_matcher.sub(' ', text).strip()
+        return _textwrap.fill(text, width, initial_indent=indent,
+                                           subsequent_indent=indent)
+
+    def _get_help_string(self, action):
+        return action.help
+
+
+class RawDescriptionHelpFormatter(HelpFormatter):
+    """Help message formatter which retains any formatting in descriptions.
+
+    Only the name of this class is considered a public API. All the methods
+    provided by the class are considered an implementation detail.
+    """
+
+    def _fill_text(self, text, width, indent):
+        return ''.join([indent + line for line in text.splitlines(True)])
+
+
+class RawTextHelpFormatter(RawDescriptionHelpFormatter):
+    """Help message formatter which retains formatting of all help text.
+
+    Only the name of this class is considered a public API. All the methods
+    provided by the class are considered an implementation detail.
+    """
+
+    def _split_lines(self, text, width):
+        return text.splitlines()
+
+
+class ArgumentDefaultsHelpFormatter(HelpFormatter):
+    """Help message formatter which adds default values to argument help.
+
+    Only the name of this class is considered a public API. All the methods
+    provided by the class are considered an implementation detail.
+    """
+
+    def _get_help_string(self, action):
+        help = action.help
+        if '%(default)' not in action.help:
+            if action.default is not SUPPRESS:
+                defaulting_nargs = [OPTIONAL, ZERO_OR_MORE]
+                if action.option_strings or action.nargs in defaulting_nargs:
+                    help += ' (default: %(default)s)'
+        return help
+
+
+# =====================
+# Options and Arguments
+# =====================
+
+def _get_action_name(argument):
+    if argument is None:
+        return None
+    elif argument.option_strings:
+        return  '/'.join(argument.option_strings)
+    elif argument.metavar not in (None, SUPPRESS):
+        return argument.metavar
+    elif argument.dest not in (None, SUPPRESS):
+        return argument.dest
+    else:
+        return None
+
+
+class ArgumentError(Exception):
+    """An error from creating or using an argument (optional or positional).
+
+    The string value of this exception is the message, augmented with
+    information about the argument that caused it.
+    """
+
+    def __init__(self, argument, message):
+        self.argument_name = _get_action_name(argument)
+        self.message = message
+
+    def __str__(self):
+        if self.argument_name is None:
+            format = '%(message)s'
+        else:
+            format = 'argument %(argument_name)s: %(message)s'
+        return format % dict(message=self.message,
+                             argument_name=self.argument_name)
+
+
+class ArgumentTypeError(Exception):
+    """An error from trying to convert a command line string to a type."""
+    pass
+
+
+# ==============
+# Action classes
+# ==============
+
+class Action(_AttributeHolder):
+    """Information about how to convert command line strings to Python objects.
+
+    Action objects are used by an ArgumentParser to represent the information
+    needed to parse a single argument from one or more strings from the
+    command line. The keyword arguments to the Action constructor are also
+    all attributes of Action instances.
+
+    Keyword Arguments:
+
+        - option_strings -- A list of command-line option strings which
+            should be associated with this action.
+
+        - dest -- The name of the attribute to hold the created object(s)
+
+        - nargs -- The number of command-line arguments that should be
+            consumed. By default, one argument will be consumed and a single
+            value will be produced.  Other values include:
+                - N (an integer) consumes N arguments (and produces a list)
+                - '?' consumes zero or one arguments
+                - '*' consumes zero or more arguments (and produces a list)
+                - '+' consumes one or more arguments (and produces a list)
+            Note that the difference between the default and nargs=1 is that
+            with the default, a single value will be produced, while with
+            nargs=1, a list containing a single value will be produced.
+
+        - const -- The value to be produced if the option is specified and the
+            option uses an action that takes no values.
+
+        - default -- The value to be produced if the option is not specified.
+
+        - type -- The type which the command-line arguments should be converted
+            to, should be one of 'string', 'int', 'float', 'complex' or a
+            callable object that accepts a single string argument. If None,
+            'string' is assumed.
+
+        - choices -- A container of values that should be allowed. If not None,
+            after a command-line argument has been converted to the appropriate
+            type, an exception will be raised if it is not a member of this
+            collection.
+
+        - required -- True if the action must always be specified at the
+            command line. This is only meaningful for optional command-line
+            arguments.
+
+        - help -- The help string describing the argument.
+
+        - metavar -- The name to be used for the option's argument with the
+            help string. If None, the 'dest' value will be used as the name.
+    """
+
+    def __init__(self,
+                 option_strings,
+                 dest,
+                 nargs=None,
+                 const=None,
+                 default=None,
+                 type=None,
+                 choices=None,
+                 required=False,
+                 help=None,
+                 metavar=None):
+        self.option_strings = option_strings
+        self.dest = dest
+        self.nargs = nargs
+        self.const = const
+        self.default = default
+        self.type = type
+        self.choices = choices
+        self.required = required
+        self.help = help
+        self.metavar = metavar
+
+    def _get_kwargs(self):
+        names = [
+            'option_strings',
+            'dest',
+            'nargs',
+            'const',
+            'default',
+            'type',
+            'choices',
+            'help',
+            'metavar',
+        ]
+        return [(name, getattr(self, name)) for name in names]
+
+    def __call__(self, parser, namespace, values, option_string=None):
+        raise NotImplementedError(_('.__call__() not defined'))
+
+
+class _StoreAction(Action):
+
+    def __init__(self,
+                 option_strings,
+                 dest,
+                 nargs=None,
+                 const=None,
+                 default=None,
+                 type=None,
+                 choices=None,
+                 required=False,
+                 help=None,
+                 metavar=None):
+        if nargs == 0:
+            raise ValueError('nargs for store actions must be > 0; if you '
+                             'have nothing to store, actions such as store '
+                             'true or store const may be more appropriate')
+        if const is not None and nargs != OPTIONAL:
+            raise ValueError('nargs must be %r to supply const' % OPTIONAL)
+        super(_StoreAction, self).__init__(
+            option_strings=option_strings,
+            dest=dest,
+            nargs=nargs,
+            const=const,
+            default=default,
+            type=type,
+            choices=choices,
+            required=required,
+            help=help,
+            metavar=metavar)
+
+    def __call__(self, parser, namespace, values, option_string=None):
+        setattr(namespace, self.dest, values)
+
+
+class _StoreConstAction(Action):
+
+    def __init__(self,
+                 option_strings,
+                 dest,
+                 const,
+                 default=None,
+                 required=False,
+                 help=None,
+                 metavar=None):
+        super(_StoreConstAction, self).__init__(
+            option_strings=option_strings,
+            dest=dest,
+            nargs=0,
+            const=const,
+            default=default,
+            required=required,
+            help=help)
+
+    def __call__(self, parser, namespace, values, option_string=None):
+        setattr(namespace, self.dest, self.const)
+
+
+class _StoreTrueAction(_StoreConstAction):
+
+    def __init__(self,
+                 option_strings,
+                 dest,
+                 default=False,
+                 required=False,
+                 help=None):
+        super(_StoreTrueAction, self).__init__(
+            option_strings=option_strings,
+            dest=dest,
+            const=True,
+            default=default,
+            required=required,
+            help=help)
+
+
+class _StoreFalseAction(_StoreConstAction):
+
+    def __init__(self,
+                 option_strings,
+                 dest,
+                 default=True,
+                 required=False,
+                 help=None):
+        super(_StoreFalseAction, self).__init__(
+            option_strings=option_strings,
+            dest=dest,
+            const=False,
+            default=default,
+            required=required,
+            help=help)
+
+
+class _AppendAction(Action):
+
+    def __init__(self,
+                 option_strings,
+                 dest,
+                 nargs=None,
+                 const=None,
+                 default=None,
+                 type=None,
+                 choices=None,
+                 required=False,
+                 help=None,
+                 metavar=None):
+        if nargs == 0:
+            raise ValueError('nargs for append actions must be > 0; if arg '
+                             'strings are not supplying the value to append, '
+                             'the append const action may be more appropriate')
+        if const is not None and nargs != OPTIONAL:
+            raise ValueError('nargs must be %r to supply const' % OPTIONAL)
+        super(_AppendAction, self).__init__(
+            option_strings=option_strings,
+            dest=dest,
+            nargs=nargs,
+            const=const,
+            default=default,
+            type=type,
+            choices=choices,
+            required=required,
+            help=help,
+            metavar=metavar)
+
+    def __call__(self, parser, namespace, values, option_string=None):
+        items = _copy.copy(_ensure_value(namespace, self.dest, []))
+        items.append(values)
+        setattr(namespace, self.dest, items)
+
+
+class _AppendConstAction(Action):
+
+    def __init__(self,
+                 option_strings,
+                 dest,
+                 const,
+                 default=None,
+                 required=False,
+                 help=None,
+                 metavar=None):
+        super(_AppendConstAction, self).__init__(
+            option_strings=option_strings,
+            dest=dest,
+            nargs=0,
+            const=const,
+            default=default,
+            required=required,
+            help=help,
+            metavar=metavar)
+
+    def __call__(self, parser, namespace, values, option_string=None):
+        items = _copy.copy(_ensure_value(namespace, self.dest, []))
+        items.append(self.const)
+        setattr(namespace, self.dest, items)
+
+
+class _CountAction(Action):
+
+    def __init__(self,
+                 option_strings,
+                 dest,
+                 default=None,
+                 required=False,
+                 help=None):
+        super(_CountAction, self).__init__(
+            option_strings=option_strings,
+            dest=dest,
+            nargs=0,
+            default=default,
+            required=required,
+            help=help)
+
+    def __call__(self, parser, namespace, values, option_string=None):
+        new_count = _ensure_value(namespace, self.dest, 0) + 1
+        setattr(namespace, self.dest, new_count)
+
+
+class _HelpAction(Action):
+
+    def __init__(self,
+                 option_strings,
+                 dest=SUPPRESS,
+                 default=SUPPRESS,
+                 help=None):
+        super(_HelpAction, self).__init__(
+            option_strings=option_strings,
+            dest=dest,
+            default=default,
+            nargs=0,
+            help=help)
+
+    def __call__(self, parser, namespace, values, option_string=None):
+        parser.print_help()
+        parser.exit()
+
+
+class _VersionAction(Action):
+
+    def __init__(self,
+                 option_strings,
+                 version=None,
+                 dest=SUPPRESS,
+                 default=SUPPRESS,
+                 help="show program's version number and exit"):
+        super(_VersionAction, self).__init__(
+            option_strings=option_strings,
+            dest=dest,
+            default=default,
+            nargs=0,
+            help=help)
+        self.version = version
+
+    def __call__(self, parser, namespace, values, option_string=None):
+        version = self.version
+        if version is None:
+            version = parser.version
+        formatter = parser._get_formatter()
+        formatter.add_text(version)
+        parser.exit(message=formatter.format_help())
+
+
+class _SubParsersAction(Action):
+
+    class _ChoicesPseudoAction(Action):
+
+        def __init__(self, name, aliases, help):
+            metavar = dest = name
+            if aliases:
+                metavar += ' (%s)' % ', '.join(aliases)
+            sup = super(_SubParsersAction._ChoicesPseudoAction, self)
+            sup.__init__(option_strings=[], dest=dest, help=help,
+                        metavar=metavar)
+
+    def __init__(self,
+                 option_strings,
+                 prog,
+                 parser_class,
+                 dest=SUPPRESS,
+                 help=None,
+                 metavar=None):
+
+        self._prog_prefix = prog
+        self._parser_class = parser_class
+        self._name_parser_map = {}
+        self._choices_actions = []
+
+        super(_SubParsersAction, self).__init__(
+            option_strings=option_strings,
+            dest=dest,
+            nargs=PARSER,
+            choices=self._name_parser_map,
+            help=help,
+            metavar=metavar)
+
+    def add_parser(self, name, **kwargs):
+        # set prog from the existing prefix
+        if kwargs.get('prog') is None:
+            kwargs['prog'] = '%s %s' % (self._prog_prefix, name)
+
+        aliases = kwargs.pop('aliases', ())
+
+        # create a pseudo-action to hold the choice help
+        if 'help' in kwargs:
+            help = kwargs.pop('help')
+            choice_action = self._ChoicesPseudoAction(name, aliases, help)
+            self._choices_actions.append(choice_action)
+
+        # create the parser and add it to the map
+        parser = self._parser_class(**kwargs)
+        self._name_parser_map[name] = parser
+
+        # make parser available under aliases also
+        for alias in aliases:
+            self._name_parser_map[alias] = parser
+
+        return parser
+
+    def _get_subactions(self):
+        return self._choices_actions
+
+    def __call__(self, parser, namespace, values, option_string=None):
+        parser_name = values[0]
+        arg_strings = values[1:]
+
+        # set the parser name if requested
+        if self.dest is not SUPPRESS:
+            setattr(namespace, self.dest, parser_name)
+
+        # select the parser
+        try:
+            parser = self._name_parser_map[parser_name]
+        except KeyError:
+            tup = parser_name, ', '.join(self._name_parser_map)
+            msg = _('unknown parser %r (choices: %s)' % tup)
+            raise ArgumentError(self, msg)
+
+        # parse all the remaining options into the namespace
+        # store any unrecognized options on the object, so that the top
+        # level parser can decide what to do with them
+        namespace, arg_strings = parser.parse_known_args(arg_strings, namespace)
+        if arg_strings:
+            vars(namespace).setdefault(_UNRECOGNIZED_ARGS_ATTR, [])
+            getattr(namespace, _UNRECOGNIZED_ARGS_ATTR).extend(arg_strings)
+
+
+# ==============
+# Type classes
+# ==============
+
+class FileType(object):
+    """Factory for creating file object types
+
+    Instances of FileType are typically passed as type= arguments to the
+    ArgumentParser add_argument() method.
+
+    Keyword Arguments:
+        - mode -- A string indicating how the file is to be opened. Accepts the
+            same values as the builtin open() function.
+        - bufsize -- The file's desired buffer size. Accepts the same values as
+            the builtin open() function.
+    """
+
+    def __init__(self, mode='r', bufsize=None):
+        self._mode = mode
+        self._bufsize = bufsize
+
+    def __call__(self, string):
+        # the special argument "-" means sys.std{in,out}
+        if string == '-':
+            if 'r' in self._mode:
+                return _sys.stdin
+            elif 'w' in self._mode:
+                return _sys.stdout
+            else:
+                msg = _('argument "-" with mode %r' % self._mode)
+                raise ValueError(msg)
+
+        try:
+            # all other arguments are used as file names
+            if self._bufsize:
+                return open(string, self._mode, self._bufsize)
+            else:
+                return open(string, self._mode)
+        except IOError:
+            err = _sys.exc_info()[1]
+            message = _("can't open '%s': %s")
+            raise ArgumentTypeError(message % (string, err))
+
+    def __repr__(self):
+        args = [self._mode, self._bufsize]
+        args_str = ', '.join([repr(arg) for arg in args if arg is not None])
+        return '%s(%s)' % (type(self).__name__, args_str)
+
+# ===========================
+# Optional and Positional Parsing
+# ===========================
+
+class Namespace(_AttributeHolder):
+    """Simple object for storing attributes.
+
+    Implements equality by attribute names and values, and provides a simple
+    string representation.
+    """
+
+    def __init__(self, **kwargs):
+        for name in kwargs:
+            setattr(self, name, kwargs[name])
+
+    __hash__ = None
+
+    def __eq__(self, other):
+        return vars(self) == vars(other)
+
+    def __ne__(self, other):
+        return not (self == other)
+
+    def __contains__(self, key):
+        return key in self.__dict__
+
+
+class _ActionsContainer(object):
+
+    def __init__(self,
+                 description,
+                 prefix_chars,
+                 argument_default,
+                 conflict_handler):
+        super(_ActionsContainer, self).__init__()
+
+        self.description = description
+        self.argument_default = argument_default
+        self.prefix_chars = prefix_chars
+        self.conflict_handler = conflict_handler
+
+        # set up registries
+        self._registries = {}
+
+        # register actions
+        self.register('action', None, _StoreAction)
+        self.register('action', 'store', _StoreAction)
+        self.register('action', 'store_const', _StoreConstAction)
+        self.register('action', 'store_true', _StoreTrueAction)
+        self.register('action', 'store_false', _StoreFalseAction)
+        self.register('action', 'append', _AppendAction)
+        self.register('action', 'append_const', _AppendConstAction)
+        self.register('action', 'count', _CountAction)
+        self.register('action', 'help', _HelpAction)
+        self.register('action', 'version', _VersionAction)
+        self.register('action', 'parsers', _SubParsersAction)
+
+        # raise an exception if the conflict handler is invalid
+        self._get_handler()
+
+        # action storage
+        self._actions = []
+        self._option_string_actions = {}
+
+        # groups
+        self._action_groups = []
+        self._mutually_exclusive_groups = []
+
+        # defaults storage
+        self._defaults = {}
+
+        # determines whether an "option" looks like a negative number
+        self._negative_number_matcher = _re.compile(r'^-\d+$|^-\d*\.\d+$')
+
+        # whether or not there are any optionals that look like negative
+        # numbers -- uses a list so it can be shared and edited
+        self._has_negative_number_optionals = []
+
+    # ====================
+    # Registration methods
+    # ====================
+    def register(self, registry_name, value, object):
+        registry = self._registries.setdefault(registry_name, {})
+        registry[value] = object
+
+    def _registry_get(self, registry_name, value, default=None):
+        return self._registries[registry_name].get(value, default)
+
+    # ==================================
+    # Namespace default accessor methods
+    # ==================================
+    def set_defaults(self, **kwargs):
+        self._defaults.update(kwargs)
+
+        # if these defaults match any existing arguments, replace
+        # the previous default on the object with the new one
+        for action in self._actions:
+            if action.dest in kwargs:
+                action.default = kwargs[action.dest]
+
+    def get_default(self, dest):
+        for action in self._actions:
+            if action.dest == dest and action.default is not None:
+                return action.default
+        return self._defaults.get(dest, None)
+
+
+    # =======================
+    # Adding argument actions
+    # =======================
+    def add_argument(self, *args, **kwargs):
+        """
+        add_argument(dest, ..., name=value, ...)
+        add_argument(option_string, option_string, ..., name=value, ...)
+        """
+
+        # if no positional args are supplied or only one is supplied and
+        # it doesn't look like an option string, parse a positional
+        # argument
+        chars = self.prefix_chars
+        if not args or len(args) == 1 and args[0][0] not in chars:
+            if args and 'dest' in kwargs:
+                raise ValueError('dest supplied twice for positional argument')
+            kwargs = self._get_positional_kwargs(*args, **kwargs)
+
+        # otherwise, we're adding an optional argument
+        else:
+            kwargs = self._get_optional_kwargs(*args, **kwargs)
+
+        # if no default was supplied, use the parser-level default
+        if 'default' not in kwargs:
+            dest = kwargs['dest']
+            if dest in self._defaults:
+                kwargs['default'] = self._defaults[dest]
+            elif self.argument_default is not None:
+                kwargs['default'] = self.argument_default
+
+        # create the action object, and add it to the parser
+        action_class = self._pop_action_class(kwargs)
+        if not _callable(action_class):
+            raise ValueError('unknown action "%s"' % action_class)
+        action = action_class(**kwargs)
+
+        # raise an error if the action type is not callable
+        type_func = self._registry_get('type', action.type, action.type)
+        if not _callable(type_func):
+            raise ValueError('%r is not callable' % type_func)
+
+        return self._add_action(action)
+
+    def add_argument_group(self, *args, **kwargs):
+        group = _ArgumentGroup(self, *args, **kwargs)
+        self._action_groups.append(group)
+        return group
+
+    def add_mutually_exclusive_group(self, **kwargs):
+        group = _MutuallyExclusiveGroup(self, **kwargs)
+        self._mutually_exclusive_groups.append(group)
+        return group
+
+    def _add_action(self, action):
+        # resolve any conflicts
+        self._check_conflict(action)
+
+        # add to actions list
+        self._actions.append(action)
+        action.container = self
+
+        # index the action by any option strings it has
+        for option_string in action.option_strings:
+            self._option_string_actions[option_string] = action
+
+        # set the flag if any option strings look like negative numbers
+        for option_string in action.option_strings:
+            if self._negative_number_matcher.match(option_string):
+                if not self._has_negative_number_optionals:
+                    self._has_negative_number_optionals.append(True)
+
+        # return the created action
+        return action
+
+    def _remove_action(self, action):
+        self._actions.remove(action)
+
+    def _add_container_actions(self, container):
+        # collect groups by titles
+        title_group_map = {}
+        for group in self._action_groups:
+            if group.title in title_group_map:
+                msg = _('cannot merge actions - two groups are named %r')
+                raise ValueError(msg % (group.title))
+            title_group_map[group.title] = group
+
+        # map each action to its group
+        group_map = {}
+        for group in container._action_groups:
+
+            # if a group with the title exists, use that, otherwise
+            # create a new group matching the container's group
+            if group.title not in title_group_map:
+                title_group_map[group.title] = self.add_argument_group(
+                    title=group.title,
+                    description=group.description,
+                    conflict_handler=group.conflict_handler)
+
+            # map the actions to their new group
+            for action in group._group_actions:
+                group_map[action] = title_group_map[group.title]
+
+        # add container's mutually exclusive groups
+        # NOTE: if add_mutually_exclusive_group ever gains title= and
+        # description= then this code will need to be expanded as above
+        for group in container._mutually_exclusive_groups:
+            mutex_group = self.add_mutually_exclusive_group(
+                required=group.required)
+
+            # map the actions to their new mutex group
+            for action in group._group_actions:
+                group_map[action] = mutex_group
+
+        # add all actions to this container or their group
+        for action in container._actions:
+            group_map.get(action, self)._add_action(action)
+
+    def _get_positional_kwargs(self, dest, **kwargs):
+        # make sure required is not specified
+        if 'required' in kwargs:
+            msg = _("'required' is an invalid argument for positionals")
+            raise TypeError(msg)
+
+        # mark positional arguments as required if at least one is
+        # always required
+        if kwargs.get('nargs') not in [OPTIONAL, ZERO_OR_MORE]:
+            kwargs['required'] = True
+        if kwargs.get('nargs') == ZERO_OR_MORE and 'default' not in kwargs:
+            kwargs['required'] = True
+
+        # return the keyword arguments with no option strings
+        return dict(kwargs, dest=dest, option_strings=[])
+
+    def _get_optional_kwargs(self, *args, **kwargs):
+        # determine short and long option strings
+        option_strings = []
+        long_option_strings = []
+        for option_string in args:
+            # error on strings that don't start with an appropriate prefix
+            if not option_string[0] in self.prefix_chars:
+                msg = _('invalid option string %r: '
+                        'must start with a character %r')
+                tup = option_string, self.prefix_chars
+                raise ValueError(msg % tup)
+
+            # strings starting with two prefix characters are long options
+            option_strings.append(option_string)
+            if option_string[0] in self.prefix_chars:
+                if len(option_string) > 1:
+                    if option_string[1] in self.prefix_chars:
+                        long_option_strings.append(option_string)
+
+        # infer destination, '--foo-bar' -> 'foo_bar' and '-x' -> 'x'
+        dest = kwargs.pop('dest', None)
+        if dest is None:
+            if long_option_strings:
+                dest_option_string = long_option_strings[0]
+            else:
+                dest_option_string = option_strings[0]
+            dest = dest_option_string.lstrip(self.prefix_chars)
+            if not dest:
+                msg = _('dest= is required for options like %r')
+                raise ValueError(msg % option_string)
+            dest = dest.replace('-', '_')
+
+        # return the updated keyword arguments
+        return dict(kwargs, dest=dest, option_strings=option_strings)
+
+    def _pop_action_class(self, kwargs, default=None):
+        action = kwargs.pop('action', default)
+        return self._registry_get('action', action, action)
+
+    def _get_handler(self):
+        # determine function from conflict handler string
+        handler_func_name = '_handle_conflict_%s' % self.conflict_handler
+        try:
+            return getattr(self, handler_func_name)
+        except AttributeError:
+            msg = _('invalid conflict_resolution value: %r')
+            raise ValueError(msg % self.conflict_handler)
+
+    def _check_conflict(self, action):
+
+        # find all options that conflict with this option
+        confl_optionals = []
+        for option_string in action.option_strings:
+            if option_string in self._option_string_actions:
+                confl_optional = self._option_string_actions[option_string]
+                confl_optionals.append((option_string, confl_optional))
+
+        # resolve any conflicts
+        if confl_optionals:
+            conflict_handler = self._get_handler()
+            conflict_handler(action, confl_optionals)
+
+    def _handle_conflict_error(self, action, conflicting_actions):
+        message = _('conflicting option string(s): %s')
+        conflict_string = ', '.join([option_string
+                                     for option_string, action
+                                     in conflicting_actions])
+        raise ArgumentError(action, message % conflict_string)
+
+    def _handle_conflict_resolve(self, action, conflicting_actions):
+
+        # remove all conflicting options
+        for option_string, action in conflicting_actions:
+
+            # remove the conflicting option
+            action.option_strings.remove(option_string)
+            self._option_string_actions.pop(option_string, None)
+
+            # if the option now has no option string, remove it from the
+            # container holding it
+            if not action.option_strings:
+                action.container._remove_action(action)
+
+
+class _ArgumentGroup(_ActionsContainer):
+
+    def __init__(self, container, title=None, description=None, **kwargs):
+        # add any missing keyword arguments by checking the container
+        update = kwargs.setdefault
+        update('conflict_handler', container.conflict_handler)
+        update('prefix_chars', container.prefix_chars)
+        update('argument_default', container.argument_default)
+        super_init = super(_ArgumentGroup, self).__init__
+        super_init(description=description, **kwargs)
+
+        # group attributes
+        self.title = title
+        self._group_actions = []
+
+        # share most attributes with the container
+        self._registries = container._registries
+        self._actions = container._actions
+        self._option_string_actions = container._option_string_actions
+        self._defaults = container._defaults
+        self._has_negative_number_optionals = \
+            container._has_negative_number_optionals
+
+    def _add_action(self, action):
+        action = super(_ArgumentGroup, self)._add_action(action)
+        self._group_actions.append(action)
+        return action
+
+    def _remove_action(self, action):
+        super(_ArgumentGroup, self)._remove_action(action)
+        self._group_actions.remove(action)
+
+
+class _MutuallyExclusiveGroup(_ArgumentGroup):
+
+    def __init__(self, container, required=False):
+        super(_MutuallyExclusiveGroup, self).__init__(container)
+        self.required = required
+        self._container = container
+
+    def _add_action(self, action):
+        if action.required:
+            msg = _('mutually exclusive arguments must be optional')
+            raise ValueError(msg)
+        action = self._container._add_action(action)
+        self._group_actions.append(action)
+        return action
+
+    def _remove_action(self, action):
+        self._container._remove_action(action)
+        self._group_actions.remove(action)
+
+
+class ArgumentParser(_AttributeHolder, _ActionsContainer):
+    """Object for parsing command line strings into Python objects.
+
+    Keyword Arguments:
+        - prog -- The name of the program (default: sys.argv[0])
+        - usage -- A usage message (default: auto-generated from arguments)
+        - description -- A description of what the program does
+        - epilog -- Text following the argument descriptions
+        - parents -- Parsers whose arguments should be copied into this one
+        - formatter_class -- HelpFormatter class for printing help messages
+        - prefix_chars -- Characters that prefix optional arguments
+        - fromfile_prefix_chars -- Characters that prefix files containing
+            additional arguments
+        - argument_default -- The default value for all arguments
+        - conflict_handler -- String indicating how to handle conflicts
+        - add_help -- Add a -h/-help option
+    """
+
+    def __init__(self,
+                 prog=None,
+                 usage=None,
+                 description=None,
+                 epilog=None,
+                 version=None,
+                 parents=[],
+                 formatter_class=HelpFormatter,
+                 prefix_chars='-',
+                 fromfile_prefix_chars=None,
+                 argument_default=None,
+                 conflict_handler='error',
+                 add_help=True):
+
+        if version is not None:
+            import warnings
+            warnings.warn(
+                """The "version" argument to ArgumentParser is deprecated. """
+                """Please use """
+                """"add_argument(..., action='version', version="N", ...)" """
+                """instead""", DeprecationWarning)
+
+        superinit = super(ArgumentParser, self).__init__
+        superinit(description=description,
+                  prefix_chars=prefix_chars,
+                  argument_default=argument_default,
+                  conflict_handler=conflict_handler)
+
+        # default setting for prog
+        if prog is None:
+            prog = _os.path.basename(_sys.argv[0])
+
+        self.prog = prog
+        self.usage = usage
+        self.epilog = epilog
+        self.version = version
+        self.formatter_class = formatter_class
+        self.fromfile_prefix_chars = fromfile_prefix_chars
+        self.add_help = add_help
+
+        add_group = self.add_argument_group
+        self._positionals = add_group(_('positional arguments'))
+        self._optionals = add_group(_('optional arguments'))
+        self._subparsers = None
+
+        # register types
+        def identity(string):
+            return string
+        self.register('type', None, identity)
+
+        # add help and version arguments if necessary
+        # (using explicit default to override global argument_default)
+        if '-' in prefix_chars:
+            default_prefix = '-'
+        else:
+            default_prefix = prefix_chars[0]
+        if self.add_help:
+            self.add_argument(
+                default_prefix+'h', default_prefix*2+'help',
+                action='help', default=SUPPRESS,
+                help=_('show this help message and exit'))
+        if self.version:
+            self.add_argument(
+                default_prefix+'v', default_prefix*2+'version',
+                action='version', default=SUPPRESS,
+                version=self.version,
+                help=_("show program's version number and exit"))
+
+        # add parent arguments and defaults
+        for parent in parents:
+            self._add_container_actions(parent)
+            try:
+                defaults = parent._defaults
+            except AttributeError:
+                pass
+            else:
+                self._defaults.update(defaults)
+
+    # =======================
+    # Pretty __repr__ methods
+    # =======================
+    def _get_kwargs(self):
+        names = [
+            'prog',
+            'usage',
+            'description',
+            'version',
+            'formatter_class',
+            'conflict_handler',
+            'add_help',
+        ]
+        return [(name, getattr(self, name)) for name in names]
+
+    # ==================================
+    # Optional/Positional adding methods
+    # ==================================
+    def add_subparsers(self, **kwargs):
+        if self._subparsers is not None:
+            self.error(_('cannot have multiple subparser arguments'))
+
+        # add the parser class to the arguments if it's not present
+        kwargs.setdefault('parser_class', type(self))
+
+        if 'title' in kwargs or 'description' in kwargs:
+            title = _(kwargs.pop('title', 'subcommands'))
+            description = _(kwargs.pop('description', None))
+            self._subparsers = self.add_argument_group(title, description)
+        else:
+            self._subparsers = self._positionals
+
+        # prog defaults to the usage message of this parser, skipping
+        # optional arguments and with no "usage:" prefix
+        if kwargs.get('prog') is None:
+            formatter = self._get_formatter()
+            positionals = self._get_positional_actions()
+            groups = self._mutually_exclusive_groups
+            formatter.add_usage(self.usage, positionals, groups, '')
+            kwargs['prog'] = formatter.format_help().strip()
+
+        # create the parsers action and add it to the positionals list
+        parsers_class = self._pop_action_class(kwargs, 'parsers')
+        action = parsers_class(option_strings=[], **kwargs)
+        self._subparsers._add_action(action)
+
+        # return the created parsers action
+        return action
+
+    def _add_action(self, action):
+        if action.option_strings:
+            self._optionals._add_action(action)
+        else:
+            self._positionals._add_action(action)
+        return action
+
+    def _get_optional_actions(self):
+        return [action
+                for action in self._actions
+                if action.option_strings]
+
+    def _get_positional_actions(self):
+        return [action
+                for action in self._actions
+                if not action.option_strings]
+
+    # =====================================
+    # Command line argument parsing methods
+    # =====================================
+    def parse_args(self, args=None, namespace=None):
+        args, argv = self.parse_known_args(args, namespace)
+        if argv:
+            msg = _('unrecognized arguments: %s')
+            self.error(msg % ' '.join(argv))
+        return args
+
+    def parse_known_args(self, args=None, namespace=None):
+        # args default to the system args
+        if args is None:
+            args = _sys.argv[1:]
+
+        # default Namespace built from parser defaults
+        if namespace is None:
+            namespace = Namespace()
+
+        # add any action defaults that aren't present
+        for action in self._actions:
+            if action.dest is not SUPPRESS:
+                if not hasattr(namespace, action.dest):
+                    if action.default is not SUPPRESS:
+                        setattr(namespace, action.dest, action.default)
+
+        # add any parser defaults that aren't present
+        for dest in self._defaults:
+            if not hasattr(namespace, dest):
+                setattr(namespace, dest, self._defaults[dest])
+
+        # parse the arguments and exit if there are any errors
+        try:
+            namespace, args = self._parse_known_args(args, namespace)
+            if hasattr(namespace, _UNRECOGNIZED_ARGS_ATTR):
+                args.extend(getattr(namespace, _UNRECOGNIZED_ARGS_ATTR))
+                delattr(namespace, _UNRECOGNIZED_ARGS_ATTR)
+            return namespace, args
+        except ArgumentError:
+            err = _sys.exc_info()[1]
+            self.error(str(err))
+
+    def _parse_known_args(self, arg_strings, namespace):
+        # replace arg strings that are file references
+        if self.fromfile_prefix_chars is not None:
+            arg_strings = self._read_args_from_files(arg_strings)
+
+        # map all mutually exclusive arguments to the other arguments
+        # they can't occur with
+        action_conflicts = {}
+        for mutex_group in self._mutually_exclusive_groups:
+            group_actions = mutex_group._group_actions
+            for i, mutex_action in enumerate(mutex_group._group_actions):
+                conflicts = action_conflicts.setdefault(mutex_action, [])
+                conflicts.extend(group_actions[:i])
+                conflicts.extend(group_actions[i + 1:])
+
+        # find all option indices, and determine the arg_string_pattern
+        # which has an 'O' if there is an option at an index,
+        # an 'A' if there is an argument, or a '-' if there is a '--'
+        option_string_indices = {}
+        arg_string_pattern_parts = []
+        arg_strings_iter = iter(arg_strings)
+        for i, arg_string in enumerate(arg_strings_iter):
+
+            # all args after -- are non-options
+            if arg_string == '--':
+                arg_string_pattern_parts.append('-')
+                for arg_string in arg_strings_iter:
+                    arg_string_pattern_parts.append('A')
+
+            # otherwise, add the arg to the arg strings
+            # and note the index if it was an option
+            else:
+                option_tuple = self._parse_optional(arg_string)
+                if option_tuple is None:
+                    pattern = 'A'
+                else:
+                    option_string_indices[i] = option_tuple
+                    pattern = 'O'
+                arg_string_pattern_parts.append(pattern)
+
+        # join the pieces together to form the pattern
+        arg_strings_pattern = ''.join(arg_string_pattern_parts)
+
+        # converts arg strings to the appropriate and then takes the action
+        seen_actions = set()
+        seen_non_default_actions = set()
+
+        def take_action(action, argument_strings, option_string=None):
+            seen_actions.add(action)
+            argument_values = self._get_values(action, argument_strings)
+
+            # error if this argument is not allowed with other previously
+            # seen arguments, assuming that actions that use the default
+            # value don't really count as "present"
+            if argument_values is not action.default:
+                seen_non_default_actions.add(action)
+                for conflict_action in action_conflicts.get(action, []):
+                    if conflict_action in seen_non_default_actions:
+                        msg = _('not allowed with argument %s')
+                        action_name = _get_action_name(conflict_action)
+                        raise ArgumentError(action, msg % action_name)
+
+            # take the action if we didn't receive a SUPPRESS value
+            # (e.g. from a default)
+            if argument_values is not SUPPRESS:
+                action(self, namespace, argument_values, option_string)
+
+        # function to convert arg_strings into an optional action
+        def consume_optional(start_index):
+
+            # get the optional identified at this index
+            option_tuple = option_string_indices[start_index]
+            action, option_string, explicit_arg = option_tuple
+
+            # identify additional optionals in the same arg string
+            # (e.g. -xyz is the same as -x -y -z if no args are required)
+            match_argument = self._match_argument
+            action_tuples = []
+            while True:
+
+                # if we found no optional action, skip it
+                if action is None:
+                    extras.append(arg_strings[start_index])
+                    return start_index + 1
+
+                # if there is an explicit argument, try to match the
+                # optional's string arguments to only this
+                if explicit_arg is not None:
+                    arg_count = match_argument(action, 'A')
+
+                    # if the action is a single-dash option and takes no
+                    # arguments, try to parse more single-dash options out
+                    # of the tail of the option string
+                    chars = self.prefix_chars
+                    if arg_count == 0 and option_string[1] not in chars:
+                        action_tuples.append((action, [], option_string))
+                        char = option_string[0]
+                        option_string = char + explicit_arg[0]
+                        new_explicit_arg = explicit_arg[1:] or None
+                        optionals_map = self._option_string_actions
+                        if option_string in optionals_map:
+                            action = optionals_map[option_string]
+                            explicit_arg = new_explicit_arg
+                        else:
+                            msg = _('ignored explicit argument %r')
+                            raise ArgumentError(action, msg % explicit_arg)
+
+                    # if the action expect exactly one argument, we've
+                    # successfully matched the option; exit the loop
+                    elif arg_count == 1:
+                        stop = start_index + 1
+                        args = [explicit_arg]
+                        action_tuples.append((action, args, option_string))
+                        break
+
+                    # error if a double-dash option did not use the
+                    # explicit argument
+                    else:
+                        msg = _('ignored explicit argument %r')
+                        raise ArgumentError(action, msg % explicit_arg)
+
+                # if there is no explicit argument, try to match the
+                # optional's string arguments with the following strings
+                # if successful, exit the loop
+                else:
+                    start = start_index + 1
+                    selected_patterns = arg_strings_pattern[start:]
+                    arg_count = match_argument(action, selected_patterns)
+                    stop = start + arg_count
+                    args = arg_strings[start:stop]
+                    action_tuples.append((action, args, option_string))
+                    break
+
+            # add the Optional to the list and return the index at which
+            # the Optional's string args stopped
+            assert action_tuples
+            for action, args, option_string in action_tuples:
+                take_action(action, args, option_string)
+            return stop
+
+        # the list of Positionals left to be parsed; this is modified
+        # by consume_positionals()
+        positionals = self._get_positional_actions()
+
+        # function to convert arg_strings into positional actions
+        def consume_positionals(start_index):
+            # match as many Positionals as possible
+            match_partial = self._match_arguments_partial
+            selected_pattern = arg_strings_pattern[start_index:]
+            arg_counts = match_partial(positionals, selected_pattern)
+
+            # slice off the appropriate arg strings for each Positional
+            # and add the Positional and its args to the list
+            for action, arg_count in zip(positionals, arg_counts):
+                args = arg_strings[start_index: start_index + arg_count]
+                start_index += arg_count
+                take_action(action, args)
+
+            # slice off the Positionals that we just parsed and return the
+            # index at which the Positionals' string args stopped
+            positionals[:] = positionals[len(arg_counts):]
+            return start_index
+
+        # consume Positionals and Optionals alternately, until we have
+        # passed the last option string
+        extras = []
+        start_index = 0
+        if option_string_indices:
+            max_option_string_index = max(option_string_indices)
+        else:
+            max_option_string_index = -1
+        while start_index <= max_option_string_index:
+
+            # consume any Positionals preceding the next option
+            next_option_string_index = min([
+                index
+                for index in option_string_indices
+                if index >= start_index])
+            if start_index != next_option_string_index:
+                positionals_end_index = consume_positionals(start_index)
+
+                # only try to parse the next optional if we didn't consume
+                # the option string during the positionals parsing
+                if positionals_end_index > start_index:
+                    start_index = positionals_end_index
+                    continue
+                else:
+                    start_index = positionals_end_index
+
+            # if we consumed all the positionals we could and we're not
+            # at the index of an option string, there were extra arguments
+            if start_index not in option_string_indices:
+                strings = arg_strings[start_index:next_option_string_index]
+                extras.extend(strings)
+                start_index = next_option_string_index
+
+            # consume the next optional and any arguments for it
+            start_index = consume_optional(start_index)
+
+        # consume any positionals following the last Optional
+        stop_index = consume_positionals(start_index)
+
+        # if we didn't consume all the argument strings, there were extras
+        extras.extend(arg_strings[stop_index:])
+
+        # if we didn't use all the Positional objects, there were too few
+        # arg strings supplied.
+        if positionals:
+            self.error(_('too few arguments'))
+
+        # make sure all required actions were present, and convert defaults.
+        for action in self._actions:
+            if action not in seen_actions:
+                if action.required:
+                    name = _get_action_name(action)
+                    self.error(_('argument %s is required') % name)
+                else:
+                    # Convert action default now instead of doing it before
+                    # parsing arguments to avoid calling convert functions
+                    # twice (which may fail) if the argument was given, but
+                    # only if it was defined already in the namespace
+                    if (action.default is not None and
+                            isinstance(action.default, basestring) and
+                            hasattr(namespace, action.dest) and
+                            action.default is getattr(namespace, action.dest)):
+                        setattr(namespace, action.dest,
+                                self._get_value(action, action.default))
+
+        # make sure all required groups had one option present
+        for group in self._mutually_exclusive_groups:
+            if group.required:
+                for action in group._group_actions:
+                    if action in seen_non_default_actions:
+                        break
+
+                # if no actions were used, report the error
+                else:
+                    names = [_get_action_name(action)
+                             for action in group._group_actions
+                             if action.help is not SUPPRESS]
+                    msg = _('one of the arguments %s is required')
+                    self.error(msg % ' '.join(names))
+
+        # return the updated namespace and the extra arguments
+        return namespace, extras
+
+    def _read_args_from_files(self, arg_strings):
+        # expand arguments referencing files
+        new_arg_strings = []
+        for arg_string in arg_strings:
+
+            # for regular arguments, just add them back into the list
+            if arg_string[0] not in self.fromfile_prefix_chars:
+                new_arg_strings.append(arg_string)
+
+            # replace arguments referencing files with the file content
+            else:
+                try:
+                    args_file = open(arg_string[1:])
+                    try:
+                        arg_strings = []
+                        for arg_line in args_file.read().splitlines():
+                            for arg in self.convert_arg_line_to_args(arg_line):
+                                arg_strings.append(arg)
+                        arg_strings = self._read_args_from_files(arg_strings)
+                        new_arg_strings.extend(arg_strings)
+                    finally:
+                        args_file.close()
+                except IOError:
+                    err = _sys.exc_info()[1]
+                    self.error(str(err))
+
+        # return the modified argument list
+        return new_arg_strings
+
+    def convert_arg_line_to_args(self, arg_line):
+        return [arg_line]
+
+    def _match_argument(self, action, arg_strings_pattern):
+        # match the pattern for this action to the arg strings
+        nargs_pattern = self._get_nargs_pattern(action)
+        match = _re.match(nargs_pattern, arg_strings_pattern)
+
+        # raise an exception if we weren't able to find a match
+        if match is None:
+            nargs_errors = {
+                None: _('expected one argument'),
+                OPTIONAL: _('expected at most one argument'),
+                ONE_OR_MORE: _('expected at least one argument'),
+            }
+            default = _('expected %s argument(s)') % action.nargs
+            msg = nargs_errors.get(action.nargs, default)
+            raise ArgumentError(action, msg)
+
+        # return the number of arguments matched
+        return len(match.group(1))
+
+    def _match_arguments_partial(self, actions, arg_strings_pattern):
+        # progressively shorten the actions list by slicing off the
+        # final actions until we find a match
+        result = []
+        for i in range(len(actions), 0, -1):
+            actions_slice = actions[:i]
+            pattern = ''.join([self._get_nargs_pattern(action)
+                               for action in actions_slice])
+            match = _re.match(pattern, arg_strings_pattern)
+            if match is not None:
+                result.extend([len(string) for string in match.groups()])
+                break
+
+        # return the list of arg string counts
+        return result
+
+    def _parse_optional(self, arg_string):
+        # if it's an empty string, it was meant to be a positional
+        if not arg_string:
+            return None
+
+        # if it doesn't start with a prefix, it was meant to be positional
+        if not arg_string[0] in self.prefix_chars:
+            return None
+
+        # if the option string is present in the parser, return the action
+        if arg_string in self._option_string_actions:
+            action = self._option_string_actions[arg_string]
+            return action, arg_string, None
+
+        # if it's just a single character, it was meant to be positional
+        if len(arg_string) == 1:
+            return None
+
+        # if the option string before the "=" is present, return the action
+        if '=' in arg_string:
+            option_string, explicit_arg = arg_string.split('=', 1)
+            if option_string in self._option_string_actions:
+                action = self._option_string_actions[option_string]
+                return action, option_string, explicit_arg
+
+        # search through all possible prefixes of the option string
+        # and all actions in the parser for possible interpretations
+        option_tuples = self._get_option_tuples(arg_string)
+
+        # if multiple actions match, the option string was ambiguous
+        if len(option_tuples) > 1:
+            options = ', '.join([option_string
+                for action, option_string, explicit_arg in option_tuples])
+            tup = arg_string, options
+            self.error(_('ambiguous option: %s could match %s') % tup)
+
+        # if exactly one action matched, this segmentation is good,
+        # so return the parsed action
+        elif len(option_tuples) == 1:
+            option_tuple, = option_tuples
+            return option_tuple
+
+        # if it was not found as an option, but it looks like a negative
+        # number, it was meant to be positional
+        # unless there are negative-number-like options
+        if self._negative_number_matcher.match(arg_string):
+            if not self._has_negative_number_optionals:
+                return None
+
+        # if it contains a space, it was meant to be a positional
+        if ' ' in arg_string:
+            return None
+
+        # it was meant to be an optional but there is no such option
+        # in this parser (though it might be a valid option in a subparser)
+        return None, arg_string, None
+
+    def _get_option_tuples(self, option_string):
+        result = []
+
+        # option strings starting with two prefix characters are only
+        # split at the '='
+        chars = self.prefix_chars
+        if option_string[0] in chars and option_string[1] in chars:
+            if '=' in option_string:
+                option_prefix, explicit_arg = option_string.split('=', 1)
+            else:
+                option_prefix = option_string
+                explicit_arg = None
+            for option_string in self._option_string_actions:
+                if option_string.startswith(option_prefix):
+                    action = self._option_string_actions[option_string]
+                    tup = action, option_string, explicit_arg
+                    result.append(tup)
+
+        # single character options can be concatenated with their arguments
+        # but multiple character options always have to have their argument
+        # separate
+        elif option_string[0] in chars and option_string[1] not in chars:
+            option_prefix = option_string
+            explicit_arg = None
+            short_option_prefix = option_string[:2]
+            short_explicit_arg = option_string[2:]
+
+            for option_string in self._option_string_actions:
+                if option_string == short_option_prefix:
+                    action = self._option_string_actions[option_string]
+                    tup = action, option_string, short_explicit_arg
+                    result.append(tup)
+                elif option_string.startswith(option_prefix):
+                    action = self._option_string_actions[option_string]
+                    tup = action, option_string, explicit_arg
+                    result.append(tup)
+
+        # shouldn't ever get here
+        else:
+            self.error(_('unexpected option string: %s') % option_string)
+
+        # return the collected option tuples
+        return result
+
+    def _get_nargs_pattern(self, action):
+        # in all examples below, we have to allow for '--' args
+        # which are represented as '-' in the pattern
+        nargs = action.nargs
+
+        # the default (None) is assumed to be a single argument
+        if nargs is None:
+            nargs_pattern = '(-*A-*)'
+
+        # allow zero or one arguments
+        elif nargs == OPTIONAL:
+            nargs_pattern = '(-*A?-*)'
+
+        # allow zero or more arguments
+        elif nargs == ZERO_OR_MORE:
+            nargs_pattern = '(-*[A-]*)'
+
+        # allow one or more arguments
+        elif nargs == ONE_OR_MORE:
+            nargs_pattern = '(-*A[A-]*)'
+
+        # allow any number of options or arguments
+        elif nargs == REMAINDER:
+            nargs_pattern = '([-AO]*)'
+
+        # allow one argument followed by any number of options or arguments
+        elif nargs == PARSER:
+            nargs_pattern = '(-*A[-AO]*)'
+
+        # all others should be integers
+        else:
+            nargs_pattern = '(-*%s-*)' % '-*'.join('A' * nargs)
+
+        # if this is an optional action, -- is not allowed
+        if action.option_strings:
+            nargs_pattern = nargs_pattern.replace('-*', '')
+            nargs_pattern = nargs_pattern.replace('-', '')
+
+        # return the pattern
+        return nargs_pattern
+
+    # ========================
+    # Value conversion methods
+    # ========================
+    def _get_values(self, action, arg_strings):
+        # for everything but PARSER args, strip out '--'
+        if action.nargs not in [PARSER, REMAINDER]:
+            arg_strings = [s for s in arg_strings if s != '--']
+
+        # optional argument produces a default when not present
+        if not arg_strings and action.nargs == OPTIONAL:
+            if action.option_strings:
+                value = action.const
+            else:
+                value = action.default
+            if isinstance(value, basestring):
+                value = self._get_value(action, value)
+                self._check_value(action, value)
+
+        # when nargs='*' on a positional, if there were no command-line
+        # args, use the default if it is anything other than None
+        elif (not arg_strings and action.nargs == ZERO_OR_MORE and
+              not action.option_strings):
+            if action.default is not None:
+                value = action.default
+            else:
+                value = arg_strings
+            self._check_value(action, value)
+
+        # single argument or optional argument produces a single value
+        elif len(arg_strings) == 1 and action.nargs in [None, OPTIONAL]:
+            arg_string, = arg_strings
+            value = self._get_value(action, arg_string)
+            self._check_value(action, value)
+
+        # REMAINDER arguments convert all values, checking none
+        elif action.nargs == REMAINDER:
+            value = [self._get_value(action, v) for v in arg_strings]
+
+        # PARSER arguments convert all values, but check only the first
+        elif action.nargs == PARSER:
+            value = [self._get_value(action, v) for v in arg_strings]
+            self._check_value(action, value[0])
+
+        # all other types of nargs produce a list
+        else:
+            value = [self._get_value(action, v) for v in arg_strings]
+            for v in value:
+                self._check_value(action, v)
+
+        # return the converted value
+        return value
+
+    def _get_value(self, action, arg_string):
+        type_func = self._registry_get('type', action.type, action.type)
+        if not _callable(type_func):
+            msg = _('%r is not callable')
+            raise ArgumentError(action, msg % type_func)
+
+        # convert the value to the appropriate type
+        try:
+            result = type_func(arg_string)
+
+        # ArgumentTypeErrors indicate errors
+        except ArgumentTypeError:
+            name = getattr(action.type, '__name__', repr(action.type))
+            msg = str(_sys.exc_info()[1])
+            raise ArgumentError(action, msg)
+
+        # TypeErrors or ValueErrors also indicate errors
+        except (TypeError, ValueError):
+            name = getattr(action.type, '__name__', repr(action.type))
+            msg = _('invalid %s value: %r')
+            raise ArgumentError(action, msg % (name, arg_string))
+
+        # return the converted value
+        return result
+
+    def _check_value(self, action, value):
+        # converted value must be one of the choices (if specified)
+        if action.choices is not None and value not in action.choices:
+            tup = value, ', '.join(map(repr, action.choices))
+            msg = _('invalid choice: %r (choose from %s)') % tup
+            raise ArgumentError(action, msg)
+
+    # =======================
+    # Help-formatting methods
+    # =======================
+    def format_usage(self):
+        formatter = self._get_formatter()
+        formatter.add_usage(self.usage, self._actions,
+                            self._mutually_exclusive_groups)
+        return formatter.format_help()
+
+    def format_help(self):
+        formatter = self._get_formatter()
+
+        # usage
+        formatter.add_usage(self.usage, self._actions,
+                            self._mutually_exclusive_groups)
+
+        # description
+        formatter.add_text(self.description)
+
+        # positionals, optionals and user-defined groups
+        for action_group in self._action_groups:
+            formatter.start_section(action_group.title)
+            formatter.add_text(action_group.description)
+            formatter.add_arguments(action_group._group_actions)
+            formatter.end_section()
+
+        # epilog
+        formatter.add_text(self.epilog)
+
+        # determine help from format above
+        return formatter.format_help()
+
+    def format_version(self):
+        import warnings
+        warnings.warn(
+            'The format_version method is deprecated -- the "version" '
+            'argument to ArgumentParser is no longer supported.',
+            DeprecationWarning)
+        formatter = self._get_formatter()
+        formatter.add_text(self.version)
+        return formatter.format_help()
+
+    def _get_formatter(self):
+        return self.formatter_class(prog=self.prog)
+
+    # =====================
+    # Help-printing methods
+    # =====================
+    def print_usage(self, file=None):
+        if file is None:
+            file = _sys.stdout
+        self._print_message(self.format_usage(), file)
+
+    def print_help(self, file=None):
+        if file is None:
+            file = _sys.stdout
+        self._print_message(self.format_help(), file)
+
+    def print_version(self, file=None):
+        import warnings
+        warnings.warn(
+            'The print_version method is deprecated -- the "version" '
+            'argument to ArgumentParser is no longer supported.',
+            DeprecationWarning)
+        self._print_message(self.format_version(), file)
+
+    def _print_message(self, message, file=None):
+        if message:
+            if file is None:
+                file = _sys.stderr
+            file.write(message)
+
+    # ===============
+    # Exiting methods
+    # ===============
+    def exit(self, status=0, message=None):
+        if message:
+            self._print_message(message, _sys.stderr)
+        _sys.exit(status)
+
+    def error(self, message):
+        """error(message: string)
+
+        Prints a usage message incorporating the message to stderr and
+        exits.
+
+        If you override this in a subclass, it should not return -- it
+        should either exit or raise an exception.
+        """
+        self.print_usage(_sys.stderr)
+        self.exit(2, _('%s: error: %s\n') % (self.prog, message))
diff --git a/keycodemapdb/tools/keymap-gen b/keycodemapdb/tools/keymap-gen
new file mode 100755 (executable)
index 0000000..d4594b4
--- /dev/null
@@ -0,0 +1,972 @@
+#!/usr/bin/python
+# -*- python -*-
+#
+# Keycode Map Generator
+#
+# Copyright (C) 2009-2017 Red Hat, Inc.
+#
+# This file is dual license under the terms of the GPLv2 or later
+# and 3-clause BSD licenses.
+#
+
+# Requires >= 2.6
+from __future__ import print_function
+
+import csv
+try:
+    import argparse
+except:
+    import os, sys
+    sys.path.append(os.path.join(os.path.dirname(__file__), "../thirdparty"))
+    import argparse
+import hashlib
+import time
+import sys
+
+class Database:
+
+    # Linux: linux/input.h
+    MAP_LINUX = "linux"
+
+    # OS-X: Carbon/HIToolbox/Events.h
+    MAP_OSX = "osx"
+
+    # AT Set 1: linux/drivers/input/keyboard/atkbd.c
+    #           (atkbd_set2_keycode + atkbd_unxlate_table)
+    MAP_ATSET1 = "atset1"
+
+    # AT Set 2: linux/drivers/input/keyboard/atkbd.c
+    #           (atkbd_set2_keycode)
+    MAP_ATSET2 = "atset2"
+
+    # AT Set 3: linux/drivers/input/keyboard/atkbd.c
+    #           (atkbd_set3_keycode)
+    MAP_ATSET3 = "atset3"
+
+    # Linux RAW: linux/drivers/char/keyboard.c (x86_keycodes)
+    MAP_XTKBD = "xtkbd"
+
+    # USB HID: linux/drivers/hid/usbhid/usbkbd.c (usb_kbd_keycode)
+    MAP_USB = "usb"
+
+    # Win32: mingw32/winuser.h
+    MAP_WIN32 = "win32"
+
+    # XWin XT: xorg-server/hw/xwin/{winkeybd.c,winkeynames.h}
+    #          (xt + manually transcribed)
+    MAP_XWINXT = "xwinxt"
+
+    # X11: http://cgit.freedesktop.org/xorg/proto/x11proto/plain/keysymdef.h
+    MAP_X11 = "x11"
+
+    # XKBD XT: xf86-input-keyboard/src/at_scancode.c
+    #          (xt + manually transcribed)
+    MAP_XKBDXT = "xkbdxt"
+
+    # Xorg with evdev: linux + an offset
+    MAP_XORGEVDEV = "xorgevdev"
+
+    # Xorg with kbd: xkbdxt + an offset
+    MAP_XORGKBD = "xorgkbd"
+
+    # Xorg with OS-X: osx + an offset
+    MAP_XORGXQUARTZ = "xorgxquartz"
+
+    # Xorg + Cygwin: xwinxt + an offset
+    MAP_XORGXWIN = "xorgxwin"
+
+    # QEMU key numbers: xtkbd + special re-encoding of high bit
+    MAP_QNUM = "qnum"
+
+    # HTML codes
+    MAP_HTML = "html"
+
+    # XKB key names
+    MAP_XKB = "xkb"
+
+    # QEMU keycodes
+    MAP_QCODE = "qcode"
+
+    # Sun / Sparc  scan codes
+    # Reference: "SPARC International Keyboard Spec 1", page 7 "US scan set"
+    MAP_SUN = "sun"
+
+    # Apple Desktop Bus
+    # Reference: http://www.archive.org/stream/apple-guide-macintosh-family-hardware/Apple_Guide_to_the_Macintosh_Family_Hardware_2e#page/n345/mode/2up
+    MAP_ADB = "adb"
+
+    MAP_LIST = (
+        MAP_LINUX,
+        MAP_OSX,
+        MAP_ATSET1,
+        MAP_ATSET2,
+        MAP_ATSET3,
+        MAP_USB,
+        MAP_WIN32,
+        MAP_XWINXT,
+        MAP_XKBDXT,
+        MAP_X11,
+        MAP_HTML,
+        MAP_XKB,
+        MAP_QCODE,
+        MAP_SUN,
+        MAP_ADB,
+
+        # These are derived from maps above
+        MAP_XTKBD,
+        MAP_XORGEVDEV,
+        MAP_XORGKBD,
+        MAP_XORGXQUARTZ,
+        MAP_XORGXWIN,
+        MAP_QNUM,
+    )
+
+    CODE_COLUMNS = {
+        MAP_LINUX: 1,
+        MAP_OSX: 3,
+        MAP_ATSET1: 4,
+        MAP_ATSET2: 5,
+        MAP_ATSET3: 6,
+        MAP_USB: 7,
+        MAP_WIN32: 9,
+        MAP_XWINXT: 10,
+        MAP_XKBDXT: 11,
+        MAP_X11: 13,
+        MAP_HTML: 14,
+        MAP_XKB: 15,
+        MAP_SUN: 17,
+        MAP_ADB: 18,
+    }
+
+    ENUM_COLUMNS = {
+        MAP_QCODE: 14,
+    }
+
+    NAME_COLUMNS = {
+        MAP_LINUX: 0,
+        MAP_OSX: 2,
+        MAP_WIN32: 8,
+        MAP_X11: 12,
+        MAP_HTML: 14,
+        MAP_XKB: 15,
+        MAP_QCODE: 16,
+    }
+
+    ENUM_BOUND = {
+        MAP_QCODE: "Q_KEY_CODE__MAX",
+    }
+
+    def __init__(self):
+
+        self.mapto = {}
+        self.mapfrom = {}
+        self.mapname = {}
+        self.mapchecksum = None
+
+        for name in self.MAP_LIST:
+            # Key is a MAP_LINUX, value is a MAP_XXX
+            self.mapto[name] = {}
+            # key is a MAP_XXX, value is a MAP_LINUX
+            self.mapfrom[name] = {}
+
+        for name in self.NAME_COLUMNS.keys():
+            # key is a MAP_LINUX, value is a string
+            self.mapname[name] = {}
+
+    def _generate_checksum(self, filename):
+        hash = hashlib.sha256()
+        with open(filename, "rb") as f:
+            for chunk in iter(lambda: f.read(4096), b""):
+                hash.update(chunk)
+        self.mapchecksum = hash.hexdigest()
+
+    def load(self, filename):
+        self._generate_checksum(filename)
+
+        with open(filename, 'r') as f:
+            reader = csv.reader(f)
+
+            first = True
+
+            for row in reader:
+                # Discard column headings
+                if first:
+                    first = False
+                    continue
+
+                # We special case MAP_LINUX since that is out
+                # master via which all other mappings are done
+                linux = self.load_linux(row)
+
+                # Now load all the remaining master data values
+                self.load_data(row, linux)
+
+                # Then load all the keycode names
+                self.load_names(row, linux)
+
+                # Finally calculate derived key maps
+                self.derive_data(row, linux)
+
+    def load_linux(self, row):
+        col = self.CODE_COLUMNS[self.MAP_LINUX]
+        linux = row[col]
+
+        if linux.startswith("0x"):
+            linux = int(linux, 16)
+        else:
+            linux = int(linux, 10)
+
+        self.mapto[self.MAP_LINUX][linux] = linux
+        self.mapfrom[self.MAP_LINUX][linux] = linux
+
+        return linux
+
+
+    def load_data(self, row, linux):
+        for mapname in self.CODE_COLUMNS:
+            if mapname == self.MAP_LINUX:
+                continue
+
+            col = self.CODE_COLUMNS[mapname]
+            val = row[col]
+
+            if val == "":
+                continue
+
+            if val.startswith("0x"):
+                val = int(val, 16)
+            elif val.isdigit():
+                val = int(val, 10)
+
+            self.mapto[mapname][linux] = val
+            self.mapfrom[mapname][val] = linux
+
+    def load_names(self, row, linux):
+        for mapname in self.NAME_COLUMNS:
+            col = self.NAME_COLUMNS[mapname]
+            val = row[col]
+
+            if val == "":
+                continue
+
+            self.mapname[mapname][linux] = val
+
+
+    def derive_data(self, row, linux):
+        # Linux RAW is XT scan codes with special encoding of the
+        # 0xe0 scan codes
+        if linux in self.mapto[self.MAP_ATSET1]:
+            at1 = self.mapto[self.MAP_ATSET1][linux]
+            if at1 > 0x7f:
+                assert((at1 & ~0x7f) == 0xe000)
+                xtkbd = 0x100 | (at1 & 0x7f)
+            else:
+                xtkbd = at1
+            self.mapto[self.MAP_XTKBD][linux] = xtkbd
+            self.mapfrom[self.MAP_XTKBD][xtkbd] = linux
+
+        # Xorg KBD is XKBD XT offset by 8
+        if linux in self.mapto[self.MAP_XKBDXT]:
+            xorgkbd = self.mapto[self.MAP_XKBDXT][linux] + 8
+            self.mapto[self.MAP_XORGKBD][linux] = xorgkbd
+            self.mapfrom[self.MAP_XORGKBD][xorgkbd] = linux
+
+        # Xorg evdev is Linux offset by 8
+        self.mapto[self.MAP_XORGEVDEV][linux] = linux + 8
+        self.mapfrom[self.MAP_XORGEVDEV][linux + 8] = linux
+
+        # Xorg XQuartx is OS-X offset by 8
+        if linux in self.mapto[self.MAP_OSX]:
+            xorgxquartz = self.mapto[self.MAP_OSX][linux] + 8
+            self.mapto[self.MAP_XORGXQUARTZ][linux] = xorgxquartz
+            self.mapfrom[self.MAP_XORGXQUARTZ][xorgxquartz] = linux
+
+        # Xorg Xwin (aka Cygwin) is XWin XT offset by 8
+        if linux in self.mapto[self.MAP_XWINXT]:
+            xorgxwin = self.mapto[self.MAP_XWINXT][linux] + 8
+            self.mapto[self.MAP_XORGXWIN][linux] = xorgxwin
+            self.mapfrom[self.MAP_XORGXWIN][xorgxwin] = linux
+
+        # QNUM keycodes are XT scan codes with a slightly
+        # different encoding of 0xe0 scan codes
+        if linux in self.mapto[self.MAP_ATSET1]:
+            at1 = self.mapto[self.MAP_ATSET1][linux]
+            if at1 > 0x7f:
+                assert((at1 & ~0x7f) == 0xe000)
+                qnum = 0x80 | (at1 & 0x7f)
+            else:
+                qnum = at1
+            self.mapto[self.MAP_QNUM][linux] = qnum
+            self.mapfrom[self.MAP_QNUM][qnum] = linux
+
+            # Hack for compatibility with previous mistakes in handling
+            # Print/SysRq. The preferred qnum for Print/SysRq is 0x54,
+            # but QEMU previously allowed 0xb7 too
+            if qnum == 0x54:
+                self.mapfrom[self.MAP_QNUM][0xb7] = self.mapfrom[self.MAP_QNUM][0x54]
+
+        if linux in self.mapname[self.MAP_QCODE]:
+            qcodeenum = self.mapname[self.MAP_QCODE][linux]
+            qcodeenum = "Q_KEY_CODE_" + qcodeenum.upper()
+            self.mapto[self.MAP_QCODE][linux] = qcodeenum
+            self.mapfrom[self.MAP_QCODE][qcodeenum] = linux
+
+class LanguageGenerator(object):
+
+    def _boilerplate(self, lines):
+        raise NotImplementedError()
+
+    def generate_header(self, database, args):
+        today = time.strftime("%Y-%m-%d %H:%M")
+        self._boilerplate([
+            "This file is auto-generated from keymaps.csv on %s" % today,
+            "Database checksum sha256(%s)" % database.mapchecksum,
+            "To re-generate, run:",
+            "  %s" % args,
+        ])
+
+class LanguageSrcGenerator(LanguageGenerator):
+
+    TYPE_INT = "integer"
+    TYPE_STRING = "string"
+    TYPE_ENUM = "enum"
+
+    def _array_start(self, varname, length, defvalue, fromtype, totype):
+        raise NotImplementedError()
+
+    def _array_end(self, fromtype, totype):
+        raise NotImplementedError()
+
+    def _array_entry(self, index, value, comment, fromtype, totype):
+        raise NotImplementedError()
+
+    def generate_code_map(self, varname, database, frommapname, tomapname):
+        if frommapname not in database.mapfrom:
+            raise Exception("Unknown map %s, expected one of %s" % (
+                            frommapname, ", ".join(database.mapfrom.keys())))
+        if tomapname not in database.mapto:
+            raise Exception("Unknown map %s, expected one of %s" % (
+                            tomapname, ", ".join(database.mapto.keys())))
+
+        tolinux = database.mapfrom[frommapname]
+        fromlinux = database.mapto[tomapname]
+
+        if varname is None:
+            varname = "code_map_%s_to_%s" % (frommapname, tomapname)
+
+        if frommapname in database.ENUM_COLUMNS:
+            fromtype = self.TYPE_ENUM
+        elif type(tolinux.keys()[0]) == str:
+            fromtype = self.TYPE_STRING
+        else:
+            fromtype = self.TYPE_INT
+
+        if tomapname in database.ENUM_COLUMNS:
+            totype = self.TYPE_ENUM
+        elif type(fromlinux.values()[0]) == str:
+            totype = self.TYPE_STRING
+        else:
+            totype = self.TYPE_INT
+
+        keys = tolinux.keys()
+        keys.sort()
+        if fromtype == self.TYPE_INT:
+            keys = range(keys[-1] + 1)
+
+        if fromtype == self.TYPE_ENUM:
+            keymax = database.ENUM_BOUND[frommapname]
+        else:
+            keymax = len(keys)
+
+        defvalue = fromlinux.get(0, None)
+        if fromtype == self.TYPE_ENUM:
+            self._array_start(varname, keymax, defvalue, fromtype, totype)
+        else:
+            self._array_start(varname, keymax, None, fromtype, totype)
+
+        for src in keys:
+            linux = tolinux.get(src, None)
+            if linux is None:
+                dst = None
+            else:
+                dst = fromlinux.get(linux, defvalue)
+
+            comment = "%s -> %s -> %s" % (self._label(database, frommapname, src, linux),
+                                          self._label(database, Database.MAP_LINUX, linux, linux),
+                                          self._label(database, tomapname, dst, linux))
+            self._array_entry(src, dst, comment, fromtype, totype)
+        self._array_end(fromtype, totype)
+
+    def generate_code_table(self, varname, database, mapname):
+        if mapname not in database.mapto:
+            raise Exception("Unknown map %s, expected one of %s" % (
+                            mapname, ", ".join(database.mapto.keys())))
+
+        keys = database.mapto[Database.MAP_LINUX].keys()
+        keys.sort()
+        names = [database.mapname[Database.MAP_LINUX].get(key, "unnamed") for key in keys]
+
+        if varname is None:
+            varname = "code_table_%s" % mapname
+
+        if mapname in database.ENUM_COLUMNS:
+            totype = self.TYPE_ENUM
+        elif type(database.mapto[mapname].values()[0]) == str:
+            totype = self.TYPE_STRING
+        else:
+            totype = self.TYPE_INT
+
+        self._array_start(varname, len(keys), None, self.TYPE_INT, totype)
+
+        defvalue = database.mapto[mapname].get(0, None)
+        for i in range(len(keys)):
+            key = keys[i]
+            dst = database.mapto[mapname].get(key, defvalue)
+            self._array_entry(i, dst, names[i], self.TYPE_INT, totype)
+
+        self._array_end(self.TYPE_INT, totype)
+
+    def generate_name_map(self, varname, database, frommapname, tomapname):
+        if frommapname not in database.mapfrom:
+            raise Exception("Unknown map %s, expected one of %s" % (
+                            frommapname, ", ".join(database.mapfrom.keys())))
+        if tomapname not in database.mapname:
+            raise Exception("Unknown map %s, expected one of %s" % (
+                            tomapname, ", ".join(database.mapname.keys())))
+
+        tolinux = database.mapfrom[frommapname]
+        fromlinux = database.mapname[tomapname]
+
+        if varname is None:
+            varname = "name_map_%s_to_%s" % (frommapname, tomapname)
+
+        keys = tolinux.keys()
+        keys.sort()
+        if type(keys[0]) == int:
+            keys = range(keys[-1] + 1)
+
+        if type(keys[0]) == int:
+            fromtype = self.TYPE_INT
+        else:
+            fromtype = self.TYPE_STRING
+
+        self._array_start(varname, len(keys), None, fromtype, self.TYPE_STRING)
+
+        for src in keys:
+            linux = tolinux.get(src, None)
+            if linux is None:
+                dst = None
+            else:
+                dst = fromlinux.get(linux, None)
+
+            comment = "%s -> %s -> %s" % (self._label(database, frommapname, src, linux),
+                                          self._label(database, Database.MAP_LINUX, linux, linux),
+                                          self._label(database, tomapname, dst, linux))
+            self._array_entry(src, dst, comment, fromtype, self.TYPE_STRING)
+        self._array_end(fromtype, self.TYPE_STRING)
+
+    def generate_name_table(self, varname, database, mapname):
+        if mapname not in database.mapname:
+            raise Exception("Unknown map %s, expected one of %s" % (
+                            mapname, ", ".join(database.mapname.keys())))
+
+        keys = database.mapto[Database.MAP_LINUX].keys()
+        keys.sort()
+        names = [database.mapname[Database.MAP_LINUX].get(key, "unnamed") for key in keys]
+
+        if varname is None:
+            varname = "name_table_%s" % mapname
+
+        self._array_start(varname, len(keys), None, self.TYPE_INT, self.TYPE_STRING)
+
+        for i in range(len(keys)):
+            key = keys[i]
+            dst = database.mapname[mapname].get(key, None)
+            self._array_entry(i, dst, names[i], self.TYPE_INT, self.TYPE_STRING)
+
+        self._array_end(self.TYPE_INT, self.TYPE_STRING)
+
+    def _label(self, database, mapname, val, linux):
+        if mapname in database.mapname:
+            return "%s:%s (%s)" % (mapname, val, database.mapname[mapname].get(linux, "unnamed"))
+        else:
+            return "%s:%s" % (mapname, val)
+
+class LanguageDocGenerator(LanguageGenerator):
+
+    def _array_start_name_doc(self, varname, namemap):
+        raise NotImplementedError()
+
+    def _array_start_code_doc(self, varname, namemap, codemap):
+        raise NotImplementedError()
+
+    def _array_end(self):
+        raise NotImplementedError()
+
+    def _array_name_entry(self, value, name):
+        raise NotImplementedError()
+
+    def _array_code_entry(self, value, name):
+        raise NotImplementedError()
+
+    def generate_name_docs(self, varname, database, mapname):
+        if mapname not in database.mapname:
+            raise Exception("Unknown map %s, expected one of %s" % (
+                            mapname, ", ".join(database.mapname.keys())))
+
+        keys = database.mapto[Database.MAP_LINUX].keys()
+        keys.sort()
+        names = [database.mapname[Database.MAP_LINUX].get(key, "unnamed") for key in keys]
+
+        if varname is None:
+            varname = mapname
+
+        self._array_start_name_doc(varname, mapname)
+
+        for i in range(len(keys)):
+            key = keys[i]
+            dst = database.mapname[mapname].get(key, None)
+            self._array_name_entry(key, dst)
+
+        self._array_end()
+
+
+    def generate_code_docs(self, varname, database, mapname):
+        if mapname not in database.mapfrom:
+            raise Exception("Unknown map %s, expected one of %s" % (
+                            mapname, ", ".join(database.mapfrom.keys())))
+
+        tolinux = database.mapfrom[mapname]
+        keys = tolinux.keys()
+        keys.sort()
+        if mapname in database.mapname:
+            names = database.mapname[mapname]
+            namemap = mapname
+        else:
+            names = database.mapname[Database.MAP_LINUX]
+            namemap = Database.MAP_LINUX
+
+        if varname is None:
+            varname = mapname
+
+        self._array_start_code_doc(varname, mapname, namemap)
+
+        for i in range(len(keys)):
+            key = keys[i]
+            self._array_code_entry(key, names.get(tolinux[key], "unnamed"))
+
+        self._array_end()
+
+class CLanguageGenerator(LanguageSrcGenerator):
+
+    def __init__(self, inttypename, strtypename, lentypename):
+        self.inttypename = inttypename
+        self.strtypename = strtypename
+        self.lentypename = lentypename
+
+    def _boilerplate(self, lines):
+        print("/*")
+        for line in lines:
+            print(" * %s" % line)
+        print("*/")
+
+    def _array_start(self, varname, length, defvalue, fromtype, totype):
+        self._varname = varname;
+        totypename = self.strtypename if totype == self.TYPE_STRING else self.inttypename
+        if fromtype in (self.TYPE_INT, self.TYPE_ENUM):
+            if type(length) == str:
+                print("const %s %s[%s] = {" % (totypename, varname, length))
+            else:
+                print("const %s %s[%d] = {" % (totypename, varname, length))
+        else:
+            print("const struct _%s {" % varname)
+            print("  const %s from;" % self.strtypename)
+            print("  const %s to;" % totypename)
+            print("} %s[] = {" % varname)
+
+        if defvalue != None:
+            if totype == self.TYPE_ENUM:
+                if type(length) == str:
+                    print("  [0 ... %s-1] = %s," % (length, defvalue))
+                else:
+                    print("  [0 ... 0x%x-1] = %s," % (length, defvalue))
+            else:
+                if type(length) == str:
+                    print("  [0 ... %s-1] = 0x%x," % (length, defvalue))
+                else:
+                    print("  [0 ... 0x%x-1] = 0x%x," % (length, defvalue))
+
+    def _array_end(self, fromtype, totype):
+        print("};")
+        print("const %s %s_len = sizeof(%s)/sizeof(%s[0]);" %
+              (self.lentypename, self._varname, self._varname, self._varname))
+
+    def _array_entry(self, index, value, comment, fromtype, totype):
+        if value is None:
+            return
+        if fromtype == self.TYPE_INT:
+            indexfmt = "0x%x"
+        elif fromtype == self.TYPE_ENUM:
+            indexfmt = "%s"
+        else:
+            indexfmt = "\"%s\""
+
+        if totype == self.TYPE_INT:
+            valuefmt = "0x%x"
+        elif totype == self.TYPE_ENUM:
+            valuefmt = "%s"
+        else:
+            valuefmt = "\"%s\""
+
+        if fromtype != self.TYPE_STRING:
+            print(("  [" + indexfmt + "] = " + valuefmt + ", /* %s */") % (index, value, comment))
+        else:
+            print(("  {" + indexfmt + ", " + valuefmt + "}, /* %s */") % (index, value, comment))
+
+class CppLanguageGenerator(CLanguageGenerator):
+
+    def _array_start(self, varname, length, defvalue, fromtype, totype):
+        if fromtype == self.TYPE_ENUM:
+            raise NotImplementedError("Enums not supported as source in C++ generator")
+        totypename = "const " + self.strtypename if totype == self.TYPE_STRING else self.inttypename
+        if fromtype == self.TYPE_INT:
+            print("#include <vector>")
+            print("const std::vector<%s> %s = {" % (totypename, varname))
+        else:
+            print("#include <map>")
+            print("#include <string>")
+            print("const std::map<const std::string, %s> %s = {" % (totypename, varname))
+
+    def _array_end(self, fromtype, totype):
+        print("};")
+
+    # designated initializers not available in C++
+    def _array_entry(self, index, value, comment, fromtype, totype):
+        if fromtype == self.TYPE_STRING:
+            return super(CppLanguageGenerator, self)._array_entry(index, value, comment, fromtype, totype)
+
+        if value is None:
+            print("  0, /* %s */" % comment)
+        elif totype == self.TYPE_INT:
+            print("  0x%x, /* %s */" % (value, comment))
+        elif totype == self.TYPE_ENUM:
+            print("  %s, /* %s */" % (value, comment))
+        else:
+            print("  \"%s\", /* %s */" % (value, comment))
+
+class StdCLanguageGenerator(CLanguageGenerator):
+
+    def __init__(self):
+        super(StdCLanguageGenerator, self).__init__("unsigned short", "char *", "unsigned int")
+
+class StdCppLanguageGenerator(CppLanguageGenerator):
+
+    def __init__(self):
+        super(StdCppLanguageGenerator, self).__init__("unsigned short", "char *", "unsigned int")
+
+class GLib2LanguageGenerator(CLanguageGenerator):
+
+    def __init__(self):
+        super(GLib2LanguageGenerator, self).__init__("guint16", "gchar *", "guint")
+
+class PythonLanguageGenerator(LanguageSrcGenerator):
+
+    def _boilerplate(self, lines):
+        print("#")
+        for line in lines:
+            print("# %s" % line)
+        print("#")
+
+    def _array_start(self, varname, length, defvalue, fromtype, totype):
+        if fromtype == self.TYPE_ENUM:
+            raise NotImplementedError("Enums not supported as source in Python generator")
+
+        if fromtype != self.TYPE_STRING:
+            print("%s = [" % varname)
+        else:
+            print("%s = {" % varname)
+
+    def _array_end(self, fromtype, totype):
+        if fromtype != self.TYPE_STRING:
+            print("]")
+        else:
+            print("}")
+
+    def _array_entry(self, index, value, comment, fromtype, totype):
+        if fromtype == self.TYPE_INT:
+            if value is None:
+                print("  None, # %s" % (comment))
+            elif totype == self.TYPE_INT:
+                print("  0x%x, # %s" % (value, comment))
+            elif totype == self.TYPE_ENUM:
+                print("  %s, # %s" % (value, comment))
+            else:
+                print("  \"%s\", # %s" % (value, comment))
+        else:
+            if value is None:
+                print("  \"%s\": None, # %s" % (index, comment))
+            elif totype == self.TYPE_INT:
+                print("  \"%s\": 0x%x, # %s" % (index, value, comment))
+            elif totype == self.TYPE_ENUM:
+                print("  \"%s\": %s, # %s" % (index, value, comment))
+            else:
+                print("  \"%s\": \"%s\", # %s" % (index, value, comment))
+
+class PerlLanguageGenerator(LanguageSrcGenerator):
+
+    def _boilerplate(self, lines):
+        print("#")
+        for line in lines:
+            print("# %s" % line)
+        print("#")
+
+    def _array_start(self, varname, length, defvalue, fromtype, totype):
+        if fromtype == self.TYPE_ENUN:
+            raise NotImplementedError("Enums not supported as source in Python generator")
+        if fromtype == self.TYPE_INT:
+            print("my @%s = (" % varname)
+        else:
+            print("my %%%s = (" % varname)
+
+    def _array_end(self, fromtype, totype):
+        print(");")
+
+    def _array_entry(self, index, value, comment, fromtype, totype):
+        if fromtype == self.TYPE_INT:
+            if value is None:
+                print("  undef, # %s" % (comment))
+            elif totype == self.TYPE_INT:
+                print("  0x%x, # %s" % (value, comment))
+            elif totype == self.TYPE_ENUM:
+                print("  %s, # %s" % (value, comment))
+            else:
+                print("  \"%s\", # %s" % (value, comment))
+        else:
+            if value is None:
+                print("  \"%s\", undef, # %s" % (index, comment))
+            elif totype == self.TYPE_INT:
+                print("  \"%s\", 0x%x, # %s" % (index, value, comment))
+            elif totype == self.TYPE_ENUM:
+                print("  \"%s\", 0x%x, # %s" % (index, value, comment))
+            else:
+                print("  \"%s\", \"%s\", # %s" % (index, value, comment))
+
+class JavaScriptLanguageGenerator(LanguageSrcGenerator):
+
+    def _boilerplate(self, lines):
+        print("/*")
+        for line in lines:
+            print(" * %s" % line)
+        print("*/")
+
+    def _array_start(self, varname, length, defvalue, fromtype, totype):
+        print("export default {")
+
+    def _array_end(self, fromtype, totype):
+        print("};")
+
+    def _array_entry(self, index, value, comment, fromtype, totype):
+        if value is None:
+            return
+
+        if fromtype == self.TYPE_INT:
+            fromfmt = "0x%x"
+        elif fromtype == self.TYPE_ENUM:
+            fromfmt = "%s"
+        else:
+            fromfmt = "\"%s\""
+
+        if totype == self.TYPE_INT:
+            tofmt = "0x%x"
+        elif totype == self.TYPE_ENUM:
+            tofmt = "%s"
+        else:
+            tofmt = "\"%s\""
+
+        print(("  " + fromfmt + ": " + tofmt + ", /* %s */") % (index, value, comment))
+
+class PodLanguageGenerator(LanguageDocGenerator):
+
+    def _boilerplate(self, lines):
+        print("#")
+        for line in lines:
+            print("# %s" % line)
+        print("#")
+
+    def _array_start_name_doc(self, varname, namemap):
+        print("=head1 %s" % varname)
+        print("")
+        print("List of %s key code names, with corresponding key code values" % namemap)
+        print("")
+        print("=over 4")
+        print("")
+
+    def _array_start_code_doc(self, varname, codemap, namemap):
+        print("=head1 %s" % varname)
+        print("")
+        print("List of %s key code values, with corresponding %s key code names" % (codemap, namemap))
+        print("")
+        print("=over 4")
+        print("")
+
+    def _array_end(self):
+        print("=back")
+        print("")
+
+    def _array_name_entry(self, value, name):
+        print("=item %s" % name)
+        print("")
+        print("Key value %d (0x%x)" % (value, value))
+        print("")
+
+    def _array_code_entry(self, value, name):
+        print("=item %d (0x%x)" % (value, value))
+        print("")
+        print("Key name %s" % name)
+        print("")
+
+SRC_GENERATORS = {
+    "stdc": StdCLanguageGenerator(),
+    "stdc++": StdCppLanguageGenerator(),
+    "glib2": GLib2LanguageGenerator(),
+    "python2": PythonLanguageGenerator(),
+    "python3": PythonLanguageGenerator(),
+    "perl": PerlLanguageGenerator(),
+    "js": JavaScriptLanguageGenerator(),
+}
+DOC_GENERATORS = {
+    "pod": PodLanguageGenerator(),
+}
+
+def code_map(args):
+    database = Database()
+    database.load(args.keymaps)
+
+    cliargs = ["keymap-gen", "--lang=%s" % args.lang]
+    if args.varname is not None:
+        cliargs.append("--varname=%s" % args.varname)
+    cliargs.extend(["code-map", "keymaps.csv", args.frommapname, args.tomapname])
+    SRC_GENERATORS[args.lang].generate_header(database, " ".join(cliargs))
+
+    SRC_GENERATORS[args.lang].generate_code_map(args.varname, database, args.frommapname, args.tomapname)
+
+def code_table(args):
+    database = Database()
+    database.load(args.keymaps)
+
+    cliargs = ["keymap-gen", "--lang=%s" % args.lang]
+    if args.varname is not None:
+        cliargs.append("--varname=%s" % args.varname)
+    cliargs.extend(["code-table", "keymaps.csv", args.mapname])
+    SRC_GENERATORS[args.lang].generate_header(database, " ".join(cliargs))
+
+    SRC_GENERATORS[args.lang].generate_code_table(args.varname, database, args.mapname)
+
+def name_map(args):
+    database = Database()
+    database.load(args.keymaps)
+
+    cliargs = ["keymap-gen", "--lang=%s" % args.lang]
+    if args.varname is not None:
+        cliargs.append("--varname=%s" % args.varname)
+    cliargs.extend(["name-map", "keymaps.csv", args.frommapname, args.tomapname])
+    SRC_GENERATORS[args.lang].generate_header(database, " ".join(cliargs))
+
+    SRC_GENERATORS[args.lang].generate_name_map(args.varname, database, args.frommapname, args.tomapname)
+
+def name_table(args):
+    database = Database()
+    database.load(args.keymaps)
+
+
+    cliargs = ["keymap-gen", "--lang=%s" % args.lang]
+    if args.varname is not None:
+        cliargs.append("--varname=%s" % args.varname)
+    cliargs.extend(["name-table", "keymaps.csv", args.mapname])
+    SRC_GENERATORS[args.lang].generate_header(database, " ".join(cliargs))
+
+    SRC_GENERATORS[args.lang].generate_name_table(args.varname, database, args.mapname)
+
+def code_docs(args):
+    database = Database()
+    database.load(args.keymaps)
+
+
+    cliargs = ["keymap-gen", "--lang=%s" % args.lang]
+    if args.varname is not None:
+        cliargs.append("--varname=%s" % args.varname)
+    cliargs.extend(["code-docs", "keymaps.csv", args.mapname])
+    DOC_GENERATORS[args.lang].generate_header(database, " ".join(cliargs))
+
+    DOC_GENERATORS[args.lang].generate_code_docs(args.varname, database, args.mapname)
+
+def name_docs(args):
+    database = Database()
+    database.load(args.keymaps)
+
+
+    cliargs = ["keymap-gen", "--lang=%s" % args.lang]
+    if args.varname is not None:
+        cliargs.append("--varname=%s" % args.varname)
+    cliargs.extend(["name-docs", "keymaps.csv", args.mapname])
+    DOC_GENERATORS[args.lang].generate_header(database, " ".join(cliargs))
+
+    DOC_GENERATORS[args.lang].generate_name_docs(args.varname, database, args.mapname)
+
+def usage():
+    print ("Please select a command:")
+    print ("  'code-map', 'code-table', 'name-map', 'name-table', 'docs'")
+    sys.exit(1)
+
+def main():
+    parser = argparse.ArgumentParser()
+
+    parser.add_argument("--lang", default="stdc",
+                        help="Output language, (src=%s, doc=%s)" % (
+                            ",".join(SRC_GENERATORS.keys()),
+                            ",".join(DOC_GENERATORS.keys())))
+    parser.add_argument("--varname", default=None,
+                        help="Data variable name")
+
+    subparsers = parser.add_subparsers(help="sub-command help")
+
+    codemapparser = subparsers.add_parser("code-map", help="Generate a mapping between code tables")
+    codemapparser.add_argument("keymaps", help="Path to keymap CSV data file")
+    codemapparser.add_argument("frommapname", help="Source code table name")
+    codemapparser.add_argument("tomapname", help="Target code table name")
+    codemapparser.set_defaults(func=code_map)
+
+    codetableparser = subparsers.add_parser("code-table", help="Generate a flat code table")
+    codetableparser.add_argument("keymaps", help="Path to keymap CSV data file")
+    codetableparser.add_argument("mapname", help="Code table name")
+    codetableparser.set_defaults(func=code_table)
+
+    namemapparser = subparsers.add_parser("name-map", help="Generate a mapping to names")
+    namemapparser.add_argument("keymaps", help="Path to keymap CSV data file")
+    namemapparser.add_argument("frommapname", help="Source code table name")
+    namemapparser.add_argument("tomapname", help="Target name table name")
+    namemapparser.set_defaults(func=name_map)
+
+    nametableparser = subparsers.add_parser("name-table", help="Generate a flat name table")
+    nametableparser.add_argument("keymaps", help="Path to keymap CSV data file")
+    nametableparser.add_argument("mapname", help="Name table name")
+    nametableparser.set_defaults(func=name_table)
+
+    codedocsparser = subparsers.add_parser("code-docs", help="Generate code documentation")
+    codedocsparser.add_argument("keymaps", help="Path to keymap CSV data file")
+    codedocsparser.add_argument("mapname", help="Code table name")
+    codedocsparser.set_defaults(func=code_docs)
+
+    namedocsparser = subparsers.add_parser("name-docs", help="Generate name documentation")
+    namedocsparser.add_argument("keymaps", help="Path to keymap CSV data file")
+    namedocsparser.add_argument("mapname", help="Name table name")
+    namedocsparser.set_defaults(func=name_docs)
+
+    args = parser.parse_args()
+    if hasattr(args, "func"):
+        args.func(args)
+    else:
+        usage()
+
+
+main()
diff --git a/qemu b/qemu
index 4cd42653f5c1df326a2678a84f24a78fb9601277..7c1beb52ed86191d9e965444d934adaa2531710f 160000 (submodule)
--- a/qemu
+++ b/qemu
@@ -1 +1 @@
-Subproject commit 4cd42653f5c1df326a2678a84f24a78fb9601277
+Subproject commit 7c1beb52ed86191d9e965444d934adaa2531710f