we now read the extension on open and write it on update, but do not yet use it.
Signed-off-by: Peter Lieven <p...@kamp.de> --- block/qcow2.c | 94 +++++++++++++++++++++++++++++++++++++++++++++++++++++++---- block/qcow2.h | 21 +++++++++---- 2 files changed, 105 insertions(+), 10 deletions(-) diff --git a/block/qcow2.c b/block/qcow2.c index 6bf4ea5..1dd2a48 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -70,6 +70,7 @@ typedef struct { #define QCOW2_EXT_MAGIC_FEATURE_TABLE 0x6803f857 #define QCOW2_EXT_MAGIC_CRYPTO_HEADER 0x0537be77 #define QCOW2_EXT_MAGIC_BITMAPS 0x23852875 +#define QCOW2_EXT_MAGIC_COMPRESS_FORMAT 0xC03183A3 static int qcow2_probe(const uint8_t *buf, int buf_size, const char *filename) { @@ -164,6 +165,17 @@ static ssize_t qcow2_crypto_hdr_write_func(QCryptoBlock *block, size_t offset, } +static void qcow2_compress_level_supported(int format, uint8_t level, + Error **errp) +{ + if (format == QCOW2_COMPRESS_FORMAT_ZLIB && level > 9) { + error_setg(errp, "ERROR: compress level %" PRIu8 " is not" + " supported for format '%s'", level, + Qcow2CompressFormat_lookup[format]); + } +} + + /* * read qcow2 extension and fill bs * start reading from start_offset @@ -241,6 +253,48 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, #endif break; + case QCOW2_EXT_MAGIC_COMPRESS_FORMAT: + { + Qcow2CompressFormatExt compress_ext; + Error *local_err = NULL; + if (ext.len != sizeof(compress_ext)) { + error_setg(errp, "ERROR: ext_compress_format: len=%" + PRIu32 " invalid (!=%zu)", ext.len, + sizeof(compress_ext)); + return 2; + } + ret = bdrv_pread(bs->file, offset, &compress_ext, + ext.len); + if (ret < 0) { + error_setg_errno(errp, -ret, "ERROR: ext_compress_fromat:" + " Could not read extension"); + return 3; + } + + s->compress_format = + qapi_enum_parse(Qcow2CompressFormat_lookup, + compress_ext.name, QCOW2_COMPRESS_FORMAT__MAX, + -1, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return 4; + } + + qcow2_compress_level_supported(s->compress_format, + compress_ext.level, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return 5; + } + s->compress_level = compress_ext.level; + +#ifdef DEBUG_EXT + printf("Qcow2: Got compress format %s with compress level %" + PRIu8 "\n", Qcow2CompressFormat_lookup[s->compress_format], + s->compress_level); +#endif + break; + } case QCOW2_EXT_MAGIC_FEATURE_TABLE: if (p_feature_table != NULL) { void* feature_table = g_malloc0(ext.len + 2 * sizeof(Qcow2Feature)); @@ -1374,6 +1428,7 @@ static int qcow2_do_open(BlockDriverState *bs, QDict *options, int flags, s->cluster_cache_offset = -1; s->flags = flags; + s->compress_format = -1; ret = qcow2_refcount_init(bs); if (ret != 0) { @@ -2292,6 +2347,25 @@ int qcow2_update_header(BlockDriverState *bs) buflen -= ret; } + /* Compress Format header extension */ + if (s->compress_format >= 0) { + Qcow2CompressFormatExt ext; + assert(s->compress_format < QCOW2_COMPRESS_FORMAT__MAX); + strncpy((char *) &ext.name, + Qcow2CompressFormat_lookup[s->compress_format], + sizeof(ext.name)); + ext.level = s->compress_level; + ret = header_ext_add(buf, QCOW2_EXT_MAGIC_COMPRESS_FORMAT, + &ext, sizeof(ext), + buflen); + if (ret < 0) { + goto fail; + } + buf += ret; + buflen -= ret; + header->incompatible_features |= cpu_to_be64(QCOW2_INCOMPAT_COMPRESS); + } + /* Feature table */ if (s->qcow_version >= 3) { Qcow2Feature features[] = { @@ -2306,6 +2380,11 @@ int qcow2_update_header(BlockDriverState *bs) .name = "corrupt bit", }, { + .type = QCOW2_FEAT_TYPE_INCOMPATIBLE, + .bit = QCOW2_INCOMPAT_COMPRESS_BITNR, + .name = "compress format bit", + }, + { .type = QCOW2_FEAT_TYPE_COMPATIBLE, .bit = QCOW2_COMPAT_LAZY_REFCOUNTS_BITNR, .name = "lazy refcounts", @@ -2827,6 +2906,12 @@ static int qcow2_create2(const char *filename, int64_t total_size, abort(); } + if (compress) { + BDRVQcow2State *s = blk_bs(blk)->opaque; + s->compress_format = compress->format; + s->compress_level = compress->level; + } + /* Create a full header (including things like feature table) */ ret = qcow2_update_header(blk_bs(blk)); if (ret < 0) { @@ -3002,11 +3087,10 @@ static int qcow2_create(const char *filename, QemuOpts *opts, Error **errp) visit_free(v); QDECREF(compressopts); QDECREF(options); - if (compress.format == QCOW2_COMPRESS_FORMAT_ZLIB && - compress.level > 9) { - error_setg(errp, "Compress level %" PRIu8 " is not supported for" - " format '%s'", compress.level, - Qcow2CompressFormat_lookup[compress.format]); + qcow2_compress_level_supported(compress.format, compress.level, + &local_err); + if (local_err) { + error_propagate(errp, local_err); ret = -EINVAL; goto finish; } diff --git a/block/qcow2.h b/block/qcow2.h index 96a8d43..c48d29e 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -188,13 +188,16 @@ enum { /* Incompatible feature bits */ enum { - QCOW2_INCOMPAT_DIRTY_BITNR = 0, - QCOW2_INCOMPAT_CORRUPT_BITNR = 1, - QCOW2_INCOMPAT_DIRTY = 1 << QCOW2_INCOMPAT_DIRTY_BITNR, - QCOW2_INCOMPAT_CORRUPT = 1 << QCOW2_INCOMPAT_CORRUPT_BITNR, + QCOW2_INCOMPAT_DIRTY_BITNR = 0, + QCOW2_INCOMPAT_CORRUPT_BITNR = 1, + QCOW2_INCOMPAT_COMPRESS_BITNR = 2, + QCOW2_INCOMPAT_DIRTY = 1 << QCOW2_INCOMPAT_DIRTY_BITNR, + QCOW2_INCOMPAT_CORRUPT = 1 << QCOW2_INCOMPAT_CORRUPT_BITNR, + QCOW2_INCOMPAT_COMPRESS = 1 << QCOW2_INCOMPAT_COMPRESS_BITNR, QCOW2_INCOMPAT_MASK = QCOW2_INCOMPAT_DIRTY - | QCOW2_INCOMPAT_CORRUPT, + | QCOW2_INCOMPAT_CORRUPT + | QCOW2_INCOMPAT_COMPRESS, }; /* Compatible feature bits */ @@ -228,6 +231,11 @@ typedef struct Qcow2Feature { char name[46]; } QEMU_PACKED Qcow2Feature; +typedef struct Qcow2CompressFormatExt { + char name[16]; + uint8_t level; +} QEMU_PACKED Qcow2CompressFormatExt; + typedef struct Qcow2DiscardRegion { BlockDriverState *bs; uint64_t offset; @@ -304,6 +312,9 @@ typedef struct BDRVQcow2State { int refcount_bits; uint64_t refcount_max; + int compress_format; + uint8_t compress_level; + Qcow2GetRefcountFunc *get_refcount; Qcow2SetRefcountFunc *set_refcount; -- 1.9.1