| From 53db4963174d9f65bd8ba04636ba0adba7615996 Mon Sep 17 00:00:00 2001 |
| From: Max Reitz <mreitz@redhat.com> |
| Date: Tue, 7 Jan 2014 21:57:13 +0100 |
| Subject: [PATCH 08/14] qcow2: Implement bdrv_amend_options |
| |
| RH-Author: Max Reitz <mreitz@redhat.com> |
| Message-id: <1389131839-12920-9-git-send-email-mreitz@redhat.com> |
| Patchwork-id: 56544 |
| O-Subject: [RHEL-7.0 qemu-kvm PATCH v2 08/14] qcow2: Implement bdrv_amend_options |
| Bugzilla: 1033490 |
| RH-Acked-by: Kevin Wolf <kwolf@redhat.com> |
| RH-Acked-by: Fam Zheng <famz@redhat.com> |
| RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com> |
| |
| BZ: 1033490 |
| |
| Implement bdrv_amend_options for compat, size, backing_file, backing_fmt |
| and lazy_refcounts. |
| |
| Downgrading images from compat=1.1 to compat=0.10 is achieved through |
| handling all incompatible flags accordingly, clearing all compatible and |
| autoclear flags and expanding all zero clusters. |
| |
| Signed-off-by: Max Reitz <mreitz@redhat.com> |
| Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
| (cherry picked from commit 9296b3ed7050cc6e0645fbc3b0aea74406d7eeb2) |
| |
| Signed-off-by: Max Reitz <mreitz@redhat.com> |
| |
| block/qcow2.c | 194 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| 1 file changed, 194 insertions(+) |
| |
| Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com> |
| |
| block/qcow2.c | 194 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| 1 files changed, 194 insertions(+), 0 deletions(-) |
| |
| diff --git a/block/qcow2.c b/block/qcow2.c |
| index 0a53a3c..5b28259 100644 |
| |
| |
| @@ -1989,6 +1989,199 @@ static int qcow2_load_vmstate(BlockDriverState *bs, uint8_t *buf, |
| return ret; |
| } |
| |
| +/* |
| + * Downgrades an image's version. To achieve this, any incompatible features |
| + * have to be removed. |
| + */ |
| +static int qcow2_downgrade(BlockDriverState *bs, int target_version) |
| +{ |
| + BDRVQcowState *s = bs->opaque; |
| + int current_version = s->qcow_version; |
| + int ret; |
| + |
| + if (target_version == current_version) { |
| + return 0; |
| + } else if (target_version > current_version) { |
| + return -EINVAL; |
| + } else if (target_version != 2) { |
| + return -EINVAL; |
| + } |
| + |
| + if (s->refcount_order != 4) { |
| + /* we would have to convert the image to a refcount_order == 4 image |
| + * here; however, since qemu (at the time of writing this) does not |
| + * support anything different than 4 anyway, there is no point in doing |
| + * so right now; however, we should error out (if qemu supports this in |
| + * the future and this code has not been adapted) */ |
| + error_report("qcow2_downgrade: Image refcount orders other than 4 are" |
| + "currently not supported."); |
| + return -ENOTSUP; |
| + } |
| + |
| + /* clear incompatible features */ |
| + if (s->incompatible_features & QCOW2_INCOMPAT_DIRTY) { |
| + ret = qcow2_mark_clean(bs); |
| + if (ret < 0) { |
| + return ret; |
| + } |
| + } |
| + |
| + /* with QCOW2_INCOMPAT_CORRUPT, it is pretty much impossible to get here in |
| + * the first place; if that happens nonetheless, returning -ENOTSUP is the |
| + * best thing to do anyway */ |
| + |
| + if (s->incompatible_features) { |
| + return -ENOTSUP; |
| + } |
| + |
| + /* since we can ignore compatible features, we can set them to 0 as well */ |
| + s->compatible_features = 0; |
| + /* if lazy refcounts have been used, they have already been fixed through |
| + * clearing the dirty flag */ |
| + |
| + /* clearing autoclear features is trivial */ |
| + s->autoclear_features = 0; |
| + |
| + ret = qcow2_expand_zero_clusters(bs); |
| + if (ret < 0) { |
| + return ret; |
| + } |
| + |
| + s->qcow_version = target_version; |
| + ret = qcow2_update_header(bs); |
| + if (ret < 0) { |
| + s->qcow_version = current_version; |
| + return ret; |
| + } |
| + return 0; |
| +} |
| + |
| +static int qcow2_amend_options(BlockDriverState *bs, |
| + QEMUOptionParameter *options) |
| +{ |
| + BDRVQcowState *s = bs->opaque; |
| + int old_version = s->qcow_version, new_version = old_version; |
| + uint64_t new_size = 0; |
| + const char *backing_file = NULL, *backing_format = NULL; |
| + bool lazy_refcounts = s->use_lazy_refcounts; |
| + int ret; |
| + int i; |
| + |
| + for (i = 0; options[i].name; i++) |
| + { |
| + if (!options[i].assigned) { |
| + /* only change explicitly defined options */ |
| + continue; |
| + } |
| + |
| + if (!strcmp(options[i].name, "compat")) { |
| + if (!options[i].value.s) { |
| + /* preserve default */ |
| + } else if (!strcmp(options[i].value.s, "0.10")) { |
| + new_version = 2; |
| + } else if (!strcmp(options[i].value.s, "1.1")) { |
| + new_version = 3; |
| + } else { |
| + fprintf(stderr, "Unknown compatibility level %s.\n", |
| + options[i].value.s); |
| + return -EINVAL; |
| + } |
| + } else if (!strcmp(options[i].name, "preallocation")) { |
| + fprintf(stderr, "Cannot change preallocation mode.\n"); |
| + return -ENOTSUP; |
| + } else if (!strcmp(options[i].name, "size")) { |
| + new_size = options[i].value.n; |
| + } else if (!strcmp(options[i].name, "backing_file")) { |
| + backing_file = options[i].value.s; |
| + } else if (!strcmp(options[i].name, "backing_fmt")) { |
| + backing_format = options[i].value.s; |
| + } else if (!strcmp(options[i].name, "encryption")) { |
| + if ((options[i].value.n != !!s->crypt_method)) { |
| + fprintf(stderr, "Changing the encryption flag is not " |
| + "supported.\n"); |
| + return -ENOTSUP; |
| + } |
| + } else if (!strcmp(options[i].name, "cluster_size")) { |
| + if (options[i].value.n != s->cluster_size) { |
| + fprintf(stderr, "Changing the cluster size is not " |
| + "supported.\n"); |
| + return -ENOTSUP; |
| + } |
| + } else if (!strcmp(options[i].name, "lazy_refcounts")) { |
| + lazy_refcounts = options[i].value.n; |
| + } else { |
| + /* if this assertion fails, this probably means a new option was |
| + * added without having it covered here */ |
| + assert(false); |
| + } |
| + } |
| + |
| + if (new_version != old_version) { |
| + if (new_version > old_version) { |
| + /* Upgrade */ |
| + s->qcow_version = new_version; |
| + ret = qcow2_update_header(bs); |
| + if (ret < 0) { |
| + s->qcow_version = old_version; |
| + return ret; |
| + } |
| + } else { |
| + ret = qcow2_downgrade(bs, new_version); |
| + if (ret < 0) { |
| + return ret; |
| + } |
| + } |
| + } |
| + |
| + if (backing_file || backing_format) { |
| + ret = qcow2_change_backing_file(bs, backing_file ?: bs->backing_file, |
| + backing_format ?: bs->backing_format); |
| + if (ret < 0) { |
| + return ret; |
| + } |
| + } |
| + |
| + if (s->use_lazy_refcounts != lazy_refcounts) { |
| + if (lazy_refcounts) { |
| + if (s->qcow_version < 3) { |
| + fprintf(stderr, "Lazy refcounts only supported with compatibility " |
| + "level 1.1 and above (use compat=1.1 or greater)\n"); |
| + return -EINVAL; |
| + } |
| + s->compatible_features |= QCOW2_COMPAT_LAZY_REFCOUNTS; |
| + ret = qcow2_update_header(bs); |
| + if (ret < 0) { |
| + s->compatible_features &= ~QCOW2_COMPAT_LAZY_REFCOUNTS; |
| + return ret; |
| + } |
| + s->use_lazy_refcounts = true; |
| + } else { |
| + /* make image clean first */ |
| + ret = qcow2_mark_clean(bs); |
| + if (ret < 0) { |
| + return ret; |
| + } |
| + /* now disallow lazy refcounts */ |
| + s->compatible_features &= ~QCOW2_COMPAT_LAZY_REFCOUNTS; |
| + ret = qcow2_update_header(bs); |
| + if (ret < 0) { |
| + s->compatible_features |= QCOW2_COMPAT_LAZY_REFCOUNTS; |
| + return ret; |
| + } |
| + s->use_lazy_refcounts = false; |
| + } |
| + } |
| + |
| + if (new_size) { |
| + ret = bdrv_truncate(bs, new_size); |
| + if (ret < 0) { |
| + return ret; |
| + } |
| + } |
| + |
| + return 0; |
| +} |
| + |
| static QEMUOptionParameter qcow2_create_options[] = { |
| { |
| .name = BLOCK_OPT_SIZE, |
| @@ -2073,6 +2266,7 @@ static BlockDriver bdrv_qcow2 = { |
| |
| .create_options = qcow2_create_options, |
| .bdrv_check = qcow2_check, |
| + .bdrv_amend_options = qcow2_amend_options, |
| }; |
| |
| static void bdrv_qcow2_init(void) |
| -- |
| 1.7.1 |
| |