]> git.proxmox.com Git - pve-qemu.git/blobdiff - debian/patches/pve/0027-PVE-Backup-add-vma-backup-format-code.patch
regenerate patch stats
[pve-qemu.git] / debian / patches / pve / 0027-PVE-Backup-add-vma-backup-format-code.patch
index b44d882b5ff752d95015a8b4dc71675356eee659..15db16fe214ce237af299a78ed5a4697e56942ae 100644 (file)
@@ -3,58 +3,70 @@ From: Dietmar Maurer <dietmar@proxmox.com>
 Date: Mon, 6 Apr 2020 12:16:57 +0200
 Subject: [PATCH] PVE-Backup: add vma backup format code
 
+Notes about partial restoring: skipping a certain drive is done via a
+map line of the form skip=drive-scsi0. Since in PVE, most archives are
+compressed and piped to vma for restore, it's not easily possible to
+skip reads.
+
+Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
+[FE: improvements during create
+     allow partial restore]
+Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
 ---
- Makefile      |   3 +-
Makefile.objs |   1 +
- vma-reader.c  | 857 ++++++++++++++++++++++++++++++++++++++++++++++++++
- vma-writer.c  | 790 ++++++++++++++++++++++++++++++++++++++++++++++
- vma.c         | 839 ++++++++++++++++++++++++++++++++++++++++++++++++
- vma.h         | 150 +++++++++
- 6 files changed, 2639 insertions(+), 1 deletion(-)
+ block/meson.build |   2 +
meson.build       |   5 +
+ vma-reader.c      | 867 ++++++++++++++++++++++++++++++++++++++++++++
+ vma-writer.c      | 793 ++++++++++++++++++++++++++++++++++++++++
+ vma.c             | 900 ++++++++++++++++++++++++++++++++++++++++++++++
+ vma.h             | 150 ++++++++
+ 6 files changed, 2717 insertions(+)
  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 8a9113e666..74c2039005 100644
---- a/Makefile
-+++ b/Makefile
-@@ -479,7 +479,7 @@ dummy := $(call unnest-vars,, \
- include $(SRC_PATH)/tests/Makefile.include
--all: $(DOCS) $(if $(BUILD_DOCS),sphinxdocs) $(TOOLS) $(HELPERS-y) recurse-all modules $(vhost-user-json-y)
-+all: $(DOCS) $(if $(BUILD_DOCS),sphinxdocs) $(TOOLS) vma$(EXESUF) $(HELPERS-y) recurse-all modules $(vhost-user-json-y)
+diff --git a/block/meson.build b/block/meson.build
+index 253fe49fa2..744b698a82 100644
+--- a/block/meson.build
++++ b/block/meson.build
+@@ -47,6 +47,8 @@ block_ss.add(files(
+   'zeroinit.c',
+ ), zstd, zlib, gnutls)
  
- qemu-version.h: FORCE
-       $(call quiet-command, \
-@@ -608,6 +608,7 @@ qemu-img$(EXESUF): qemu-img.o $(authz-obj-y) $(block-obj-y) $(crypto-obj-y) $(io
- qemu-nbd$(EXESUF): qemu-nbd.o $(authz-obj-y) $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS)
- qemu-io$(EXESUF): qemu-io.o $(authz-obj-y) $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS)
- qemu-storage-daemon$(EXESUF): qemu-storage-daemon.o $(authz-obj-y) $(block-obj-y) $(crypto-obj-y) $(chardev-obj-y) $(io-obj-y) $(qom-obj-y) $(storage-daemon-obj-y) $(COMMON_LDADDS)
-+vma$(EXESUF): vma.o vma-reader.o $(authz-obj-y) $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS)
++block_ss.add(files('../vma-writer.c'), libuuid)
++
+ softmmu_ss.add(when: 'CONFIG_TCG', if_true: files('blkreplay.c'))
+ softmmu_ss.add(files('block-ram-registrar.c'))
  
- qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o $(COMMON_LDADDS)
+diff --git a/meson.build b/meson.build
+index 30447cfaef..38a4e2bcef 100644
+--- a/meson.build
++++ b/meson.build
+@@ -1527,6 +1527,8 @@ keyutils = dependency('libkeyutils', required: false,
  
-diff --git a/Makefile.objs b/Makefile.objs
-index d0b4dde836..05031a3da7 100644
---- a/Makefile.objs
-+++ b/Makefile.objs
-@@ -18,6 +18,7 @@ block-obj-y += block.o blockjob.o job.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
+ has_gettid = cc.has_function('gettid')
  
- block-obj-m = block/
++libuuid = cc.find_library('uuid', required: true)
++
+ # libselinux
+ selinux = dependency('libselinux',
+                      required: get_option('selinux'),
+@@ -3650,6 +3652,9 @@ if have_tools
+                dependencies: [blockdev, qemuutil, gnutls, selinux],
+                install: true)
  
++  vma = executable('vma', files('vma.c', 'vma-reader.c') + genh,
++                   dependencies: [authz, block, crypto, io, qom], install: true)
++
+   subdir('storage-daemon')
+   subdir('contrib/rdmacm-mux')
+   subdir('contrib/elf2dmp')
 diff --git a/vma-reader.c b/vma-reader.c
 new file mode 100644
-index 0000000000..2b1d1cdab3
+index 0000000000..81a891c6b1
 --- /dev/null
 +++ b/vma-reader.c
-@@ -0,0 +1,857 @@
+@@ -0,0 +1,867 @@
 +/*
 + * VMA: Virtual Machine Archive
 + *
@@ -72,7 +84,6 @@ index 0000000000..2b1d1cdab3
 +#include <glib.h>
 +#include <uuid/uuid.h>
 +
-+#include "qemu-common.h"
 +#include "qemu/timer.h"
 +#include "qemu/ratelimit.h"
 +#include "vma.h"
@@ -86,6 +97,7 @@ index 0000000000..2b1d1cdab3
 +    bool write_zeroes;
 +    unsigned long *bitmap;
 +    int bitmap_size;
++    bool skip;
 +}  VmaRestoreState;
 +
 +struct VmaReader {
@@ -249,6 +261,9 @@ index 0000000000..2b1d1cdab3
 +        if (vmar->rstate[i].bitmap) {
 +            g_free(vmar->rstate[i].bitmap);
 +        }
++        if (vmar->rstate[i].target) {
++            blk_unref(vmar->rstate[i].target);
++        }
 +    }
 +
 +    if (vmar->md5csum) {
@@ -480,13 +495,14 @@ index 0000000000..2b1d1cdab3
 +}
 +
 +static void allocate_rstate(VmaReader *vmar,  guint8 dev_id,
-+                            BlockBackend *target, bool write_zeroes)
++                            BlockBackend *target, bool write_zeroes, bool skip)
 +{
 +    assert(vmar);
 +    assert(dev_id);
 +
 +    vmar->rstate[dev_id].target = target;
 +    vmar->rstate[dev_id].write_zeroes = write_zeroes;
++    vmar->rstate[dev_id].skip = skip;
 +
 +    int64_t size = vmar->devinfo[dev_id].size;
 +
@@ -501,28 +517,30 @@ index 0000000000..2b1d1cdab3
 +}
 +
 +int vma_reader_register_bs(VmaReader *vmar, guint8 dev_id, BlockBackend *target,
-+                           bool write_zeroes, Error **errp)
++                           bool write_zeroes, bool skip, Error **errp)
 +{
 +    assert(vmar);
-+    assert(target != NULL);
++    assert(target != NULL || skip);
 +    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;
++    assert(vmar->rstate[dev_id].target == NULL && !vmar->rstate[dev_id].skip);
 +
-+    /* 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;
++    if (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);
++    allocate_rstate(vmar, dev_id, target, write_zeroes, skip);
 +
 +    return 0;
 +}
@@ -580,7 +598,7 @@ index 0000000000..2b1d1cdab3
 +            }
 +        }
 +    } else {
-+        int res = blk_pwrite(target, sector_num * BDRV_SECTOR_SIZE, buf, nb_sectors * BDRV_SECTOR_SIZE, 0);
++        int res = blk_pwrite(target, sector_num * BDRV_SECTOR_SIZE, nb_sectors * BDRV_SECTOR_SIZE, buf, 0);
 +        if (res < 0) {
 +            error_setg(errp, "blk_pwrite to %s failed (%d)",
 +                       bdrv_get_device_name(blk_bs(target)), res);
@@ -615,19 +633,23 @@ index 0000000000..2b1d1cdab3
 +        VmaRestoreState *rstate = &vmar->rstate[dev_id];
 +        BlockBackend *target = NULL;
 +
++        bool skip = rstate->skip;
++
 +        if (dev_id != vmar->vmstate_stream) {
 +            target = rstate->target;
-+            if (!verify && !target) {
++            if (!verify && !target && !skip) {
 +                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;
++            if (!skip) {
++                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);
 +            }
-+            vma_reader_set_bitmap(rstate, cluster_num, 1);
 +
 +            max_sector = vmar->devinfo[dev_id].size/BDRV_SECTOR_SIZE;
 +        } else {
@@ -673,7 +695,7 @@ index 0000000000..2b1d1cdab3
 +                return -1;
 +            }
 +
-+            if (!verify) {
++            if (!verify && !skip) {
 +                int nb_sectors = end_sector - sector_num;
 +                if (restore_write_data(vmar, dev_id, target, vmstate_fd,
 +                                       buf + start, sector_num, nb_sectors,
@@ -709,7 +731,7 @@ index 0000000000..2b1d1cdab3
 +                        return -1;
 +                    }
 +
-+                    if (!verify) {
++                    if (!verify && !skip) {
 +                        int nb_sectors = end_sector - sector_num;
 +                        if (restore_write_data(vmar, dev_id, target, vmstate_fd,
 +                                               buf + start, sector_num,
@@ -734,7 +756,7 @@ index 0000000000..2b1d1cdab3
 +                            vmar->partial_zero_cluster_data += zero_size;
 +                        }
 +
-+                        if (rstate->write_zeroes && !verify) {
++                        if (rstate->write_zeroes && !verify && !skip) {
 +                            if (restore_write_data(vmar, dev_id, target, vmstate_fd,
 +                                                   zero_vma_block, sector_num,
 +                                                   nb_sectors, errp) < 0) {
@@ -905,7 +927,7 @@ index 0000000000..2b1d1cdab3
 +
 +    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);
++            allocate_rstate(vmar, dev_id, NULL, false, false);
 +        }
 +    }
 +
@@ -914,10 +936,10 @@ index 0000000000..2b1d1cdab3
 +
 diff --git a/vma-writer.c b/vma-writer.c
 new file mode 100644
-index 0000000000..f5d2c5d23c
+index 0000000000..ac7da237d0
 --- /dev/null
 +++ b/vma-writer.c
-@@ -0,0 +1,790 @@
+@@ -0,0 +1,793 @@
 +/*
 + * VMA: Virtual Machine Archive
 + *
@@ -941,6 +963,7 @@ index 0000000000..f5d2c5d23c
 +#include "qemu/main-loop.h"
 +#include "qemu/coroutine.h"
 +#include "qemu/cutils.h"
++#include "qemu/memalign.h"
 +
 +#define DEBUG_VMA 0
 +
@@ -1124,9 +1147,9 @@ index 0000000000..f5d2c5d23c
 +    assert(qemu_in_coroutine());
 +    AioContext *ctx = qemu_get_current_aio_context();
 +    aio_set_fd_handler(ctx, fd, false, NULL, (IOHandler *)qemu_coroutine_enter,
-+                       NULL, qemu_coroutine_self());
++                       NULL, NULL, qemu_coroutine_self());
 +    qemu_coroutine_yield();
-+    aio_set_fd_handler(ctx, fd, false, NULL, NULL, NULL, NULL);
++    aio_set_fd_handler(ctx, fd, false, NULL, NULL, NULL, NULL, NULL);
 +}
 +
 +static ssize_t coroutine_fn
@@ -1213,23 +1236,25 @@ index 0000000000..f5d2c5d23c
 +
 +        if ((stat(filename, &st) == 0) && S_ISFIFO(st.st_mode)) {
 +            oflags = O_NONBLOCK|O_WRONLY;
-+            vmaw->fd = qemu_open(filename, oflags, 0644);
++            vmaw->fd = qemu_open(filename, oflags, errp);
 +        } else if (strstart(filename, "/dev/fdset/", &tmp_id_str)) {
 +            oflags = O_NONBLOCK|O_WRONLY;
-+            vmaw->fd = qemu_open(filename, oflags, 0644);
++            vmaw->fd = qemu_open(filename, oflags, errp);
 +        } else if (strstart(filename, "/dev/fdname/", &tmp_id_str)) {
-+            vmaw->fd = monitor_get_fd(cur_mon, tmp_id_str, errp);
++            vmaw->fd = monitor_get_fd(monitor_cur(), 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);
++            oflags = O_NONBLOCK|O_DIRECT|O_WRONLY|O_EXCL;
++            vmaw->fd = qemu_create(filename, oflags, 0644, errp);
 +        }
 +
 +        if (vmaw->fd < 0) {
++            error_free(*errp);
++            *errp = NULL;
 +            error_setg(errp, "can't open file %s - %s\n", filename,
 +                       g_strerror(errno));
 +            goto err;
@@ -1710,10 +1735,10 @@ index 0000000000..f5d2c5d23c
 +}
 diff --git a/vma.c b/vma.c
 new file mode 100644
-index 0000000000..2eea2fc281
+index 0000000000..cf2a2a74af
 --- /dev/null
 +++ b/vma.c
-@@ -0,0 +1,839 @@
+@@ -0,0 +1,900 @@
 +/*
 + * VMA: Virtual Machine Archive
 + *
@@ -1731,11 +1756,11 @@ index 0000000000..2eea2fc281
 +#include <glib.h>
 +
 +#include "vma.h"
-+#include "qemu-common.h"
 +#include "qemu/module.h"
 +#include "qemu/error-report.h"
 +#include "qemu/main-loop.h"
 +#include "qemu/cutils.h"
++#include "qemu/memalign.h"
 +#include "qapi/qmp/qdict.h"
 +#include "sysemu/block-backend.h"
 +
@@ -1747,7 +1772,7 @@ index 0000000000..2eea2fc281
 +        "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 extract <filename> [-d <drive-list>] [-r <fifo>] <targetdir>\n"
 +        "vma verify <filename> [-v]\n"
 +        ;
 +
@@ -1854,6 +1879,7 @@ index 0000000000..2eea2fc281
 +    char *throttling_group;
 +    char *cache;
 +    bool write_zero;
++    bool skip;
 +} RestoreMap;
 +
 +static bool try_parse_option(char **line, const char *optname, char **out, const char *inbuf) {
@@ -1891,9 +1917,10 @@ index 0000000000..2eea2fc281
 +    const char *filename;
 +    const char *dirname;
 +    const char *readmap = NULL;
++    const gchar **drive_list = NULL;
 +
 +    for (;;) {
-+        c = getopt(argc, argv, "hvr:");
++        c = getopt(argc, argv, "hvd:r:");
 +        if (c == -1) {
 +            break;
 +        }
@@ -1902,6 +1929,9 @@ index 0000000000..2eea2fc281
 +        case 'h':
 +            help();
 +            break;
++        case 'd':
++            drive_list = g_strsplit(optarg, ",", 254);
++            break;
 +        case 'r':
 +            readmap = optarg;
 +            break;
@@ -1961,47 +1991,61 @@ index 0000000000..2eea2fc281
 +            char *bps = NULL;
 +            char *group = NULL;
 +            char *cache = NULL;
++            char *devname = NULL;
++            bool skip = false;
++            uint64_t bps_value = 0;
++            const char *path = NULL;
++            bool write_zero = true;
++
 +            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) {
++                len = len - 1;
++                if (len == 0) {
 +                    break;
 +                }
 +            }
 +
-+            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) &&
-+                    !try_parse_option(&line, "cache", &cache, inbuf))
-+                {
-+                    break;
++            if (strncmp(line, "skip", 4) == 0) {
++                if (len < 6 || line[4] != '=') {
++                    g_error("read map failed - option 'skip' has no value ('%s')",
++                            inbuf);
++                } else {
++                    devname = line + 5;
++                    skip = true;
++                }
++            } else {
++                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) &&
++                        !try_parse_option(&line, "cache", &cache, inbuf))
++                    {
++                        break;
++                    }
 +                }
-+            }
 +
-+            uint64_t bps_value = 0;
-+            if (bps) {
-+                bps_value = verify_u64(bps);
-+                g_free(bps);
-+            }
++                if (bps) {
++                    bps_value = verify_u64(bps);
++                    g_free(bps);
++                }
 +
-+            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);
++                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);
++                }
++
++                path = extract_devname(path, &devname, -1);
 +            }
 +
-+            char *devname = NULL;
-+            path = extract_devname(path, &devname, -1);
 +            if (!devname) {
 +                g_error("read map failed - no dev name specified ('%s')",
 +                        inbuf);
@@ -2015,6 +2059,7 @@ index 0000000000..2eea2fc281
 +            map->throttling_group = group;
 +            map->cache = cache;
 +            map->write_zero = write_zero;
++            map->skip = skip;
 +
 +            g_hash_table_insert(devmap, map->devname, map);
 +
@@ -2023,14 +2068,12 @@ index 0000000000..2eea2fc281
 +
 +    int i;
 +    int vmstate_fd = -1;
-+    guint8 vmstate_stream = 0;
-+
-+    BlockBackend *blk = NULL;
++    bool drive_rename_bitmap[255];
++    memset(drive_rename_bitmap, 0, sizeof(drive_rename_bitmap));
 +
 +    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) {
@@ -2046,8 +2089,25 @@ index 0000000000..2eea2fc281
 +            const char *cache = NULL;
 +            int flags = BDRV_O_RDWR;
 +            bool write_zero = true;
++            bool skip = false;
++
++            BlockBackend *blk = NULL;
++
++            if (drive_list) {
++                skip = true;
++                int j;
++                for (j = 0; drive_list[j]; j++) {
++                    if (strcmp(drive_list[j], di->devname) == 0) {
++                        skip = false;
++                        drive_rename_bitmap[i] = true;
++                        break;
++                    }
++                }
++            } else {
++                drive_rename_bitmap[i] = true;
++            }
 +
-+            if (readmap) {
++            if (!skip && readmap) {
 +                RestoreMap *map;
 +                map = (RestoreMap *)g_hash_table_lookup(devmap, di->devname);
 +                if (map == NULL) {
@@ -2059,7 +2119,8 @@ index 0000000000..2eea2fc281
 +                throttling_group = map->throttling_group;
 +                cache = map->cache;
 +                write_zero = map->write_zero;
-+            } else {
++                skip = map->skip;
++            } else if (!skip) {
 +                devfn = g_strdup_printf("%s/tmp-disk-%s.raw",
 +                                        dirname, di->devname);
 +                printf("DEVINFO %s %zd\n", devfn, di->size);
@@ -2077,57 +2138,60 @@ index 0000000000..2eea2fc281
 +                write_zero = false;
 +            }
 +
-+          size_t devlen = strlen(devfn);
-+          QDict *options = NULL;
-+            bool writethrough;
-+            if (format) {
-+                /* explicit format from commandline */
-+                options = qdict_new();
-+                qdict_put_str(options, "driver", 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_str(options, "driver", "raw");
-+          }
-+            if (cache && bdrv_parse_cache_mode(cache, &flags, &writethrough)) {
-+                g_error("invalid cache option: %s\n", cache);
-+            }
++            if (!skip) {
++                size_t devlen = strlen(devfn);
++                QDict *options = NULL;
++                bool writethrough;
++                if (format) {
++                    /* explicit format from commandline */
++                    options = qdict_new();
++                    qdict_put_str(options, "driver", 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_str(options, "driver", "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 (cache && bdrv_parse_cache_mode(cache, &flags, &writethrough)) {
++                    g_error("invalid cache option: %s\n", cache);
++                }
 +
-+            if (cache) {
-+                blk_set_enable_write_cache(blk, !writethrough);
-+            }
++                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 (cache) {
++                    blk_set_enable_write_cache(blk, !writethrough);
++                }
 +
-+            if (throttling_bps) {
-+                if (!throttling_group) {
-+                    blk_io_limits_enable(blk, devfn);
++                if (throttling_group) {
++                    blk_io_limits_enable(blk, throttling_group);
 +                }
 +
-+                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");
++                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);
 +                }
-+                blk_set_io_limits(blk, &cfg);
 +            }
 +
-+            if (vma_reader_register_bs(vmar, i, blk, write_zero, &errp) < 0) {
++            if (vma_reader_register_bs(vmar, i, blk, write_zero, skip, &errp) < 0) {
 +                g_error("%s", error_get_pretty(errp));
 +            }
 +
@@ -2137,6 +2201,10 @@ index 0000000000..2eea2fc281
 +        }
 +    }
 +
++    if (drive_list) {
++        g_strfreev(drive_list);
++    }
++
 +    if (vma_reader_restore(vmar, vmstate_fd, verbose, &errp) < 0) {
 +        g_error("restore failed - %s", error_get_pretty(errp));
 +    }
@@ -2144,7 +2212,7 @@ index 0000000000..2eea2fc281
 +    if (!readmap) {
 +        for (i = 1; i < 255; i++) {
 +            VmaDeviceInfo *di = vma_reader_get_device_info(vmar, i);
-+            if (di && (i != vmstate_stream)) {
++            if (di && drive_rename_bitmap[i]) {
 +                char *tmpfn = g_strdup_printf("%s/tmp-disk-%s.raw",
 +                                              dirname, di->devname);
 +                char *fn = g_strdup_printf("%s/disk-%s.raw",
@@ -2159,8 +2227,6 @@ index 0000000000..2eea2fc281
 +
 +    vma_reader_destroy(vmar);
 +
-+    blk_unref(blk);
-+
 +    bdrv_close_all();
 +
 +    return ret;
@@ -2245,7 +2311,7 @@ index 0000000000..2eea2fc281
 +    struct iovec iov;
 +    QEMUIOVector qiov;
 +
-+    int64_t start, end;
++    int64_t start, end, readlen;
 +    int ret = 0;
 +
 +    unsigned char *buf = blk_blockalign(job->target, VMA_CLUSTER_SIZE);
@@ -2259,16 +2325,24 @@ index 0000000000..2eea2fc281
 +        iov.iov_len = VMA_CLUSTER_SIZE;
 +        qemu_iovec_init_external(&qiov, &iov, 1);
 +
++        if (start + 1 == end) {
++            memset(buf, 0, VMA_CLUSTER_SIZE);
++            readlen = job->len - start * VMA_CLUSTER_SIZE;
++            assert(readlen > 0 && readlen <= VMA_CLUSTER_SIZE);
++        } else {
++            readlen = VMA_CLUSTER_SIZE;
++        }
++
 +        ret = blk_co_preadv(job->target, start * VMA_CLUSTER_SIZE,
-+                            VMA_CLUSTER_SIZE, &qiov, 0);
++                            readlen, &qiov, 0);
 +        if (ret < 0) {
-+            vma_writer_set_error(job->vmaw, "read error", -1);
++            vma_writer_set_error(job->vmaw, "read error");
 +            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);
++            vma_writer_set_error(job->vmaw, "backup_dump_cb vma_writer_write failed");
 +            goto out;
 +        }
 +    }
@@ -2289,6 +2363,7 @@ index 0000000000..2eea2fc281
 +    int i, c;
 +    int verbose = 0;
 +    const char *archivename;
++    GList *backup_coroutines = NULL;
 +    GList *config_files = NULL;
 +
 +    for (;;) {
@@ -2377,7 +2452,9 @@ index 0000000000..2eea2fc281
 +        job->dev_id = dev_id;
 +
 +        Coroutine *co = qemu_coroutine_create(backup_run, job);
-+        qemu_coroutine_enter(co);
++        // Don't enter coroutine yet, because it might write the header before
++        // all streams can be registered.
++        backup_coroutines = g_list_append(backup_coroutines, co);
 +    }
 +
 +    VmaStatus vmastat;
@@ -2385,6 +2462,13 @@ index 0000000000..2eea2fc281
 +    int last_percent = -1;
 +
 +    if (devcount) {
++        GList *entry = backup_coroutines;
++        while (entry && entry->data) {
++            Coroutine *co = entry->data;
++            qemu_coroutine_enter(co);
++            entry = g_list_next(entry);
++        }
++
 +        while (1) {
 +            main_loop_wait(false);
 +            vma_writer_get_status(vmaw, &vmastat);
@@ -2449,6 +2533,8 @@ index 0000000000..2eea2fc281
 +        g_error("creating vma archive failed");
 +    }
 +
++    g_list_free(backup_coroutines);
++    g_list_free(config_files);
 +    vma_writer_destroy(vmaw);
 +    return 0;
 +}
@@ -2555,7 +2641,7 @@ index 0000000000..2eea2fc281
 +}
 diff --git a/vma.h b/vma.h
 new file mode 100644
-index 0000000000..c895c97f6d
+index 0000000000..86d2873aa5
 --- /dev/null
 +++ b/vma.h
 @@ -0,0 +1,150 @@
@@ -2693,7 +2779,7 @@ index 0000000000..c895c97f6d
 +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, ...);
++void vma_writer_set_error(VmaWriter *vmaw, const char *fmt, ...) G_GNUC_PRINTF(2, 3);
 +
 +
 +VmaReader *vma_reader_create(const char *filename, Error **errp);
@@ -2703,7 +2789,7 @@ index 0000000000..c895c97f6d
 +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);
++                           bool skip, 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);