X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=qemu-img.c;h=4ee436fc948f31c7488a6c905519a7be8132df8f;hb=d8b92bd41822c053daa4dbf88cf36b14241a0279;hp=28fba1e7a7b12590bcea125f6873a904e989b128;hpb=1c6e87799252d166426f642c00be8927b68ab164;p=mirror_qemu.git diff --git a/qemu-img.c b/qemu-img.c index 28fba1e7a7..4ee436fc94 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -25,6 +25,7 @@ #include "qemu/osdep.h" #include +#include "qemu-common.h" #include "qemu-version.h" #include "qapi/error.h" #include "qapi/qapi-visit-block-core.h" @@ -37,9 +38,10 @@ #include "qemu/option.h" #include "qemu/error-report.h" #include "qemu/log.h" +#include "qemu/main-loop.h" +#include "qemu/module.h" #include "qemu/units.h" #include "qom/object_interfaces.h" -#include "sysemu/sysemu.h" #include "sysemu/block-backend.h" #include "block/block_int.h" #include "block/blockjob.h" @@ -67,6 +69,7 @@ enum { OPTION_SIZE = 264, OPTION_PREALLOCATION = 265, OPTION_SHRINK = 266, + OPTION_SALVAGE = 267, }; typedef enum OutputFormat { @@ -1575,10 +1578,13 @@ typedef struct ImgConvertState { bool has_zero_init; bool compressed; bool unallocated_blocks_are_zero; + bool target_is_new; bool target_has_backing; int64_t target_backing_sectors; /* negative if unknown */ bool wr_in_order; bool copy_range; + bool salvage; + bool quiet; int min_sparse; int alignment; size_t cluster_sectors; @@ -1625,25 +1631,44 @@ static int convert_iteration_sectors(ImgConvertState *s, int64_t sector_num) } if (s->sector_next_status <= sector_num) { - int64_t count = n * BDRV_SECTOR_SIZE; + uint64_t offset = (sector_num - src_cur_offset) * BDRV_SECTOR_SIZE; + int64_t count; - if (s->target_has_backing) { + do { + count = n * BDRV_SECTOR_SIZE; + + if (s->target_has_backing) { + ret = bdrv_block_status(blk_bs(s->src[src_cur]), offset, + count, &count, NULL, NULL); + } else { + ret = bdrv_block_status_above(blk_bs(s->src[src_cur]), NULL, + offset, count, &count, NULL, + NULL); + } + + if (ret < 0) { + if (s->salvage) { + if (n == 1) { + if (!s->quiet) { + warn_report("error while reading block status at " + "offset %" PRIu64 ": %s", offset, + strerror(-ret)); + } + /* Just try to read the data, then */ + ret = BDRV_BLOCK_DATA; + count = BDRV_SECTOR_SIZE; + } else { + /* Retry on a shorter range */ + n = DIV_ROUND_UP(n, 4); + } + } else { + error_report("error while reading block status at offset " + "%" PRIu64 ": %s", offset, strerror(-ret)); + return ret; + } + } + } while (ret < 0); - ret = bdrv_block_status(blk_bs(s->src[src_cur]), - (sector_num - src_cur_offset) * - BDRV_SECTOR_SIZE, - count, &count, NULL, NULL); - } else { - ret = bdrv_block_status_above(blk_bs(s->src[src_cur]), NULL, - (sector_num - src_cur_offset) * - BDRV_SECTOR_SIZE, - count, &count, NULL, NULL); - } - if (ret < 0) { - error_report("error while reading block status of sector %" PRId64 - ": %s", sector_num, strerror(-ret)); - return ret; - } n = DIV_ROUND_UP(count, BDRV_SECTOR_SIZE); if (ret & BDRV_BLOCK_ZERO) { @@ -1680,6 +1705,7 @@ static int convert_iteration_sectors(ImgConvertState *s, int64_t sector_num) static int coroutine_fn convert_co_read(ImgConvertState *s, int64_t sector_num, int nb_sectors, uint8_t *buf) { + uint64_t single_read_until = 0; int n, ret; assert(nb_sectors <= s->buf_sectors); @@ -1687,6 +1713,7 @@ static int coroutine_fn convert_co_read(ImgConvertState *s, int64_t sector_num, BlockBackend *blk; int src_cur; int64_t bs_sectors, src_cur_offset; + uint64_t offset; /* In the case of compression with multiple source files, we can get a * nb_sectors that spreads into the next part. So we must be able to @@ -1695,13 +1722,29 @@ static int coroutine_fn convert_co_read(ImgConvertState *s, int64_t sector_num, blk = s->src[src_cur]; bs_sectors = s->src_sectors[src_cur]; + offset = (sector_num - src_cur_offset) << BDRV_SECTOR_BITS; + n = MIN(nb_sectors, bs_sectors - (sector_num - src_cur_offset)); + if (single_read_until > offset) { + n = 1; + } - ret = blk_co_pread( - blk, (sector_num - src_cur_offset) << BDRV_SECTOR_BITS, - n << BDRV_SECTOR_BITS, buf, 0); + ret = blk_co_pread(blk, offset, n << BDRV_SECTOR_BITS, buf, 0); if (ret < 0) { - return ret; + if (s->salvage) { + if (n > 1) { + single_read_until = offset + (n << BDRV_SECTOR_BITS); + continue; + } else { + if (!s->quiet) { + warn_report("error while reading offset %" PRIu64 + ": %s", offset, strerror(-ret)); + } + memset(buf, 0, BDRV_SECTOR_SIZE); + } + } else { + return ret; + } } sector_num += n; @@ -1933,9 +1976,11 @@ static int convert_do_copy(ImgConvertState *s) int64_t sector_num = 0; /* Check whether we have zero initialisation or can get it efficiently */ - s->has_zero_init = s->min_sparse && !s->target_has_backing - ? bdrv_has_zero_init(blk_bs(s->target)) - : false; + if (s->target_is_new && s->min_sparse && !s->target_has_backing) { + s->has_zero_init = bdrv_has_zero_init(blk_bs(s->target)); + } else { + s->has_zero_init = false; + } if (!s->has_zero_init && !s->target_has_backing && bdrv_can_write_zeroes_with_unmap(blk_bs(s->target))) @@ -2010,7 +2055,7 @@ static int img_convert(int argc, char **argv) QDict *open_opts = NULL; char *options = NULL; Error *local_err = NULL; - bool writethrough, src_writethrough, quiet = false, image_opts = false, + bool writethrough, src_writethrough, image_opts = false, skip_create = false, progress = false, tgt_image_opts = false; int64_t ret = -EINVAL; bool force_share = false; @@ -2032,6 +2077,7 @@ static int img_convert(int argc, char **argv) {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, {"force-share", no_argument, 0, 'U'}, {"target-image-opts", no_argument, 0, OPTION_TARGET_IMAGE_OPTS}, + {"salvage", no_argument, 0, OPTION_SALVAGE}, {0, 0, 0, 0} }; c = getopt_long(argc, argv, ":hf:O:B:Cco:l:S:pt:T:qnm:WU", @@ -2118,7 +2164,7 @@ static int img_convert(int argc, char **argv) src_cache = optarg; break; case 'q': - quiet = true; + s.quiet = true; break; case 'n': skip_create = true; @@ -2149,6 +2195,9 @@ static int img_convert(int argc, char **argv) case OPTION_IMAGE_OPTS: image_opts = true; break; + case OPTION_SALVAGE: + s.salvage = true; + break; case OPTION_TARGET_IMAGE_OPTS: tgt_image_opts = true; break; @@ -2175,11 +2224,21 @@ static int img_convert(int argc, char **argv) goto fail_getopt; } + if (s.copy_range && s.salvage) { + error_report("Cannot use copy offloading in salvaging mode"); + goto fail_getopt; + } + if (tgt_image_opts && !skip_create) { error_report("--target-image-opts requires use of -n flag"); goto fail_getopt; } + if (skip_create && options) { + warn_report("-o has no effect when skipping image creation"); + warn_report("This will become an error in future QEMU versions."); + } + s.src_num = argc - optind - 1; out_filename = s.src_num >= 1 ? argv[argc - 1] : NULL; @@ -2207,7 +2266,7 @@ static int img_convert(int argc, char **argv) } /* Initialize before goto out */ - if (quiet) { + if (s.quiet) { progress = false; } qemu_progress_init(progress, 1.0); @@ -2218,7 +2277,7 @@ static int img_convert(int argc, char **argv) for (bs_i = 0; bs_i < s.src_num; bs_i++) { s.src[bs_i] = img_open(image_opts, argv[optind + bs_i], - fmt, src_flags, src_writethrough, quiet, + fmt, src_flags, src_writethrough, s.quiet, force_share); if (!s.src[bs_i]) { ret = -1; @@ -2329,7 +2388,7 @@ static int img_convert(int argc, char **argv) const char *preallocation = qemu_opt_get(opts, BLOCK_OPT_PREALLOC); - if (drv && !drv->bdrv_co_pwritev_compressed) { + if (drv && !block_driver_can_compress(drv)) { error_report("Compression not supported for this file format"); ret = -1; goto out; @@ -2372,6 +2431,8 @@ static int img_convert(int argc, char **argv) } } + s.target_is_new = !skip_create; + flags = s.min_sparse ? (BDRV_O_RDWR | BDRV_O_UNMAP) : BDRV_O_RDWR; ret = bdrv_parse_cache_mode(cache, &flags, &writethrough); if (ret < 0) { @@ -2381,7 +2442,7 @@ static int img_convert(int argc, char **argv) if (skip_create) { s.target = img_open(tgt_image_opts, out_filename, out_fmt, - flags, writethrough, quiet, false); + flags, writethrough, s.quiet, false); } else { /* TODO ultimately we should allow --target-image-opts * to be used even when -n is not given. @@ -2389,7 +2450,7 @@ static int img_convert(int argc, char **argv) * to allow filenames in option syntax */ s.target = img_open_file(out_filename, open_opts, out_fmt, - flags, writethrough, quiet, false); + flags, writethrough, s.quiet, false); open_opts = NULL; /* blk_new_open will have freed it */ } if (!s.target) { @@ -2398,7 +2459,7 @@ static int img_convert(int argc, char **argv) } out_bs = blk_bs(s.target); - if (s.compressed && !out_bs->drv->bdrv_co_pwritev_compressed) { + if (s.compressed && !block_driver_can_compress(out_bs->drv)) { error_report("Compression not supported for this file format"); ret = -1; goto out; @@ -3164,7 +3225,7 @@ static int img_rebase(int argc, char **argv) BlockBackend *blk = NULL, *blk_old_backing = NULL, *blk_new_backing = NULL; uint8_t *buf_old = NULL; uint8_t *buf_new = NULL; - BlockDriverState *bs = NULL; + BlockDriverState *bs = NULL, *prefix_chain_bs = NULL; char *filename; const char *fmt, *cache, *src_cache, *out_basefmt, *out_baseimg; int c, flags, src_flags, ret; @@ -3309,29 +3370,19 @@ static int img_rebase(int argc, char **argv) /* For safe rebasing we need to compare old and new backing file */ if (!unsafe) { - char backing_name[PATH_MAX]; QDict *options = NULL; - - if (bs->backing) { - if (bs->backing_format[0] != '\0') { - options = qdict_new(); - qdict_put_str(options, "driver", bs->backing_format); - } - - if (force_share) { - if (!options) { - options = qdict_new(); - } - qdict_put_bool(options, BDRV_OPT_FORCE_SHARE, true); - } - bdrv_get_backing_filename(bs, backing_name, sizeof(backing_name)); - blk_old_backing = blk_new_open(backing_name, NULL, - options, src_flags, &local_err); - if (!blk_old_backing) { + BlockDriverState *base_bs = backing_bs(bs); + + if (base_bs) { + blk_old_backing = blk_new(qemu_get_aio_context(), + BLK_PERM_CONSISTENT_READ, + BLK_PERM_ALL); + ret = blk_insert_bs(blk_old_backing, base_bs, + &local_err); + if (ret < 0) { error_reportf_err(local_err, - "Could not open old backing file '%s': ", - backing_name); - ret = -1; + "Could not reuse old backing file '%s': ", + base_bs->filename); goto out; } } else { @@ -3358,21 +3409,44 @@ static int img_rebase(int argc, char **argv) out_baseimg, &local_err); if (local_err) { + qobject_unref(options); error_reportf_err(local_err, "Could not resolve backing filename: "); ret = -1; goto out; } - blk_new_backing = blk_new_open(out_real_path, NULL, - options, src_flags, &local_err); - g_free(out_real_path); - if (!blk_new_backing) { - error_reportf_err(local_err, - "Could not open new backing file '%s': ", - out_baseimg); - ret = -1; - goto out; + /* + * Find out whether we rebase an image on top of a previous image + * in its chain. + */ + prefix_chain_bs = bdrv_find_backing_image(bs, out_real_path); + if (prefix_chain_bs) { + qobject_unref(options); + g_free(out_real_path); + + blk_new_backing = blk_new(qemu_get_aio_context(), + BLK_PERM_CONSISTENT_READ, + BLK_PERM_ALL); + ret = blk_insert_bs(blk_new_backing, prefix_chain_bs, + &local_err); + if (ret < 0) { + error_reportf_err(local_err, + "Could not reuse backing file '%s': ", + out_baseimg); + goto out; + } + } else { + blk_new_backing = blk_new_open(out_real_path, NULL, + options, src_flags, &local_err); + g_free(out_real_path); + if (!blk_new_backing) { + error_reportf_err(local_err, + "Could not open new backing file '%s': ", + out_baseimg); + ret = -1; + goto out; + } } } } @@ -3448,6 +3522,23 @@ static int img_rebase(int argc, char **argv) continue; } + if (prefix_chain_bs) { + /* + * If cluster wasn't changed since prefix_chain, we don't need + * to take action + */ + ret = bdrv_is_allocated_above(backing_bs(bs), prefix_chain_bs, + false, offset, n, &n); + if (ret < 0) { + error_report("error while reading image metadata: %s", + strerror(-ret)); + goto out; + } + if (!ret) { + continue; + } + } + /* * Read old and new backing file and take into consideration that * backing files may be smaller than the COW image.