#include "block_int.h"
#include "module.h"
-#if defined(HAVE_UUID_H)
+#if defined(CONFIG_UUID)
#include <uuid/uuid.h>
#else
/* TODO: move uuid emulation to some central place in QEMU. */
#define MiB (KiB * KiB)
#define SECTOR_SIZE 512
+#define DEFAULT_CLUSTER_SIZE (1 * MiB)
#if defined(CONFIG_VDI_DEBUG)
#define logout(fmt, ...) \
*/
#define VDI_TEXT "<<< QEMU VM Virtual Disk Image >>>\n"
-/* Unallocated blocks use this index (no need to convert endianess). */
+/* Unallocated blocks use this index (no need to convert endianness). */
#define VDI_UNALLOCATED UINT32_MAX
-#if !defined(HAVE_UUID_H)
+#if !defined(CONFIG_UUID)
void uuid_generate(uuid_t out)
{
- memset(out, 0, sizeof(out));
+ memset(out, 0, sizeof(uuid_t));
}
int uuid_is_null(const uuid_t uu)
{
uuid_t null_uuid = { 0 };
- return memcmp(uu, null_uuid, sizeof(uu)) == 0;
+ return memcmp(uu, null_uuid, sizeof(uuid_t)) == 0;
}
void uuid_unparse(const uuid_t uu, char *out)
/* Buffer for new allocated block. */
void *block_buffer;
void *orig_buf;
+ bool is_write;
int header_modified;
BlockDriverAIOCB *hd_aiocb;
struct iovec hd_iov;
} VdiHeader;
typedef struct {
- BlockDriverState *hd;
/* The block map entries are little endian (even in memory). */
uint32_t *bmap;
/* Size of block (bytes). */
uint32_t block_sectors;
/* First sector of block map. */
uint32_t bmap_sector;
- /* VDI header (converted to host endianess). */
+ /* VDI header (converted to host endianness). */
VdiHeader header;
} BDRVVdiState;
}
#endif
-static int vdi_check(BlockDriverState *bs)
+static int vdi_check(BlockDriverState *bs, BdrvCheckResult *res)
{
/* TODO: additional checks possible. */
BDRVVdiState *s = (BDRVVdiState *)bs->opaque;
- int n_errors = 0;
uint32_t blocks_allocated = 0;
uint32_t block;
uint32_t *bmap;
} else {
fprintf(stderr, "ERROR: block index %" PRIu32
" also used by %" PRIu32 "\n", bmap[bmap_entry], bmap_entry);
+ res->corruptions++;
}
} else {
fprintf(stderr, "ERROR: block index %" PRIu32
" too large, is %" PRIu32 "\n", block, bmap_entry);
- n_errors++;
+ res->corruptions++;
}
}
}
fprintf(stderr, "ERROR: allocated blocks mismatch, is %" PRIu32
", should be %" PRIu32 "\n",
blocks_allocated, s->header.blocks_allocated);
- n_errors++;
+ res->corruptions++;
}
qemu_free(bmap);
- return n_errors;
+ return 0;
}
static int vdi_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
return result;
}
-static int vdi_open(BlockDriverState *bs, const char *filename, int flags)
+static int vdi_open(BlockDriverState *bs, int flags)
{
BDRVVdiState *s = bs->opaque;
VdiHeader header;
size_t bmap_size;
- int ret;
logout("\n");
- ret = bdrv_file_open(&s->hd, filename, flags);
- if (ret < 0) {
- return ret;
- }
-
- if (bdrv_read(s->hd, 0, (uint8_t *)&header, 1) < 0) {
+ if (bdrv_read(bs->file, 0, (uint8_t *)&header, 1) < 0) {
goto fail;
}
vdi_header_print(&header);
#endif
+ if (header.disk_size % SECTOR_SIZE != 0) {
+ /* 'VBoxManage convertfromraw' can create images with odd disk sizes.
+ We accept them but round the disk size to the next multiple of
+ SECTOR_SIZE. */
+ logout("odd disk size %" PRIu64 " B, round up\n", header.disk_size);
+ header.disk_size += SECTOR_SIZE - 1;
+ header.disk_size &= ~(SECTOR_SIZE - 1);
+ }
+
if (header.version != VDI_VERSION_1_1) {
logout("unsupported version %u.%u\n",
header.version >> 16, header.version & 0xffff);
} else if (header.block_size != 1 * MiB) {
logout("unsupported block size %u B\n", header.block_size);
goto fail;
- } else if (header.disk_size !=
+ } else if (header.disk_size >
(uint64_t)header.blocks_in_image * header.block_size) {
- logout("unexpected block number %u B\n", header.blocks_in_image);
+ logout("unsupported disk size %" PRIu64 " B\n", header.disk_size);
goto fail;
} else if (!uuid_is_null(header.uuid_link)) {
logout("link uuid != 0, unsupported\n");
s->header = header;
bmap_size = header.blocks_in_image * sizeof(uint32_t);
- s->bmap = qemu_malloc(bmap_size);
- if (bdrv_read(s->hd, s->bmap_sector,
- (uint8_t *)s->bmap, bmap_size / SECTOR_SIZE) < 0) {
+ bmap_size = (bmap_size + SECTOR_SIZE - 1) / SECTOR_SIZE;
+ if (bmap_size > 0) {
+ s->bmap = qemu_malloc(bmap_size * SECTOR_SIZE);
+ }
+ if (bdrv_read(bs->file, s->bmap_sector, (uint8_t *)s->bmap, bmap_size) < 0) {
goto fail_free_bmap;
}
qemu_free(s->bmap);
fail:
- bdrv_delete(s->hd);
return -1;
}
static void vdi_aio_cancel(BlockDriverAIOCB *blockacb)
{
/* TODO: This code is untested. How can I get it executed? */
- VdiAIOCB *acb = (VdiAIOCB *)blockacb;
+ VdiAIOCB *acb = container_of(blockacb, VdiAIOCB, common);
logout("\n");
if (acb->hd_aiocb) {
bdrv_aio_cancel(acb->hd_aiocb);
acb->hd_aiocb = NULL;
acb->sector_num = sector_num;
acb->qiov = qiov;
+ acb->is_write = is_write;
+
if (qiov->niov > 1) {
acb->buf = qemu_blockalign(bs, qiov->size);
acb->orig_buf = acb->buf;
}
static void vdi_aio_read_cb(void *opaque, int ret);
+static void vdi_aio_write_cb(void *opaque, int ret);
-static void vdi_aio_read_bh(void *opaque)
+static void vdi_aio_rw_bh(void *opaque)
{
VdiAIOCB *acb = opaque;
logout("\n");
qemu_bh_delete(acb->bh);
acb->bh = NULL;
- vdi_aio_read_cb(opaque, 0);
+
+ if (acb->is_write) {
+ vdi_aio_write_cb(opaque, 0);
+ } else {
+ vdi_aio_read_cb(opaque, 0);
+ }
}
static void vdi_aio_read_cb(void *opaque, int ret)
if (bmap_entry == VDI_UNALLOCATED) {
/* Block not allocated, return zeros, no need to wait. */
memset(acb->buf, 0, n_sectors * SECTOR_SIZE);
- ret = vdi_schedule_bh(vdi_aio_read_bh, acb);
+ ret = vdi_schedule_bh(vdi_aio_rw_bh, acb);
if (ret < 0) {
goto done;
}
acb->hd_iov.iov_base = (void *)acb->buf;
acb->hd_iov.iov_len = n_sectors * SECTOR_SIZE;
qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1);
- acb->hd_aiocb = bdrv_aio_readv(s->hd, offset, &acb->hd_qiov,
+ acb->hd_aiocb = bdrv_aio_readv(bs->file, offset, &acb->hd_qiov,
n_sectors, vdi_aio_read_cb, acb);
if (acb->hd_aiocb == NULL) {
+ ret = -EIO;
goto done;
}
}
BlockDriverCompletionFunc *cb, void *opaque)
{
VdiAIOCB *acb;
+ int ret;
+
logout("\n");
acb = vdi_aio_setup(bs, sector_num, qiov, nb_sectors, cb, opaque, 0);
if (!acb) {
return NULL;
}
- vdi_aio_read_cb(acb, 0);
+
+ ret = vdi_schedule_bh(vdi_aio_rw_bh, acb);
+ if (ret < 0) {
+ if (acb->qiov->niov > 1) {
+ qemu_vfree(acb->orig_buf);
+ }
+ qemu_aio_release(acb);
+ return NULL;
+ }
+
return &acb->common;
}
acb->hd_iov.iov_base = acb->block_buffer;
acb->hd_iov.iov_len = SECTOR_SIZE;
qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1);
- acb->hd_aiocb = bdrv_aio_writev(s->hd, 0, &acb->hd_qiov, 1,
+ acb->hd_aiocb = bdrv_aio_writev(bs->file, 0, &acb->hd_qiov, 1,
vdi_aio_write_cb, acb);
if (acb->hd_aiocb == NULL) {
+ ret = -EIO;
goto done;
}
return;
n_sectors = bmap_last - bmap_first + 1;
offset = s->bmap_sector + bmap_first;
acb->bmap_first = VDI_UNALLOCATED;
- acb->hd_iov.iov_base = (uint8_t *)&s->bmap[0] +
- bmap_first * SECTOR_SIZE;
+ acb->hd_iov.iov_base = (void *)((uint8_t *)&s->bmap[0] +
+ bmap_first * SECTOR_SIZE);
acb->hd_iov.iov_len = n_sectors * SECTOR_SIZE;
qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1);
logout("will write %u block map sectors starting from entry %u\n",
n_sectors, bmap_first);
- acb->hd_aiocb = bdrv_aio_writev(s->hd, offset, &acb->hd_qiov,
+ acb->hd_aiocb = bdrv_aio_writev(bs->file, offset, &acb->hd_qiov,
n_sectors, vdi_aio_write_cb, acb);
if (acb->hd_aiocb == NULL) {
+ ret = -EIO;
goto done;
}
return;
acb->bmap_last = block_index;
memcpy(block + sector_in_block * SECTOR_SIZE,
acb->buf, n_sectors * SECTOR_SIZE);
- acb->hd_iov.iov_base = block;
+ acb->hd_iov.iov_base = (void *)block;
acb->hd_iov.iov_len = s->block_size;
qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1);
- acb->hd_aiocb = bdrv_aio_writev(s->hd, offset,
+ acb->hd_aiocb = bdrv_aio_writev(bs->file, offset,
&acb->hd_qiov, s->block_sectors,
vdi_aio_write_cb, acb);
if (acb->hd_aiocb == NULL) {
+ ret = -EIO;
goto done;
}
} else {
uint64_t offset = s->header.offset_data / SECTOR_SIZE +
(uint64_t)bmap_entry * s->block_sectors +
sector_in_block;
- acb->hd_iov.iov_base = acb->buf;
+ acb->hd_iov.iov_base = (void *)acb->buf;
acb->hd_iov.iov_len = n_sectors * SECTOR_SIZE;
qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1);
- acb->hd_aiocb = bdrv_aio_writev(s->hd, offset, &acb->hd_qiov,
+ acb->hd_aiocb = bdrv_aio_writev(bs->file, offset, &acb->hd_qiov,
n_sectors, vdi_aio_write_cb, acb);
if (acb->hd_aiocb == NULL) {
+ ret = -EIO;
goto done;
}
}
BlockDriverCompletionFunc *cb, void *opaque)
{
VdiAIOCB *acb;
+ int ret;
+
logout("\n");
acb = vdi_aio_setup(bs, sector_num, qiov, nb_sectors, cb, opaque, 1);
if (!acb) {
return NULL;
}
- vdi_aio_write_cb(acb, 0);
+
+ ret = vdi_schedule_bh(vdi_aio_rw_bh, acb);
+ if (ret < 0) {
+ if (acb->qiov->niov > 1) {
+ qemu_vfree(acb->orig_buf);
+ }
+ qemu_aio_release(acb);
+ return NULL;
+ }
+
return &acb->common;
}
int result = 0;
uint64_t bytes = 0;
uint32_t blocks;
- size_t block_size = 1 * MiB;
+ size_t block_size = DEFAULT_CLUSTER_SIZE;
uint32_t image_type = VDI_TYPE_DYNAMIC;
VdiHeader header;
size_t i;
#endif
#if defined(CONFIG_VDI_STATIC_IMAGE)
} else if (!strcmp(options->name, BLOCK_OPT_STATIC)) {
- image_type = VDI_TYPE_STATIC;
+ if (options->value.n) {
+ image_type = VDI_TYPE_STATIC;
+ }
#endif
}
options++;
return -errno;
}
- blocks = bytes / block_size;
+ /* We need enough blocks to store the given disk size,
+ so always round up. */
+ blocks = (bytes + block_size - 1) / block_size;
+
bmap_size = blocks * sizeof(uint32_t);
bmap_size = ((bmap_size + SECTOR_SIZE - 1) & ~(SECTOR_SIZE -1));
header.disk_size = bytes;
header.block_size = block_size;
header.blocks_in_image = blocks;
+ if (image_type == VDI_TYPE_STATIC) {
+ header.blocks_allocated = blocks;
+ }
uuid_generate(header.uuid_image);
uuid_generate(header.uuid_last_snap);
/* There is no need to set header.uuid_link or header.uuid_parent here. */
result = -errno;
}
- bmap = (uint32_t *)qemu_mallocz(bmap_size);
+ bmap = NULL;
+ if (bmap_size > 0) {
+ bmap = (uint32_t *)qemu_mallocz(bmap_size);
+ }
for (i = 0; i < blocks; i++) {
if (image_type == VDI_TYPE_STATIC) {
bmap[i] = i;
static void vdi_close(BlockDriverState *bs)
{
- BDRVVdiState *s = bs->opaque;
- logout("\n");
- bdrv_delete(s->hd);
}
-static void vdi_flush(BlockDriverState *bs)
+static int vdi_flush(BlockDriverState *bs)
{
- BDRVVdiState *s = bs->opaque;
logout("\n");
- bdrv_flush(s->hd);
+ return bdrv_flush(bs->file);
}
{
.name = BLOCK_OPT_CLUSTER_SIZE,
.type = OPT_SIZE,
- .help = "VDI cluster (block) size"
+ .help = "VDI cluster (block) size",
+ .value = { .n = DEFAULT_CLUSTER_SIZE },
},
#endif
#if defined(CONFIG_VDI_STATIC_IMAGE)