mirror of https://github.com/xqemu/xqemu.git
Merge remote-tracking branch 'kwolf/for-anthony' into staging
# By Max Reitz (11) and others # Via Kevin Wolf * kwolf/for-anthony: (26 commits) qemu-iotests: Overlapping cluster allocations qcow2_check: Mark image consistent qcow2-refcount: Repair shared refcount blocks qcow2-refcount: Repair OFLAG_COPIED errors qcow2-refcount: Move OFLAG_COPIED checks qcow2: Employ metadata overlap checks qcow2: Metadata overlap checks qcow2: Add corrupt bit qemu-iotests: Snapshotting zero clusters qcow2-refcount: Snapshot update for zero clusters option: Add assigned flag to QEMUOptionParameter gluster: Abort on AIO completion failure block: Remove old raw driver switch raw block driver from "raw.o" to "raw_bsd.o" raw_bsd: register bdrv_raw raw_bsd: add raw_create_options raw_bsd: introduce "special members" raw_bsd: add raw_create() raw_bsd: emit debug events in bdrv_co_readv() and bdrv_co_writev() add skeleton for BSD licensed "raw" BlockDriver ... Message-id: 1378111792-20436-1-git-send-email-kwolf@redhat.com Signed-off-by: Anthony Liguori <anthony@codemonkey.ws>
This commit is contained in:
commit
bb7d4d82b6
1
block.c
1
block.c
|
@ -743,7 +743,6 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file,
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto free_and_fail;
|
goto free_and_fail;
|
||||||
}
|
}
|
||||||
assert(file != NULL);
|
|
||||||
bs->file = file;
|
bs->file = file;
|
||||||
ret = drv->bdrv_open(bs, options, open_flags);
|
ret = drv->bdrv_open(bs, options, open_flags);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
block-obj-y += raw.o cow.o qcow.o vdi.o vmdk.o cloop.o dmg.o bochs.o vpc.o vvfat.o
|
block-obj-y += raw_bsd.o cow.o qcow.o vdi.o vmdk.o cloop.o dmg.o bochs.o vpc.o vvfat.o
|
||||||
block-obj-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o qcow2-cache.o
|
block-obj-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o qcow2-cache.o
|
||||||
block-obj-y += qed.o qed-gencb.o qed-l2-cache.o qed-table.o qed-cluster.o
|
block-obj-y += qed.o qed-gencb.o qed-l2-cache.o qed-table.o qed-cluster.o
|
||||||
block-obj-y += qed-check.o
|
block-obj-y += qed-check.o
|
||||||
|
|
|
@ -168,6 +168,7 @@ static const char *event_names[BLKDBG_EVENT_MAX] = {
|
||||||
|
|
||||||
[BLKDBG_REFTABLE_LOAD] = "reftable_load",
|
[BLKDBG_REFTABLE_LOAD] = "reftable_load",
|
||||||
[BLKDBG_REFTABLE_GROW] = "reftable_grow",
|
[BLKDBG_REFTABLE_GROW] = "reftable_grow",
|
||||||
|
[BLKDBG_REFTABLE_UPDATE] = "reftable_update",
|
||||||
|
|
||||||
[BLKDBG_REFBLOCK_LOAD] = "refblock_load",
|
[BLKDBG_REFBLOCK_LOAD] = "refblock_load",
|
||||||
[BLKDBG_REFBLOCK_UPDATE] = "refblock_update",
|
[BLKDBG_REFBLOCK_UPDATE] = "refblock_update",
|
||||||
|
|
|
@ -427,20 +427,9 @@ static void gluster_finish_aiocb(struct glfs_fd *fd, ssize_t ret, void *arg)
|
||||||
/*
|
/*
|
||||||
* Gluster AIO callback thread failed to notify the waiting
|
* Gluster AIO callback thread failed to notify the waiting
|
||||||
* QEMU thread about IO completion.
|
* QEMU thread about IO completion.
|
||||||
*
|
|
||||||
* Complete this IO request and make the disk inaccessible for
|
|
||||||
* subsequent reads and writes.
|
|
||||||
*/
|
*/
|
||||||
error_report("Gluster failed to notify QEMU about IO completion");
|
error_report("Gluster AIO completion failed: %s", strerror(errno));
|
||||||
|
abort();
|
||||||
qemu_mutex_lock_iothread(); /* We are in gluster thread context */
|
|
||||||
acb->common.cb(acb->common.opaque, -EIO);
|
|
||||||
qemu_aio_release(acb);
|
|
||||||
close(s->fds[GLUSTER_FD_READ]);
|
|
||||||
close(s->fds[GLUSTER_FD_WRITE]);
|
|
||||||
qemu_aio_set_fd_handler(s->fds[GLUSTER_FD_READ], NULL, NULL, NULL);
|
|
||||||
bs->drv = NULL; /* Make the disk inaccessible */
|
|
||||||
qemu_mutex_unlock_iothread();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -114,6 +114,23 @@ static int qcow2_cache_entry_flush(BlockDriverState *bs, Qcow2Cache *c, int i)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (c == s->refcount_block_cache) {
|
||||||
|
ret = qcow2_pre_write_overlap_check(bs,
|
||||||
|
QCOW2_OL_DEFAULT & ~QCOW2_OL_REFCOUNT_BLOCK,
|
||||||
|
c->entries[i].offset, s->cluster_size);
|
||||||
|
} else if (c == s->l2_table_cache) {
|
||||||
|
ret = qcow2_pre_write_overlap_check(bs,
|
||||||
|
QCOW2_OL_DEFAULT & ~QCOW2_OL_ACTIVE_L2,
|
||||||
|
c->entries[i].offset, s->cluster_size);
|
||||||
|
} else {
|
||||||
|
ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_DEFAULT,
|
||||||
|
c->entries[i].offset, s->cluster_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
if (c == s->refcount_block_cache) {
|
if (c == s->refcount_block_cache) {
|
||||||
BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_UPDATE_PART);
|
BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_UPDATE_PART);
|
||||||
} else if (c == s->l2_table_cache) {
|
} else if (c == s->l2_table_cache) {
|
||||||
|
|
|
@ -80,6 +80,14 @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size,
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* the L1 position has not yet been updated, so these clusters must
|
||||||
|
* indeed be completely free */
|
||||||
|
ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_DEFAULT,
|
||||||
|
new_l1_table_offset, new_l1_size2);
|
||||||
|
if (ret < 0) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
BLKDBG_EVENT(bs->file, BLKDBG_L1_GROW_WRITE_TABLE);
|
BLKDBG_EVENT(bs->file, BLKDBG_L1_GROW_WRITE_TABLE);
|
||||||
for(i = 0; i < s->l1_size; i++)
|
for(i = 0; i < s->l1_size; i++)
|
||||||
new_l1_table[i] = cpu_to_be64(new_l1_table[i]);
|
new_l1_table[i] = cpu_to_be64(new_l1_table[i]);
|
||||||
|
@ -137,7 +145,7 @@ static int l2_load(BlockDriverState *bs, uint64_t l2_offset,
|
||||||
* and we really don't want bdrv_pread to perform a read-modify-write)
|
* and we really don't want bdrv_pread to perform a read-modify-write)
|
||||||
*/
|
*/
|
||||||
#define L1_ENTRIES_PER_SECTOR (512 / 8)
|
#define L1_ENTRIES_PER_SECTOR (512 / 8)
|
||||||
static int write_l1_entry(BlockDriverState *bs, int l1_index)
|
int qcow2_write_l1_entry(BlockDriverState *bs, int l1_index)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcowState *s = bs->opaque;
|
||||||
uint64_t buf[L1_ENTRIES_PER_SECTOR];
|
uint64_t buf[L1_ENTRIES_PER_SECTOR];
|
||||||
|
@ -149,6 +157,13 @@ static int write_l1_entry(BlockDriverState *bs, int l1_index)
|
||||||
buf[i] = cpu_to_be64(s->l1_table[l1_start_index + i]);
|
buf[i] = cpu_to_be64(s->l1_table[l1_start_index + i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret = qcow2_pre_write_overlap_check(bs,
|
||||||
|
QCOW2_OL_DEFAULT & ~QCOW2_OL_ACTIVE_L1,
|
||||||
|
s->l1_table_offset + 8 * l1_start_index, sizeof(buf));
|
||||||
|
if (ret < 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
BLKDBG_EVENT(bs->file, BLKDBG_L1_UPDATE);
|
BLKDBG_EVENT(bs->file, BLKDBG_L1_UPDATE);
|
||||||
ret = bdrv_pwrite_sync(bs->file, s->l1_table_offset + 8 * l1_start_index,
|
ret = bdrv_pwrite_sync(bs->file, s->l1_table_offset + 8 * l1_start_index,
|
||||||
buf, sizeof(buf));
|
buf, sizeof(buf));
|
||||||
|
@ -239,7 +254,7 @@ static int l2_allocate(BlockDriverState *bs, int l1_index, uint64_t **table)
|
||||||
/* update the L1 entry */
|
/* update the L1 entry */
|
||||||
trace_qcow2_l2_allocate_write_l1(bs, l1_index);
|
trace_qcow2_l2_allocate_write_l1(bs, l1_index);
|
||||||
s->l1_table[l1_index] = l2_offset | QCOW_OFLAG_COPIED;
|
s->l1_table[l1_index] = l2_offset | QCOW_OFLAG_COPIED;
|
||||||
ret = write_l1_entry(bs, l1_index);
|
ret = qcow2_write_l1_entry(bs, l1_index);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
@ -368,6 +383,12 @@ static int coroutine_fn copy_sectors(BlockDriverState *bs,
|
||||||
&s->aes_encrypt_key);
|
&s->aes_encrypt_key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_DEFAULT,
|
||||||
|
cluster_offset + n_start * BDRV_SECTOR_SIZE, n * BDRV_SECTOR_SIZE);
|
||||||
|
if (ret < 0) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
BLKDBG_EVENT(bs->file, BLKDBG_COW_WRITE);
|
BLKDBG_EVENT(bs->file, BLKDBG_COW_WRITE);
|
||||||
ret = bdrv_co_writev(bs->file, (cluster_offset >> 9) + n_start, n, &qiov);
|
ret = bdrv_co_writev(bs->file, (cluster_offset >> 9) + n_start, n, &qiov);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
|
|
@ -25,6 +25,8 @@
|
||||||
#include "qemu-common.h"
|
#include "qemu-common.h"
|
||||||
#include "block/block_int.h"
|
#include "block/block_int.h"
|
||||||
#include "block/qcow2.h"
|
#include "block/qcow2.h"
|
||||||
|
#include "qemu/range.h"
|
||||||
|
#include "qapi/qmp/types.h"
|
||||||
|
|
||||||
static int64_t alloc_clusters_noref(BlockDriverState *bs, int64_t size);
|
static int64_t alloc_clusters_noref(BlockDriverState *bs, int64_t size);
|
||||||
static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs,
|
static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs,
|
||||||
|
@ -861,11 +863,14 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
|
||||||
}
|
}
|
||||||
|
|
||||||
for(j = 0; j < s->l2_size; j++) {
|
for(j = 0; j < s->l2_size; j++) {
|
||||||
|
uint64_t cluster_index;
|
||||||
|
|
||||||
offset = be64_to_cpu(l2_table[j]);
|
offset = be64_to_cpu(l2_table[j]);
|
||||||
if (offset != 0) {
|
old_offset = offset;
|
||||||
old_offset = offset;
|
offset &= ~QCOW_OFLAG_COPIED;
|
||||||
offset &= ~QCOW_OFLAG_COPIED;
|
|
||||||
if (offset & QCOW_OFLAG_COMPRESSED) {
|
switch (qcow2_get_cluster_type(offset)) {
|
||||||
|
case QCOW2_CLUSTER_COMPRESSED:
|
||||||
nb_csectors = ((offset >> s->csize_shift) &
|
nb_csectors = ((offset >> s->csize_shift) &
|
||||||
s->csize_mask) + 1;
|
s->csize_mask) + 1;
|
||||||
if (addend != 0) {
|
if (addend != 0) {
|
||||||
|
@ -880,8 +885,16 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
|
||||||
}
|
}
|
||||||
/* compressed clusters are never modified */
|
/* compressed clusters are never modified */
|
||||||
refcount = 2;
|
refcount = 2;
|
||||||
} else {
|
break;
|
||||||
uint64_t cluster_index = (offset & L2E_OFFSET_MASK) >> s->cluster_bits;
|
|
||||||
|
case QCOW2_CLUSTER_NORMAL:
|
||||||
|
case QCOW2_CLUSTER_ZERO:
|
||||||
|
cluster_index = (offset & L2E_OFFSET_MASK) >> s->cluster_bits;
|
||||||
|
if (!cluster_index) {
|
||||||
|
/* unallocated */
|
||||||
|
refcount = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
if (addend != 0) {
|
if (addend != 0) {
|
||||||
refcount = update_cluster_refcount(bs, cluster_index, addend,
|
refcount = update_cluster_refcount(bs, cluster_index, addend,
|
||||||
QCOW2_DISCARD_SNAPSHOT);
|
QCOW2_DISCARD_SNAPSHOT);
|
||||||
|
@ -893,19 +906,26 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
|
||||||
ret = refcount;
|
ret = refcount;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
}
|
break;
|
||||||
|
|
||||||
if (refcount == 1) {
|
case QCOW2_CLUSTER_UNALLOCATED:
|
||||||
offset |= QCOW_OFLAG_COPIED;
|
refcount = 0;
|
||||||
}
|
break;
|
||||||
if (offset != old_offset) {
|
|
||||||
if (addend > 0) {
|
default:
|
||||||
qcow2_cache_set_dependency(bs, s->l2_table_cache,
|
abort();
|
||||||
s->refcount_block_cache);
|
}
|
||||||
}
|
|
||||||
l2_table[j] = cpu_to_be64(offset);
|
if (refcount == 1) {
|
||||||
qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table);
|
offset |= QCOW_OFLAG_COPIED;
|
||||||
|
}
|
||||||
|
if (offset != old_offset) {
|
||||||
|
if (addend > 0) {
|
||||||
|
qcow2_cache_set_dependency(bs, s->l2_table_cache,
|
||||||
|
s->refcount_block_cache);
|
||||||
}
|
}
|
||||||
|
l2_table[j] = cpu_to_be64(offset);
|
||||||
|
qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1033,7 +1053,7 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res,
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcowState *s = bs->opaque;
|
||||||
uint64_t *l2_table, l2_entry;
|
uint64_t *l2_table, l2_entry;
|
||||||
uint64_t next_contiguous_offset = 0;
|
uint64_t next_contiguous_offset = 0;
|
||||||
int i, l2_size, nb_csectors, refcount;
|
int i, l2_size, nb_csectors;
|
||||||
|
|
||||||
/* Read L2 table from disk */
|
/* Read L2 table from disk */
|
||||||
l2_size = s->l2_size * sizeof(uint64_t);
|
l2_size = s->l2_size * sizeof(uint64_t);
|
||||||
|
@ -1085,23 +1105,8 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res,
|
||||||
|
|
||||||
case QCOW2_CLUSTER_NORMAL:
|
case QCOW2_CLUSTER_NORMAL:
|
||||||
{
|
{
|
||||||
/* QCOW_OFLAG_COPIED must be set iff refcount == 1 */
|
|
||||||
uint64_t offset = l2_entry & L2E_OFFSET_MASK;
|
uint64_t offset = l2_entry & L2E_OFFSET_MASK;
|
||||||
|
|
||||||
if (flags & CHECK_OFLAG_COPIED) {
|
|
||||||
refcount = get_refcount(bs, offset >> s->cluster_bits);
|
|
||||||
if (refcount < 0) {
|
|
||||||
fprintf(stderr, "Can't get refcount for offset %"
|
|
||||||
PRIx64 ": %s\n", l2_entry, strerror(-refcount));
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
if ((refcount == 1) != ((l2_entry & QCOW_OFLAG_COPIED) != 0)) {
|
|
||||||
fprintf(stderr, "ERROR OFLAG_COPIED: offset=%"
|
|
||||||
PRIx64 " refcount=%d\n", l2_entry, refcount);
|
|
||||||
res->corruptions++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (flags & CHECK_FRAG_INFO) {
|
if (flags & CHECK_FRAG_INFO) {
|
||||||
res->bfi.allocated_clusters++;
|
res->bfi.allocated_clusters++;
|
||||||
if (next_contiguous_offset &&
|
if (next_contiguous_offset &&
|
||||||
|
@ -1158,7 +1163,7 @@ static int check_refcounts_l1(BlockDriverState *bs,
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcowState *s = bs->opaque;
|
||||||
uint64_t *l1_table, l2_offset, l1_size2;
|
uint64_t *l1_table, l2_offset, l1_size2;
|
||||||
int i, refcount, ret;
|
int i, ret;
|
||||||
|
|
||||||
l1_size2 = l1_size * sizeof(uint64_t);
|
l1_size2 = l1_size * sizeof(uint64_t);
|
||||||
|
|
||||||
|
@ -1182,22 +1187,6 @@ static int check_refcounts_l1(BlockDriverState *bs,
|
||||||
for(i = 0; i < l1_size; i++) {
|
for(i = 0; i < l1_size; i++) {
|
||||||
l2_offset = l1_table[i];
|
l2_offset = l1_table[i];
|
||||||
if (l2_offset) {
|
if (l2_offset) {
|
||||||
/* QCOW_OFLAG_COPIED must be set iff refcount == 1 */
|
|
||||||
if (flags & CHECK_OFLAG_COPIED) {
|
|
||||||
refcount = get_refcount(bs, (l2_offset & ~QCOW_OFLAG_COPIED)
|
|
||||||
>> s->cluster_bits);
|
|
||||||
if (refcount < 0) {
|
|
||||||
fprintf(stderr, "Can't get refcount for l2_offset %"
|
|
||||||
PRIx64 ": %s\n", l2_offset, strerror(-refcount));
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
if ((refcount == 1) != ((l2_offset & QCOW_OFLAG_COPIED) != 0)) {
|
|
||||||
fprintf(stderr, "ERROR OFLAG_COPIED: l2_offset=%" PRIx64
|
|
||||||
" refcount=%d\n", l2_offset, refcount);
|
|
||||||
res->corruptions++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Mark L2 table as used */
|
/* Mark L2 table as used */
|
||||||
l2_offset &= L1E_OFFSET_MASK;
|
l2_offset &= L1E_OFFSET_MASK;
|
||||||
inc_refcounts(bs, res, refcount_table, refcount_table_size,
|
inc_refcounts(bs, res, refcount_table, refcount_table_size,
|
||||||
|
@ -1228,6 +1217,241 @@ fail:
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Checks the OFLAG_COPIED flag for all L1 and L2 entries.
|
||||||
|
*
|
||||||
|
* This function does not print an error message nor does it increment
|
||||||
|
* check_errors if get_refcount fails (this is because such an error will have
|
||||||
|
* been already detected and sufficiently signaled by the calling function
|
||||||
|
* (qcow2_check_refcounts) by the time this function is called).
|
||||||
|
*/
|
||||||
|
static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res,
|
||||||
|
BdrvCheckMode fix)
|
||||||
|
{
|
||||||
|
BDRVQcowState *s = bs->opaque;
|
||||||
|
uint64_t *l2_table = qemu_blockalign(bs, s->cluster_size);
|
||||||
|
int ret;
|
||||||
|
int refcount;
|
||||||
|
int i, j;
|
||||||
|
|
||||||
|
for (i = 0; i < s->l1_size; i++) {
|
||||||
|
uint64_t l1_entry = s->l1_table[i];
|
||||||
|
uint64_t l2_offset = l1_entry & L1E_OFFSET_MASK;
|
||||||
|
bool l2_dirty = false;
|
||||||
|
|
||||||
|
if (!l2_offset) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
refcount = get_refcount(bs, l2_offset >> s->cluster_bits);
|
||||||
|
if (refcount < 0) {
|
||||||
|
/* don't print message nor increment check_errors */
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ((refcount == 1) != ((l1_entry & QCOW_OFLAG_COPIED) != 0)) {
|
||||||
|
fprintf(stderr, "%s OFLAG_COPIED L2 cluster: l1_index=%d "
|
||||||
|
"l1_entry=%" PRIx64 " refcount=%d\n",
|
||||||
|
fix & BDRV_FIX_ERRORS ? "Repairing" :
|
||||||
|
"ERROR",
|
||||||
|
i, l1_entry, refcount);
|
||||||
|
if (fix & BDRV_FIX_ERRORS) {
|
||||||
|
s->l1_table[i] = refcount == 1
|
||||||
|
? l1_entry | QCOW_OFLAG_COPIED
|
||||||
|
: l1_entry & ~QCOW_OFLAG_COPIED;
|
||||||
|
ret = qcow2_write_l1_entry(bs, i);
|
||||||
|
if (ret < 0) {
|
||||||
|
res->check_errors++;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
res->corruptions_fixed++;
|
||||||
|
} else {
|
||||||
|
res->corruptions++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = bdrv_pread(bs->file, l2_offset, l2_table,
|
||||||
|
s->l2_size * sizeof(uint64_t));
|
||||||
|
if (ret < 0) {
|
||||||
|
fprintf(stderr, "ERROR: Could not read L2 table: %s\n",
|
||||||
|
strerror(-ret));
|
||||||
|
res->check_errors++;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (j = 0; j < s->l2_size; j++) {
|
||||||
|
uint64_t l2_entry = be64_to_cpu(l2_table[j]);
|
||||||
|
uint64_t data_offset = l2_entry & L2E_OFFSET_MASK;
|
||||||
|
int cluster_type = qcow2_get_cluster_type(l2_entry);
|
||||||
|
|
||||||
|
if ((cluster_type == QCOW2_CLUSTER_NORMAL) ||
|
||||||
|
((cluster_type == QCOW2_CLUSTER_ZERO) && (data_offset != 0))) {
|
||||||
|
refcount = get_refcount(bs, data_offset >> s->cluster_bits);
|
||||||
|
if (refcount < 0) {
|
||||||
|
/* don't print message nor increment check_errors */
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ((refcount == 1) != ((l2_entry & QCOW_OFLAG_COPIED) != 0)) {
|
||||||
|
fprintf(stderr, "%s OFLAG_COPIED data cluster: "
|
||||||
|
"l2_entry=%" PRIx64 " refcount=%d\n",
|
||||||
|
fix & BDRV_FIX_ERRORS ? "Repairing" :
|
||||||
|
"ERROR",
|
||||||
|
l2_entry, refcount);
|
||||||
|
if (fix & BDRV_FIX_ERRORS) {
|
||||||
|
l2_table[j] = cpu_to_be64(refcount == 1
|
||||||
|
? l2_entry | QCOW_OFLAG_COPIED
|
||||||
|
: l2_entry & ~QCOW_OFLAG_COPIED);
|
||||||
|
l2_dirty = true;
|
||||||
|
res->corruptions_fixed++;
|
||||||
|
} else {
|
||||||
|
res->corruptions++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (l2_dirty) {
|
||||||
|
ret = qcow2_pre_write_overlap_check(bs,
|
||||||
|
QCOW2_OL_DEFAULT & ~QCOW2_OL_ACTIVE_L2, l2_offset,
|
||||||
|
s->cluster_size);
|
||||||
|
if (ret < 0) {
|
||||||
|
fprintf(stderr, "ERROR: Could not write L2 table; metadata "
|
||||||
|
"overlap check failed: %s\n", strerror(-ret));
|
||||||
|
res->check_errors++;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = bdrv_pwrite(bs->file, l2_offset, l2_table, s->cluster_size);
|
||||||
|
if (ret < 0) {
|
||||||
|
fprintf(stderr, "ERROR: Could not write L2 table: %s\n",
|
||||||
|
strerror(-ret));
|
||||||
|
res->check_errors++;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
qemu_vfree(l2_table);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Writes one sector of the refcount table to the disk
|
||||||
|
*/
|
||||||
|
#define RT_ENTRIES_PER_SECTOR (512 / sizeof(uint64_t))
|
||||||
|
static int write_reftable_entry(BlockDriverState *bs, int rt_index)
|
||||||
|
{
|
||||||
|
BDRVQcowState *s = bs->opaque;
|
||||||
|
uint64_t buf[RT_ENTRIES_PER_SECTOR];
|
||||||
|
int rt_start_index;
|
||||||
|
int i, ret;
|
||||||
|
|
||||||
|
rt_start_index = rt_index & ~(RT_ENTRIES_PER_SECTOR - 1);
|
||||||
|
for (i = 0; i < RT_ENTRIES_PER_SECTOR; i++) {
|
||||||
|
buf[i] = cpu_to_be64(s->refcount_table[rt_start_index + i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = qcow2_pre_write_overlap_check(bs,
|
||||||
|
QCOW2_OL_DEFAULT & ~QCOW2_OL_REFCOUNT_TABLE,
|
||||||
|
s->refcount_table_offset + rt_start_index * sizeof(uint64_t),
|
||||||
|
sizeof(buf));
|
||||||
|
if (ret < 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
BLKDBG_EVENT(bs->file, BLKDBG_REFTABLE_UPDATE);
|
||||||
|
ret = bdrv_pwrite_sync(bs->file, s->refcount_table_offset +
|
||||||
|
rt_start_index * sizeof(uint64_t), buf, sizeof(buf));
|
||||||
|
if (ret < 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Allocates a new cluster for the given refcount block (represented by its
|
||||||
|
* offset in the image file) and copies the current content there. This function
|
||||||
|
* does _not_ decrement the reference count for the currently occupied cluster.
|
||||||
|
*
|
||||||
|
* This function prints an informative message to stderr on error (and returns
|
||||||
|
* -errno); on success, 0 is returned.
|
||||||
|
*/
|
||||||
|
static int64_t realloc_refcount_block(BlockDriverState *bs, int reftable_index,
|
||||||
|
uint64_t offset)
|
||||||
|
{
|
||||||
|
BDRVQcowState *s = bs->opaque;
|
||||||
|
int64_t new_offset = 0;
|
||||||
|
void *refcount_block = NULL;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* allocate new refcount block */
|
||||||
|
new_offset = qcow2_alloc_clusters(bs, s->cluster_size);
|
||||||
|
if (new_offset < 0) {
|
||||||
|
fprintf(stderr, "Could not allocate new cluster: %s\n",
|
||||||
|
strerror(-new_offset));
|
||||||
|
ret = new_offset;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* fetch current refcount block content */
|
||||||
|
ret = qcow2_cache_get(bs, s->refcount_block_cache, offset, &refcount_block);
|
||||||
|
if (ret < 0) {
|
||||||
|
fprintf(stderr, "Could not fetch refcount block: %s\n", strerror(-ret));
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* new block has not yet been entered into refcount table, therefore it is
|
||||||
|
* no refcount block yet (regarding this check) */
|
||||||
|
ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_DEFAULT, new_offset,
|
||||||
|
s->cluster_size);
|
||||||
|
if (ret < 0) {
|
||||||
|
fprintf(stderr, "Could not write refcount block; metadata overlap "
|
||||||
|
"check failed: %s\n", strerror(-ret));
|
||||||
|
/* the image will be marked corrupt, so don't even attempt on freeing
|
||||||
|
* the cluster */
|
||||||
|
new_offset = 0;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* write to new block */
|
||||||
|
ret = bdrv_write(bs->file, new_offset / BDRV_SECTOR_SIZE, refcount_block,
|
||||||
|
s->cluster_sectors);
|
||||||
|
if (ret < 0) {
|
||||||
|
fprintf(stderr, "Could not write refcount block: %s\n", strerror(-ret));
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* update refcount table */
|
||||||
|
assert(!(new_offset & (s->cluster_size - 1)));
|
||||||
|
s->refcount_table[reftable_index] = new_offset;
|
||||||
|
ret = write_reftable_entry(bs, reftable_index);
|
||||||
|
if (ret < 0) {
|
||||||
|
fprintf(stderr, "Could not update refcount table: %s\n",
|
||||||
|
strerror(-ret));
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
fail:
|
||||||
|
if (new_offset && (ret < 0)) {
|
||||||
|
qcow2_free_clusters(bs, new_offset, s->cluster_size,
|
||||||
|
QCOW2_DISCARD_ALWAYS);
|
||||||
|
}
|
||||||
|
if (refcount_block) {
|
||||||
|
if (ret < 0) {
|
||||||
|
qcow2_cache_put(bs, s->refcount_block_cache, &refcount_block);
|
||||||
|
} else {
|
||||||
|
ret = qcow2_cache_put(bs, s->refcount_block_cache, &refcount_block);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ret < 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
return new_offset;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Checks an image for refcount consistency.
|
* Checks an image for refcount consistency.
|
||||||
*
|
*
|
||||||
|
@ -1304,10 +1528,39 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
|
||||||
inc_refcounts(bs, res, refcount_table, nb_clusters,
|
inc_refcounts(bs, res, refcount_table, nb_clusters,
|
||||||
offset, s->cluster_size);
|
offset, s->cluster_size);
|
||||||
if (refcount_table[cluster] != 1) {
|
if (refcount_table[cluster] != 1) {
|
||||||
fprintf(stderr, "ERROR refcount block %" PRId64
|
fprintf(stderr, "%s refcount block %" PRId64
|
||||||
" refcount=%d\n",
|
" refcount=%d\n",
|
||||||
|
fix & BDRV_FIX_ERRORS ? "Repairing" :
|
||||||
|
"ERROR",
|
||||||
i, refcount_table[cluster]);
|
i, refcount_table[cluster]);
|
||||||
res->corruptions++;
|
|
||||||
|
if (fix & BDRV_FIX_ERRORS) {
|
||||||
|
int64_t new_offset;
|
||||||
|
|
||||||
|
new_offset = realloc_refcount_block(bs, i, offset);
|
||||||
|
if (new_offset < 0) {
|
||||||
|
res->corruptions++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* update refcounts */
|
||||||
|
if ((new_offset >> s->cluster_bits) >= nb_clusters) {
|
||||||
|
/* increase refcount_table size if necessary */
|
||||||
|
int old_nb_clusters = nb_clusters;
|
||||||
|
nb_clusters = (new_offset >> s->cluster_bits) + 1;
|
||||||
|
refcount_table = g_realloc(refcount_table,
|
||||||
|
nb_clusters * sizeof(uint16_t));
|
||||||
|
memset(&refcount_table[old_nb_clusters], 0, (nb_clusters
|
||||||
|
- old_nb_clusters) * sizeof(uint16_t));
|
||||||
|
}
|
||||||
|
refcount_table[cluster]--;
|
||||||
|
inc_refcounts(bs, res, refcount_table, nb_clusters,
|
||||||
|
new_offset, s->cluster_size);
|
||||||
|
|
||||||
|
res->corruptions_fixed++;
|
||||||
|
} else {
|
||||||
|
res->corruptions++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1363,6 +1616,12 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* check OFLAG_COPIED */
|
||||||
|
ret = check_oflag_copied(bs, res, fix);
|
||||||
|
if (ret < 0) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
res->image_end_offset = (highest_cluster + 1) * s->cluster_size;
|
res->image_end_offset = (highest_cluster + 1) * s->cluster_size;
|
||||||
ret = 0;
|
ret = 0;
|
||||||
|
|
||||||
|
@ -1372,3 +1631,173 @@ fail:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define overlaps_with(ofs, sz) \
|
||||||
|
ranges_overlap(offset, size, ofs, sz)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Checks if the given offset into the image file is actually free to use by
|
||||||
|
* looking for overlaps with important metadata sections (L1/L2 tables etc.),
|
||||||
|
* i.e. a sanity check without relying on the refcount tables.
|
||||||
|
*
|
||||||
|
* The chk parameter specifies exactly what checks to perform (being a bitmask
|
||||||
|
* of QCow2MetadataOverlap values).
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* - 0 if writing to this offset will not affect the mentioned metadata
|
||||||
|
* - a positive QCow2MetadataOverlap value indicating one overlapping section
|
||||||
|
* - a negative value (-errno) indicating an error while performing a check,
|
||||||
|
* e.g. when bdrv_read failed on QCOW2_OL_INACTIVE_L2
|
||||||
|
*/
|
||||||
|
int qcow2_check_metadata_overlap(BlockDriverState *bs, int chk, int64_t offset,
|
||||||
|
int64_t size)
|
||||||
|
{
|
||||||
|
BDRVQcowState *s = bs->opaque;
|
||||||
|
int i, j;
|
||||||
|
|
||||||
|
if (!size) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chk & QCOW2_OL_MAIN_HEADER) {
|
||||||
|
if (offset < s->cluster_size) {
|
||||||
|
return QCOW2_OL_MAIN_HEADER;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* align range to test to cluster boundaries */
|
||||||
|
size = align_offset(offset_into_cluster(s, offset) + size, s->cluster_size);
|
||||||
|
offset = start_of_cluster(s, offset);
|
||||||
|
|
||||||
|
if ((chk & QCOW2_OL_ACTIVE_L1) && s->l1_size) {
|
||||||
|
if (overlaps_with(s->l1_table_offset, s->l1_size * sizeof(uint64_t))) {
|
||||||
|
return QCOW2_OL_ACTIVE_L1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((chk & QCOW2_OL_REFCOUNT_TABLE) && s->refcount_table_size) {
|
||||||
|
if (overlaps_with(s->refcount_table_offset,
|
||||||
|
s->refcount_table_size * sizeof(uint64_t))) {
|
||||||
|
return QCOW2_OL_REFCOUNT_TABLE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((chk & QCOW2_OL_SNAPSHOT_TABLE) && s->snapshots_size) {
|
||||||
|
if (overlaps_with(s->snapshots_offset, s->snapshots_size)) {
|
||||||
|
return QCOW2_OL_SNAPSHOT_TABLE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((chk & QCOW2_OL_INACTIVE_L1) && s->snapshots) {
|
||||||
|
for (i = 0; i < s->nb_snapshots; i++) {
|
||||||
|
if (s->snapshots[i].l1_size &&
|
||||||
|
overlaps_with(s->snapshots[i].l1_table_offset,
|
||||||
|
s->snapshots[i].l1_size * sizeof(uint64_t))) {
|
||||||
|
return QCOW2_OL_INACTIVE_L1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((chk & QCOW2_OL_ACTIVE_L2) && s->l1_table) {
|
||||||
|
for (i = 0; i < s->l1_size; i++) {
|
||||||
|
if ((s->l1_table[i] & L1E_OFFSET_MASK) &&
|
||||||
|
overlaps_with(s->l1_table[i] & L1E_OFFSET_MASK,
|
||||||
|
s->cluster_size)) {
|
||||||
|
return QCOW2_OL_ACTIVE_L2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((chk & QCOW2_OL_REFCOUNT_BLOCK) && s->refcount_table) {
|
||||||
|
for (i = 0; i < s->refcount_table_size; i++) {
|
||||||
|
if ((s->refcount_table[i] & REFT_OFFSET_MASK) &&
|
||||||
|
overlaps_with(s->refcount_table[i] & REFT_OFFSET_MASK,
|
||||||
|
s->cluster_size)) {
|
||||||
|
return QCOW2_OL_REFCOUNT_BLOCK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((chk & QCOW2_OL_INACTIVE_L2) && s->snapshots) {
|
||||||
|
for (i = 0; i < s->nb_snapshots; i++) {
|
||||||
|
uint64_t l1_ofs = s->snapshots[i].l1_table_offset;
|
||||||
|
uint32_t l1_sz = s->snapshots[i].l1_size;
|
||||||
|
uint64_t *l1 = g_malloc(l1_sz * sizeof(uint64_t));
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = bdrv_read(bs->file, l1_ofs / BDRV_SECTOR_SIZE, (uint8_t *)l1,
|
||||||
|
l1_sz * sizeof(uint64_t) / BDRV_SECTOR_SIZE);
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
g_free(l1);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (j = 0; j < l1_sz; j++) {
|
||||||
|
if ((l1[j] & L1E_OFFSET_MASK) &&
|
||||||
|
overlaps_with(l1[j] & L1E_OFFSET_MASK, s->cluster_size)) {
|
||||||
|
g_free(l1);
|
||||||
|
return QCOW2_OL_INACTIVE_L2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
g_free(l1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *metadata_ol_names[] = {
|
||||||
|
[QCOW2_OL_MAIN_HEADER_BITNR] = "qcow2_header",
|
||||||
|
[QCOW2_OL_ACTIVE_L1_BITNR] = "active L1 table",
|
||||||
|
[QCOW2_OL_ACTIVE_L2_BITNR] = "active L2 table",
|
||||||
|
[QCOW2_OL_REFCOUNT_TABLE_BITNR] = "refcount table",
|
||||||
|
[QCOW2_OL_REFCOUNT_BLOCK_BITNR] = "refcount block",
|
||||||
|
[QCOW2_OL_SNAPSHOT_TABLE_BITNR] = "snapshot table",
|
||||||
|
[QCOW2_OL_INACTIVE_L1_BITNR] = "inactive L1 table",
|
||||||
|
[QCOW2_OL_INACTIVE_L2_BITNR] = "inactive L2 table",
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* First performs a check for metadata overlaps (through
|
||||||
|
* qcow2_check_metadata_overlap); if that fails with a negative value (error
|
||||||
|
* while performing a check), that value is returned. If an impending overlap
|
||||||
|
* is detected, the BDS will be made unusable, the qcow2 file marked corrupt
|
||||||
|
* and -EIO returned.
|
||||||
|
*
|
||||||
|
* Returns 0 if there were neither overlaps nor errors while checking for
|
||||||
|
* overlaps; or a negative value (-errno) on error.
|
||||||
|
*/
|
||||||
|
int qcow2_pre_write_overlap_check(BlockDriverState *bs, int chk, int64_t offset,
|
||||||
|
int64_t size)
|
||||||
|
{
|
||||||
|
int ret = qcow2_check_metadata_overlap(bs, chk, offset, size);
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
return ret;
|
||||||
|
} else if (ret > 0) {
|
||||||
|
int metadata_ol_bitnr = ffs(ret) - 1;
|
||||||
|
char *message;
|
||||||
|
QObject *data;
|
||||||
|
|
||||||
|
assert(metadata_ol_bitnr < QCOW2_OL_MAX_BITNR);
|
||||||
|
|
||||||
|
fprintf(stderr, "qcow2: Preventing invalid write on metadata (overlaps "
|
||||||
|
"with %s); image marked as corrupt.\n",
|
||||||
|
metadata_ol_names[metadata_ol_bitnr]);
|
||||||
|
message = g_strdup_printf("Prevented %s overwrite",
|
||||||
|
metadata_ol_names[metadata_ol_bitnr]);
|
||||||
|
data = qobject_from_jsonf("{ 'device': %s, 'msg': %s, 'offset': %"
|
||||||
|
PRId64 ", 'size': %" PRId64 " }", bs->device_name, message,
|
||||||
|
offset, size);
|
||||||
|
monitor_protocol_event(QEVENT_BLOCK_IMAGE_CORRUPTED, data);
|
||||||
|
g_free(message);
|
||||||
|
qobject_decref(data);
|
||||||
|
|
||||||
|
qcow2_mark_corrupt(bs);
|
||||||
|
bs->drv = NULL; /* make BDS unusable */
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
|
@ -189,6 +189,15 @@ static int qcow2_write_snapshots(BlockDriverState *bs)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* The snapshot list position has not yet been updated, so these clusters
|
||||||
|
* must indeed be completely free */
|
||||||
|
ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_DEFAULT, offset,
|
||||||
|
s->snapshots_size);
|
||||||
|
if (ret < 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Write all snapshots to the new list */
|
/* Write all snapshots to the new list */
|
||||||
for(i = 0; i < s->nb_snapshots; i++) {
|
for(i = 0; i < s->nb_snapshots; i++) {
|
||||||
sn = s->snapshots + i;
|
sn = s->snapshots + i;
|
||||||
|
@ -363,6 +372,12 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
|
||||||
l1_table[i] = cpu_to_be64(s->l1_table[i]);
|
l1_table[i] = cpu_to_be64(s->l1_table[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_DEFAULT,
|
||||||
|
sn->l1_table_offset, s->l1_size * sizeof(uint64_t));
|
||||||
|
if (ret < 0) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
ret = bdrv_pwrite(bs->file, sn->l1_table_offset, l1_table,
|
ret = bdrv_pwrite(bs->file, sn->l1_table_offset, l1_table,
|
||||||
s->l1_size * sizeof(uint64_t));
|
s->l1_size * sizeof(uint64_t));
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
@ -475,6 +490,13 @@ int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id)
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret = qcow2_pre_write_overlap_check(bs,
|
||||||
|
QCOW2_OL_DEFAULT & ~QCOW2_OL_ACTIVE_L1,
|
||||||
|
s->l1_table_offset, cur_l1_bytes);
|
||||||
|
if (ret < 0) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
ret = bdrv_pwrite_sync(bs->file, s->l1_table_offset, sn_l1_table,
|
ret = bdrv_pwrite_sync(bs->file, s->l1_table_offset, sn_l1_table,
|
||||||
cur_l1_bytes);
|
cur_l1_bytes);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
|
|
@ -272,6 +272,37 @@ static int qcow2_mark_clean(BlockDriverState *bs)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Marks the image as corrupt.
|
||||||
|
*/
|
||||||
|
int qcow2_mark_corrupt(BlockDriverState *bs)
|
||||||
|
{
|
||||||
|
BDRVQcowState *s = bs->opaque;
|
||||||
|
|
||||||
|
s->incompatible_features |= QCOW2_INCOMPAT_CORRUPT;
|
||||||
|
return qcow2_update_header(bs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Marks the image as consistent, i.e., unsets the corrupt bit, and flushes
|
||||||
|
* before if necessary.
|
||||||
|
*/
|
||||||
|
int qcow2_mark_consistent(BlockDriverState *bs)
|
||||||
|
{
|
||||||
|
BDRVQcowState *s = bs->opaque;
|
||||||
|
|
||||||
|
if (s->incompatible_features & QCOW2_INCOMPAT_CORRUPT) {
|
||||||
|
int ret = bdrv_flush(bs);
|
||||||
|
if (ret < 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
s->incompatible_features &= ~QCOW2_INCOMPAT_CORRUPT;
|
||||||
|
return qcow2_update_header(bs);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int qcow2_check(BlockDriverState *bs, BdrvCheckResult *result,
|
static int qcow2_check(BlockDriverState *bs, BdrvCheckResult *result,
|
||||||
BdrvCheckMode fix)
|
BdrvCheckMode fix)
|
||||||
{
|
{
|
||||||
|
@ -281,7 +312,11 @@ static int qcow2_check(BlockDriverState *bs, BdrvCheckResult *result,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fix && result->check_errors == 0 && result->corruptions == 0) {
|
if (fix && result->check_errors == 0 && result->corruptions == 0) {
|
||||||
return qcow2_mark_clean(bs);
|
ret = qcow2_mark_clean(bs);
|
||||||
|
if (ret < 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
return qcow2_mark_consistent(bs);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -402,6 +437,17 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (s->incompatible_features & QCOW2_INCOMPAT_CORRUPT) {
|
||||||
|
/* Corrupt images may not be written to unless they are being repaired
|
||||||
|
*/
|
||||||
|
if ((flags & BDRV_O_RDWR) && !(flags & BDRV_O_CHECK)) {
|
||||||
|
error_report("qcow2: Image is corrupt; cannot be opened "
|
||||||
|
"read/write.");
|
||||||
|
ret = -EACCES;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Check support for various header values */
|
/* Check support for various header values */
|
||||||
if (header.refcount_order != 4) {
|
if (header.refcount_order != 4) {
|
||||||
report_unsupported(bs, "%d bit reference counts",
|
report_unsupported(bs, "%d bit reference counts",
|
||||||
|
@ -582,6 +628,8 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
|
||||||
qcow2_free_snapshots(bs);
|
qcow2_free_snapshots(bs);
|
||||||
qcow2_refcount_close(bs);
|
qcow2_refcount_close(bs);
|
||||||
g_free(s->l1_table);
|
g_free(s->l1_table);
|
||||||
|
/* else pre-write overlap checks in cache_destroy may crash */
|
||||||
|
s->l1_table = NULL;
|
||||||
if (s->l2_table_cache) {
|
if (s->l2_table_cache) {
|
||||||
qcow2_cache_destroy(bs, s->l2_table_cache);
|
qcow2_cache_destroy(bs, s->l2_table_cache);
|
||||||
}
|
}
|
||||||
|
@ -881,6 +929,13 @@ static coroutine_fn int qcow2_co_writev(BlockDriverState *bs,
|
||||||
cur_nr_sectors * 512);
|
cur_nr_sectors * 512);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_DEFAULT,
|
||||||
|
cluster_offset + index_in_cluster * BDRV_SECTOR_SIZE,
|
||||||
|
cur_nr_sectors * BDRV_SECTOR_SIZE);
|
||||||
|
if (ret < 0) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
qemu_co_mutex_unlock(&s->lock);
|
qemu_co_mutex_unlock(&s->lock);
|
||||||
BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO);
|
BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO);
|
||||||
trace_qcow2_writev_data(qemu_coroutine_self(),
|
trace_qcow2_writev_data(qemu_coroutine_self(),
|
||||||
|
@ -947,6 +1002,8 @@ static void qcow2_close(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcowState *s = bs->opaque;
|
||||||
g_free(s->l1_table);
|
g_free(s->l1_table);
|
||||||
|
/* else pre-write overlap checks in cache_destroy may crash */
|
||||||
|
s->l1_table = NULL;
|
||||||
|
|
||||||
qcow2_cache_flush(bs, s->l2_table_cache);
|
qcow2_cache_flush(bs, s->l2_table_cache);
|
||||||
qcow2_cache_flush(bs, s->refcount_block_cache);
|
qcow2_cache_flush(bs, s->refcount_block_cache);
|
||||||
|
@ -1129,6 +1186,11 @@ int qcow2_update_header(BlockDriverState *bs)
|
||||||
.bit = QCOW2_INCOMPAT_DIRTY_BITNR,
|
.bit = QCOW2_INCOMPAT_DIRTY_BITNR,
|
||||||
.name = "dirty bit",
|
.name = "dirty bit",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.type = QCOW2_FEAT_TYPE_INCOMPATIBLE,
|
||||||
|
.bit = QCOW2_INCOMPAT_CORRUPT_BITNR,
|
||||||
|
.name = "corrupt bit",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
.type = QCOW2_FEAT_TYPE_COMPATIBLE,
|
.type = QCOW2_FEAT_TYPE_COMPATIBLE,
|
||||||
.bit = QCOW2_COMPAT_LAZY_REFCOUNTS_BITNR,
|
.bit = QCOW2_COMPAT_LAZY_REFCOUNTS_BITNR,
|
||||||
|
@ -1429,7 +1491,9 @@ static int qcow2_create(const char *filename, QEMUOptionParameter *options)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
} else if (!strcmp(options->name, BLOCK_OPT_COMPAT_LEVEL)) {
|
} else if (!strcmp(options->name, BLOCK_OPT_COMPAT_LEVEL)) {
|
||||||
if (!options->value.s || !strcmp(options->value.s, "0.10")) {
|
if (!options->value.s) {
|
||||||
|
/* keep the default */
|
||||||
|
} else if (!strcmp(options->value.s, "0.10")) {
|
||||||
version = 2;
|
version = 2;
|
||||||
} else if (!strcmp(options->value.s, "1.1")) {
|
} else if (!strcmp(options->value.s, "1.1")) {
|
||||||
version = 3;
|
version = 3;
|
||||||
|
@ -1619,6 +1683,14 @@ static int qcow2_write_compressed(BlockDriverState *bs, int64_t sector_num,
|
||||||
|
|
||||||
if (ret != Z_STREAM_END || out_len >= s->cluster_size) {
|
if (ret != Z_STREAM_END || out_len >= s->cluster_size) {
|
||||||
/* could not compress: write normal cluster */
|
/* could not compress: write normal cluster */
|
||||||
|
|
||||||
|
ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_DEFAULT,
|
||||||
|
sector_num * BDRV_SECTOR_SIZE,
|
||||||
|
s->cluster_sectors * BDRV_SECTOR_SIZE);
|
||||||
|
if (ret < 0) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
ret = bdrv_write(bs, sector_num, buf, s->cluster_sectors);
|
ret = bdrv_write(bs, sector_num, buf, s->cluster_sectors);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto fail;
|
goto fail;
|
||||||
|
@ -1631,6 +1703,13 @@ static int qcow2_write_compressed(BlockDriverState *bs, int64_t sector_num,
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
cluster_offset &= s->cluster_offset_mask;
|
cluster_offset &= s->cluster_offset_mask;
|
||||||
|
|
||||||
|
ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_DEFAULT,
|
||||||
|
cluster_offset, out_len);
|
||||||
|
if (ret < 0) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
BLKDBG_EVENT(bs->file, BLKDBG_WRITE_COMPRESSED);
|
BLKDBG_EVENT(bs->file, BLKDBG_WRITE_COMPRESSED);
|
||||||
ret = bdrv_pwrite(bs->file, cluster_offset, out_buf, out_len);
|
ret = bdrv_pwrite(bs->file, cluster_offset, out_buf, out_len);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
|
|
@ -40,11 +40,11 @@
|
||||||
#define QCOW_MAX_CRYPT_CLUSTERS 32
|
#define QCOW_MAX_CRYPT_CLUSTERS 32
|
||||||
|
|
||||||
/* indicate that the refcount of the referenced cluster is exactly one. */
|
/* indicate that the refcount of the referenced cluster is exactly one. */
|
||||||
#define QCOW_OFLAG_COPIED (1LL << 63)
|
#define QCOW_OFLAG_COPIED (1ULL << 63)
|
||||||
/* indicate that the cluster is compressed (they never have the copied flag) */
|
/* indicate that the cluster is compressed (they never have the copied flag) */
|
||||||
#define QCOW_OFLAG_COMPRESSED (1LL << 62)
|
#define QCOW_OFLAG_COMPRESSED (1ULL << 62)
|
||||||
/* The cluster reads as all zeros */
|
/* The cluster reads as all zeros */
|
||||||
#define QCOW_OFLAG_ZERO (1LL << 0)
|
#define QCOW_OFLAG_ZERO (1ULL << 0)
|
||||||
|
|
||||||
#define REFCOUNT_SHIFT 1 /* refcount size is 2 bytes */
|
#define REFCOUNT_SHIFT 1 /* refcount size is 2 bytes */
|
||||||
|
|
||||||
|
@ -119,9 +119,12 @@ enum {
|
||||||
/* Incompatible feature bits */
|
/* Incompatible feature bits */
|
||||||
enum {
|
enum {
|
||||||
QCOW2_INCOMPAT_DIRTY_BITNR = 0,
|
QCOW2_INCOMPAT_DIRTY_BITNR = 0,
|
||||||
|
QCOW2_INCOMPAT_CORRUPT_BITNR = 1,
|
||||||
QCOW2_INCOMPAT_DIRTY = 1 << QCOW2_INCOMPAT_DIRTY_BITNR,
|
QCOW2_INCOMPAT_DIRTY = 1 << QCOW2_INCOMPAT_DIRTY_BITNR,
|
||||||
|
QCOW2_INCOMPAT_CORRUPT = 1 << QCOW2_INCOMPAT_CORRUPT_BITNR,
|
||||||
|
|
||||||
QCOW2_INCOMPAT_MASK = QCOW2_INCOMPAT_DIRTY,
|
QCOW2_INCOMPAT_MASK = QCOW2_INCOMPAT_DIRTY
|
||||||
|
| QCOW2_INCOMPAT_CORRUPT,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Compatible feature bits */
|
/* Compatible feature bits */
|
||||||
|
@ -286,6 +289,40 @@ enum {
|
||||||
QCOW2_CLUSTER_ZERO
|
QCOW2_CLUSTER_ZERO
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef enum QCow2MetadataOverlap {
|
||||||
|
QCOW2_OL_MAIN_HEADER_BITNR = 0,
|
||||||
|
QCOW2_OL_ACTIVE_L1_BITNR = 1,
|
||||||
|
QCOW2_OL_ACTIVE_L2_BITNR = 2,
|
||||||
|
QCOW2_OL_REFCOUNT_TABLE_BITNR = 3,
|
||||||
|
QCOW2_OL_REFCOUNT_BLOCK_BITNR = 4,
|
||||||
|
QCOW2_OL_SNAPSHOT_TABLE_BITNR = 5,
|
||||||
|
QCOW2_OL_INACTIVE_L1_BITNR = 6,
|
||||||
|
QCOW2_OL_INACTIVE_L2_BITNR = 7,
|
||||||
|
|
||||||
|
QCOW2_OL_MAX_BITNR = 8,
|
||||||
|
|
||||||
|
QCOW2_OL_NONE = 0,
|
||||||
|
QCOW2_OL_MAIN_HEADER = (1 << QCOW2_OL_MAIN_HEADER_BITNR),
|
||||||
|
QCOW2_OL_ACTIVE_L1 = (1 << QCOW2_OL_ACTIVE_L1_BITNR),
|
||||||
|
QCOW2_OL_ACTIVE_L2 = (1 << QCOW2_OL_ACTIVE_L2_BITNR),
|
||||||
|
QCOW2_OL_REFCOUNT_TABLE = (1 << QCOW2_OL_REFCOUNT_TABLE_BITNR),
|
||||||
|
QCOW2_OL_REFCOUNT_BLOCK = (1 << QCOW2_OL_REFCOUNT_BLOCK_BITNR),
|
||||||
|
QCOW2_OL_SNAPSHOT_TABLE = (1 << QCOW2_OL_SNAPSHOT_TABLE_BITNR),
|
||||||
|
QCOW2_OL_INACTIVE_L1 = (1 << QCOW2_OL_INACTIVE_L1_BITNR),
|
||||||
|
/* NOTE: Checking overlaps with inactive L2 tables will result in bdrv
|
||||||
|
* reads. */
|
||||||
|
QCOW2_OL_INACTIVE_L2 = (1 << QCOW2_OL_INACTIVE_L2_BITNR),
|
||||||
|
} QCow2MetadataOverlap;
|
||||||
|
|
||||||
|
/* Perform all overlap checks which don't require disk access */
|
||||||
|
#define QCOW2_OL_CACHED \
|
||||||
|
(QCOW2_OL_MAIN_HEADER | QCOW2_OL_ACTIVE_L1 | QCOW2_OL_ACTIVE_L2 | \
|
||||||
|
QCOW2_OL_REFCOUNT_TABLE | QCOW2_OL_REFCOUNT_BLOCK | \
|
||||||
|
QCOW2_OL_SNAPSHOT_TABLE | QCOW2_OL_INACTIVE_L1)
|
||||||
|
|
||||||
|
/* The default checks to perform */
|
||||||
|
#define QCOW2_OL_DEFAULT QCOW2_OL_CACHED
|
||||||
|
|
||||||
#define L1E_OFFSET_MASK 0x00ffffffffffff00ULL
|
#define L1E_OFFSET_MASK 0x00ffffffffffff00ULL
|
||||||
#define L2E_OFFSET_MASK 0x00ffffffffffff00ULL
|
#define L2E_OFFSET_MASK 0x00ffffffffffff00ULL
|
||||||
#define L2E_COMPRESSED_OFFSET_SIZE_MASK 0x3fffffffffffffffULL
|
#define L2E_COMPRESSED_OFFSET_SIZE_MASK 0x3fffffffffffffffULL
|
||||||
|
@ -361,6 +398,8 @@ int qcow2_backing_read1(BlockDriverState *bs, QEMUIOVector *qiov,
|
||||||
int64_t sector_num, int nb_sectors);
|
int64_t sector_num, int nb_sectors);
|
||||||
|
|
||||||
int qcow2_mark_dirty(BlockDriverState *bs);
|
int qcow2_mark_dirty(BlockDriverState *bs);
|
||||||
|
int qcow2_mark_corrupt(BlockDriverState *bs);
|
||||||
|
int qcow2_mark_consistent(BlockDriverState *bs);
|
||||||
int qcow2_update_header(BlockDriverState *bs);
|
int qcow2_update_header(BlockDriverState *bs);
|
||||||
|
|
||||||
/* qcow2-refcount.c functions */
|
/* qcow2-refcount.c functions */
|
||||||
|
@ -385,9 +424,15 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
|
||||||
|
|
||||||
void qcow2_process_discards(BlockDriverState *bs, int ret);
|
void qcow2_process_discards(BlockDriverState *bs, int ret);
|
||||||
|
|
||||||
|
int qcow2_check_metadata_overlap(BlockDriverState *bs, int chk, int64_t offset,
|
||||||
|
int64_t size);
|
||||||
|
int qcow2_pre_write_overlap_check(BlockDriverState *bs, int chk, int64_t offset,
|
||||||
|
int64_t size);
|
||||||
|
|
||||||
/* qcow2-cluster.c functions */
|
/* qcow2-cluster.c functions */
|
||||||
int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size,
|
int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size,
|
||||||
bool exact_size);
|
bool exact_size);
|
||||||
|
int qcow2_write_l1_entry(BlockDriverState *bs, int l1_index);
|
||||||
void qcow2_l2_cache_reset(BlockDriverState *bs);
|
void qcow2_l2_cache_reset(BlockDriverState *bs);
|
||||||
int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset);
|
int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset);
|
||||||
void qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num,
|
void qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num,
|
||||||
|
|
|
@ -1,13 +1,17 @@
|
||||||
/*
|
/* BlockDriver implementation for "raw"
|
||||||
* Block driver for RAW format
|
|
||||||
*
|
*
|
||||||
* Copyright (c) 2006 Fabrice Bellard
|
* Copyright (C) 2010, 2013, Red Hat, Inc.
|
||||||
|
* Copyright (C) 2010, Blue Swirl <blauwirbel@gmail.com>
|
||||||
|
* Copyright (C) 2009, Anthony Liguori <aliguori@us.ibm.com>
|
||||||
|
*
|
||||||
|
* Author:
|
||||||
|
* Laszlo Ersek <lersek@redhat.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to
|
||||||
* in the Software without restriction, including without limitation the rights
|
* deal in the Software without restriction, including without limitation the
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
* sell copies of the Software, and to permit persons to whom the Software is
|
||||||
* furnished to do so, subject to the following conditions:
|
* furnished to do so, subject to the following conditions:
|
||||||
*
|
*
|
||||||
* The above copyright notice and this permission notice shall be included in
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
@ -15,27 +19,27 @@
|
||||||
*
|
*
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
* THE SOFTWARE.
|
* IN THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "qemu-common.h"
|
|
||||||
#include "block/block_int.h"
|
#include "block/block_int.h"
|
||||||
#include "qemu/module.h"
|
#include "qemu/option.h"
|
||||||
|
|
||||||
static int raw_open(BlockDriverState *bs, QDict *options, int flags)
|
static QEMUOptionParameter raw_create_options[] = {
|
||||||
{
|
{
|
||||||
bs->sg = bs->file->sg;
|
.name = BLOCK_OPT_SIZE,
|
||||||
return 0;
|
.type = OPT_SIZE,
|
||||||
}
|
.help = "Virtual disk size"
|
||||||
|
},
|
||||||
|
{ 0 }
|
||||||
|
};
|
||||||
|
|
||||||
/* We have nothing to do for raw reopen, stubs just return
|
static int raw_reopen_prepare(BDRVReopenState *reopen_state,
|
||||||
* success */
|
BlockReopenQueue *queue, Error **errp)
|
||||||
static int raw_reopen_prepare(BDRVReopenState *state,
|
|
||||||
BlockReopenQueue *queue, Error **errp)
|
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -54,45 +58,40 @@ static int coroutine_fn raw_co_writev(BlockDriverState *bs, int64_t sector_num,
|
||||||
return bdrv_co_writev(bs->file, sector_num, nb_sectors, qiov);
|
return bdrv_co_writev(bs->file, sector_num, nb_sectors, qiov);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void raw_close(BlockDriverState *bs)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
static int coroutine_fn raw_co_is_allocated(BlockDriverState *bs,
|
static int coroutine_fn raw_co_is_allocated(BlockDriverState *bs,
|
||||||
int64_t sector_num,
|
int64_t sector_num, int nb_sectors,
|
||||||
int nb_sectors, int *pnum)
|
int *pnum)
|
||||||
{
|
{
|
||||||
return bdrv_co_is_allocated(bs->file, sector_num, nb_sectors, pnum);
|
return bdrv_co_is_allocated(bs->file, sector_num, nb_sectors, pnum);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int coroutine_fn raw_co_write_zeroes(BlockDriverState *bs,
|
static int coroutine_fn raw_co_write_zeroes(BlockDriverState *bs,
|
||||||
int64_t sector_num,
|
int64_t sector_num, int nb_sectors)
|
||||||
int nb_sectors)
|
|
||||||
{
|
{
|
||||||
return bdrv_co_write_zeroes(bs->file, sector_num, nb_sectors);
|
return bdrv_co_write_zeroes(bs->file, sector_num, nb_sectors);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int coroutine_fn raw_co_discard(BlockDriverState *bs,
|
||||||
|
int64_t sector_num, int nb_sectors)
|
||||||
|
{
|
||||||
|
return bdrv_co_discard(bs->file, sector_num, nb_sectors);
|
||||||
|
}
|
||||||
|
|
||||||
static int64_t raw_getlength(BlockDriverState *bs)
|
static int64_t raw_getlength(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
return bdrv_getlength(bs->file);
|
return bdrv_getlength(bs->file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int raw_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
|
||||||
|
{
|
||||||
|
return bdrv_get_info(bs->file, bdi);
|
||||||
|
}
|
||||||
|
|
||||||
static int raw_truncate(BlockDriverState *bs, int64_t offset)
|
static int raw_truncate(BlockDriverState *bs, int64_t offset)
|
||||||
{
|
{
|
||||||
return bdrv_truncate(bs->file, offset);
|
return bdrv_truncate(bs->file, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int raw_probe(const uint8_t *buf, int buf_size, const char *filename)
|
|
||||||
{
|
|
||||||
return 1; /* everything can be opened as raw image */
|
|
||||||
}
|
|
||||||
|
|
||||||
static int coroutine_fn raw_co_discard(BlockDriverState *bs,
|
|
||||||
int64_t sector_num, int nb_sectors)
|
|
||||||
{
|
|
||||||
return bdrv_co_discard(bs->file, sector_num, nb_sectors);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int raw_is_inserted(BlockDriverState *bs)
|
static int raw_is_inserted(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
return bdrv_is_inserted(bs->file);
|
return bdrv_is_inserted(bs->file);
|
||||||
|
@ -115,14 +114,20 @@ static void raw_lock_medium(BlockDriverState *bs, bool locked)
|
||||||
|
|
||||||
static int raw_ioctl(BlockDriverState *bs, unsigned long int req, void *buf)
|
static int raw_ioctl(BlockDriverState *bs, unsigned long int req, void *buf)
|
||||||
{
|
{
|
||||||
return bdrv_ioctl(bs->file, req, buf);
|
return bdrv_ioctl(bs->file, req, buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
static BlockDriverAIOCB *raw_aio_ioctl(BlockDriverState *bs,
|
static BlockDriverAIOCB *raw_aio_ioctl(BlockDriverState *bs,
|
||||||
unsigned long int req, void *buf,
|
unsigned long int req, void *buf,
|
||||||
BlockDriverCompletionFunc *cb, void *opaque)
|
BlockDriverCompletionFunc *cb,
|
||||||
|
void *opaque)
|
||||||
{
|
{
|
||||||
return bdrv_aio_ioctl(bs->file, req, buf, cb, opaque);
|
return bdrv_aio_ioctl(bs->file, req, buf, cb, opaque);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int raw_has_zero_init(BlockDriverState *bs)
|
||||||
|
{
|
||||||
|
return bdrv_has_zero_init(bs->file);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int raw_create(const char *filename, QEMUOptionParameter *options)
|
static int raw_create(const char *filename, QEMUOptionParameter *options)
|
||||||
|
@ -130,58 +135,47 @@ static int raw_create(const char *filename, QEMUOptionParameter *options)
|
||||||
return bdrv_create_file(filename, options);
|
return bdrv_create_file(filename, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
static QEMUOptionParameter raw_create_options[] = {
|
static int raw_open(BlockDriverState *bs, QDict *options, int flags)
|
||||||
{
|
|
||||||
.name = BLOCK_OPT_SIZE,
|
|
||||||
.type = OPT_SIZE,
|
|
||||||
.help = "Virtual disk size"
|
|
||||||
},
|
|
||||||
{ NULL }
|
|
||||||
};
|
|
||||||
|
|
||||||
static int raw_has_zero_init(BlockDriverState *bs)
|
|
||||||
{
|
{
|
||||||
return bdrv_has_zero_init(bs->file);
|
bs->sg = bs->file->sg;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int raw_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
|
static void raw_close(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
return bdrv_get_info(bs->file, bdi);
|
}
|
||||||
|
|
||||||
|
static int raw_probe(const uint8_t *buf, int buf_size, const char *filename)
|
||||||
|
{
|
||||||
|
/* smallest possible positive score so that raw is used if and only if no
|
||||||
|
* other block driver works
|
||||||
|
*/
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static BlockDriver bdrv_raw = {
|
static BlockDriver bdrv_raw = {
|
||||||
.format_name = "raw",
|
.format_name = "raw",
|
||||||
|
.bdrv_probe = &raw_probe,
|
||||||
/* It's really 0, but we need to make g_malloc() happy */
|
.bdrv_reopen_prepare = &raw_reopen_prepare,
|
||||||
.instance_size = 1,
|
.bdrv_open = &raw_open,
|
||||||
|
.bdrv_close = &raw_close,
|
||||||
.bdrv_open = raw_open,
|
.bdrv_create = &raw_create,
|
||||||
.bdrv_close = raw_close,
|
.bdrv_co_readv = &raw_co_readv,
|
||||||
|
.bdrv_co_writev = &raw_co_writev,
|
||||||
.bdrv_reopen_prepare = raw_reopen_prepare,
|
.bdrv_co_write_zeroes = &raw_co_write_zeroes,
|
||||||
|
.bdrv_co_discard = &raw_co_discard,
|
||||||
.bdrv_co_readv = raw_co_readv,
|
.bdrv_co_is_allocated = &raw_co_is_allocated,
|
||||||
.bdrv_co_writev = raw_co_writev,
|
.bdrv_truncate = &raw_truncate,
|
||||||
.bdrv_co_is_allocated = raw_co_is_allocated,
|
.bdrv_getlength = &raw_getlength,
|
||||||
.bdrv_co_write_zeroes = raw_co_write_zeroes,
|
.bdrv_get_info = &raw_get_info,
|
||||||
.bdrv_co_discard = raw_co_discard,
|
.bdrv_is_inserted = &raw_is_inserted,
|
||||||
|
.bdrv_media_changed = &raw_media_changed,
|
||||||
.bdrv_probe = raw_probe,
|
.bdrv_eject = &raw_eject,
|
||||||
.bdrv_getlength = raw_getlength,
|
.bdrv_lock_medium = &raw_lock_medium,
|
||||||
.bdrv_get_info = raw_get_info,
|
.bdrv_ioctl = &raw_ioctl,
|
||||||
.bdrv_truncate = raw_truncate,
|
.bdrv_aio_ioctl = &raw_aio_ioctl,
|
||||||
|
.create_options = &raw_create_options[0],
|
||||||
.bdrv_is_inserted = raw_is_inserted,
|
.bdrv_has_zero_init = &raw_has_zero_init
|
||||||
.bdrv_media_changed = raw_media_changed,
|
|
||||||
.bdrv_eject = raw_eject,
|
|
||||||
.bdrv_lock_medium = raw_lock_medium,
|
|
||||||
|
|
||||||
.bdrv_ioctl = raw_ioctl,
|
|
||||||
.bdrv_aio_ioctl = raw_aio_ioctl,
|
|
||||||
|
|
||||||
.bdrv_create = raw_create,
|
|
||||||
.create_options = raw_create_options,
|
|
||||||
.bdrv_has_zero_init = raw_has_zero_init,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static void bdrv_raw_init(void)
|
static void bdrv_raw_init(void)
|
143
blockdev.c
143
blockdev.c
|
@ -46,7 +46,6 @@
|
||||||
|
|
||||||
static QTAILQ_HEAD(drivelist, DriveInfo) drives = QTAILQ_HEAD_INITIALIZER(drives);
|
static QTAILQ_HEAD(drivelist, DriveInfo) drives = QTAILQ_HEAD_INITIALIZER(drives);
|
||||||
extern QemuOptsList qemu_common_drive_opts;
|
extern QemuOptsList qemu_common_drive_opts;
|
||||||
extern QemuOptsList qemu_old_drive_opts;
|
|
||||||
|
|
||||||
static const char *const if_name[IF_COUNT] = {
|
static const char *const if_name[IF_COUNT] = {
|
||||||
[IF_NONE] = "none",
|
[IF_NONE] = "none",
|
||||||
|
@ -755,26 +754,6 @@ DriveInfo *drive_init(QemuOpts *all_opts, BlockInterfaceType block_default_type)
|
||||||
{
|
{
|
||||||
const char *value;
|
const char *value;
|
||||||
|
|
||||||
/*
|
|
||||||
* Check that only old options are used by copying into a QemuOpts with
|
|
||||||
* stricter checks. Going through a QDict seems to be the easiest way to
|
|
||||||
* achieve this...
|
|
||||||
*/
|
|
||||||
QemuOpts* check_opts;
|
|
||||||
QDict *qdict;
|
|
||||||
Error *local_err = NULL;
|
|
||||||
|
|
||||||
qdict = qemu_opts_to_qdict(all_opts, NULL);
|
|
||||||
check_opts = qemu_opts_from_qdict(&qemu_old_drive_opts, qdict, &local_err);
|
|
||||||
QDECREF(qdict);
|
|
||||||
|
|
||||||
if (error_is_set(&local_err)) {
|
|
||||||
qerror_report_err(local_err);
|
|
||||||
error_free(local_err);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
qemu_opts_del(check_opts);
|
|
||||||
|
|
||||||
/* Change legacy command line options into QMP ones */
|
/* Change legacy command line options into QMP ones */
|
||||||
qemu_opt_rename(all_opts, "iops", "throttling.iops-total");
|
qemu_opt_rename(all_opts, "iops", "throttling.iops-total");
|
||||||
qemu_opt_rename(all_opts, "iops_rd", "throttling.iops-read");
|
qemu_opt_rename(all_opts, "iops_rd", "throttling.iops-read");
|
||||||
|
@ -2001,128 +1980,6 @@ QemuOptsList qemu_common_drive_opts = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
QemuOptsList qemu_old_drive_opts = {
|
|
||||||
.name = "drive",
|
|
||||||
.head = QTAILQ_HEAD_INITIALIZER(qemu_old_drive_opts.head),
|
|
||||||
.desc = {
|
|
||||||
{
|
|
||||||
.name = "bus",
|
|
||||||
.type = QEMU_OPT_NUMBER,
|
|
||||||
.help = "bus number",
|
|
||||||
},{
|
|
||||||
.name = "unit",
|
|
||||||
.type = QEMU_OPT_NUMBER,
|
|
||||||
.help = "unit number (i.e. lun for scsi)",
|
|
||||||
},{
|
|
||||||
.name = "if",
|
|
||||||
.type = QEMU_OPT_STRING,
|
|
||||||
.help = "interface (ide, scsi, sd, mtd, floppy, pflash, virtio)",
|
|
||||||
},{
|
|
||||||
.name = "index",
|
|
||||||
.type = QEMU_OPT_NUMBER,
|
|
||||||
.help = "index number",
|
|
||||||
},{
|
|
||||||
.name = "cyls",
|
|
||||||
.type = QEMU_OPT_NUMBER,
|
|
||||||
.help = "number of cylinders (ide disk geometry)",
|
|
||||||
},{
|
|
||||||
.name = "heads",
|
|
||||||
.type = QEMU_OPT_NUMBER,
|
|
||||||
.help = "number of heads (ide disk geometry)",
|
|
||||||
},{
|
|
||||||
.name = "secs",
|
|
||||||
.type = QEMU_OPT_NUMBER,
|
|
||||||
.help = "number of sectors (ide disk geometry)",
|
|
||||||
},{
|
|
||||||
.name = "trans",
|
|
||||||
.type = QEMU_OPT_STRING,
|
|
||||||
.help = "chs translation (auto, lba. none)",
|
|
||||||
},{
|
|
||||||
.name = "media",
|
|
||||||
.type = QEMU_OPT_STRING,
|
|
||||||
.help = "media type (disk, cdrom)",
|
|
||||||
},{
|
|
||||||
.name = "snapshot",
|
|
||||||
.type = QEMU_OPT_BOOL,
|
|
||||||
.help = "enable/disable snapshot mode",
|
|
||||||
},{
|
|
||||||
.name = "file",
|
|
||||||
.type = QEMU_OPT_STRING,
|
|
||||||
.help = "disk image",
|
|
||||||
},{
|
|
||||||
.name = "discard",
|
|
||||||
.type = QEMU_OPT_STRING,
|
|
||||||
.help = "discard operation (ignore/off, unmap/on)",
|
|
||||||
},{
|
|
||||||
.name = "cache",
|
|
||||||
.type = QEMU_OPT_STRING,
|
|
||||||
.help = "host cache usage (none, writeback, writethrough, "
|
|
||||||
"directsync, unsafe)",
|
|
||||||
},{
|
|
||||||
.name = "aio",
|
|
||||||
.type = QEMU_OPT_STRING,
|
|
||||||
.help = "host AIO implementation (threads, native)",
|
|
||||||
},{
|
|
||||||
.name = "format",
|
|
||||||
.type = QEMU_OPT_STRING,
|
|
||||||
.help = "disk format (raw, qcow2, ...)",
|
|
||||||
},{
|
|
||||||
.name = "serial",
|
|
||||||
.type = QEMU_OPT_STRING,
|
|
||||||
.help = "disk serial number",
|
|
||||||
},{
|
|
||||||
.name = "rerror",
|
|
||||||
.type = QEMU_OPT_STRING,
|
|
||||||
.help = "read error action",
|
|
||||||
},{
|
|
||||||
.name = "werror",
|
|
||||||
.type = QEMU_OPT_STRING,
|
|
||||||
.help = "write error action",
|
|
||||||
},{
|
|
||||||
.name = "addr",
|
|
||||||
.type = QEMU_OPT_STRING,
|
|
||||||
.help = "pci address (virtio only)",
|
|
||||||
},{
|
|
||||||
.name = "readonly",
|
|
||||||
.type = QEMU_OPT_BOOL,
|
|
||||||
.help = "open drive file as read-only",
|
|
||||||
},{
|
|
||||||
.name = "iops",
|
|
||||||
.type = QEMU_OPT_NUMBER,
|
|
||||||
.help = "limit total I/O operations per second",
|
|
||||||
},{
|
|
||||||
.name = "iops_rd",
|
|
||||||
.type = QEMU_OPT_NUMBER,
|
|
||||||
.help = "limit read operations per second",
|
|
||||||
},{
|
|
||||||
.name = "iops_wr",
|
|
||||||
.type = QEMU_OPT_NUMBER,
|
|
||||||
.help = "limit write operations per second",
|
|
||||||
},{
|
|
||||||
.name = "bps",
|
|
||||||
.type = QEMU_OPT_NUMBER,
|
|
||||||
.help = "limit total bytes per second",
|
|
||||||
},{
|
|
||||||
.name = "bps_rd",
|
|
||||||
.type = QEMU_OPT_NUMBER,
|
|
||||||
.help = "limit read bytes per second",
|
|
||||||
},{
|
|
||||||
.name = "bps_wr",
|
|
||||||
.type = QEMU_OPT_NUMBER,
|
|
||||||
.help = "limit write bytes per second",
|
|
||||||
},{
|
|
||||||
.name = "copy-on-read",
|
|
||||||
.type = QEMU_OPT_BOOL,
|
|
||||||
.help = "copy read data from backing file into image file",
|
|
||||||
},{
|
|
||||||
.name = "boot",
|
|
||||||
.type = QEMU_OPT_BOOL,
|
|
||||||
.help = "(deprecated, ignored)",
|
|
||||||
},
|
|
||||||
{ /* end of list */ }
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
QemuOptsList qemu_drive_opts = {
|
QemuOptsList qemu_drive_opts = {
|
||||||
.name = "drive",
|
.name = "drive",
|
||||||
.head = QTAILQ_HEAD_INITIALIZER(qemu_drive_opts.head),
|
.head = QTAILQ_HEAD_INITIALIZER(qemu_drive_opts.head),
|
||||||
|
|
|
@ -80,7 +80,12 @@ in the description of a field.
|
||||||
tables to repair refcounts before accessing the
|
tables to repair refcounts before accessing the
|
||||||
image.
|
image.
|
||||||
|
|
||||||
Bits 1-63: Reserved (set to 0)
|
Bit 1: Corrupt bit. If this bit is set then any data
|
||||||
|
structure may be corrupt and the image must not
|
||||||
|
be written to (unless for regaining
|
||||||
|
consistency).
|
||||||
|
|
||||||
|
Bits 2-63: Reserved (set to 0)
|
||||||
|
|
||||||
80 - 87: compatible_features
|
80 - 87: compatible_features
|
||||||
Bitmask of compatible features. An implementation can
|
Bitmask of compatible features. An implementation can
|
||||||
|
|
|
@ -413,6 +413,7 @@ typedef enum {
|
||||||
|
|
||||||
BLKDBG_REFTABLE_LOAD,
|
BLKDBG_REFTABLE_LOAD,
|
||||||
BLKDBG_REFTABLE_GROW,
|
BLKDBG_REFTABLE_GROW,
|
||||||
|
BLKDBG_REFTABLE_UPDATE,
|
||||||
|
|
||||||
BLKDBG_REFBLOCK_LOAD,
|
BLKDBG_REFBLOCK_LOAD,
|
||||||
BLKDBG_REFBLOCK_UPDATE,
|
BLKDBG_REFBLOCK_UPDATE,
|
||||||
|
|
|
@ -48,6 +48,7 @@ typedef enum MonitorEvent {
|
||||||
QEVENT_BALLOON_CHANGE,
|
QEVENT_BALLOON_CHANGE,
|
||||||
QEVENT_SPICE_MIGRATE_COMPLETED,
|
QEVENT_SPICE_MIGRATE_COMPLETED,
|
||||||
QEVENT_GUEST_PANICKED,
|
QEVENT_GUEST_PANICKED,
|
||||||
|
QEVENT_BLOCK_IMAGE_CORRUPTED,
|
||||||
|
|
||||||
/* Add to 'monitor_event_names' array in monitor.c when
|
/* Add to 'monitor_event_names' array in monitor.c when
|
||||||
* defining new events here */
|
* defining new events here */
|
||||||
|
|
|
@ -46,6 +46,7 @@ typedef struct QEMUOptionParameter {
|
||||||
char* s;
|
char* s;
|
||||||
} value;
|
} value;
|
||||||
const char *help;
|
const char *help;
|
||||||
|
bool assigned;
|
||||||
} QEMUOptionParameter;
|
} QEMUOptionParameter;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -506,6 +506,7 @@ static const char *monitor_event_names[] = {
|
||||||
[QEVENT_BALLOON_CHANGE] = "BALLOON_CHANGE",
|
[QEVENT_BALLOON_CHANGE] = "BALLOON_CHANGE",
|
||||||
[QEVENT_SPICE_MIGRATE_COMPLETED] = "SPICE_MIGRATE_COMPLETED",
|
[QEVENT_SPICE_MIGRATE_COMPLETED] = "SPICE_MIGRATE_COMPLETED",
|
||||||
[QEVENT_GUEST_PANICKED] = "GUEST_PANICKED",
|
[QEVENT_GUEST_PANICKED] = "GUEST_PANICKED",
|
||||||
|
[QEVENT_BLOCK_IMAGE_CORRUPTED] = "BLOCK_IMAGE_CORRUPTED",
|
||||||
};
|
};
|
||||||
QEMU_BUILD_BUG_ON(ARRAY_SIZE(monitor_event_names) != QEVENT_MAX)
|
QEMU_BUILD_BUG_ON(ARRAY_SIZE(monitor_event_names) != QEVENT_MAX)
|
||||||
|
|
||||||
|
|
|
@ -57,12 +57,8 @@ typedef struct %(name)sList
|
||||||
''',
|
''',
|
||||||
name=name)
|
name=name)
|
||||||
|
|
||||||
def generate_struct(structname, fieldname, members):
|
def generate_struct_fields(members):
|
||||||
ret = mcgen('''
|
ret = ''
|
||||||
struct %(name)s
|
|
||||||
{
|
|
||||||
''',
|
|
||||||
name=structname)
|
|
||||||
|
|
||||||
for argname, argentry, optional, structured in parse_args(members):
|
for argname, argentry, optional, structured in parse_args(members):
|
||||||
if optional:
|
if optional:
|
||||||
|
@ -80,6 +76,17 @@ struct %(name)s
|
||||||
''',
|
''',
|
||||||
c_type=c_type(argentry), c_name=c_var(argname))
|
c_type=c_type(argentry), c_name=c_var(argname))
|
||||||
|
|
||||||
|
return ret
|
||||||
|
|
||||||
|
def generate_struct(structname, fieldname, members):
|
||||||
|
ret = mcgen('''
|
||||||
|
struct %(name)s
|
||||||
|
{
|
||||||
|
''',
|
||||||
|
name=structname)
|
||||||
|
|
||||||
|
ret += generate_struct_fields(members)
|
||||||
|
|
||||||
if len(fieldname):
|
if len(fieldname):
|
||||||
fieldname = " " + fieldname
|
fieldname = " " + fieldname
|
||||||
ret += mcgen('''
|
ret += mcgen('''
|
||||||
|
|
|
@ -54,7 +54,7 @@ header_length 72
|
||||||
|
|
||||||
Header extension:
|
Header extension:
|
||||||
magic 0x6803f857
|
magic 0x6803f857
|
||||||
length 96
|
length 144
|
||||||
data <binary>
|
data <binary>
|
||||||
|
|
||||||
Header extension:
|
Header extension:
|
||||||
|
@ -68,7 +68,7 @@ No errors were found on the image.
|
||||||
|
|
||||||
magic 0x514649fb
|
magic 0x514649fb
|
||||||
version 2
|
version 2
|
||||||
backing_file_offset 0xf8
|
backing_file_offset 0x128
|
||||||
backing_file_size 0x17
|
backing_file_size 0x17
|
||||||
cluster_bits 16
|
cluster_bits 16
|
||||||
size 67108864
|
size 67108864
|
||||||
|
@ -92,7 +92,7 @@ data 'host_device'
|
||||||
|
|
||||||
Header extension:
|
Header extension:
|
||||||
magic 0x6803f857
|
magic 0x6803f857
|
||||||
length 96
|
length 144
|
||||||
data <binary>
|
data <binary>
|
||||||
|
|
||||||
Header extension:
|
Header extension:
|
||||||
|
@ -155,7 +155,7 @@ header_length 104
|
||||||
|
|
||||||
Header extension:
|
Header extension:
|
||||||
magic 0x6803f857
|
magic 0x6803f857
|
||||||
length 96
|
length 144
|
||||||
data <binary>
|
data <binary>
|
||||||
|
|
||||||
Header extension:
|
Header extension:
|
||||||
|
@ -169,7 +169,7 @@ No errors were found on the image.
|
||||||
|
|
||||||
magic 0x514649fb
|
magic 0x514649fb
|
||||||
version 3
|
version 3
|
||||||
backing_file_offset 0x118
|
backing_file_offset 0x148
|
||||||
backing_file_size 0x17
|
backing_file_size 0x17
|
||||||
cluster_bits 16
|
cluster_bits 16
|
||||||
size 67108864
|
size 67108864
|
||||||
|
@ -193,7 +193,7 @@ data 'host_device'
|
||||||
|
|
||||||
Header extension:
|
Header extension:
|
||||||
magic 0x6803f857
|
magic 0x6803f857
|
||||||
length 96
|
length 144
|
||||||
data <binary>
|
data <binary>
|
||||||
|
|
||||||
Header extension:
|
Header extension:
|
||||||
|
|
|
@ -46,7 +46,7 @@ header_length 104
|
||||||
|
|
||||||
Header extension:
|
Header extension:
|
||||||
magic 0x6803f857
|
magic 0x6803f857
|
||||||
length 96
|
length 144
|
||||||
data <binary>
|
data <binary>
|
||||||
|
|
||||||
*** done
|
*** done
|
||||||
|
|
|
@ -85,7 +85,6 @@ QEMU_PROG: -drive if=virtio: Device 'virtio-blk-pci' could not be initialized
|
||||||
Testing: -drive if=scsi
|
Testing: -drive if=scsi
|
||||||
QEMU X.Y.Z monitor - type 'help' for more information
|
QEMU X.Y.Z monitor - type 'help' for more information
|
||||||
(qemu) QEMU_PROG: -drive if=scsi: Device needs media, but drive is empty
|
(qemu) QEMU_PROG: -drive if=scsi: Device needs media, but drive is empty
|
||||||
QEMU_PROG: -drive if=scsi: Device initialization failed.
|
|
||||||
QEMU_PROG: Device initialization failed.
|
QEMU_PROG: Device initialization failed.
|
||||||
QEMU_PROG: Initialization of device lsi53c895a failed
|
QEMU_PROG: Initialization of device lsi53c895a failed
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,111 @@
|
||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# Test case for image corruption (overlapping data structures) in qcow2
|
||||||
|
#
|
||||||
|
# Copyright (C) 2013 Red Hat, Inc.
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
|
||||||
|
# creator
|
||||||
|
owner=mreitz@redhat.com
|
||||||
|
|
||||||
|
seq=`basename $0`
|
||||||
|
echo "QA output created by $seq"
|
||||||
|
|
||||||
|
here=`pwd`
|
||||||
|
tmp=/tmp/$$
|
||||||
|
status=1 # failure is the default!
|
||||||
|
|
||||||
|
_cleanup()
|
||||||
|
{
|
||||||
|
_cleanup_test_img
|
||||||
|
}
|
||||||
|
trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||||
|
|
||||||
|
# get standard environment, filters and checks
|
||||||
|
. ./common.rc
|
||||||
|
. ./common.filter
|
||||||
|
|
||||||
|
# This tests qocw2-specific low-level functionality
|
||||||
|
_supported_fmt qcow2
|
||||||
|
_supported_proto generic
|
||||||
|
_supported_os Linux
|
||||||
|
|
||||||
|
rt_offset=65536 # 0x10000 (XXX: just an assumption)
|
||||||
|
rb_offset=131072 # 0x20000 (XXX: just an assumption)
|
||||||
|
l1_offset=196608 # 0x30000 (XXX: just an assumption)
|
||||||
|
l2_offset=262144 # 0x40000 (XXX: just an assumption)
|
||||||
|
|
||||||
|
IMGOPTS="compat=1.1"
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "=== Testing L2 reference into L1 ==="
|
||||||
|
echo
|
||||||
|
_make_test_img 64M
|
||||||
|
# Link first L1 entry (first L2 table) onto itself
|
||||||
|
# (Note the MSb in the L1 entry is set, ensuring the refcount is one - else any
|
||||||
|
# later write will result in a COW operation, effectively ruining this attempt
|
||||||
|
# on image corruption)
|
||||||
|
poke_file "$TEST_IMG" "$l1_offset" "\x80\x00\x00\x00\x00\x03\x00\x00"
|
||||||
|
_check_test_img
|
||||||
|
|
||||||
|
# The corrupt bit should not be set anyway
|
||||||
|
./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
|
||||||
|
|
||||||
|
# Try to write something, thereby forcing the corrupt bit to be set
|
||||||
|
$QEMU_IO -c "write -P 0x2a 0 512" "$TEST_IMG" | _filter_qemu_io
|
||||||
|
|
||||||
|
# The corrupt bit must now be set
|
||||||
|
./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
|
||||||
|
|
||||||
|
# Try to open the image R/W (which should fail)
|
||||||
|
$QEMU_IO -c "read 0 512" "$TEST_IMG" 2>&1 | _filter_qemu_io | sed -e "s/can't open device .*$/can't open device/"
|
||||||
|
|
||||||
|
# Try to open it RO (which should succeed)
|
||||||
|
$QEMU_IO -c "read 0 512" -r "$TEST_IMG" | _filter_qemu_io
|
||||||
|
|
||||||
|
# We could now try to fix the image, but this would probably fail (how should an
|
||||||
|
# L2 table linked onto the L1 table be fixed?)
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "=== Testing cluster data reference into refcount block ==="
|
||||||
|
echo
|
||||||
|
_make_test_img 64M
|
||||||
|
# Allocate L2 table
|
||||||
|
truncate -s "$(($l2_offset+65536))" "$TEST_IMG"
|
||||||
|
poke_file "$TEST_IMG" "$l1_offset" "\x80\x00\x00\x00\x00\x04\x00\x00"
|
||||||
|
# Mark cluster as used
|
||||||
|
poke_file "$TEST_IMG" "$(($rb_offset+8))" "\x00\x01"
|
||||||
|
# Redirect new data cluster onto refcount block
|
||||||
|
poke_file "$TEST_IMG" "$l2_offset" "\x80\x00\x00\x00\x00\x02\x00\x00"
|
||||||
|
_check_test_img
|
||||||
|
./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
|
||||||
|
$QEMU_IO -c "write -P 0x2a 0 512" "$TEST_IMG" | _filter_qemu_io
|
||||||
|
./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
|
||||||
|
|
||||||
|
# Try to fix it
|
||||||
|
_check_test_img -r all
|
||||||
|
|
||||||
|
# The corrupt bit should be cleared
|
||||||
|
./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
|
||||||
|
|
||||||
|
# Look if it's really really fixed
|
||||||
|
$QEMU_IO -c "write -P 0x2a 0 512" "$TEST_IMG" | _filter_qemu_io
|
||||||
|
./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
|
||||||
|
|
||||||
|
# success, all done
|
||||||
|
echo "*** done"
|
||||||
|
rm -f $seq.full
|
||||||
|
status=0
|
|
@ -0,0 +1,44 @@
|
||||||
|
QA output created by 060
|
||||||
|
|
||||||
|
=== Testing L2 reference into L1 ===
|
||||||
|
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
|
||||||
|
ERROR cluster 3 refcount=1 reference=3
|
||||||
|
|
||||||
|
1 errors were found on the image.
|
||||||
|
Data may be corrupted, or further writes to the image may corrupt it.
|
||||||
|
incompatible_features 0x0
|
||||||
|
qcow2: Preventing invalid write on metadata (overlaps with active L1 table); image marked as corrupt.
|
||||||
|
write failed: Input/output error
|
||||||
|
incompatible_features 0x2
|
||||||
|
qcow2: Image is corrupt; cannot be opened read/write.
|
||||||
|
qemu-io: can't open device
|
||||||
|
no file open, try 'help open'
|
||||||
|
read 512/512 bytes at offset 0
|
||||||
|
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
|
||||||
|
=== Testing cluster data reference into refcount block ===
|
||||||
|
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
|
||||||
|
ERROR refcount block 0 refcount=2
|
||||||
|
ERROR cluster 2 refcount=1 reference=2
|
||||||
|
|
||||||
|
2 errors were found on the image.
|
||||||
|
Data may be corrupted, or further writes to the image may corrupt it.
|
||||||
|
incompatible_features 0x0
|
||||||
|
qcow2: Preventing invalid write on metadata (overlaps with refcount block); image marked as corrupt.
|
||||||
|
write failed: Input/output error
|
||||||
|
incompatible_features 0x2
|
||||||
|
Repairing refcount block 0 refcount=2
|
||||||
|
The following inconsistencies were found and repaired:
|
||||||
|
|
||||||
|
0 leaked clusters
|
||||||
|
1 corruptions
|
||||||
|
|
||||||
|
Double checking the fixed image now...
|
||||||
|
No errors were found on the image.
|
||||||
|
incompatible_features 0x0
|
||||||
|
wrote 512/512 bytes at offset 0
|
||||||
|
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
incompatible_features 0x0
|
||||||
|
*** done
|
|
@ -0,0 +1,64 @@
|
||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# Test case for snapshotting images with unallocated zero clusters in
|
||||||
|
# qcow2
|
||||||
|
#
|
||||||
|
# Copyright (C) 2013 Red Hat, Inc.
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
|
||||||
|
# creator
|
||||||
|
owner=mreitz@redhat.com
|
||||||
|
|
||||||
|
seq=`basename $0`
|
||||||
|
echo "QA output created by $seq"
|
||||||
|
|
||||||
|
here=`pwd`
|
||||||
|
tmp=/tmp/$$
|
||||||
|
status=1 # failure is the default!
|
||||||
|
|
||||||
|
_cleanup()
|
||||||
|
{
|
||||||
|
_cleanup_test_img
|
||||||
|
}
|
||||||
|
trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||||
|
|
||||||
|
# get standard environment, filters and checks
|
||||||
|
. ./common.rc
|
||||||
|
. ./common.filter
|
||||||
|
|
||||||
|
# This tests qocw2-specific low-level functionality
|
||||||
|
_supported_fmt qcow2
|
||||||
|
_supported_proto generic
|
||||||
|
_supported_os Linux
|
||||||
|
|
||||||
|
IMGOPTS="compat=1.1"
|
||||||
|
IMG_SIZE=64M
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "=== Testing snapshotting an image with zero clusters ==="
|
||||||
|
echo
|
||||||
|
_make_test_img $IMG_SIZE
|
||||||
|
# Write some zero clusters
|
||||||
|
$QEMU_IO -c "write -z 0 256k" "$TEST_IMG" | _filter_qemu_io
|
||||||
|
# Create a snapshot
|
||||||
|
$QEMU_IMG snapshot -c foo "$TEST_IMG"
|
||||||
|
# Check the image (there shouldn't be any errors or leaks)
|
||||||
|
_check_test_img
|
||||||
|
|
||||||
|
# success, all done
|
||||||
|
echo "*** done"
|
||||||
|
rm -f $seq.full
|
||||||
|
status=0
|
|
@ -0,0 +1,9 @@
|
||||||
|
QA output created by 062
|
||||||
|
|
||||||
|
=== Testing snapshotting an image with zero clusters ===
|
||||||
|
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
|
||||||
|
wrote 262144/262144 bytes at offset 0
|
||||||
|
256 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
No errors were found on the image.
|
||||||
|
*** done
|
|
@ -57,10 +57,12 @@
|
||||||
048 img auto quick
|
048 img auto quick
|
||||||
049 rw auto
|
049 rw auto
|
||||||
050 rw auto backing quick
|
050 rw auto backing quick
|
||||||
#051 rw auto
|
051 rw auto
|
||||||
052 rw auto backing
|
052 rw auto backing
|
||||||
053 rw auto
|
053 rw auto
|
||||||
054 rw auto
|
054 rw auto
|
||||||
055 rw auto
|
055 rw auto
|
||||||
056 rw auto backing
|
056 rw auto backing
|
||||||
059 rw auto
|
059 rw auto
|
||||||
|
060 rw auto
|
||||||
|
062 rw auto
|
||||||
|
|
|
@ -275,6 +275,8 @@ int set_option_parameter(QEMUOptionParameter *list, const char *name,
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
list->assigned = true;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -306,6 +308,8 @@ int set_option_parameter_int(QEMUOptionParameter *list, const char *name,
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
list->assigned = true;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -397,6 +401,7 @@ QEMUOptionParameter *parse_option_parameters(const char *param,
|
||||||
char value[256];
|
char value[256];
|
||||||
char *param_delim, *value_delim;
|
char *param_delim, *value_delim;
|
||||||
char next_delim;
|
char next_delim;
|
||||||
|
int i;
|
||||||
|
|
||||||
if (list == NULL) {
|
if (list == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -406,6 +411,10 @@ QEMUOptionParameter *parse_option_parameters(const char *param,
|
||||||
dest = allocated = append_option_parameters(NULL, list);
|
dest = allocated = append_option_parameters(NULL, list);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (i = 0; dest[i].name; i++) {
|
||||||
|
dest[i].assigned = false;
|
||||||
|
}
|
||||||
|
|
||||||
while (*param) {
|
while (*param) {
|
||||||
|
|
||||||
// Find parameter name and value in the string
|
// Find parameter name and value in the string
|
||||||
|
|
Loading…
Reference in New Issue