Re: [PATCH v1 1/8] qcow2: introduce compression type feature

2020-02-28 Thread Denis Plotnikov




On 27.02.2020 16:48, Eric Blake wrote:

On 2/27/20 1:29 AM, Denis Plotnikov wrote:

The patch adds some preparation parts for incompatible compression type
feature to Qcow2 that indicates which allow to use different compression


to qcow2, allowing the use of different


methods for image clusters (de)compressing.

It is implied that the compression type is set on the image creation and
can be changed only later by image conversion, thus compression type
defines the only compression algorithm used for the image, and thus,
for all image clusters.

The goal of the feature is to add support of other compression methods
to qcow2. For example, ZSTD which is more effective on compression 
than ZLIB.


The default compression is ZLIB. Images created with ZLIB compression 
type

are backward compatible with older qemu versions.

Signed-off-by: Denis Plotnikov 
---
  block/qcow2.c | 105 ++
  block/qcow2.h |  31 ---
  include/block/block_int.h |   1 +
  qapi/block-core.json  |  22 +++-
  4 files changed, 150 insertions(+), 9 deletions(-)

diff --git a/block/qcow2.c b/block/qcow2.c
index 3c754f616b..2ccb2cabd1 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -1242,6 +1242,50 @@ static int 
qcow2_update_options(BlockDriverState *bs, QDict *options,

  return ret;
  }
  +static int validate_compression_type(BDRVQcow2State *s, Error **errp)
+{
+    /*
+ * Sanity check
+ * according to qcow2 spec, the compression type is 1-byte field
+ * but in BDRVQcow2State the compression_type is enum sizeof(int)
+ * so, the max compression_type value is 255.
+ */
+    if (s->compression_type > 0xff) {
+    error_setg(errp, "qcow2: compression type value is too big");
+    return -EINVAL;
+    }


Hmm - I think it may be worth a tweak to qcow2.txt to call out:

104: compression_type
105 - 111: padding, must be 0

or else call out:

104-111: compression type

and just blindly use all 8 bytes for the value even though really only 
1 or two values will ever be defined.  Of course, that moves the byte 
in question from 104 to 111, thanks to our big endian encoding, but as 
this series is the first one installing a non-zero value in those 8 
bytes, and as we just finished documenting that the header length must 
be a multiple of 8, there is no real impact - we can make such tweaks 
up until the 5.0 release.



+
+    switch (s->compression_type) {
+    case QCOW2_COMPRESSION_TYPE_ZLIB:
+    break;
+
+    default:
+    error_setg(errp, "qcow2: unknown compression type: %u",
+   s->compression_type);
+    return -ENOTSUP;
+    }


Having two checks feels redundant, compared to just letting the 
default catch all unrecognized values in that field.

Looks like it is.



+
+    /*
+ * if the compression type differs from QCOW2_COMPRESSION_TYPE_ZLIB
+ * the incompatible feature flag must be set
+ */
+    if (s->compression_type == QCOW2_COMPRESSION_TYPE_ZLIB) {
+    if (s->incompatible_features & 
QCOW2_INCOMPAT_COMPRESSION_TYPE) {
+    error_setg(errp, "qcow2: Compression type incompatible 
feature "

+ "bit must not be set");
+    return -EINVAL;
+    }
+    } else {
+    if (!(s->incompatible_features & 
QCOW2_INCOMPAT_COMPRESSION_TYPE)) {
+    error_setg(errp, "qcow2: Compression type incompatible 
feature "

+ "bit must be set");
+    return -EINVAL;
+    }
+    }


Matches what we documented in the spec.


+
+    return 0;
+}
+
  /* Called with s->lock held.  */
  static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict 
*options,

    int flags, Error **errp)
