aboutsummaryrefslogtreecommitdiff
path: root/block/qcow2.c
diff options
context:
space:
mode:
Diffstat (limited to 'block/qcow2.c')
-rw-r--r--block/qcow2.c78
1 files changed, 51 insertions, 27 deletions
diff --git a/block/qcow2.c b/block/qcow2.c
index 2f9e58e..9cee653 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -23,11 +23,14 @@
*/
#include "qemu/osdep.h"
+
+#define ZLIB_CONST
+#include <zlib.h>
+
#include "block/block_int.h"
#include "block/qdict.h"
#include "sysemu/block-backend.h"
#include "qemu/module.h"
-#include <zlib.h>
#include "qcow2.h"
#include "qemu/error-report.h"
#include "qapi/error.h"
@@ -3650,6 +3653,48 @@ fail:
return ret;
}
+/*
+ * qcow2_compress()
+ *
+ * @dest - destination buffer, at least of @size-1 bytes
+ * @src - source buffer, @size bytes
+ *
+ * Returns: compressed size on success
+ * -1 if compression is inefficient
+ * -2 on any other error
+ */
+static ssize_t qcow2_compress(void *dest, const void *src, size_t size)
+{
+ ssize_t ret;
+ z_stream strm;
+
+ /* best compression, small window, no zlib header */
+ memset(&strm, 0, sizeof(strm));
+ ret = deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED,
+ -12, 9, Z_DEFAULT_STRATEGY);
+ if (ret != 0) {
+ return -2;
+ }
+
+ /* strm.next_in is not const in old zlib versions, such as those used on
+ * OpenBSD/NetBSD, so cast the const away */
+ strm.avail_in = size;
+ strm.next_in = (void *) src;
+ strm.avail_out = size - 1;
+ strm.next_out = dest;
+
+ ret = deflate(&strm, Z_FINISH);
+ if (ret == Z_STREAM_END) {
+ ret = size - 1 - strm.avail_out;
+ } else {
+ ret = (ret == Z_OK ? -1 : -2);
+ }
+
+ deflateEnd(&strm);
+
+ return ret;
+}
+
/* XXX: put compressed sectors first, then all the cluster aligned
tables to avoid losing bytes in alignment */
static coroutine_fn int
@@ -3659,8 +3704,8 @@ qcow2_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset,
BDRVQcow2State *s = bs->opaque;
QEMUIOVector hd_qiov;
struct iovec iov;
- z_stream strm;
- int ret, out_len;
+ int ret;
+ size_t out_len;
uint8_t *buf, *out_buf;
int64_t cluster_offset;
@@ -3694,32 +3739,11 @@ qcow2_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset,
out_buf = g_malloc(s->cluster_size);
- /* best compression, small window, no zlib header */
- memset(&strm, 0, sizeof(strm));
- ret = deflateInit2(&strm, Z_DEFAULT_COMPRESSION,
- Z_DEFLATED, -12,
- 9, Z_DEFAULT_STRATEGY);
- if (ret != 0) {
+ out_len = qcow2_compress(out_buf, buf, s->cluster_size);
+ if (out_len == -2) {
ret = -EINVAL;
goto fail;
- }
-
- strm.avail_in = s->cluster_size;
- strm.next_in = (uint8_t *)buf;
- strm.avail_out = s->cluster_size;
- strm.next_out = out_buf;
-
- ret = deflate(&strm, Z_FINISH);
- if (ret != Z_STREAM_END && ret != Z_OK) {
- deflateEnd(&strm);
- ret = -EINVAL;
- goto fail;
- }
- out_len = strm.next_out - out_buf;
-
- deflateEnd(&strm);
-
- if (ret != Z_STREAM_END || out_len >= s->cluster_size) {
+ } else if (out_len == -1) {
/* could not compress: write normal cluster */
ret = qcow2_co_pwritev(bs, offset, bytes, qiov, 0);
if (ret < 0) {