#include "qemu/osdep.h"
#include <zlib.h>
-#include "qapi/error.h"
#include "qemu-common.h"
#include "block/block_int.h"
#include "block/qcow2.h"
#include "qemu/bswap.h"
#include "trace.h"
+int qcow2_shrink_l1_table(BlockDriverState *bs, uint64_t exact_size)
+{
+ BDRVQcow2State *s = bs->opaque;
+ int new_l1_size, i, ret;
+
+ if (exact_size >= s->l1_size) {
+ return 0;
+ }
+
+ new_l1_size = exact_size;
+
+#ifdef DEBUG_ALLOC2
+ fprintf(stderr, "shrink l1_table from %d to %d\n", s->l1_size, new_l1_size);
+#endif
+
+ BLKDBG_EVENT(bs->file, BLKDBG_L1_SHRINK_WRITE_TABLE);
+ ret = bdrv_pwrite_zeroes(bs->file, s->l1_table_offset +
+ new_l1_size * sizeof(uint64_t),
+ (s->l1_size - new_l1_size) * sizeof(uint64_t), 0);
+ if (ret < 0) {
+ goto fail;
+ }
+
+ ret = bdrv_flush(bs->file->bs);
+ if (ret < 0) {
+ goto fail;
+ }
+
+ BLKDBG_EVENT(bs->file, BLKDBG_L1_SHRINK_FREE_L2_CLUSTERS);
+ for (i = s->l1_size - 1; i > new_l1_size - 1; i--) {
+ if ((s->l1_table[i] & L1E_OFFSET_MASK) == 0) {
+ continue;
+ }
+ qcow2_free_clusters(bs, s->l1_table[i] & L1E_OFFSET_MASK,
+ s->cluster_size, QCOW2_DISCARD_ALWAYS);
+ s->l1_table[i] = 0;
+ }
+ return 0;
+
+fail:
+ /*
+ * If the write in the l1_table failed the image may contain a partially
+ * overwritten l1_table. In this case it would be better to clear the
+ * l1_table in memory to avoid possible image corruption.
+ */
+ memset(s->l1_table + new_l1_size, 0,
+ (s->l1_size - new_l1_size) * sizeof(uint64_t));
+ return ret;
+}
+
int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size,
bool exact_size)
{
new_l1_size = 1;
}
while (min_size > new_l1_size) {
- new_l1_size = (new_l1_size * 3 + 1) / 2;
+ new_l1_size = DIV_ROUND_UP(new_l1_size * 3, 2);
}
}
goto fail;
}
+ /* If we're allocating the table at offset 0 then something is wrong */
+ if (l2_offset == 0) {
+ qcow2_signal_corruption(bs, true, -1, -1, "Preventing invalid "
+ "allocation of L2 table at offset 0");
+ ret = -EIO;
+ goto fail;
+ }
+
ret = qcow2_cache_flush(bs, s->refcount_block_cache);
if (ret < 0) {
goto fail;
BLKDBG_EVENT(bs->file, BLKDBG_L2_ALLOC_WRITE);
trace_qcow2_l2_allocate_write_l2(bs, l1_index);
- qcow2_cache_entry_mark_dirty(bs, s->l2_table_cache, l2_table);
+ qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table);
ret = qcow2_cache_flush(bs, s->l2_table_cache);
if (ret < 0) {
goto fail;
{
if (bytes && bs->encrypted) {
BDRVQcow2State *s = bs->opaque;
- int64_t sector = (s->crypt_physical_offset ?
+ int64_t offset = (s->crypt_physical_offset ?
(cluster_offset + offset_in_cluster) :
- (src_cluster_offset + offset_in_cluster))
- >> BDRV_SECTOR_BITS;
+ (src_cluster_offset + offset_in_cluster));
assert((offset_in_cluster & ~BDRV_SECTOR_MASK) == 0);
assert((bytes & ~BDRV_SECTOR_MASK) == 0);
assert(s->crypto);
- if (qcrypto_block_encrypt(s->crypto, sector, buffer,
- bytes, NULL) < 0) {
+ if (qcrypto_block_encrypt(s->crypto, offset, buffer, bytes, NULL) < 0) {
return false;
}
}
* for a given disk offset, load (and allocate if needed)
* the l2 table.
*
- * the l2 table offset in the qcow2 file and the cluster index
- * in the l2 table are given to the caller.
+ * the cluster index in the l2 table is given to the caller.
*
* Returns 0 on success, -errno in failure case
*/
/* compressed clusters never have the copied flag */
BLKDBG_EVENT(bs->file, BLKDBG_L2_UPDATE_COMPRESSED);
- qcow2_cache_entry_mark_dirty(bs, s->l2_table_cache, l2_table);
+ qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table);
l2_table[l2_index] = cpu_to_be64(cluster_offset);
qcow2_cache_put(bs, s->l2_table_cache, (void **) &l2_table);
if (ret < 0) {
goto err;
}
- qcow2_cache_entry_mark_dirty(bs, s->l2_table_cache, l2_table);
+ qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table);
assert(l2_index + m->nb_clusters <= s->l2_size);
for (i = 0; i < m->nb_clusters; i++) {
(!*host_offset ||
start_of_cluster(s, *host_offset) == (entry & L2E_OFFSET_MASK)))
{
+ int preallocated_nb_clusters;
+
+ if (offset_into_cluster(s, entry & L2E_OFFSET_MASK)) {
+ qcow2_signal_corruption(bs, true, -1, -1, "Preallocated zero "
+ "cluster offset %#llx unaligned (guest "
+ "offset: %#" PRIx64 ")",
+ entry & L2E_OFFSET_MASK, guest_offset);
+ ret = -EIO;
+ goto fail;
+ }
+
/* Try to reuse preallocated zero clusters; contiguous normal clusters
* would be fine, too, but count_cow_clusters() above has limited
* nb_clusters already to a range of COW clusters */
- int preallocated_nb_clusters =
+ preallocated_nb_clusters =
count_contiguous_clusters(nb_clusters, s->cluster_size,
&l2_table[l2_index], QCOW_OFLAG_COPIED);
assert(preallocated_nb_clusters > 0);
* cluster is already marked as zero, or if it's unallocated and we
* don't have a backing file.
*
- * TODO We might want to use bdrv_get_block_status(bs) here, but we're
+ * TODO We might want to use bdrv_block_status(bs) here, but we're
* holding s->lock, so that doesn't work today.
*
* If full_discard is true, the sector should not read back as zeroes,
}
/* First remove L2 entries */
- qcow2_cache_entry_mark_dirty(bs, s->l2_table_cache, l2_table);
+ qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table);
if (!full_discard && s->qcow_version >= 3) {
l2_table[l2_index + i] = cpu_to_be64(QCOW_OFLAG_ZERO);
} else {
continue;
}
- qcow2_cache_entry_mark_dirty(bs, s->l2_table_cache, l2_table);
+ qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table);
if (cluster_type == QCOW2_CLUSTER_COMPRESSED || unmap) {
l2_table[l2_index + i] = cpu_to_be64(QCOW_OFLAG_ZERO);
qcow2_free_any_clusters(bs, old_offset, 1, QCOW2_DISCARD_REQUEST);
if (is_active_l1) {
if (l2_dirty) {
- qcow2_cache_entry_mark_dirty(bs, s->l2_table_cache, l2_table);
+ qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table);
qcow2_cache_depends_on_flush(s->l2_table_cache);
}
qcow2_cache_put(bs, s->l2_table_cache, (void **) &l2_table);
int l1_sectors = DIV_ROUND_UP(s->snapshots[i].l1_size *
sizeof(uint64_t), BDRV_SECTOR_SIZE);
- l1_table = g_realloc(l1_table, l1_sectors * BDRV_SECTOR_SIZE);
+ uint64_t *new_l1_table =
+ g_try_realloc(l1_table, l1_sectors * BDRV_SECTOR_SIZE);
+
+ if (!new_l1_table) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ l1_table = new_l1_table;
ret = bdrv_read(bs->file,
s->snapshots[i].l1_table_offset / BDRV_SECTOR_SIZE,