aboutsummaryrefslogtreecommitdiff
path: root/block/qcow2.c
diff options
context:
space:
mode:
authorPavel Butsykin <pbutsykin@virtuozzo.com>2017-09-18 15:42:29 +0300
committerMax Reitz <mreitz@redhat.com>2017-09-26 15:00:32 +0200
commit46b732cdf3737ea8f9654f6ccd67ff52ddeddc20 (patch)
tree2bacf7bfdcf21d73c098822a236960fb7dd03825 /block/qcow2.c
parentf71c08ea8e60f035485a512fd2af8908567592f0 (diff)
qcow2: add shrink image support
This patch add shrinking of the image file for qcow2. As a result, this allows us to reduce the virtual image size and free up space on the disk without copying the image. Image can be fragmented and shrink is done by punching holes in the image file. Signed-off-by: Pavel Butsykin <pbutsykin@virtuozzo.com> Reviewed-by: Max Reitz <mreitz@redhat.com> Reviewed-by: John Snow <jsnow@redhat.com> Message-id: 20170918124230.8152-4-pbutsykin@virtuozzo.com Signed-off-by: Max Reitz <mreitz@redhat.com>
Diffstat (limited to 'block/qcow2.c')
-rw-r--r--block/qcow2.c43
1 files changed, 34 insertions, 9 deletions
diff --git a/block/qcow2.c b/block/qcow2.c
index d33fb3ecdd..970006fc1d 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -3104,18 +3104,43 @@ static int qcow2_truncate(BlockDriverState *bs, int64_t offset,
}
old_length = bs->total_sectors * 512;
+ new_l1_size = size_to_l1(s, offset);
- /* shrinking is currently not supported */
if (offset < old_length) {
- error_setg(errp, "qcow2 doesn't support shrinking images yet");
- return -ENOTSUP;
- }
+ if (prealloc != PREALLOC_MODE_OFF) {
+ error_setg(errp,
+ "Preallocation can't be used for shrinking an image");
+ return -EINVAL;
+ }
- new_l1_size = size_to_l1(s, offset);
- ret = qcow2_grow_l1_table(bs, new_l1_size, true);
- if (ret < 0) {
- error_setg_errno(errp, -ret, "Failed to grow the L1 table");
- return ret;
+ ret = qcow2_cluster_discard(bs, ROUND_UP(offset, s->cluster_size),
+ old_length - ROUND_UP(offset,
+ s->cluster_size),
+ QCOW2_DISCARD_ALWAYS, true);
+ if (ret < 0) {
+ error_setg_errno(errp, -ret, "Failed to discard cropped clusters");
+ return ret;
+ }
+
+ ret = qcow2_shrink_l1_table(bs, new_l1_size);
+ if (ret < 0) {
+ error_setg_errno(errp, -ret,
+ "Failed to reduce the number of L2 tables");
+ return ret;
+ }
+
+ ret = qcow2_shrink_reftable(bs);
+ if (ret < 0) {
+ error_setg_errno(errp, -ret,
+ "Failed to discard unused refblocks");
+ return ret;
+ }
+ } else {
+ ret = qcow2_grow_l1_table(bs, new_l1_size, true);
+ if (ret < 0) {
+ error_setg_errno(errp, -ret, "Failed to grow the L1 table");
+ return ret;
+ }
}
switch (prealloc) {