block/vhdx: add check for truncated image files
qemu is currently not able to detect truncated vhdx image files. Add a basic check if all allocated blocks are reachable at open and report all errors during bdrv_co_check. Signed-off-by: Peter Lieven <pl@kamp.de> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
This commit is contained in:
parent
22dbfdecc3
commit
6caaad46de
120
block/vhdx.c
120
block/vhdx.c
@ -24,6 +24,7 @@
|
|||||||
#include "qemu/option.h"
|
#include "qemu/option.h"
|
||||||
#include "qemu/crc32c.h"
|
#include "qemu/crc32c.h"
|
||||||
#include "qemu/bswap.h"
|
#include "qemu/bswap.h"
|
||||||
|
#include "qemu/error-report.h"
|
||||||
#include "vhdx.h"
|
#include "vhdx.h"
|
||||||
#include "migration/blocker.h"
|
#include "migration/blocker.h"
|
||||||
#include "qemu/uuid.h"
|
#include "qemu/uuid.h"
|
||||||
@ -235,6 +236,9 @@ static int vhdx_region_check(BDRVVHDXState *s, uint64_t start, uint64_t length)
|
|||||||
end = start + length;
|
end = start + length;
|
||||||
QLIST_FOREACH(r, &s->regions, entries) {
|
QLIST_FOREACH(r, &s->regions, entries) {
|
||||||
if (!((start >= r->end) || (end <= r->start))) {
|
if (!((start >= r->end) || (end <= r->start))) {
|
||||||
|
error_report("VHDX region %" PRIu64 "-%" PRIu64 " overlaps with "
|
||||||
|
"region %" PRIu64 "-%." PRIu64, start, end, r->start,
|
||||||
|
r->end);
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
@ -877,6 +881,95 @@ static void vhdx_calc_bat_entries(BDRVVHDXState *s)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int vhdx_check_bat_entries(BlockDriverState *bs, int *errcnt)
|
||||||
|
{
|
||||||
|
BDRVVHDXState *s = bs->opaque;
|
||||||
|
int64_t image_file_size = bdrv_getlength(bs->file->bs);
|
||||||
|
uint64_t payblocks = s->chunk_ratio;
|
||||||
|
uint64_t i;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (image_file_size < 0) {
|
||||||
|
error_report("Could not determinate VHDX image file size.");
|
||||||
|
return image_file_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < s->bat_entries; i++) {
|
||||||
|
if ((s->bat[i] & VHDX_BAT_STATE_BIT_MASK) ==
|
||||||
|
PAYLOAD_BLOCK_FULLY_PRESENT) {
|
||||||
|
uint64_t offset = s->bat[i] & VHDX_BAT_FILE_OFF_MASK;
|
||||||
|
/*
|
||||||
|
* Allow that the last block exists only partially. The VHDX spec
|
||||||
|
* states that the image file can only grow in blocksize increments,
|
||||||
|
* but QEMU created images with partial last blocks in the past.
|
||||||
|
*/
|
||||||
|
uint32_t block_length = MIN(s->block_size,
|
||||||
|
bs->total_sectors * BDRV_SECTOR_SIZE - i * s->block_size);
|
||||||
|
/*
|
||||||
|
* Check for BAT entry overflow.
|
||||||
|
*/
|
||||||
|
if (offset > INT64_MAX - s->block_size) {
|
||||||
|
error_report("VHDX BAT entry %" PRIu64 " offset overflow.", i);
|
||||||
|
ret = -EINVAL;
|
||||||
|
if (!errcnt) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
(*errcnt)++;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Check if fully allocated BAT entries do not reside after
|
||||||
|
* end of the image file.
|
||||||
|
*/
|
||||||
|
if (offset >= image_file_size) {
|
||||||
|
error_report("VHDX BAT entry %" PRIu64 " start offset %" PRIu64
|
||||||
|
" points after end of file (%" PRIi64 "). Image"
|
||||||
|
" has probably been truncated.",
|
||||||
|
i, offset, image_file_size);
|
||||||
|
ret = -EINVAL;
|
||||||
|
if (!errcnt) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
(*errcnt)++;
|
||||||
|
} else if (offset + block_length > image_file_size) {
|
||||||
|
error_report("VHDX BAT entry %" PRIu64 " end offset %" PRIu64
|
||||||
|
" points after end of file (%" PRIi64 "). Image"
|
||||||
|
" has probably been truncated.",
|
||||||
|
i, offset + block_length - 1, image_file_size);
|
||||||
|
ret = -EINVAL;
|
||||||
|
if (!errcnt) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
(*errcnt)++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* verify populated BAT field file offsets against
|
||||||
|
* region table and log entries
|
||||||
|
*/
|
||||||
|
if (payblocks--) {
|
||||||
|
/* payload bat entries */
|
||||||
|
int ret2;
|
||||||
|
ret2 = vhdx_region_check(s, offset, s->block_size);
|
||||||
|
if (ret2 < 0) {
|
||||||
|
ret = -EINVAL;
|
||||||
|
if (!errcnt) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
(*errcnt)++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
payblocks = s->chunk_ratio;
|
||||||
|
/*
|
||||||
|
* Once differencing files are supported, verify sector bitmap
|
||||||
|
* blocks here
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static void vhdx_close(BlockDriverState *bs)
|
static void vhdx_close(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
BDRVVHDXState *s = bs->opaque;
|
BDRVVHDXState *s = bs->opaque;
|
||||||
@ -981,25 +1074,15 @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t payblocks = s->chunk_ratio;
|
/* endian convert populated BAT field entires */
|
||||||
/* endian convert, and verify populated BAT field file offsets against
|
|
||||||
* region table and log entries */
|
|
||||||
for (i = 0; i < s->bat_entries; i++) {
|
for (i = 0; i < s->bat_entries; i++) {
|
||||||
s->bat[i] = le64_to_cpu(s->bat[i]);
|
s->bat[i] = le64_to_cpu(s->bat[i]);
|
||||||
if (payblocks--) {
|
}
|
||||||
/* payload bat entries */
|
|
||||||
if ((s->bat[i] & VHDX_BAT_STATE_BIT_MASK) ==
|
if (!(flags & BDRV_O_CHECK)) {
|
||||||
PAYLOAD_BLOCK_FULLY_PRESENT) {
|
ret = vhdx_check_bat_entries(bs, NULL);
|
||||||
ret = vhdx_region_check(s, s->bat[i] & VHDX_BAT_FILE_OFF_MASK,
|
if (ret < 0) {
|
||||||
s->block_size);
|
goto fail;
|
||||||
if (ret < 0) {
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
payblocks = s->chunk_ratio;
|
|
||||||
/* Once differencing files are supported, verify sector bitmap
|
|
||||||
* blocks here */
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2072,6 +2155,9 @@ static int coroutine_fn vhdx_co_check(BlockDriverState *bs,
|
|||||||
if (s->log_replayed_on_open) {
|
if (s->log_replayed_on_open) {
|
||||||
result->corruptions_fixed++;
|
result->corruptions_fixed++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vhdx_check_bat_entries(bs, &result->corruptions);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user