summaryrefslogtreecommitdiffstats
path: root/qemu-img.c
diff options
context:
space:
mode:
authorMax Reitz2019-05-07 22:35:03 +0200
committerMax Reitz2019-06-14 14:16:57 +0200
commit8eaac025fb43b16a38671e6b80e7059cd8af3403 (patch)
treef437fa3bf14089b130284fa9f305a50632283c9c /qemu-img.c
parentqemu-img: Move quiet into ImgConvertState (diff)
downloadqemu-8eaac025fb43b16a38671e6b80e7059cd8af3403.tar.gz
qemu-8eaac025fb43b16a38671e6b80e7059cd8af3403.tar.xz
qemu-8eaac025fb43b16a38671e6b80e7059cd8af3403.zip
qemu-img: Add salvaging mode to convert
This adds a salvaging mode (--salvage) to qemu-img convert which ignores read errors and treats the respective areas as containing only zeroes. This can be used for instance to at least partially recover the data from terminally corrupted qcow2 images. Signed-off-by: Max Reitz <mreitz@redhat.com> Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> Message-id: 20190507203508.18026-3-mreitz@redhat.com Signed-off-by: Max Reitz <mreitz@redhat.com>
Diffstat (limited to 'qemu-img.c')
-rw-r--r--qemu-img.c90
1 files changed, 69 insertions, 21 deletions
diff --git a/qemu-img.c b/qemu-img.c
index e15e617256..158b3a505f 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -69,6 +69,7 @@ enum {
OPTION_SIZE = 264,
OPTION_PREALLOCATION = 265,
OPTION_SHRINK = 266,
+ OPTION_SALVAGE = 267,
};
typedef enum OutputFormat {
@@ -1581,6 +1582,7 @@ typedef struct ImgConvertState {
int64_t target_backing_sectors; /* negative if unknown */
bool wr_in_order;
bool copy_range;
+ bool salvage;
bool quiet;
int min_sparse;
int alignment;
@@ -1628,25 +1630,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) {
@@ -1683,6 +1704,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);
@@ -1690,6 +1712,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
@@ -1698,13 +1721,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;
@@ -2035,6 +2074,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",
@@ -2152,6 +2192,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;
@@ -2178,6 +2221,11 @@ 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;