diff --git a/block/qcow2.c b/block/qcow2.c index d50901675699..c8115e1cba0f 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -5279,6 +5279,38 @@ static int qcow2_load_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, return bs->drv->bdrv_co_preadv_part(bs, offset, qiov->size, qiov, 0, 0); } +static int qcow2_has_compressed_clusters(BlockDriverState *bs) +{ + int64_t offset = 0; + int64_t bytes = bdrv_getlength(bs); + + if (bytes < 0) { + return bytes; + } + + while (bytes != 0) { + int ret; + QCow2SubclusterType type; + unsigned int cur_bytes = MIN(INT_MAX, bytes); + uint64_t host_offset; + + ret = qcow2_get_host_offset(bs, offset, &cur_bytes, &host_offset, + &type); + if (ret < 0) { + return ret; + } + + if (type == QCOW2_SUBCLUSTER_COMPRESSED) { + return 1; + } + + offset += cur_bytes; + bytes -= cur_bytes; + } + + return 0; +} + /* * Downgrades an image's version. To achieve this, any incompatible features * have to be removed. @@ -5336,9 +5368,10 @@ static int qcow2_downgrade(BlockDriverState *bs, int target_version, * the first place; if that happens nonetheless, returning -ENOTSUP is the * best thing to do anyway */ - if (s->incompatible_features) { + if (s->incompatible_features & ~QCOW2_INCOMPAT_COMPRESSION) { error_setg(errp, "Cannot downgrade an image with incompatible features " - "%#" PRIx64 " set", s->incompatible_features); + "0x%" PRIx64 " set", + s->incompatible_features & ~QCOW2_INCOMPAT_COMPRESSION); return -ENOTSUP; } @@ -5356,6 +5389,27 @@ static int qcow2_downgrade(BlockDriverState *bs, int target_version, return ret; } + if (s->incompatible_features & QCOW2_INCOMPAT_COMPRESSION) { + ret = qcow2_has_compressed_clusters(bs); + if (ret < 0) { + error_setg(errp, "Failed to check block status"); + return -EINVAL; + } + if (ret) { + error_setg(errp, "Cannot downgrade an image with zstd compression " + "type and existing compressed clusters"); + return -ENOTSUP; + } + /* + * No compressed clusters for now, so just chose default zlib + * compression. + */ + s->incompatible_features &= ~QCOW2_INCOMPAT_COMPRESSION; + s->compression_type = QCOW2_COMPRESSION_TYPE_ZLIB; + } + + assert(s->incompatible_features == 0); + s->qcow_version = target_version; ret = qcow2_update_header(bs); if (ret < 0) {