qcow2_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset, uint64_t bytes, QEMUIOVector *qiov) { BDRVQcow2State *s = bs->opaque; QEMUIOVector hd_qiov; struct iovec iov; z_stream strm; int ret, out_len; uint8_t *buf, *out_buf; uint64_t cluster_offset; if (bytes == 0) { /* align end of file to a sector boundary to ease reading with sector based I/Os */ cluster_offset = bdrv_getlength(bs->file->bs); return bdrv_truncate(bs->file, cluster_offset, PREALLOC_MODE_OFF, NULL); } buf = qemu_blockalign(bs, s->cluster_size); if (bytes != s->cluster_size) { if (bytes > s->cluster_size || offset + bytes != bs->total_sectors << BDRV_SECTOR_BITS) { qemu_vfree(buf); return -EINVAL; } /* Zero-pad last write if image size is not cluster aligned */ memset(buf + bytes, 0, s->cluster_size - bytes); } qemu_iovec_to_buf(qiov, 0, buf, bytes); 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) { 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) { /* could not compress: write normal cluster */ ret = qcow2_co_pwritev(bs, offset, bytes, qiov, 0); if (ret < 0) { goto fail; } goto success; } qemu_co_mutex_lock(&s->lock); cluster_offset = qcow2_alloc_compressed_cluster_offset(bs, offset, out_len); if (!cluster_offset) { qemu_co_mutex_unlock(&s->lock); ret = -EIO; goto fail; } cluster_offset &= s->cluster_offset_mask; ret = qcow2_pre_write_overlap_check(bs, 0, cluster_offset, out_len); qemu_co_mutex_unlock(&s->lock); if (ret < 0) { goto fail; } iov = (struct iovec) { .iov_base = out_buf, .iov_len = out_len, }; qemu_iovec_init_external(&hd_qiov, &iov, 1); BLKDBG_EVENT(bs->file, BLKDBG_WRITE_COMPRESSED); ret = bdrv_co_pwritev(bs->file, cluster_offset, out_len, &hd_qiov, 0); if (ret < 0) { goto fail; } success: ret = 0; fail: qemu_vfree(buf); g_free(out_buf); return ret; }