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