@@ -629,11 +629,12 @@ static int img_convert(int argc, char **argv)
int progress = 0, flags;
const char *fmt, *out_fmt, *cache, *out_baseimg, *out_filename;
BlockDriver *drv, *proto_drv;
- BlockDriverState **bs = NULL, *out_bs = NULL;
+ BlockDriverState **bs = NULL, *out_bs = NULL, *out_bf = NULL;
int64_t total_sectors, nb_sectors, sector_num, bs_offset;
- uint64_t bs_sectors;
+ uint64_t bf_sectors, bs_sectors;
uint8_t * buf = NULL;
const uint8_t *buf1;
+ uint8_t * bf_buf = NULL;
BlockDriverInfo bdi;
QEMUOptionParameter *param = NULL, *create_options = NULL;
QEMUOptionParameter *out_baseimg_param;
@@ -797,6 +798,16 @@ static int img_convert(int argc, char **argv)
out_baseimg_param = get_option_parameter(param, BLOCK_OPT_BACKING_FILE);
if (out_baseimg_param) {
out_baseimg = out_baseimg_param->value.s;
+
+ /* out_baseimg_parm != NULL even if there is no base img specified! */
+ if (out_baseimg) {
+ out_bf = bdrv_new_open(out_baseimg, NULL, BDRV_O_FLAGS);
+ if (!out_bf) {
+ ret = -1;
+ goto out;
+ }
+ bdrv_get_geometry(out_bf, &bf_sectors);
+ }
}
/* Check if compression is supported */
@@ -862,6 +873,9 @@ static int img_convert(int argc, char **argv)
bs_offset = 0;
bdrv_get_geometry(bs[0], &bs_sectors);
buf = qemu_blockalign(out_bs, IO_BUF_SIZE);
+ if (out_baseimg) {
+ bf_buf = qemu_blockalign(out_bs, IO_BUF_SIZE);
+ }
if (compress) {
ret = bdrv_get_info(out_bs, &bdi);
@@ -979,31 +993,52 @@ static int img_convert(int argc, char **argv)
n = bs_offset + bs_sectors - sector_num;
}
+ ret = bdrv_read(bs[bs_i], sector_num - bs_offset, buf, n);
+ if (ret < 0) {
+ error_report("error while reading sector %" PRId64 ": %s",
+ sector_num - bs_offset, strerror(-ret));
+ goto out;
+ }
+
if (has_zero_init) {
/* If the output image is being created as a copy on write image,
- assume that sectors which are unallocated in the input image
- are present in both the output's and input's base images (no
- need to copy them). */
+ check that sectors which are unallocated in the input image
+ are present in both the output's and input's base images (or
+ copy them). */
if (out_baseimg) {
- if (!bdrv_is_allocated(bs[bs_i], sector_num - bs_offset,
- n, &n1)) {
- sector_num += n1;
- continue;
+ if (sector_num < bf_sectors) {
+ /* Check if source sectors match those in the new backing file and
+ skip the copy if true. */
+ n = MIN(n, bf_sectors - sector_num);
+ ret = bdrv_read(out_bf, sector_num - bs_offset, bf_buf, n);
+ if (ret < 0) {
+ error_report("error while reading backing file");
+ goto out;
+ }
+ if (!compare_sectors(buf, bf_buf, n, &n1)) {
+ sector_num += n1;
+ continue;
+ }
+
+ } else {
+ /* Source file(s) is larger than the backing file. Just check for
+ allocated sectors */
+ if (!is_allocated_sectors_min(buf, n, &n1, min_sparse)) {
+ /* if we got zero, how many of the next ones are zero? */
+ is_allocated_sectors(buf, n, &n1);
+ sector_num += n1;
+ continue;
+ }
}
- /* The next 'n1' sectors are allocated in the input image. Copy
- only those as they may be followed by unallocated sectors. */
+ /* The next 'n1' sectors are allocated in the input image or the
+ backing files are different. Copy only those as they may be
+ followed by unallocated sectors. */
n = n1;
}
} else {
n1 = n;
}
- ret = bdrv_read(bs[bs_i], sector_num - bs_offset, buf, n);
- if (ret < 0) {
- error_report("error while reading sector %" PRId64 ": %s",
- sector_num - bs_offset, strerror(-ret));
- goto out;
- }
/* NOTE: at the same time we convert, we do not write zero
sectors to have a chance to compress the image. Ideally, we
should add a specific call to have the info to go faster */
@@ -1037,6 +1072,7 @@ out:
free_option_parameters(create_options);
free_option_parameters(param);
qemu_vfree(buf);
+ qemu_vfree(bf_buf);
if (out_bs) {
bdrv_delete(out_bs);
}
@@ -1048,6 +1084,10 @@ out:
}
g_free(bs);
}
+ if (out_bf) {
+ bdrv_delete(out_bf);
+ }
+
if (ret) {
return 1;
}