@@ -1357,6 +1401,26 @@ static int coroutine_fn 
qcow2_do_open(BlockDriverState *bs, QDict *options,

  s->compatible_features  = header.compatible_features;
  s->autoclear_features   = header.autoclear_features;
  +    /*
+ * Handle compression type
+ * Older qcow2 images don't contain the compression type header.
+ * Distinguish them by the header length and use
+ * the only valid (default) compression type in that case
+ */
+    if (header.header_length > offsetof(QCowHeader, 
compression_type)) {

+    /*
+ * don't deal with endians since compression_type is 1 byte 
long

+ */
+    s->compression_type = header.compression_type;


Changes if you go with my suggestion of just making the 
compression_type field occupy 8 bytes in the qcow2 header.  (And if 
you want to keep it 1 byte, I still think the spec should call out 
explicit padding bytes).



+    } else {
+    s->compression_type = QCOW2_COMPRESSION_TYPE_ZLIB;
+    }
+
+    ret = validate_compression_type(s, errp);
+    if (ret) {
+    goto fail;
+    }
+
  if (s->incompatible_features & ~QCOW2_INCOMPAT_MASK) {
  void *feature_table = NULL;

Re: [PATCH v1 1/8] qcow2: introduce compression type feature

2020-02-27 Thread Eric Blake

On 2/27/20 8:30 AM, Vladimir Sementsov-Ogievskiy wrote:

But what is the benefit? We have already documented padding in the 
spec, and discussed it so much time... What is the problem with 
padding? And why to add 8 bytes for every new feature which needs 
only one byte?


Okay, so requiring an 8-byte field is not necessary.  But still, at 
least mentioning padding bytes (that may be assigned meanings later) 
is consistent with the rest of the document (for example, we have 
padding bits documented for the compatible/incompatible/autoclear 
feature bits), and reminds implementers to keep size rounded to a 
multiple of 8.




Yes, we can add something about it.. But I'm not sure we need, and I 
can't imaging correct short wording.



We have section about padding:

"
=== Header padding ===

@header_length must be a multiple of 8, which means that if the end of 
the last
additional field is not aligned, some padding is needed. This padding 
must be
zeroed, so that if some existing (or future) additional field will fall 
into
the padding, it will be interpreted accordingly to point [3.] of the 
previous

paragraph, i.e.  in the same manner as when this field is not present.
"


So, if we want to add something about 104-111, it should be added to 
this section, not to previous "=== Additional fields (version 3 and 
higher) ===".


And, if we want, to add something, we should consider both cases when 
compression type field exists and when not... What to write?


"105 - 111: These bytes are padding, if header length > 104. May be 
turned into new additional fields in future."


Sounds a bit strange... Keeping in mind that different versions of qemu 
may consider the same bytes as additional field or as padding, and it is 
correct.


Looking at the header extension, it can probably be as simple as:

105 - m: Zero padding to round up the header size to the next
 multiple of 8.

I guess I'll propose a patch to make my idea concrete.

--
Eric Blake, Principal Software Engineer
Red Hat, Inc.   +1-919-301-3226
Virtualization:  qemu.org | libvirt.org




Re: [PATCH v1 1/8] qcow2: introduce compression type feature

2020-02-27 Thread Vladimir Sementsov-Ogievskiy

27.02.2020 17:13, Eric Blake wrote:

On 2/27/20 7:59 AM, Vladimir Sementsov-Ogievskiy wrote:


Hmm - I think it may be worth a tweak to qcow2.txt to call out:

104: compression_type
105 - 111: padding, must be 0

or else call out:

104-111: compression type

and just blindly use all 8 bytes for the value even though really only 1 or two 
values will ever be defined.  Of course, that moves the byte in question from 
104 to 111, thanks to our big endian encoding, but as this series is the first 
one installing a non-zero value in those 8 bytes, and as we just finished 
documenting that the header length must be a multiple of 8, there is no real 
impact - we can make such tweaks up until the 5.0 release.


But what is the benefit? We have already documented padding in the spec, and 
discussed it so much time... What is the problem with padding? And why to add 8 
bytes for every new feature which needs only one byte?


Okay, so requiring an 8-byte field is not necessary.  But still, at least 
mentioning padding bytes (that may be assigned meanings later) is consistent 
with the rest of the document (for example, we have padding bits documented for 
the compatible/incompatible/autoclear feature bits), and reminds implementers 
to keep size rounded to a multiple of 8.



Yes, we can add something about it.. But I'm not sure we need, and I can't 
imaging correct short wording.


We have section about padding:

"
=== Header padding ===

@header_length must be a multiple of 8, which means that if the end of the last
additional field is not aligned, some padding is needed. This padding must be
zeroed, so that if some existing (or future) additional field will fall into
the padding, it will be interpreted accordingly to point [3.] of the previous
paragraph, i.e.  in the same manner as when this field is not present.
"


So, if we want to add something about 104-111, it should be added to this section, not to 
previous "=== Additional fields (version 3 and higher) ===".

And, if we want, to add something, we should consider both cases when 
compression type field exists and when not... What to write?

"105 - 111: These bytes are padding, if header length > 104. May be turned into new 
additional fields in future."

Sounds a bit strange... Keeping in mind that different versions of qemu may 
consider the same bytes as additional field or as padding, and it is correct.


--
Best regards,
Vladimir



Re: [PATCH v1 1/8] qcow2: introduce compression type feature

2020-02-27 Thread Eric Blake

On 2/27/20 7:59 AM, Vladimir Sementsov-Ogievskiy wrote:


Hmm - I think it may be worth a tweak to qcow2.txt to call out:

104: compression_type
105 - 111: padding, must be 0

or else call out:

104-111: compression type

and just blindly use all 8 bytes for the value even though really only 
1 or two values will ever be defined.  Of course, that moves the byte 
in question from 104 to 111, thanks to our big endian encoding, but as 
this series is the first one installing a non-zero value in those 8 
bytes, and as we just finished documenting that the header length must 
be a multiple of 8, there is no real impact - we can make such tweaks 
up until the 5.0 release.


But what is the benefit? We have already documented padding in the spec, 
and discussed it so much time... What is the problem with padding? And 
why to add 8 bytes for every new feature which needs only one byte?


Okay, so requiring an 8-byte field is not necessary.  But still, at 
least mentioning padding bytes (that may be assigned meanings later) is 
consistent with the rest of the document (for example, we have padding 
bits documented for the compatible/incompatible/autoclear feature bits), 
and reminds implementers to keep size rounded to a multiple of 8.


--
Eric Blake, Principal Software Engineer
Red Hat, Inc.   +1-919-301-3226
Virtualization:  qemu.org | libvirt.org




Re: [PATCH v1 1/8] qcow2: introduce compression type feature

2020-02-27 Thread Vladimir Sementsov-Ogievskiy

27.02.2020 16:48, Eric Blake wrote:

On 2/27/20 1:29 AM, Denis Plotnikov wrote:

The patch adds some preparation parts for incompatible compression type
feature to Qcow2 that indicates which allow to use different compression


to qcow2, allowing the use of different


methods for image clusters (de)compressing.

It is implied that the compression type is set on the image creation and
can be changed only later by image conversion, thus compression type
defines the only compression algorithm used for the image, and thus,
for all image clusters.

The goal of the feature is to add support of other compression methods
to qcow2. For example, ZSTD which is more effective on compression than ZLIB.

The default compression is ZLIB. Images created with ZLIB compression type
are backward compatible with older qemu versions.

Signed-off-by: Denis Plotnikov 
---
  block/qcow2.c | 105 ++
  block/qcow2.h |  31 ---
  include/block/block_int.h |   1 +
  qapi/block-core.json  |  22 +++-
  4 files changed, 150 insertions(+), 9 deletions(-)

diff --git a/block/qcow2.c b/block/qcow2.c
index 3c754f616b..2ccb2cabd1 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -1242,6 +1242,50 @@ static int qcow2_update_options(BlockDriverState *bs, 
QDict *options,
  return ret;
  }
+static int validate_compression_type(BDRVQcow2State *s, Error **errp)
+{
+    /*
+ * Sanity check
+ * according to qcow2 spec, the compression type is 1-byte field
+ * but in BDRVQcow2State the compression_type is enum sizeof(int)
+ * so, the max compression_type value is 255.
+ */
+    if (s->compression_type > 0xff) {
+    error_setg(errp, "qcow2: compression type value is too big");
+    return -EINVAL;
+    }


Hmm - I think it may be worth a tweak to qcow2.txt to call out:

104: compression_type
105 - 111: padding, must be 0

or else call out:

104-111: compression type

and just blindly use all 8 bytes for the value even though really only 1 or two 
values will ever be defined.  Of course, that moves the byte in question from 
104 to 111, thanks to our big endian encoding, but as this series is the first 
one installing a non-zero value in those 8 bytes, and as we just finished 
documenting that the header length must be a multiple of 8, there is no real 
impact - we can make such tweaks up until the 5.0 release.


But what is the benefit? We have already documented padding in the spec, and 
discussed it so much time... What is the problem with padding? And why to add 8 
bytes for every new feature which needs only one byte?




+
+    switch (s->compression_type) {
+    case QCOW2_COMPRESSION_TYPE_ZLIB:
+    break;
+
+    default:
+    error_setg(errp, "qcow2: unknown compression type: %u",
+   s->compression_type);
+    return -ENOTSUP;
+    }


Having two checks feels redundant, compared to just letting the default catch 
all unrecognized values in that field.


+
+    /*
+ * if the compression type differs from QCOW2_COMPRESSION_TYPE_ZLIB
+ * the incompatible feature flag must be set
+ */
+    if (s->compression_type == QCOW2_COMPRESSION_TYPE_ZLIB) {
+    if (s->incompatible_features & QCOW2_INCOMPAT_COMPRESSION_TYPE) {
+    error_setg(errp, "qcow2: Compression type incompatible feature "
+ "bit must not be set");
+    return -EINVAL;
+    }
+    } else {
+    if (!(s->incompatible_features & QCOW2_INCOMPAT_COMPRESSION_TYPE)) {
+    error_setg(errp, "qcow2: Compression type incompatible feature "
+ "bit must be set");
+    return -EINVAL;
+    }
+    }


Matches what we documented in the spec.


+
+    return 0;
+}
+
  /* Called with s->lock held.  */
  static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options,
    int flags, Error **errp)
