@samitouri / QOSamiQemu / commits / 7e8cc6c886

qemu-img: add sub-command --remove-all to 'qemu-img bitmap'

From time to time it is needed to remove all bitmaps from the image. Before this patch the process is not very convenient. One should perform qemu-img info and parse the output to obtain all names. After that one should sequentially call qemu-img bitmap --remove for each present bitmap. The patch adds --remove-all sub-command to 'qemu-img bitmap'. The new sub-command also composes with other bitmap actions in the same invocation, so a common "wipe and recreate" workflow can be expressed as qemu-img bitmap --remove-all --add NEW FILE instead of enumerating existing bitmaps, removing them one by one, and only then adding the fresh one. Cc: Kevin Wolf <kwolf@redhat.com> Cc: Hanna Czenczek <hreitz@redhat.com> Signed-off-by: Denis V. Lunev <den@openvz.org> Message-ID: <20260520235952.500250-1-den@openvz.org> Reviewed-by: Kevin Wolf <kwolf@redhat.com> Signed-off-by: Kevin Wolf <kwolf@redhat.com>

Denis V. Lunev committed May 21, 2026 at 01:59 UTC 7e8cc6c886b7100ac4a7ff803562228fe3a6d4fd
4 files changed +125 -10
docs/tools/qemu-img.rst
+7 -3
@@ -301,15 +301,19 @@ Command description:
301 For write tests, by default a buffer filled with zeros is written. This can be
302 overridden with a pattern byte specified by *PATTERN*.
303
304 -.. option:: bitmap (--merge SOURCE | --add | --remove | --clear | --enable | --disable)... [-b SOURCE_FILE [-F SOURCE_FMT]] [-g GRANULARITY] [--object OBJECTDEF] [--image-opts | -f FMT] FILENAME BITMAP
304 +.. option:: bitmap (--merge SOURCE | --add | --remove | --remove-all | --clear | --enable | --disable)... [-b SOURCE_FILE [-F SOURCE_FMT]] [-g GRANULARITY] [--object OBJECTDEF] [--image-opts | -f FMT] FILENAME [BITMAP]
305
306 - Perform one or more modifications of the persistent bitmap *BITMAP*
307 - in the disk image *FILENAME*. The various modifications are:
306 + Perform one or more modifications of persistent bitmaps in the disk
307 + image *FILENAME*. Most operations require *BITMAP* to be specified;
308 + ``--remove-all`` operates on all bitmaps and does not take *BITMAP*.
309 + The various modifications are:
310
311 ``--add`` to create *BITMAP*, enabled to record future edits.
312
313 ``--remove`` to remove *BITMAP*.
314
315 + ``--remove-all`` to remove all bitmaps.
316 +
317 ``--clear`` to clear *BITMAP*.
318
319 ``--enable`` to change *BITMAP* to start recording future edits.
qemu-img.c
+48 -7
@@ -87,6 +87,7 @@ enum {
87 OPTION_FORCE = 276,
88 OPTION_SKIP_BROKEN = 277,
89 OPTION_LIMITS = 278,
90 + OPTION_REMOVE_ALL = 279,
91 };
92
93 typedef enum OutputFormat {
@@ -5018,6 +5019,7 @@ enum ImgBitmapAct {
5019 BITMAP_ENABLE,
5020 BITMAP_DISABLE,
5021 BITMAP_MERGE,
5022 + BITMAP_REMOVE_ALL,
5023 };
5024 typedef struct ImgBitmapAction {
5025 enum ImgBitmapAct act;
@@ -5036,7 +5038,7 @@ static int img_bitmap(const img_cmd_t *ccmd, int argc, char **argv)
5038 BlockDriverState *bs = NULL, *src_bs = NULL;
5039 bool image_opts = false;
5040 int64_t granularity = 0;
5039 - bool add = false, merge = false;
5041 + bool add = false, merge = false, need_bitmap_name = false;
5042 QSIMPLEQ_HEAD(, ImgBitmapAction) actions;
5043 ImgBitmapAction *act, *act_next;
5044 const char *op;
@@ -5052,6 +5054,7 @@ static int img_bitmap(const img_cmd_t *ccmd, int argc, char **argv)
5054 {"add", no_argument, 0, OPTION_ADD},
5055 {"granularity", required_argument, 0, 'g'},
5056 {"remove", no_argument, 0, OPTION_REMOVE},
5057 + {"remove-all", no_argument, 0, OPTION_REMOVE_ALL},
5058 {"clear", no_argument, 0, OPTION_CLEAR},
5059 {"enable", no_argument, 0, OPTION_ENABLE},
5060 {"disable", no_argument, 0, OPTION_DISABLE},
@@ -5070,9 +5073,9 @@ static int img_bitmap(const img_cmd_t *ccmd, int argc, char **argv)
5073 switch (c) {
5074 case 'h':
5075 cmd_help(ccmd, "[-f FMT | --image-opts]\n"
5073 -" ( --add [-g SIZE] | --remove | --clear | --enable | --disable |\n"
5074 -" --merge SOURCE [-b SRC_FILE [-F SRC_FMT]] )..\n"
5075 -" [--object OBJDEF] FILE BITMAP\n"
5076 +" ( --add [-g SIZE] | --remove | --remove-all | --clear | --enable |\n"
5077 +" --disable | --merge SOURCE [-b SRC_FILE [-F SRC_FMT]] )..\n"
5078 +" [--object OBJDEF] FILE [BITMAP]\n"
5079 ,
5080 " -f, --format FMT\n"
5081 " specify FILE format explicitly (default: probing is used)\n"
@@ -5086,6 +5089,8 @@ static int img_bitmap(const img_cmd_t *ccmd, int argc, char **argv)
5089 " with optional multiplier suffix (in powers of 1024)\n"
5090 " --remove\n"
5091 " removes BITMAP from FILE\n"
5092 +" --remove-all\n"
5093 +" removes all bitmaps from FILE\n"
5094 " --clear\n"
5095 " clears BITMAP in FILE\n"
5096 " --enable, --disable\n"
@@ -5116,6 +5121,7 @@ static int img_bitmap(const img_cmd_t *ccmd, int argc, char **argv)
5121 act->act = BITMAP_ADD;
5122 QSIMPLEQ_INSERT_TAIL(&actions, act, next);
5123 add = true;
5124 + need_bitmap_name = true;
5125 break;
5126 case 'g':
5127 granularity = cvtnum("granularity", optarg, true);
@@ -5127,21 +5133,30 @@ static int img_bitmap(const img_cmd_t *ccmd, int argc, char **argv)
5133 act = g_new0(ImgBitmapAction, 1);
5134 act->act = BITMAP_REMOVE;
5135 QSIMPLEQ_INSERT_TAIL(&actions, act, next);
5136 + need_bitmap_name = true;
5137 + break;
5138 + case OPTION_REMOVE_ALL:
5139 + act = g_new0(ImgBitmapAction, 1);
5140 + act->act = BITMAP_REMOVE_ALL;
5141 + QSIMPLEQ_INSERT_TAIL(&actions, act, next);
5142 break;
5143 case OPTION_CLEAR:
5144 act = g_new0(ImgBitmapAction, 1);
5145 act->act = BITMAP_CLEAR;
5146 QSIMPLEQ_INSERT_TAIL(&actions, act, next);
5147 + need_bitmap_name = true;
5148 break;
5149 case OPTION_ENABLE:
5150 act = g_new0(ImgBitmapAction, 1);
5151 act->act = BITMAP_ENABLE;
5152 QSIMPLEQ_INSERT_TAIL(&actions, act, next);
5153 + need_bitmap_name = true;
5154 break;
5155 case OPTION_DISABLE:
5156 act = g_new0(ImgBitmapAction, 1);
5157 act->act = BITMAP_DISABLE;
5158 QSIMPLEQ_INSERT_TAIL(&actions, act, next);
5159 + need_bitmap_name = true;
5160 break;
5161 case OPTION_MERGE:
5162 act = g_new0(ImgBitmapAction, 1);
@@ -5149,6 +5164,7 @@ static int img_bitmap(const img_cmd_t *ccmd, int argc, char **argv)
5164 act->src = optarg;
5165 QSIMPLEQ_INSERT_TAIL(&actions, act, next);
5166 merge = true;
5167 + need_bitmap_name = true;
5168 break;
5169 case 'b':
5170 src_filename = optarg;
@@ -5165,8 +5181,8 @@ static int img_bitmap(const img_cmd_t *ccmd, int argc, char **argv)
5181 }
5182
5183 if (QSIMPLEQ_EMPTY(&actions)) {
5168 - error_report("Need at least one of --add, --remove, --clear, "
5169 - "--enable, --disable, or --merge");
5184 + error_report("Need at least one of --add, --remove, --remove-all, "
5185 + "--clear, --enable, --disable, or --merge");
5186 goto out;
5187 }
5188
@@ -5184,11 +5200,22 @@ static int img_bitmap(const img_cmd_t *ccmd, int argc, char **argv)
5200 goto out;
5201 }
5202
5187 - if (optind != argc - 2) {
5203 + if (need_bitmap_name && optind != argc - 2) {
5204 error_report("Expecting filename and bitmap name");
5205 goto out;
5206 }
5207
5208 + /*
5209 + * Every action other than --remove-all sets need_bitmap_name, so
5210 + * !need_bitmap_name means the only action(s) given were --remove-all
5211 + * and the BITMAP positional argument must be omitted. Combinations
5212 + * like '--remove-all --add foo' remain valid via the branch above.
5213 + */
5214 + if (!need_bitmap_name && optind != argc - 1) {
5215 + error_report("Expecting filename");
5216 + goto out;
5217 + }
5218 +
5219 filename = argv[optind];
5220 bitmap = argv[optind + 1];
5221
@@ -5225,6 +5252,20 @@ static int img_bitmap(const img_cmd_t *ccmd, int argc, char **argv)
5252 qmp_block_dirty_bitmap_remove(bs->node_name, bitmap, &err);
5253 op = "remove";
5254 break;
5255 + case BITMAP_REMOVE_ALL: {
5256 + BdrvDirtyBitmap *bm;
5257 + while ((bm = bdrv_dirty_bitmap_first(bs))) {
5258 + const char *name = bdrv_dirty_bitmap_name(bm);
5259 + qmp_block_dirty_bitmap_remove(bs->node_name, name, &err);
5260 + if (err) {
5261 + /* Save name for proper error reporting */
5262 + bitmap = name;
5263 + break;
5264 + }
5265 + }
5266 + op = "remove-all";
5267 + break;
5268 + }
5269 case BITMAP_CLEAR:
5270 qmp_block_dirty_bitmap_clear(bs->node_name, bitmap, &err);
5271 op = "clear";
tests/qemu-iotests/tests/qemu-img-bitmaps
+24
@@ -161,6 +161,30 @@ $QEMU_IMG convert --bitmaps -O qcow2 "$TEST_IMG" "$TEST_IMG.copy"
161 TEST_IMG="$TEST_IMG.copy" _img_info --format-specific \
162 | _filter_irrelevant_img_info
163
164 +echo
165 +echo "=== Check --remove-all ==="
166 +echo
167 +
168 +# Start from a fresh image so prior state does not bleed into the assertions
169 +_rm_test_img "$TEST_IMG"
170 +_make_test_img 10M
171 +$QEMU_IMG bitmap --add -f $IMGFMT "$TEST_IMG" b0
172 +$QEMU_IMG bitmap --add -f $IMGFMT "$TEST_IMG" b1
173 +$QEMU_IMG bitmap --add -f $IMGFMT "$TEST_IMG" b2
174 +_img_info --format-specific | _filter_irrelevant_img_info
175 +
176 +# Sweep every bitmap in a single command, no BITMAP positional
177 +echo
178 +$QEMU_IMG bitmap --remove-all -f $IMGFMT "$TEST_IMG"
179 +_img_info --format-specific | _filter_irrelevant_img_info
180 +
181 +# Wipe + recreate in one invocation: only 'fresh' should remain
182 +echo
183 +$QEMU_IMG bitmap --add -f $IMGFMT "$TEST_IMG" b0
184 +$QEMU_IMG bitmap --add -f $IMGFMT "$TEST_IMG" b1
185 +$QEMU_IMG bitmap --remove-all --add -f $IMGFMT "$TEST_IMG" fresh
186 +_img_info --format-specific | _filter_irrelevant_img_info
187 +
188 # success, all done
189 echo '*** done'
190 rm -f $seq.full
tests/qemu-iotests/tests/qemu-img-bitmaps.out
+46
@@ -180,4 +180,50 @@ Format specific information:
180 name: b2
181 granularity: 65536
182 corrupt: false
183 +
184 +=== Check --remove-all ===
185 +
186 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=10485760
187 +image: TEST_DIR/t.IMGFMT
188 +file format: IMGFMT
189 +virtual size: 10 MiB (10485760 bytes)
190 +cluster_size: 65536
191 +Format specific information:
192 + bitmaps:
193 + [0]:
194 + flags:
195 + [0]: auto
196 + name: b0
197 + granularity: 65536
198 + [1]:
199 + flags:
200 + [0]: auto
201 + name: b1
202 + granularity: 65536
203 + [2]:
204 + flags:
205 + [0]: auto
206 + name: b2
207 + granularity: 65536
208 + corrupt: false
209 +
210 +image: TEST_DIR/t.IMGFMT
211 +file format: IMGFMT
212 +virtual size: 10 MiB (10485760 bytes)
213 +cluster_size: 65536
214 +Format specific information:
215 + corrupt: false
216 +
217 +image: TEST_DIR/t.IMGFMT
218 +file format: IMGFMT
219 +virtual size: 10 MiB (10485760 bytes)
220 +cluster_size: 65536
221 +Format specific information:
222 + bitmaps:
223 + [0]:
224 + flags:
225 + [0]: auto
226 + name: fresh
227 + granularity: 65536
228 + corrupt: false
229 *** done