399 lines
14 KiB
Diff
399 lines
14 KiB
Diff
|
From 53baacb72e8561391841363b2acbd85a783cbc66 Mon Sep 17 00:00:00 2001
|
||
|
From: Eric Blake <eblake@redhat.com>
|
||
|
Date: Tue, 2 Jun 2020 02:34:15 +0100
|
||
|
Subject: [PATCH 10/26] qemu-img: Add bitmap sub-command
|
||
|
|
||
|
RH-Author: Eric Blake <eblake@redhat.com>
|
||
|
Message-id: <20200602023420.2133649-8-eblake@redhat.com>
|
||
|
Patchwork-id: 97074
|
||
|
O-Subject: [RHEL-AV-8.2.1 qemu-kvm PATCH 07/12] qemu-img: Add bitmap sub-command
|
||
|
Bugzilla: 1779893 1779904
|
||
|
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||
|
RH-Acked-by: Max Reitz <mreitz@redhat.com>
|
||
|
RH-Acked-by: Kevin Wolf <kwolf@redhat.com>
|
||
|
|
||
|
Include actions for --add, --remove, --clear, --enable, --disable, and
|
||
|
--merge (note that --clear is a bit of fluff, because the same can be
|
||
|
accomplished by removing a bitmap and then adding a new one in its
|
||
|
place, but it matches what QMP commands exist). Listing is omitted,
|
||
|
because it does not require a bitmap name and because it was already
|
||
|
possible with 'qemu-img info'. A single command line can play one or
|
||
|
more bitmap commands in sequence on the same bitmap name (although all
|
||
|
added bitmaps share the same granularity, and and all merged bitmaps
|
||
|
come from the same source file). Merge defaults to other bitmaps in
|
||
|
the primary image, but can also be told to merge bitmaps from a
|
||
|
distinct image.
|
||
|
|
||
|
While this supports --image-opts for the file being modified, I did
|
||
|
not think it worth the extra complexity to support that for the source
|
||
|
file in a cross-file merges. Likewise, I chose to have --merge only
|
||
|
take a single source rather than following the QMP support for
|
||
|
multiple merges in one go (although you can still use more than one
|
||
|
--merge in the command line); in part because qemu-img is offline and
|
||
|
therefore atomicity is not an issue.
|
||
|
|
||
|
Upcoming patches will add iotest coverage of these commands while
|
||
|
also testing other features.
|
||
|
|
||
|
Signed-off-by: Eric Blake <eblake@redhat.com>
|
||
|
Reviewed-by: Max Reitz <mreitz@redhat.com>
|
||
|
Message-Id: <20200513011648.166876-7-eblake@redhat.com>
|
||
|
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
|
||
|
(cherry picked from commit 3b51ab4bf0f49a01cc2db7b954e0669e081719b5)
|
||
|
|
||
|
Signed-off-by: Danilo C. L. de Paula <ddepaula@redhat.com>
|
||
|
|
||
|
Conflicts:
|
||
|
docs/tools/qemu-img.rst - lives in qemu-img.texi instead; plus
|
||
|
fix a typo in the text for --merge rather than waiting for
|
||
|
a one-line upstream followup patch
|
||
|
qemu-img-cmds.hx - context, use texi instead of rst
|
||
|
qemu-img.c - context
|
||
|
Signed-off-by: Eric Blake <eblake@redhat.com>
|
||
|
|
||
|
Signed-off-by: Danilo C. L. de Paula <ddepaula@redhat.com>
|
||
|
---
|
||
|
qemu-img-cmds.hx | 6 ++
|
||
|
qemu-img.c | 248 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||
|
qemu-img.texi | 27 ++++++
|
||
|
3 files changed, 281 insertions(+)
|
||
|
|
||
|
diff --git a/qemu-img-cmds.hx b/qemu-img-cmds.hx
|
||
|
index 1c93e6d..1a6a8e9 100644
|
||
|
--- a/qemu-img-cmds.hx
|
||
|
+++ b/qemu-img-cmds.hx
|
||
|
@@ -25,6 +25,12 @@ STEXI
|
||
|
@item bench [-c @var{count}] [-d @var{depth}] [-f @var{fmt}] [--flush-interval=@var{flush_interval}] [-n] [--no-drain] [-o @var{offset}] [--pattern=@var{pattern}] [-q] [-s @var{buffer_size}] [-S @var{step_size}] [-t @var{cache}] [-w] [-U] @var{filename}
|
||
|
ETEXI
|
||
|
|
||
|
+DEF("bitmap", img_bitmap,
|
||
|
+ "bitmap (--merge SOURCE | --add | --remove | --clear | --enable | --disable)... [-b source_file [-F source_fmt]] [-g granularity] [--object objectdef] [--image-opts | -f fmt] filename bitmap")
|
||
|
+STEXI
|
||
|
+.. option:: bitmap (--merge @var{source} | --add | --remove | --clear | --enable | --disable)... [-b @var{source_file} [-F @var{source_fmt}]] [-g @var{granularity}] [--object @var{objectdef}] [--image-opts | -f @var{fmt}] @var{filename} @var{bitmap}
|
||
|
+ETEXI
|
||
|
+
|
||
|
DEF("check", img_check,
|
||
|
"check [--object objectdef] [--image-opts] [-q] [-f fmt] [--output=ofmt] [-r [leaks | all]] [-T src_cache] [-U] filename")
|
||
|
STEXI
|
||
|
diff --git a/qemu-img.c b/qemu-img.c
|
||
|
index e69529b..11a4537 100644
|
||
|
--- a/qemu-img.c
|
||
|
+++ b/qemu-img.c
|
||
|
@@ -28,6 +28,7 @@
|
||
|
#include "qemu-common.h"
|
||
|
#include "qemu-version.h"
|
||
|
#include "qapi/error.h"
|
||
|
+#include "qapi/qapi-commands-block-core.h"
|
||
|
#include "qapi/qapi-visit-block-core.h"
|
||
|
#include "qapi/qobject-output-visitor.h"
|
||
|
#include "qapi/qmp/qjson.h"
|
||
|
@@ -70,6 +71,12 @@ enum {
|
||
|
OPTION_PREALLOCATION = 265,
|
||
|
OPTION_SHRINK = 266,
|
||
|
OPTION_SALVAGE = 267,
|
||
|
+ OPTION_ADD = 269,
|
||
|
+ OPTION_REMOVE = 270,
|
||
|
+ OPTION_CLEAR = 271,
|
||
|
+ OPTION_ENABLE = 272,
|
||
|
+ OPTION_DISABLE = 273,
|
||
|
+ OPTION_MERGE = 274,
|
||
|
};
|
||
|
|
||
|
typedef enum OutputFormat {
|
||
|
@@ -168,6 +175,14 @@ static void QEMU_NORETURN help(void)
|
||
|
" '-n' skips the target volume creation (useful if the volume is created\n"
|
||
|
" prior to running qemu-img)\n"
|
||
|
"\n"
|
||
|
+ "Parameters to bitmap subcommand:\n"
|
||
|
+ " 'bitmap' is the name of the bitmap to manipulate, through one or more\n"
|
||
|
+ " actions from '--add', '--remove', '--clear', '--enable', '--disable',\n"
|
||
|
+ " or '--merge source'\n"
|
||
|
+ " '-g granularity' sets the granularity for '--add' actions\n"
|
||
|
+ " '-b source' and '-F src_fmt' tell '--merge' actions to find the source\n"
|
||
|
+ " bitmaps from an alternative file\n"
|
||
|
+ "\n"
|
||
|
"Parameters to check subcommand:\n"
|
||
|
" '-r' tries to repair any inconsistencies that are found during the check.\n"
|
||
|
" '-r leaks' repairs only cluster leaks, whereas '-r all' fixes all\n"
|
||
|
@@ -4402,6 +4417,239 @@ out:
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
+enum ImgBitmapAct {
|
||
|
+ BITMAP_ADD,
|
||
|
+ BITMAP_REMOVE,
|
||
|
+ BITMAP_CLEAR,
|
||
|
+ BITMAP_ENABLE,
|
||
|
+ BITMAP_DISABLE,
|
||
|
+ BITMAP_MERGE,
|
||
|
+};
|
||
|
+typedef struct ImgBitmapAction {
|
||
|
+ enum ImgBitmapAct act;
|
||
|
+ const char *src; /* only used for merge */
|
||
|
+ QSIMPLEQ_ENTRY(ImgBitmapAction) next;
|
||
|
+} ImgBitmapAction;
|
||
|
+
|
||
|
+static int img_bitmap(int argc, char **argv)
|
||
|
+{
|
||
|
+ Error *err = NULL;
|
||
|
+ int c, ret = 1;
|
||
|
+ QemuOpts *opts = NULL;
|
||
|
+ const char *fmt = NULL, *src_fmt = NULL, *src_filename = NULL;
|
||
|
+ const char *filename, *bitmap;
|
||
|
+ BlockBackend *blk = NULL, *src = NULL;
|
||
|
+ BlockDriverState *bs = NULL, *src_bs = NULL;
|
||
|
+ bool image_opts = false;
|
||
|
+ int64_t granularity = 0;
|
||
|
+ bool add = false, merge = false;
|
||
|
+ QSIMPLEQ_HEAD(, ImgBitmapAction) actions;
|
||
|
+ ImgBitmapAction *act, *act_next;
|
||
|
+ const char *op;
|
||
|
+
|
||
|
+ QSIMPLEQ_INIT(&actions);
|
||
|
+
|
||
|
+ for (;;) {
|
||
|
+ static const struct option long_options[] = {
|
||
|
+ {"help", no_argument, 0, 'h'},
|
||
|
+ {"object", required_argument, 0, OPTION_OBJECT},
|
||
|
+ {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS},
|
||
|
+ {"add", no_argument, 0, OPTION_ADD},
|
||
|
+ {"remove", no_argument, 0, OPTION_REMOVE},
|
||
|
+ {"clear", no_argument, 0, OPTION_CLEAR},
|
||
|
+ {"enable", no_argument, 0, OPTION_ENABLE},
|
||
|
+ {"disable", no_argument, 0, OPTION_DISABLE},
|
||
|
+ {"merge", required_argument, 0, OPTION_MERGE},
|
||
|
+ {"granularity", required_argument, 0, 'g'},
|
||
|
+ {"source-file", required_argument, 0, 'b'},
|
||
|
+ {"source-format", required_argument, 0, 'F'},
|
||
|
+ {0, 0, 0, 0}
|
||
|
+ };
|
||
|
+ c = getopt_long(argc, argv, ":b:f:F:g:h", long_options, NULL);
|
||
|
+ if (c == -1) {
|
||
|
+ break;
|
||
|
+ }
|
||
|
+
|
||
|
+ switch (c) {
|
||
|
+ case ':':
|
||
|
+ missing_argument(argv[optind - 1]);
|
||
|
+ break;
|
||
|
+ case '?':
|
||
|
+ unrecognized_option(argv[optind - 1]);
|
||
|
+ break;
|
||
|
+ case 'h':
|
||
|
+ help();
|
||
|
+ break;
|
||
|
+ case 'b':
|
||
|
+ src_filename = optarg;
|
||
|
+ break;
|
||
|
+ case 'f':
|
||
|
+ fmt = optarg;
|
||
|
+ break;
|
||
|
+ case 'F':
|
||
|
+ src_fmt = optarg;
|
||
|
+ break;
|
||
|
+ case 'g':
|
||
|
+ granularity = cvtnum("granularity", optarg);
|
||
|
+ if (granularity < 0) {
|
||
|
+ return 1;
|
||
|
+ }
|
||
|
+ break;
|
||
|
+ case OPTION_ADD:
|
||
|
+ act = g_new0(ImgBitmapAction, 1);
|
||
|
+ act->act = BITMAP_ADD;
|
||
|
+ QSIMPLEQ_INSERT_TAIL(&actions, act, next);
|
||
|
+ add = true;
|
||
|
+ break;
|
||
|
+ case OPTION_REMOVE:
|
||
|
+ act = g_new0(ImgBitmapAction, 1);
|
||
|
+ act->act = BITMAP_REMOVE;
|
||
|
+ QSIMPLEQ_INSERT_TAIL(&actions, act, next);
|
||
|
+ break;
|
||
|
+ case OPTION_CLEAR:
|
||
|
+ act = g_new0(ImgBitmapAction, 1);
|
||
|
+ act->act = BITMAP_CLEAR;
|
||
|
+ QSIMPLEQ_INSERT_TAIL(&actions, act, next);
|
||
|
+ break;
|
||
|
+ case OPTION_ENABLE:
|
||
|
+ act = g_new0(ImgBitmapAction, 1);
|
||
|
+ act->act = BITMAP_ENABLE;
|
||
|
+ QSIMPLEQ_INSERT_TAIL(&actions, act, next);
|
||
|
+ break;
|
||
|
+ case OPTION_DISABLE:
|
||
|
+ act = g_new0(ImgBitmapAction, 1);
|
||
|
+ act->act = BITMAP_DISABLE;
|
||
|
+ QSIMPLEQ_INSERT_TAIL(&actions, act, next);
|
||
|
+ break;
|
||
|
+ case OPTION_MERGE:
|
||
|
+ act = g_new0(ImgBitmapAction, 1);
|
||
|
+ act->act = BITMAP_MERGE;
|
||
|
+ act->src = optarg;
|
||
|
+ QSIMPLEQ_INSERT_TAIL(&actions, act, next);
|
||
|
+ merge = true;
|
||
|
+ break;
|
||
|
+ case OPTION_OBJECT:
|
||
|
+ opts = qemu_opts_parse_noisily(&qemu_object_opts, optarg, true);
|
||
|
+ if (!opts) {
|
||
|
+ goto out;
|
||
|
+ }
|
||
|
+ break;
|
||
|
+ case OPTION_IMAGE_OPTS:
|
||
|
+ image_opts = true;
|
||
|
+ break;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ if (qemu_opts_foreach(&qemu_object_opts,
|
||
|
+ user_creatable_add_opts_foreach,
|
||
|
+ qemu_img_object_print_help, &error_fatal)) {
|
||
|
+ goto out;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (QSIMPLEQ_EMPTY(&actions)) {
|
||
|
+ error_report("Need at least one of --add, --remove, --clear, "
|
||
|
+ "--enable, --disable, or --merge");
|
||
|
+ goto out;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (granularity && !add) {
|
||
|
+ error_report("granularity only supported with --add");
|
||
|
+ goto out;
|
||
|
+ }
|
||
|
+ if (src_fmt && !src_filename) {
|
||
|
+ error_report("-F only supported with -b");
|
||
|
+ goto out;
|
||
|
+ }
|
||
|
+ if (src_filename && !merge) {
|
||
|
+ error_report("Merge bitmap source file only supported with "
|
||
|
+ "--merge");
|
||
|
+ goto out;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (optind != argc - 2) {
|
||
|
+ error_report("Expecting filename and bitmap name");
|
||
|
+ goto out;
|
||
|
+ }
|
||
|
+
|
||
|
+ filename = argv[optind];
|
||
|
+ bitmap = argv[optind + 1];
|
||
|
+
|
||
|
+ blk = img_open(image_opts, filename, fmt, BDRV_O_RDWR, false, false,
|
||
|
+ false);
|
||
|
+ if (!blk) {
|
||
|
+ goto out;
|
||
|
+ }
|
||
|
+ bs = blk_bs(blk);
|
||
|
+ if (src_filename) {
|
||
|
+ src = img_open(false, src_filename, src_fmt, 0, false, false, false);
|
||
|
+ if (!src) {
|
||
|
+ goto out;
|
||
|
+ }
|
||
|
+ src_bs = blk_bs(src);
|
||
|
+ } else {
|
||
|
+ src_bs = bs;
|
||
|
+ }
|
||
|
+
|
||
|
+ QSIMPLEQ_FOREACH_SAFE(act, &actions, next, act_next) {
|
||
|
+ switch (act->act) {
|
||
|
+ case BITMAP_ADD:
|
||
|
+ qmp_block_dirty_bitmap_add(bs->node_name, bitmap,
|
||
|
+ !!granularity, granularity, true, true,
|
||
|
+ false, false, &err);
|
||
|
+ op = "add";
|
||
|
+ break;
|
||
|
+ case BITMAP_REMOVE:
|
||
|
+ qmp_block_dirty_bitmap_remove(bs->node_name, bitmap, &err);
|
||
|
+ op = "remove";
|
||
|
+ break;
|
||
|
+ case BITMAP_CLEAR:
|
||
|
+ qmp_block_dirty_bitmap_clear(bs->node_name, bitmap, &err);
|
||
|
+ op = "clear";
|
||
|
+ break;
|
||
|
+ case BITMAP_ENABLE:
|
||
|
+ qmp_block_dirty_bitmap_enable(bs->node_name, bitmap, &err);
|
||
|
+ op = "enable";
|
||
|
+ break;
|
||
|
+ case BITMAP_DISABLE:
|
||
|
+ qmp_block_dirty_bitmap_disable(bs->node_name, bitmap, &err);
|
||
|
+ op = "disable";
|
||
|
+ break;
|
||
|
+ case BITMAP_MERGE: {
|
||
|
+ BlockDirtyBitmapMergeSource *merge_src;
|
||
|
+ BlockDirtyBitmapMergeSourceList *list;
|
||
|
+
|
||
|
+ merge_src = g_new0(BlockDirtyBitmapMergeSource, 1);
|
||
|
+ merge_src->type = QTYPE_QDICT;
|
||
|
+ merge_src->u.external.node = g_strdup(src_bs->node_name);
|
||
|
+ merge_src->u.external.name = g_strdup(act->src);
|
||
|
+ list = g_new0(BlockDirtyBitmapMergeSourceList, 1);
|
||
|
+ list->value = merge_src;
|
||
|
+ qmp_block_dirty_bitmap_merge(bs->node_name, bitmap, list, &err);
|
||
|
+ qapi_free_BlockDirtyBitmapMergeSourceList(list);
|
||
|
+ op = "merge";
|
||
|
+ break;
|
||
|
+ }
|
||
|
+ default:
|
||
|
+ g_assert_not_reached();
|
||
|
+ }
|
||
|
+
|
||
|
+ if (err) {
|
||
|
+ error_reportf_err(err, "Operation %s on bitmap %s failed: ",
|
||
|
+ op, bitmap);
|
||
|
+ goto out;
|
||
|
+ }
|
||
|
+ g_free(act);
|
||
|
+ }
|
||
|
+
|
||
|
+ ret = 0;
|
||
|
+
|
||
|
+ out:
|
||
|
+ blk_unref(src);
|
||
|
+ blk_unref(blk);
|
||
|
+ qemu_opts_del(opts);
|
||
|
+ return ret;
|
||
|
+}
|
||
|
+
|
||
|
#define C_BS 01
|
||
|
#define C_COUNT 02
|
||
|
#define C_IF 04
|
||
|
diff --git a/qemu-img.texi b/qemu-img.texi
|
||
|
index b5156d6..abf2771 100644
|
||
|
--- a/qemu-img.texi
|
||
|
+++ b/qemu-img.texi
|
||
|
@@ -230,6 +230,33 @@ specified as well.
|
||
|
For write tests, by default a buffer filled with zeros is written. This can be
|
||
|
overridden with a pattern byte specified by @var{pattern}.
|
||
|
|
||
|
+@item bitmap (--merge @var{source} | --add | --remove | --clear | --enable | --disable)... [-b @var{source_file} [-F @var{source_fmt}]] [-g @var{granularity}] [--object @var{objectdef}] [--image-opts | -f @var{fmt}] @var{filename} @var{bitmap}
|
||
|
+
|
||
|
+Perform one or more modifications of the persistent bitmap @var{bitmap}
|
||
|
+in the disk image @var{filename}. The various modifications are:
|
||
|
+
|
||
|
+@table @option
|
||
|
+@item add
|
||
|
+create @var{bitmap}, enabled to record future edits.
|
||
|
+@item remove
|
||
|
+remove @var{bitmap}.
|
||
|
+@item clear
|
||
|
+clear @var{bitmap}.
|
||
|
+@item enable
|
||
|
+change @var{bitmap} to start recording future edits.
|
||
|
+@item disable
|
||
|
+change @var{bitmap} to stop recording future edits.
|
||
|
+@item merge @var{source}
|
||
|
+merge the contents of the @var{source} bitmap into @var{bitmap}.
|
||
|
+@end table
|
||
|
+
|
||
|
+Additional options include @option{-g} which sets a non-default
|
||
|
+@var{granularity} for @option{--add}, and @option{-b} and @option{-F}
|
||
|
+which select an alternative source file for all @var{source} bitmaps used by
|
||
|
+@option{--merge}.
|
||
|
+
|
||
|
+To see what bitmaps are present in an image, use @code{qemu-img info}.
|
||
|
+
|
||
|
@item check [--object @var{objectdef}] [--image-opts] [-q] [-f @var{fmt}] [--output=@var{ofmt}] [-r [leaks | all]] [-T @var{src_cache}] [-U] @var{filename}
|
||
|
|
||
|
Perform a consistency check on the disk image @var{filename}. The command can
|
||
|
--
|
||
|
1.8.3.1
|
||
|
|