OPTION_MERGE = 274,
OPTION_BITMAPS = 275,
OPTION_FORCE = 276,
+ OPTION_SKIP_BROKEN = 277,
};
typedef enum OutputFormat {
static void run_block_job(BlockJob *job, Error **errp)
{
uint64_t progress_current, progress_total;
- AioContext *aio_context = blk_get_aio_context(job->blk);
+ AioContext *aio_context = block_job_get_aio_context(job);
int ret = 0;
aio_context_acquire(aio_context);
}
}
+ if (i == n) {
+ /*
+ * The whole buf is the same.
+ * No reason to split it into chunks, so return now.
+ */
+ *pnum = i;
+ return !is_zero;
+ }
+
tail = (sector_num + i) & (alignment - 1);
if (tail) {
if (is_zero && i <= tail) {
- /* treat unallocated areas which only consist
- * of a small tail as allocated. */
+ /*
+ * For sure next sector after i is data, and it will rewrite this
+ * tail anyway due to RMW. So, let's just write data now.
+ */
is_zero = false;
}
if (!is_zero) {
- /* align up end offset of allocated areas. */
+ /* If possible, align up end offset of allocated areas. */
i += alignment - tail;
i = MIN(i, n);
} else {
- /* align down end offset of zero areas. */
+ /*
+ * For sure next sector after i is data, and it will rewrite this
+ * tail anyway due to RMW. Better is avoid RMW and write zeroes up
+ * to aligned bound.
+ */
i -= tail;
}
}
return s->ret;
}
-static int convert_copy_bitmaps(BlockDriverState *src, BlockDriverState *dst)
+/* Check that bitmaps can be copied, or output an error */
+static int convert_check_bitmaps(BlockDriverState *src, bool skip_broken)
+{
+ BdrvDirtyBitmap *bm;
+
+ if (!bdrv_supports_persistent_dirty_bitmap(src)) {
+ error_report("Source lacks bitmap support");
+ return -1;
+ }
+ FOR_EACH_DIRTY_BITMAP(src, bm) {
+ if (!bdrv_dirty_bitmap_get_persistence(bm)) {
+ continue;
+ }
+ if (!skip_broken && bdrv_dirty_bitmap_inconsistent(bm)) {
+ error_report("Cannot copy inconsistent bitmap '%s'",
+ bdrv_dirty_bitmap_name(bm));
+ error_printf("Try --skip-broken-bitmaps, or "
+ "use 'qemu-img bitmap --remove' to delete it\n");
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static int convert_copy_bitmaps(BlockDriverState *src, BlockDriverState *dst,
+ bool skip_broken)
{
BdrvDirtyBitmap *bm;
Error *err = NULL;
continue;
}
name = bdrv_dirty_bitmap_name(bm);
+ if (skip_broken && bdrv_dirty_bitmap_inconsistent(bm)) {
+ warn_report("Skipping inconsistent bitmap '%s'", name);
+ continue;
+ }
qmp_block_dirty_bitmap_add(dst->node_name, name,
true, bdrv_dirty_bitmap_granularity(bm),
true, true,
&err);
if (err) {
error_reportf_err(err, "Failed to populate bitmap %s: ", name);
+ qmp_block_dirty_bitmap_remove(dst->node_name, name, NULL);
return -1;
}
}
int c, bs_i, flags, src_flags = BDRV_O_NO_SHARE;
const char *fmt = NULL, *out_fmt = NULL, *cache = "unsafe",
*src_cache = BDRV_DEFAULT_CACHE, *out_baseimg = NULL,
- *out_filename, *out_baseimg_param, *snapshot_name = NULL;
+ *out_filename, *out_baseimg_param, *snapshot_name = NULL,
+ *backing_fmt = NULL;
BlockDriver *drv = NULL, *proto_drv = NULL;
BlockDriverInfo bdi;
BlockDriverState *out_bs;
bool force_share = false;
bool explict_min_sparse = false;
bool bitmaps = false;
+ bool skip_broken = false;
int64_t rate_limit = 0;
ImgConvertState s = (ImgConvertState) {
{"salvage", no_argument, 0, OPTION_SALVAGE},
{"target-is-zero", no_argument, 0, OPTION_TARGET_IS_ZERO},
{"bitmaps", no_argument, 0, OPTION_BITMAPS},
+ {"skip-broken-bitmaps", no_argument, 0, OPTION_SKIP_BROKEN},
{0, 0, 0, 0}
};
- c = getopt_long(argc, argv, ":hf:O:B:Cco:l:S:pt:T:qnm:WUr:",
+ c = getopt_long(argc, argv, ":hf:O:B:CcF:o:l:S:pt:T:qnm:WUr:",
long_options, NULL);
if (c == -1) {
break;
case 'c':
s.compressed = true;
break;
+ case 'F':
+ backing_fmt = optarg;
+ break;
case 'o':
if (accumulate_options(&options, optarg) < 0) {
goto fail_getopt;
case OPTION_BITMAPS:
bitmaps = true;
break;
+ case OPTION_SKIP_BROKEN:
+ skip_broken = true;
+ break;
}
}
out_fmt = "raw";
}
+ if (skip_broken && !bitmaps) {
+ error_report("Use of --skip-broken-bitmaps requires --bitmaps");
+ goto fail_getopt;
+ }
+
if (s.compressed && s.copy_range) {
error_report("Cannot enable copy offloading when -c is used");
goto fail_getopt;
qemu_opt_set_number(opts, BLOCK_OPT_SIZE,
s.total_sectors * BDRV_SECTOR_SIZE, &error_abort);
- ret = add_old_style_options(out_fmt, opts, out_baseimg, NULL);
+ ret = add_old_style_options(out_fmt, opts, out_baseimg, backing_fmt);
if (ret < 0) {
goto out;
}
ret = -1;
goto out;
}
- if (!bdrv_supports_persistent_dirty_bitmap(blk_bs(s.src[0]))) {
- error_report("Source lacks bitmap support");
- ret = -1;
+ ret = convert_check_bitmaps(blk_bs(s.src[0]), skip_broken);
+ if (ret < 0) {
goto out;
}
}
goto out;
}
+ if (flags & BDRV_O_NOCACHE) {
+ /*
+ * If we open the target with O_DIRECT, it may be necessary to
+ * extend its size to align to the physical sector size.
+ */
+ flags |= BDRV_O_RESIZE;
+ }
+
if (skip_create) {
s.target = img_open(tgt_image_opts, out_filename, out_fmt,
flags, writethrough, s.quiet, false);
/* Now copy the bitmaps */
if (bitmaps && ret == 0) {
- ret = convert_copy_bitmaps(blk_bs(s.src[0]), out_bs);
+ ret = convert_copy_bitmaps(blk_bs(s.src[0]), out_bs, skip_broken);
}
out:
{
const img_cmd_t *cmd;
const char *cmdname;
- Error *local_error = NULL;
int c;
static const struct option long_options[] = {
{"help", no_argument, 0, 'h'},
module_call_init(MODULE_INIT_TRACE);
qemu_init_exec_dir(argv[0]);
- if (qemu_init_main_loop(&local_error)) {
- error_report_err(local_error);
- exit(EXIT_FAILURE);
- }
+ qemu_init_main_loop(&error_fatal);
qcrypto_init(&error_fatal);