+++ /dev/null
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Fabian Ebner <f.ebner@proxmox.com>
-Date: Thu, 21 Apr 2022 13:26:48 +0200
-Subject: [PATCH] vma: allow partial restore
-
-Introduce a new map line for skipping a certain drive, 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.
-
-For the reader, a new skip flag for VmaRestoreState is added and the
-target is allowed to be NULL if skip is specified when registering. If
-the skip flag is set, no writes will be made as well as no check for
-duplicate clusters. Therefore, the flag is not set for verify.
-
-Signed-off-by: Fabian Ebner <f.ebner@proxmox.com>
-Acked-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
-Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
----
- vma-reader.c | 64 ++++++++++++---------
- vma.c | 157 +++++++++++++++++++++++++++++----------------------
- vma.h | 2 +-
- 3 files changed, 126 insertions(+), 97 deletions(-)
-
-diff --git a/vma-reader.c b/vma-reader.c
-index e65f1e8415..81a891c6b1 100644
---- a/vma-reader.c
-+++ b/vma-reader.c
-@@ -28,6 +28,7 @@ typedef struct VmaRestoreState {
- bool write_zeroes;
- unsigned long *bitmap;
- int bitmap_size;
-+ bool skip;
- } VmaRestoreState;
-
- struct VmaReader {
-@@ -425,13 +426,14 @@ VmaDeviceInfo *vma_reader_get_device_info(VmaReader *vmar, guint8 dev_id)
- }
-
- 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;
-
-@@ -446,28 +448,30 @@ static void allocate_rstate(VmaReader *vmar, guint8 dev_id,
- }
-
- 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;
--
-- /* 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;
-+ assert(vmar->rstate[dev_id].target == NULL && !vmar->rstate[dev_id].skip);
-+
-+ 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;
- }
-@@ -560,19 +564,23 @@ static int restore_extent(VmaReader *vmar, unsigned char *buf,
- 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 {
-@@ -618,7 +626,7 @@ static int restore_extent(VmaReader *vmar, unsigned char *buf,
- 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,
-@@ -654,7 +662,7 @@ static int restore_extent(VmaReader *vmar, unsigned char *buf,
- 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,
-@@ -679,7 +687,7 @@ static int restore_extent(VmaReader *vmar, unsigned char *buf,
- 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) {
-@@ -850,7 +858,7 @@ int vma_reader_verify(VmaReader *vmar, bool verbose, Error **errp)
-
- 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);
- }
- }
-
-diff --git a/vma.c b/vma.c
-index e8dffb43e0..e6e9ffc7fe 100644
---- a/vma.c
-+++ b/vma.c
-@@ -138,6 +138,7 @@ typedef struct RestoreMap {
- 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) {
-@@ -245,47 +246,61 @@ static int extract_content(int argc, char **argv)
- 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);
-@@ -299,6 +314,7 @@ static int extract_content(int argc, char **argv)
- map->throttling_group = group;
- map->cache = cache;
- map->write_zero = write_zero;
-+ map->skip = skip;
-
- g_hash_table_insert(devmap, map->devname, map);
-
-@@ -328,6 +344,7 @@ static int extract_content(int argc, char **argv)
- const char *cache = NULL;
- int flags = BDRV_O_RDWR;
- bool write_zero = true;
-+ bool skip = false;
-
- BlockBackend *blk = NULL;
-
-@@ -343,6 +360,7 @@ static int extract_content(int argc, char **argv)
- throttling_group = map->throttling_group;
- cache = map->cache;
- write_zero = map->write_zero;
-+ skip = map->skip;
- } else {
- devfn = g_strdup_printf("%s/tmp-disk-%s.raw",
- dirname, di->devname);
-@@ -361,57 +379,60 @@ static int extract_content(int argc, char **argv)
- 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));
- }
-
-diff --git a/vma.h b/vma.h
-index c895c97f6d..1b62859165 100644
---- a/vma.h
-+++ b/vma.h
-@@ -142,7 +142,7 @@ 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);
-+ 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);