#include "qemu/error-report.h"
#include "qapi/qmp/qerror.h"
#include "qapi/qmp/qbool.h"
-#include "qapi/util.h"
#include "qapi/qmp/types.h"
#include "qapi-event.h"
#include "trace.h"
}
if (!(s->autoclear_features & QCOW2_AUTOCLEAR_BITMAPS)) {
- error_report("WARNING: a program lacking bitmap support "
- "modified this file, so all bitmaps are now "
- "considered inconsistent. Some clusters may be "
- "leaked, run 'qemu-img check -r' on the image "
+ warn_report("a program lacking bitmap support "
+ "modified this file, so all bitmaps are now "
+ "considered inconsistent");
+ error_printf("Some clusters may be leaked, "
+ "run 'qemu-img check -r' on the image "
"file to fix.");
if (need_update_header != NULL) {
/* Updating is needed to drop invalid bitmap extension. */
goto fail;
}
- s->cluster_cache = g_malloc(s->cluster_size);
- /* one more sector for decompressed data alignment */
- s->cluster_data = qemu_try_blockalign(bs->file->bs, QCOW_MAX_CRYPT_CLUSTERS
- * s->cluster_size + 512);
- if (s->cluster_data == NULL) {
- error_setg(errp, "Could not allocate temporary cluster buffer");
- ret = -ENOMEM;
- goto fail;
- }
-
s->cluster_cache_offset = -1;
s->flags = flags;
if (s->refcount_block_cache) {
qcow2_cache_destroy(bs, s->refcount_block_cache);
}
- g_free(s->cluster_cache);
- qemu_vfree(s->cluster_data);
qcrypto_block_free(s->crypto);
qapi_free_QCryptoBlockOpenOptions(s->crypto_opts);
return ret;
assert(s->crypto);
assert((offset & (BDRV_SECTOR_SIZE - 1)) == 0);
assert((cur_bytes & (BDRV_SECTOR_SIZE - 1)) == 0);
- Error *err = NULL;
if (qcrypto_block_decrypt(s->crypto,
(s->crypt_physical_offset ?
cluster_offset + offset_in_cluster :
- offset) >> BDRV_SECTOR_BITS,
+ offset),
cluster_data,
cur_bytes,
- &err) < 0) {
- error_free(err);
+ NULL) < 0) {
ret = -EIO;
goto fail;
}
qemu_iovec_concat(&hd_qiov, qiov, bytes_done, cur_bytes);
if (bs->encrypted) {
- Error *err = NULL;
assert(s->crypto);
if (!cluster_data) {
cluster_data = qemu_try_blockalign(bs->file->bs,
if (qcrypto_block_encrypt(s->crypto,
(s->crypt_physical_offset ?
cluster_offset + offset_in_cluster :
- offset) >> BDRV_SECTOR_BITS,
+ offset),
cluster_data,
- cur_bytes, &err) < 0) {
- error_free(err);
+ cur_bytes, NULL) < 0) {
ret = -EIO;
goto fail;
}
int ret, result = 0;
Error *local_err = NULL;
+ qcow2_store_persistent_dirty_bitmaps(bs, &local_err);
+ if (local_err != NULL) {
+ result = -EINVAL;
+ error_report_err(local_err);
+ error_report("Persistent bitmaps are lost for node '%s'",
+ bdrv_get_device_or_node_name(bs));
+ }
+
ret = qcow2_cache_flush(bs, s->l2_table_cache);
if (ret) {
result = ret;
strerror(-ret));
}
- qcow2_store_persistent_dirty_bitmaps(bs, &local_err);
- if (local_err != NULL) {
- result = -EINVAL;
- error_report_err(local_err);
- error_report("Persistent bitmaps are lost for node '%s'",
- bdrv_get_device_or_node_name(bs));
- }
-
if (result == 0) {
qcow2_mark_clean(bs);
}
int64_t prealloc_size =
qcow2_calc_prealloc_size(total_size, cluster_size, refcount_order);
qemu_opt_set_number(opts, BLOCK_OPT_SIZE, prealloc_size, &error_abort);
- qemu_opt_set(opts, BLOCK_OPT_PREALLOC, PreallocMode_lookup[prealloc],
+ qemu_opt_set(opts, BLOCK_OPT_PREALLOC, PreallocMode_str(prealloc),
&error_abort);
}
goto finish;
}
buf = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC);
- prealloc = qapi_enum_parse(PreallocMode_lookup, buf,
- PREALLOC_MODE__MAX, PREALLOC_MODE_OFF,
- &local_err);
+ prealloc = qapi_enum_parse(&PreallocMode_lookup, buf,
+ PREALLOC_MODE_OFF, &local_err);
if (local_err) {
error_propagate(errp, local_err);
ret = -EINVAL;
}
-static bool is_zero_sectors(BlockDriverState *bs, int64_t start,
- uint32_t count)
+static bool is_zero(BlockDriverState *bs, int64_t offset, int64_t bytes)
{
- int nr;
- BlockDriverState *file;
- int64_t res;
+ int64_t nr;
+ int res;
+ int64_t start;
- if (start + count > bs->total_sectors) {
- count = bs->total_sectors - start;
+ /* TODO: Widening to sector boundaries should only be needed as
+ * long as we can't query finer granularity. */
+ start = QEMU_ALIGN_DOWN(offset, BDRV_SECTOR_SIZE);
+ bytes = QEMU_ALIGN_UP(offset + bytes, BDRV_SECTOR_SIZE) - start;
+
+ /* Clamp to image length, before checking status of underlying sectors */
+ if (start + bytes > bs->total_sectors * BDRV_SECTOR_SIZE) {
+ bytes = bs->total_sectors * BDRV_SECTOR_SIZE - start;
}
- if (!count) {
+ if (!bytes) {
return true;
}
- res = bdrv_get_block_status_above(bs, NULL, start, count,
- &nr, &file);
- return res >= 0 && (res & BDRV_BLOCK_ZERO) && nr == count;
+ res = bdrv_block_status_above(bs, NULL, start, bytes, &nr, NULL, NULL);
+ return res >= 0 && (res & BDRV_BLOCK_ZERO) && nr == bytes;
}
static coroutine_fn int qcow2_co_pwrite_zeroes(BlockDriverState *bs,
}
if (head || tail) {
- int64_t cl_start = (offset - head) >> BDRV_SECTOR_BITS;
uint64_t off;
unsigned int nr;
assert(head + bytes <= s->cluster_size);
/* check whether remainder of cluster already reads as zero */
- if (!(is_zero_sectors(bs, cl_start,
- DIV_ROUND_UP(head, BDRV_SECTOR_SIZE)) &&
- is_zero_sectors(bs, (offset + bytes) >> BDRV_SECTOR_BITS,
- DIV_ROUND_UP(-tail & (s->cluster_size - 1),
- BDRV_SECTOR_SIZE)))) {
+ if (!(is_zero(bs, offset - head, head) &&
+ is_zero(bs, offset + bytes,
+ tail ? s->cluster_size - tail : 0))) {
return -ENOTSUP;
}
qemu_co_mutex_lock(&s->lock);
/* We can have new write after previous check */
- offset = cl_start << BDRV_SECTOR_BITS;
+ offset = QEMU_ALIGN_DOWN(offset, s->cluster_size);
bytes = s->cluster_size;
nr = s->cluster_size;
ret = qcow2_get_cluster_offset(bs, offset, &nr, &off);
prealloc != PREALLOC_MODE_FALLOC && prealloc != PREALLOC_MODE_FULL)
{
error_setg(errp, "Unsupported preallocation mode '%s'",
- PreallocMode_lookup[prealloc]);
+ PreallocMode_str(prealloc));
return -ENOTSUP;
}
}
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;
- }
+ int64_t last_cluster, old_file_size;
+ 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;
+ }
+
+ old_file_size = bdrv_getlength(bs->file->bs);
+ if (old_file_size < 0) {
+ error_setg_errno(errp, -old_file_size,
+ "Failed to inquire current file length");
+ return old_file_size;
+ }
+ last_cluster = qcow2_get_last_cluster(bs, old_file_size);
+ if (last_cluster < 0) {
+ error_setg_errno(errp, -last_cluster,
+ "Failed to find the last cluster");
+ return last_cluster;
+ }
+ if ((last_cluster + 1) * s->cluster_size < old_file_size) {
+ ret = bdrv_truncate(bs->file, (last_cluster + 1) * s->cluster_size,
+ PREALLOC_MODE_OFF, NULL);
+ if (ret < 0) {
+ warn_report("Failed to truncate the tail of the image: %s",
+ strerror(-ret));
+ ret = 0;
+ }
+ }
+ } 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) {
if (old_file_size < 0) {
error_setg_errno(errp, -old_file_size,
"Failed to inquire current file length");
- return ret;
+ return old_file_size;
}
nb_new_data_clusters = DIV_ROUND_UP(offset - old_length,
if (allocation_start < 0) {
error_setg_errno(errp, -allocation_start,
"Failed to resize refcount structures");
- return -allocation_start;
+ return allocation_start;
}
clusters_allocated = qcow2_alloc_clusters_at(bs, allocation_start,
}
optstr = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC);
- prealloc = qapi_enum_parse(PreallocMode_lookup, optstr,
- PREALLOC_MODE__MAX, PREALLOC_MODE_OFF,
- &local_err);
+ prealloc = qapi_enum_parse(&PreallocMode_lookup, optstr,
+ PREALLOC_MODE_OFF, &local_err);
g_free(optstr);
if (local_err) {
goto err;
*/
required = virtual_size;
} else {
- int cluster_sectors = cluster_size / BDRV_SECTOR_SIZE;
- int64_t sector_num;
- int pnum = 0;
-
- for (sector_num = 0;
- sector_num < ssize / BDRV_SECTOR_SIZE;
- sector_num += pnum) {
- int nb_sectors = MIN(ssize / BDRV_SECTOR_SIZE - sector_num,
- BDRV_REQUEST_MAX_SECTORS);
- BlockDriverState *file;
- int64_t ret;
-
- ret = bdrv_get_block_status_above(in_bs, NULL,
- sector_num, nb_sectors,
- &pnum, &file);
+ int64_t offset;
+ int64_t pnum = 0;
+
+ for (offset = 0; offset < ssize; offset += pnum) {
+ int ret;
+
+ ret = bdrv_block_status_above(in_bs, NULL, offset,
+ ssize - offset, &pnum, NULL,
+ NULL);
if (ret < 0) {
error_setg_errno(&local_err, -ret,
"Unable to get block status");
} else if ((ret & (BDRV_BLOCK_DATA | BDRV_BLOCK_ALLOCATED)) ==
(BDRV_BLOCK_DATA | BDRV_BLOCK_ALLOCATED)) {
/* Extend pnum to end of cluster for next iteration */
- pnum = ROUND_UP(sector_num + pnum, cluster_sectors) -
- sector_num;
+ pnum = ROUND_UP(offset + pnum, cluster_size) - offset;
/* Count clusters we've seen */
- required += (sector_num % cluster_sectors + pnum) *
- BDRV_SECTOR_SIZE;
+ required += offset % cluster_size + pnum;
}
}
}