@@ -1357,6 +1401,26 @@ static int coroutine_fn qcow2_do_open(BlockDriverState 
*bs, QDict *options,
  s->compatible_features  = header.compatible_features;
  s->autoclear_features   = header.autoclear_features;
+    /*
+ * Handle compression type
+ * Older qcow2 images don't contain the compression type header.
+ * Distinguish them by the header length and use
+ * the only valid (default) compression type in that case
+ */
+    if (header.header_length > offsetof(QCowHeader, compression_type)) {
+    /*
+ * don't deal with endians since compression_type is 1 byte long
+ */
+    s->compression_type = header.compression_type;


Changes if you go with my suggestion of just making the compression_type field 
occupy 8 bytes in the qcow2 header.  (And if you want to keep it 1 byte, I 
still think the spec should call out explicit padding bytes).


+    } else {
+    s->compression_type = QCOW2_COMPRESSION_TYPE_ZLIB;
+    }
+
+    ret = 

Re: [PATCH v1 1/8] qcow2: introduce compression type feature

2020-02-27 Thread Eric Blake

On 2/27/20 1:29 AM, Denis Plotnikov wrote:

The patch adds some preparation parts for incompatible compression type
feature to Qcow2 that indicates which allow to use different compression


to qcow2, allowing the use of different


methods for image clusters (de)compressing.

It is implied that the compression type is set on the image creation and
can be changed only later by image conversion, thus compression type
defines the only compression algorithm used for the image, and thus,
for all image clusters.

The goal of the feature is to add support of other compression methods
to qcow2. For example, ZSTD which is more effective on compression than ZLIB.

The default compression is ZLIB. Images created with ZLIB compression type
are backward compatible with older qemu versions.

Signed-off-by: Denis Plotnikov 
---
  block/qcow2.c | 105 ++
  block/qcow2.h |  31 ---
  include/block/block_int.h |   1 +
  qapi/block-core.json  |  22 +++-
  4 files changed, 150 insertions(+), 9 deletions(-)

diff --git a/block/qcow2.c b/block/qcow2.c
index 3c754f616b..2ccb2cabd1 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -1242,6 +1242,50 @@ static int qcow2_update_options(BlockDriverState *bs, 
QDict *options,
  return ret;
  }
  
+static int validate_compression_type(BDRVQcow2State *s, Error **errp)

+{
+/*
+ * Sanity check
+ * according to qcow2 spec, the compression type is 1-byte field
+ * but in BDRVQcow2State the compression_type is enum sizeof(int)
+ * so, the max compression_type value is 255.
+ */
+if (s->compression_type > 0xff) {
+error_setg(errp, "qcow2: compression type value is too big");
+return -EINVAL;
+}


Hmm - I think it may be worth a tweak to qcow2.txt to call out:

104: compression_type
105 - 111: padding, must be 0

or else call out:

104-111: compression type

and just blindly use all 8 bytes for the value even though really only 1 
or two values will ever be defined.  Of course, that moves the byte in 
question from 104 to 111, thanks to our big endian encoding, but as this 
series is the first one installing a non-zero value in those 8 bytes, 
and as we just finished documenting that the header length must be a 
multiple of 8, there is no real impact - we can make such tweaks up 
until the 5.0 release.



+
+switch (s->compression_type) {
+case QCOW2_COMPRESSION_TYPE_ZLIB:
+break;
+
+default:
+error_setg(errp, "qcow2: unknown compression type: %u",
+   s->compression_type);
+return -ENOTSUP;
+}


Having two checks feels redundant, compared to just letting the default 
catch all unrecognized values in that field.



+
+/*
+ * if the compression type differs from QCOW2_COMPRESSION_TYPE_ZLIB
+ * the incompatible feature flag must be set
+ */
+if (s->compression_type == QCOW2_COMPRESSION_TYPE_ZLIB) {
+if (s->incompatible_features & QCOW2_INCOMPAT_COMPRESSION_TYPE) {
+error_setg(errp, "qcow2: Compression type incompatible feature "
+ "bit must not be set");
+return -EINVAL;
+}
+} else {
+if (!(s->incompatible_features & QCOW2_INCOMPAT_COMPRESSION_TYPE)) {
+error_setg(errp, "qcow2: Compression type incompatible feature "
+ "bit must be set");
+return -EINVAL;
+}
+}


Matches what we documented in the spec.


+
+return 0;
+}
+
  /* Called with s->lock held.  */
  static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options,
int flags, Error **errp)
@@ -1357,6 +1401,26 @@ static int coroutine_fn qcow2_do_open(BlockDriverState 
*bs, QDict *options,
  s->compatible_features  = header.compatible_features;
  s->autoclear_features   = header.autoclear_features;
  
+/*

+ * Handle compression type
+ * Older qcow2 images don't contain the compression type header.
+ * Distinguish them by the header length and use
+ * the only valid (default) compression type in that case
+ */
+if (header.header_length > offsetof(QCowHeader, compression_type)) {
+/*
+ * don't deal with endians since compression_type is 1 byte long
+ */
+s->compression_type = header.compression_type;


Changes if you go with my suggestion of just making the compression_type 
field occupy 8 bytes in the qcow2 header.  (And if you want to keep it 1 
byte, I still think the spec should call out explicit padding bytes).



+} else {
+s->compression_type = QCOW2_COMPRESSION_TYPE_ZLIB;
+}
+
+ret = validate_compression_type(s, errp);
+if (ret) {
+goto fail;
+}
+
  if (s->incompatible_features & ~QCOW2_INCOMPAT_MASK) {
  void *feature_table = NULL;
  qcow2_read_extensions(bs, header.header_length, ext_end,
@@ -2720,6 

Re: [PATCH v1 1/8] qcow2: introduce compression type feature

2020-02-27 Thread Eric Blake

On 2/27/20 2:21 AM, Vladimir Sementsov-Ogievskiy wrote:

27.02.2020 10:29, Denis Plotnikov wrote:

The patch adds some preparation parts for incompatible compression type
feature to Qcow2 that indicates which allow to use different compression
methods for image clusters (de)compressing.

It is implied that the compression type is set on the image creation and
can be changed only later by image conversion, thus compression type
defines the only compression algorithm used for the image, and thus,
for all image clusters.

The goal of the feature is to add support of other compression methods
to qcow2. For example, ZSTD which is more effective on compression 
than ZLIB.


The default compression is ZLIB. Images created with ZLIB compression 
type

are backward compatible with older qemu versions.

Signed-off-by: Denis Plotnikov 
---
  block/qcow2.c | 105 ++
  block/qcow2.h |  31 ---
  include/block/block_int.h |   1 +
  qapi/block-core.json  |  22 +++-
  4 files changed, 150 insertions(+), 9 deletions(-)

diff --git a/block/qcow2.c b/block/qcow2.c
index 3c754f616b..2ccb2cabd1 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c


Please, add to .git/config:

[diff]
     orderFile = /path/to/qemu/scripts/git.orderfile

This will force git format-patch to sort files in more comfortable order 
(header changes first, etc).


As I learned yesterday, git 2.23 and 2.24 have a bug where git 
format-patch fails to honor diff.orderfile (fixed in 2.25): 
https://bugzilla.redhat.com/show_bug.cgi?id=1807681
(and that explains why some of my recent patches have not been ordered 
the way I wanted, as Fedora 31 currently has a git from the broken 
window in time)



--
Eric Blake, Principal Software Engineer
Red Hat, Inc.   +1-919-301-3226
Virtualization:  qemu.org | libvirt.org




Re: [PATCH v1 1/8] qcow2: introduce compression type feature

2020-02-27 Thread Vladimir Sementsov-Ogievskiy

27.02.2020 10:29, Denis Plotnikov wrote:

The patch adds some preparation parts for incompatible compression type
feature to Qcow2 that indicates which allow to use different compression
methods for image clusters (de)compressing.

It is implied that the compression type is set on the image creation and
can be changed only later by image conversion, thus compression type
defines the only compression algorithm used for the image, and thus,
for all image clusters.

The goal of the feature is to add support of other compression methods
to qcow2. For example, ZSTD which is more effective on compression than ZLIB.

The default compression is ZLIB. Images created with ZLIB compression type
are backward compatible with older qemu versions.

Signed-off-by: Denis Plotnikov 
---
  block/qcow2.c | 105 ++
  block/qcow2.h |  31 ---
  include/block/block_int.h |   1 +
  qapi/block-core.json  |  22 +++-
  4 files changed, 150 insertions(+), 9 deletions(-)

diff --git a/block/qcow2.c b/block/qcow2.c
index 3c754f616b..2ccb2cabd1 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c


Please, add to .git/config:

[diff]
orderFile = /path/to/qemu/scripts/git.orderfile

This will force git format-patch to sort files in more comfortable order 
(header changes first, etc).


@@ -1242,6 +1242,50 @@ static int qcow2_update_options(BlockDriverState *bs, 
QDict *options,
  return ret;
  }
  
+static int validate_compression_type(BDRVQcow2State *s, Error **errp)

+{
+/*
+ * Sanity check
+ * according to qcow2 spec, the compression type is 1-byte field
+ * but in BDRVQcow2State the compression_type is enum sizeof(int)
+ * so, the max compression_type value is 255.
+ */
+if (s->compression_type > 0xff) {
+error_setg(errp, "qcow2: compression type value is too big");
+return -EINVAL;
+}
+
+switch (s->compression_type) {
+case QCOW2_COMPRESSION_TYPE_ZLIB:
+break;
+
+default:
+error_setg(errp, "qcow2: unknown compression type: %u",
+   s->compression_type);
+return -ENOTSUP;
+}



honestly, I think that just

if (s->compression_type != QCOW2_COMPRESSION_TYPE_ZLIB) {
  error out
}

is enough, and don't see why to check > 0xff in separate..

But it works as is.


+
+/*
+ * if the compression type differs from QCOW2_COMPRESSION_TYPE_ZLIB
+ * the incompatible feature flag must be set
+ */
+if (s->compression_type == QCOW2_COMPRESSION_TYPE_ZLIB) {
+if (s->incompatible_features & QCOW2_INCOMPAT_COMPRESSION_TYPE) {
+error_setg(errp, "qcow2: Compression type incompatible feature "
+ "bit must not be set");
+return -EINVAL;
+}
+} else {



This is unreachable now.. But it's OK as preparation for further patches I 
think.


+if (!(s->incompatible_features & QCOW2_INCOMPAT_COMPRESSION_TYPE)) {
+error_setg(errp, "qcow2: Compression type incompatible feature "
+ "bit must be set");
+return -EINVAL;
+}
+}
+
+return 0;
+}
+
  /* Called with s->lock held.  */
  static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options,
int flags, Error **errp)
@@ -1357,6 +1401,26 @@ static int coroutine_fn qcow2_do_open(BlockDriverState 
*bs, QDict *options,
  s->compatible_features  = header.compatible_features;
  s->autoclear_features   = header.autoclear_features;
  
+/*

+ * Handle compression type
+ * Older qcow2 images don't contain the compression type header.
+ * Distinguish them by the header length and use
+ * the only valid (default) compression type in that case
+ */
+if (header.header_length > offsetof(QCowHeader, compression_type)) {
+/*
+ * don't deal with endians since compression_type is 1 byte long
+ */



this comment would be more appropriate above, where be-to-cpu transformation is 
done,
as actually previous "s->autoclear_features   = header.autoclear_features" 
doesn't
deal with endians too.


+s->compression_type = header.compression_type;
+} else {
+s->compression_type = QCOW2_COMPRESSION_TYPE_ZLIB;
+}
+
+ret = validate_compression_type(s, errp);
+if (ret) {
+goto fail;
+}
+
  if (s->incompatible_features & ~QCOW2_INCOMPAT_MASK) {
  void *feature_table = NULL;
  qcow2_read_extensions(bs, header.header_length, ext_end,
@@ -2720,6 +2784,12 @@ int qcow2_update_header(BlockDriverState *bs)
  total_size = bs->total_sectors * BDRV_SECTOR_SIZE;
  refcount_table_clusters = s->refcount_table_size >> (s->cluster_bits - 3);
  
+ret = validate_compression_type(s, NULL);

+
+if (ret) {
+goto fail;
+}
+
  *header = (QCowHeader) {
  /* Version 2 fields */
  .magic 

[PATCH v1 1/8] qcow2: introduce compression type feature

2020-02-26 Thread Denis Plotnikov
The patch adds some preparation parts for incompatible compression type
feature to Qcow2 that indicates which allow to use different compression
methods for image clusters (de)compressing.

It is implied that the compression type is set on the image creation and
can be changed only later by image conversion, thus compression type
defines the only compression algorithm used for the image, and thus,
for all image clusters.

The goal of the feature is to add support of other compression methods
to qcow2. For example, ZSTD which is more effective on compression than ZLIB.

The default compression is ZLIB. Images created with ZLIB compression type
are backward compatible with older qemu versions.

Signed-off-by: Denis Plotnikov 
---
 block/qcow2.c | 105 ++
 block/qcow2.h |  31 ---
 include/block/block_int.h |   1 +
 qapi/block-core.json  |  22 +++-
 4 files changed, 150 insertions(+), 9 deletions(-)

diff --git a/block/qcow2.c b/block/qcow2.c
index 3c754f616b..2ccb2cabd1 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -1242,6 +1242,50 @@ static int qcow2_update_options(BlockDriverState *bs, 
QDict *options,
 return ret;
 }
 
+static int validate_compression_type(BDRVQcow2State *s, Error **errp)
+{
+/*
+ * Sanity check
+ * according to qcow2 spec, the compression type is 1-byte field
+ * but in BDRVQcow2State the compression_type is enum sizeof(int)
+ * so, the max compression_type value is 255.
+ */
+if (s->compression_type > 0xff) {
+error_setg(errp, "qcow2: compression type value is too big");
+return -EINVAL;
+}
+
+switch (s->compression_type) {
+case QCOW2_COMPRESSION_TYPE_ZLIB:
+break;
+
+default:
+error_setg(errp, "qcow2: unknown compression type: %u",
+   s->compression_type);
+return -ENOTSUP;
+}
+
+/*
+ * if the compression type differs from QCOW2_COMPRESSION_TYPE_ZLIB
+ * the incompatible feature flag must be set
+ */
+if (s->compression_type == QCOW2_COMPRESSION_TYPE_ZLIB) {
+if (s->incompatible_features & QCOW2_INCOMPAT_COMPRESSION_TYPE) {
+error_setg(errp, "qcow2: Compression type incompatible feature "
+ "bit must not be set");
+return -EINVAL;
+}
+} else {
+if (!(s->incompatible_features & QCOW2_INCOMPAT_COMPRESSION_TYPE)) {
+error_setg(errp, "qcow2: Compression type incompatible feature "
+ "bit must be set");
+return -EINVAL;
+}
+}
+
+return 0;
+}
+
 /* Called with s->lock held.  */
 static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options,
   int flags, Error **errp)
@@ -1357,6 +1401,26 @@ static int coroutine_fn qcow2_do_open(BlockDriverState 
*bs, QDict *options,
 s->compatible_features  = header.compatible_features;
 s->autoclear_features   = header.autoclear_features;
 
+/*
+ * Handle compression type
+ * Older qcow2 images don't contain the compression type header.
+ * Distinguish them by the header length and use
+ * the only valid (default) compression type in that case
+ */
+if (header.header_length > offsetof(QCowHeader, compression_type)) {
+/*
+ * don't deal with endians since compression_type is 1 byte long
+ */
+s->compression_type = header.compression_type;
+} else {
+s->compression_type = QCOW2_COMPRESSION_TYPE_ZLIB;
+}
+
+ret = validate_compression_type(s, errp);
+if (ret) {
+goto fail;
+}
+
 if (s->incompatible_features & ~QCOW2_INCOMPAT_MASK) {
 void *feature_table = NULL;
 qcow2_read_extensions(bs, header.header_length, ext_end,
@@ -2720,6 +2784,12 @@ int qcow2_update_header(BlockDriverState *bs)
 total_size = bs->total_sectors * BDRV_SECTOR_SIZE;
 refcount_table_clusters = s->refcount_table_size >> (s->cluster_bits - 3);
 
+ret = validate_compression_type(s, NULL);
+
+if (ret) {
+goto fail;
+}
+
 *header = (QCowHeader) {
 /* Version 2 fields */
 .magic  = cpu_to_be32(QCOW_MAGIC),
@@ -2742,6 +2812,7 @@ int qcow2_update_header(BlockDriverState *bs)
 .autoclear_features = cpu_to_be64(s->autoclear_features),
 .refcount_order = cpu_to_be32(s->refcount_order),
 .header_length  = cpu_to_be32(header_length),
+.compression_type   = (uint8_t) s->compression_type,
 };
 
 /* For older versions, write a shorter header */
@@ -2839,6 +2910,11 @@ int qcow2_update_header(BlockDriverState *bs)
 .bit  = QCOW2_COMPAT_LAZY_REFCOUNTS_BITNR,
 .name = "lazy refcounts",
 },
+{
+.type = QCOW2_FEAT_TYPE_INCOMPATIBLE,
+.bit  =