|
@@ -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;
|