summaryrefslogtreecommitdiff
path: root/block
diff options
context:
space:
mode:
authorAnthony Liguori <aliguori@us.ibm.com>2011-10-31 11:09:00 -0500
committerAnthony Liguori <aliguori@us.ibm.com>2011-10-31 11:09:00 -0500
commit8494a397b68124f9898e7912173d460ee99ba3e2 (patch)
tree2c3ff12f56cb2587d00bab448a226d3cf0042d64 /block
parentd439b79d730bf219e47c14ab94f1546bcc5045ab (diff)
parent336a6915bc7089fb20fea4ba99972ad9a97c5f52 (diff)
downloadqemu-8494a397b68124f9898e7912173d460ee99ba3e2.zip
Merge remote-tracking branch 'kwolf/for-anthony' into staging
Conflicts: block/vmdk.c
Diffstat (limited to 'block')
-rw-r--r--block/iscsi.c591
-rw-r--r--block/qcow.c30
-rw-r--r--block/qcow2.c2
-rw-r--r--block/vdi.c23
-rw-r--r--block/vmdk.c28
5 files changed, 647 insertions, 27 deletions
diff --git a/block/iscsi.c b/block/iscsi.c
new file mode 100644
index 0000000000..938c568071
--- /dev/null
+++ b/block/iscsi.c
@@ -0,0 +1,591 @@
+/*
+ * QEMU Block driver for iSCSI images
+ *
+ * Copyright (c) 2010-2011 Ronnie Sahlberg <ronniesahlberg@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "config-host.h"
+
+#include <poll.h>
+#include "qemu-common.h"
+#include "qemu-error.h"
+#include "block_int.h"
+#include "trace.h"
+
+#include <iscsi/iscsi.h>
+#include <iscsi/scsi-lowlevel.h>
+
+
+typedef struct IscsiLun {
+ struct iscsi_context *iscsi;
+ int lun;
+ int block_size;
+ unsigned long num_blocks;
+} IscsiLun;
+
+typedef struct IscsiAIOCB {
+ BlockDriverAIOCB common;
+ QEMUIOVector *qiov;
+ QEMUBH *bh;
+ IscsiLun *iscsilun;
+ struct scsi_task *task;
+ uint8_t *buf;
+ int status;
+ int canceled;
+ size_t read_size;
+ size_t read_offset;
+} IscsiAIOCB;
+
+struct IscsiTask {
+ IscsiLun *iscsilun;
+ BlockDriverState *bs;
+ int status;
+ int complete;
+};
+
+static void
+iscsi_abort_task_cb(struct iscsi_context *iscsi, int status, void *command_data,
+ void *private_data)
+{
+}
+
+static void
+iscsi_aio_cancel(BlockDriverAIOCB *blockacb)
+{
+ IscsiAIOCB *acb = (IscsiAIOCB *)blockacb;
+ IscsiLun *iscsilun = acb->iscsilun;
+
+ acb->common.cb(acb->common.opaque, -ECANCELED);
+ acb->canceled = 1;
+
+ /* send a task mgmt call to the target to cancel the task on the target */
+ iscsi_task_mgmt_abort_task_async(iscsilun->iscsi, acb->task,
+ iscsi_abort_task_cb, NULL);
+
+ /* then also cancel the task locally in libiscsi */
+ iscsi_scsi_task_cancel(iscsilun->iscsi, acb->task);
+}
+
+static AIOPool iscsi_aio_pool = {
+ .aiocb_size = sizeof(IscsiAIOCB),
+ .cancel = iscsi_aio_cancel,
+};
+
+
+static void iscsi_process_read(void *arg);
+static void iscsi_process_write(void *arg);
+
+static int iscsi_process_flush(void *arg)
+{
+ IscsiLun *iscsilun = arg;
+
+ return iscsi_queue_length(iscsilun->iscsi) > 0;
+}
+
+static void
+iscsi_set_events(IscsiLun *iscsilun)
+{
+ struct iscsi_context *iscsi = iscsilun->iscsi;
+
+ qemu_aio_set_fd_handler(iscsi_get_fd(iscsi), iscsi_process_read,
+ (iscsi_which_events(iscsi) & POLLOUT)
+ ? iscsi_process_write : NULL,
+ iscsi_process_flush, NULL, iscsilun);
+}
+
+static void
+iscsi_process_read(void *arg)
+{
+ IscsiLun *iscsilun = arg;
+ struct iscsi_context *iscsi = iscsilun->iscsi;
+
+ iscsi_service(iscsi, POLLIN);
+ iscsi_set_events(iscsilun);
+}
+
+static void
+iscsi_process_write(void *arg)
+{
+ IscsiLun *iscsilun = arg;
+ struct iscsi_context *iscsi = iscsilun->iscsi;
+
+ iscsi_service(iscsi, POLLOUT);
+ iscsi_set_events(iscsilun);
+}
+
+
+static int
+iscsi_schedule_bh(QEMUBHFunc *cb, IscsiAIOCB *acb)
+{
+ acb->bh = qemu_bh_new(cb, acb);
+ if (!acb->bh) {
+ error_report("oom: could not create iscsi bh");
+ return -EIO;
+ }
+
+ qemu_bh_schedule(acb->bh);
+ return 0;
+}
+
+static void
+iscsi_readv_writev_bh_cb(void *p)
+{
+ IscsiAIOCB *acb = p;
+
+ qemu_bh_delete(acb->bh);
+
+ if (acb->canceled == 0) {
+ acb->common.cb(acb->common.opaque, acb->status);
+ }
+
+ qemu_aio_release(acb);
+}
+
+
+static void
+iscsi_aio_write10_cb(struct iscsi_context *iscsi, int status,
+ void *command_data, void *opaque)
+{
+ IscsiAIOCB *acb = opaque;
+
+ trace_iscsi_aio_write10_cb(iscsi, status, acb, acb->canceled);
+
+ g_free(acb->buf);
+
+ if (acb->canceled != 0) {
+ qemu_aio_release(acb);
+ scsi_free_scsi_task(acb->task);
+ acb->task = NULL;
+ return;
+ }
+
+ acb->status = 0;
+ if (status < 0) {
+ error_report("Failed to write10 data to iSCSI lun. %s",
+ iscsi_get_error(iscsi));
+ acb->status = -EIO;
+ }
+
+ iscsi_schedule_bh(iscsi_readv_writev_bh_cb, acb);
+ scsi_free_scsi_task(acb->task);
+ acb->task = NULL;
+}
+
+static int64_t sector_qemu2lun(int64_t sector, IscsiLun *iscsilun)
+{
+ return sector * BDRV_SECTOR_SIZE / iscsilun->block_size;
+}
+
+static BlockDriverAIOCB *
+iscsi_aio_writev(BlockDriverState *bs, int64_t sector_num,
+ QEMUIOVector *qiov, int nb_sectors,
+ BlockDriverCompletionFunc *cb,
+ void *opaque)
+{
+ IscsiLun *iscsilun = bs->opaque;
+ struct iscsi_context *iscsi = iscsilun->iscsi;
+ IscsiAIOCB *acb;
+ size_t size;
+ int fua = 0;
+
+ /* set FUA on writes when cache mode is write through */
+ if (!(bs->open_flags & BDRV_O_CACHE_WB)) {
+ fua = 1;
+ }
+
+ acb = qemu_aio_get(&iscsi_aio_pool, bs, cb, opaque);
+ trace_iscsi_aio_writev(iscsi, sector_num, nb_sectors, opaque, acb);
+
+ acb->iscsilun = iscsilun;
+ acb->qiov = qiov;
+
+ acb->canceled = 0;
+
+ /* XXX we should pass the iovec to write10 to avoid the extra copy */
+ /* this will allow us to get rid of 'buf' completely */
+ size = nb_sectors * BDRV_SECTOR_SIZE;
+ acb->buf = g_malloc(size);
+ qemu_iovec_to_buffer(acb->qiov, acb->buf);
+ acb->task = iscsi_write10_task(iscsi, iscsilun->lun, acb->buf, size,
+ sector_qemu2lun(sector_num, iscsilun),
+ fua, 0, iscsilun->block_size,
+ iscsi_aio_write10_cb, acb);
+ if (acb->task == NULL) {
+ error_report("iSCSI: Failed to send write10 command. %s",
+ iscsi_get_error(iscsi));
+ g_free(acb->buf);
+ qemu_aio_release(acb);
+ return NULL;
+ }
+
+ iscsi_set_events(iscsilun);
+
+ return &acb->common;
+}
+
+static void
+iscsi_aio_read10_cb(struct iscsi_context *iscsi, int status,
+ void *command_data, void *opaque)
+{
+ IscsiAIOCB *acb = opaque;
+
+ trace_iscsi_aio_read10_cb(iscsi, status, acb, acb->canceled);
+
+ if (acb->canceled != 0) {
+ qemu_aio_release(acb);
+ scsi_free_scsi_task(acb->task);
+ acb->task = NULL;
+ return;
+ }
+
+ acb->status = 0;
+ if (status != 0) {
+ error_report("Failed to read10 data from iSCSI lun. %s",
+ iscsi_get_error(iscsi));
+ acb->status = -EIO;
+ }
+
+ iscsi_schedule_bh(iscsi_readv_writev_bh_cb, acb);
+ scsi_free_scsi_task(acb->task);
+ acb->task = NULL;
+}
+
+static BlockDriverAIOCB *
+iscsi_aio_readv(BlockDriverState *bs, int64_t sector_num,
+ QEMUIOVector *qiov, int nb_sectors,
+ BlockDriverCompletionFunc *cb,
+ void *opaque)
+{
+ IscsiLun *iscsilun = bs->opaque;
+ struct iscsi_context *iscsi = iscsilun->iscsi;
+ IscsiAIOCB *acb;
+ size_t qemu_read_size, lun_read_size;
+ int i;
+
+ qemu_read_size = BDRV_SECTOR_SIZE * (size_t)nb_sectors;
+
+ acb = qemu_aio_get(&iscsi_aio_pool, bs, cb, opaque);
+ trace_iscsi_aio_readv(iscsi, sector_num, nb_sectors, opaque, acb);
+
+ acb->iscsilun = iscsilun;
+ acb->qiov = qiov;
+
+ acb->canceled = 0;
+ acb->read_size = qemu_read_size;
+ acb->buf = NULL;
+
+ /* If LUN blocksize is bigger than BDRV_BLOCK_SIZE a read from QEMU
+ * may be misaligned to the LUN, so we may need to read some extra
+ * data.
+ */
+ acb->read_offset = 0;
+ if (iscsilun->block_size > BDRV_SECTOR_SIZE) {
+ uint64_t bdrv_offset = BDRV_SECTOR_SIZE * sector_num;
+
+ acb->read_offset = bdrv_offset % iscsilun->block_size;
+ }
+
+ lun_read_size = (qemu_read_size + iscsilun->block_size
+ + acb->read_offset - 1)
+ / iscsilun->block_size * iscsilun->block_size;
+ acb->task = iscsi_read10_task(iscsi, iscsilun->lun,
+ sector_qemu2lun(sector_num, iscsilun),
+ lun_read_size, iscsilun->block_size,
+ iscsi_aio_read10_cb, acb);
+ if (acb->task == NULL) {
+ error_report("iSCSI: Failed to send read10 command. %s",
+ iscsi_get_error(iscsi));
+ qemu_aio_release(acb);
+ return NULL;
+ }
+
+ for (i = 0; i < acb->qiov->niov; i++) {
+ scsi_task_add_data_in_buffer(acb->task,
+ acb->qiov->iov[i].iov_len,
+ acb->qiov->iov[i].iov_base);
+ }
+
+ iscsi_set_events(iscsilun);
+
+ return &acb->common;
+}
+
+
+static void
+iscsi_synccache10_cb(struct iscsi_context *iscsi, int status,
+ void *command_data, void *opaque)
+{
+ IscsiAIOCB *acb = opaque;
+
+ if (acb->canceled != 0) {
+ qemu_aio_release(acb);
+ scsi_free_scsi_task(acb->task);
+ acb->task = NULL;
+ return;
+ }
+
+ acb->status = 0;
+ if (status < 0) {
+ error_report("Failed to sync10 data on iSCSI lun. %s",
+ iscsi_get_error(iscsi));
+ acb->status = -EIO;
+ }
+
+ iscsi_schedule_bh(iscsi_readv_writev_bh_cb, acb);
+ scsi_free_scsi_task(acb->task);
+ acb->task = NULL;
+}
+
+static BlockDriverAIOCB *
+iscsi_aio_flush(BlockDriverState *bs,
+ BlockDriverCompletionFunc *cb, void *opaque)
+{
+ IscsiLun *iscsilun = bs->opaque;
+ struct iscsi_context *iscsi = iscsilun->iscsi;
+ IscsiAIOCB *acb;
+
+ acb = qemu_aio_get(&iscsi_aio_pool, bs, cb, opaque);
+
+ acb->iscsilun = iscsilun;
+ acb->canceled = 0;
+
+ acb->task = iscsi_synchronizecache10_task(iscsi, iscsilun->lun,
+ 0, 0, 0, 0,
+ iscsi_synccache10_cb,
+ acb);
+ if (acb->task == NULL) {
+ error_report("iSCSI: Failed to send synchronizecache10 command. %s",
+ iscsi_get_error(iscsi));
+ qemu_aio_release(acb);
+ return NULL;
+ }
+
+ iscsi_set_events(iscsilun);
+
+ return &acb->common;
+}
+
+static int64_t
+iscsi_getlength(BlockDriverState *bs)
+{
+ IscsiLun *iscsilun = bs->opaque;
+ int64_t len;
+
+ len = iscsilun->num_blocks;
+ len *= iscsilun->block_size;
+
+ return len;
+}
+
+static void
+iscsi_readcapacity10_cb(struct iscsi_context *iscsi, int status,
+ void *command_data, void *opaque)
+{
+ struct IscsiTask *itask = opaque;
+ struct scsi_readcapacity10 *rc10;
+ struct scsi_task *task = command_data;
+
+ if (status != 0) {
+ error_report("iSCSI: Failed to read capacity of iSCSI lun. %s",
+ iscsi_get_error(iscsi));
+ itask->status = 1;
+ itask->complete = 1;
+ scsi_free_scsi_task(task);
+ return;
+ }
+
+ rc10 = scsi_datain_unmarshall(task);
+ if (rc10 == NULL) {
+ error_report("iSCSI: Failed to unmarshall readcapacity10 data.");
+ itask->status = 1;
+ itask->complete = 1;
+ scsi_free_scsi_task(task);
+ return;
+ }
+
+ itask->iscsilun->block_size = rc10->block_size;
+ itask->iscsilun->num_blocks = rc10->lba;
+ itask->bs->total_sectors = (uint64_t)rc10->lba *
+ rc10->block_size / BDRV_SECTOR_SIZE ;
+
+ itask->status = 0;
+ itask->complete = 1;
+ scsi_free_scsi_task(task);
+}
+
+
+static void
+iscsi_connect_cb(struct iscsi_context *iscsi, int status, void *command_data,
+ void *opaque)
+{
+ struct IscsiTask *itask = opaque;
+ struct scsi_task *task;
+
+ if (status != 0) {
+ itask->status = 1;
+ itask->complete = 1;
+ return;
+ }
+
+ task = iscsi_readcapacity10_task(iscsi, itask->iscsilun->lun, 0, 0,
+ iscsi_readcapacity10_cb, opaque);
+ if (task == NULL) {
+ error_report("iSCSI: failed to send readcapacity command.");
+ itask->status = 1;
+ itask->complete = 1;
+ return;
+ }
+}
+
+/*
+ * We support iscsi url's on the form
+ * iscsi://[<username>%<password>@]<host>[:<port>]/<targetname>/<lun>
+ */
+static int iscsi_open(BlockDriverState *bs, const char *filename, int flags)
+{
+ IscsiLun *iscsilun = bs->opaque;
+ struct iscsi_context *iscsi = NULL;
+ struct iscsi_url *iscsi_url = NULL;
+ struct IscsiTask task;
+ int ret;
+
+ if ((BDRV_SECTOR_SIZE % 512) != 0) {
+ error_report("iSCSI: Invalid BDRV_SECTOR_SIZE. "
+ "BDRV_SECTOR_SIZE(%lld) is not a multiple "
+ "of 512", BDRV_SECTOR_SIZE);
+ return -EINVAL;
+ }
+
+ memset(iscsilun, 0, sizeof(IscsiLun));
+
+ /* Should really append the KVM name after the ':' here */
+ iscsi = iscsi_create_context("iqn.2008-11.org.linux-kvm:");
+ if (iscsi == NULL) {
+ error_report("iSCSI: Failed to create iSCSI context.");
+ ret = -ENOMEM;
+ goto failed;
+ }
+
+ iscsi_url = iscsi_parse_full_url(iscsi, filename);
+ if (iscsi_url == NULL) {
+ error_report("Failed to parse URL : %s %s", filename,
+ iscsi_get_error(iscsi));
+ ret = -EINVAL;
+ goto failed;
+ }
+
+ if (iscsi_set_targetname(iscsi, iscsi_url->target)) {
+ error_report("iSCSI: Failed to set target name.");
+ ret = -EINVAL;
+ goto failed;
+ }
+
+ if (iscsi_url->user != NULL) {
+ ret = iscsi_set_initiator_username_pwd(iscsi, iscsi_url->user,
+ iscsi_url->passwd);
+ if (ret != 0) {
+ error_report("Failed to set initiator username and password");
+ ret = -EINVAL;
+ goto failed;
+ }
+ }
+ if (iscsi_set_session_type(iscsi, ISCSI_SESSION_NORMAL) != 0) {
+ error_report("iSCSI: Failed to set session type to normal.");
+ ret = -EINVAL;
+ goto failed;
+ }
+
+ iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_NONE_CRC32C);
+
+ task.iscsilun = iscsilun;
+ task.status = 0;
+ task.complete = 0;
+ task.bs = bs;
+
+ iscsilun->iscsi = iscsi;
+ iscsilun->lun = iscsi_url->lun;
+
+ if (iscsi_full_connect_async(iscsi, iscsi_url->portal, iscsi_url->lun,
+ iscsi_connect_cb, &task)
+ != 0) {
+ error_report("iSCSI: Failed to start async connect.");
+ ret = -EINVAL;
+ goto failed;
+ }
+
+ while (!task.complete) {
+ iscsi_set_events(iscsilun);
+ qemu_aio_wait();
+ }
+ if (task.status != 0) {
+ error_report("iSCSI: Failed to connect to LUN : %s",
+ iscsi_get_error(iscsi));
+ ret = -EINVAL;
+ goto failed;
+ }
+
+ if (iscsi_url != NULL) {
+ iscsi_destroy_url(iscsi_url);
+ }
+ return 0;
+
+failed:
+ if (iscsi_url != NULL) {
+ iscsi_destroy_url(iscsi_url);
+ }
+ if (iscsi != NULL) {
+ iscsi_destroy_context(iscsi);
+ }
+ memset(iscsilun, 0, sizeof(IscsiLun));
+ return ret;
+}
+
+static void iscsi_close(BlockDriverState *bs)
+{
+ IscsiLun *iscsilun = bs->opaque;
+ struct iscsi_context *iscsi = iscsilun->iscsi;
+
+ qemu_aio_set_fd_handler(iscsi_get_fd(iscsi), NULL, NULL, NULL, NULL, NULL);
+ iscsi_destroy_context(iscsi);
+ memset(iscsilun, 0, sizeof(IscsiLun));
+}
+
+static BlockDriver bdrv_iscsi = {
+ .format_name = "iscsi",
+ .protocol_name = "iscsi",
+
+ .instance_size = sizeof(IscsiLun),
+ .bdrv_file_open = iscsi_open,
+ .bdrv_close = iscsi_close,
+
+ .bdrv_getlength = iscsi_getlength,
+
+ .bdrv_aio_readv = iscsi_aio_readv,
+ .bdrv_aio_writev = iscsi_aio_writev,
+ .bdrv_aio_flush = iscsi_aio_flush,
+};
+
+static void iscsi_block_init(void)
+{
+ bdrv_register(&bdrv_iscsi);
+}
+
+block_init(iscsi_block_init);
diff --git a/block/qcow.c b/block/qcow.c
index ab36b2995c..35e21eb6b3 100644
--- a/block/qcow.c
+++ b/block/qcow.c
@@ -736,8 +736,6 @@ static int qcow_write_compressed(BlockDriverState *bs, int64_t sector_num,
return -EINVAL;
out_buf = g_malloc(s->cluster_size + (s->cluster_size / 1000) + 128);
- if (!out_buf)
- return -1;
/* best compression, small window, no zlib header */
memset(&strm, 0, sizeof(strm));
@@ -745,8 +743,8 @@ static int qcow_write_compressed(BlockDriverState *bs, int64_t sector_num,
Z_DEFLATED, -12,
9, Z_DEFAULT_STRATEGY);
if (ret != 0) {
- g_free(out_buf);
- return -1;
+ ret = -EINVAL;
+ goto fail;
}
strm.avail_in = s->cluster_size;
@@ -756,9 +754,9 @@ static int qcow_write_compressed(BlockDriverState *bs, int64_t sector_num,
ret = deflate(&strm, Z_FINISH);
if (ret != Z_STREAM_END && ret != Z_OK) {
- g_free(out_buf);
deflateEnd(&strm);
- return -1;
+ ret = -EINVAL;
+ goto fail;
}
out_len = strm.next_out - out_buf;
@@ -766,19 +764,29 @@ static int qcow_write_compressed(BlockDriverState *bs, int64_t sector_num,
if (ret != Z_STREAM_END || out_len >= s->cluster_size) {
/* could not compress: write normal cluster */
- bdrv_write(bs, sector_num, buf, s->cluster_sectors);
+ ret = bdrv_write(bs, sector_num, buf, s->cluster_sectors);
+ if (ret < 0) {
+ goto fail;
+ }
} else {
cluster_offset = get_cluster_offset(bs, sector_num << 9, 2,
out_len, 0, 0);
+ if (cluster_offset == 0) {
+ ret = -EIO;
+ goto fail;
+ }
+
cluster_offset &= s->cluster_offset_mask;
- if (bdrv_pwrite(bs->file, cluster_offset, out_buf, out_len) != out_len) {
- g_free(out_buf);
- return -1;
+ ret = bdrv_pwrite(bs->file, cluster_offset, out_buf, out_len);
+ if (ret < 0) {
+ goto fail;
}
}
+ ret = 0;
+fail:
g_free(out_buf);
- return 0;
+ return ret;
}
static coroutine_fn int qcow_co_flush(BlockDriverState *bs)
diff --git a/block/qcow2.c b/block/qcow2.c
index a181932b67..ef057d31e0 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -1113,11 +1113,13 @@ static int qcow2_co_flush(BlockDriverState *bs)
qemu_co_mutex_lock(&s->lock);
ret = qcow2_cache_flush(bs, s->l2_table_cache);
if (ret < 0) {
+ qemu_co_mutex_unlock(&s->lock);
return ret;
}
ret = qcow2_cache_flush(bs, s->refcount_block_cache);
if (ret < 0) {
+ qemu_co_mutex_unlock(&s->lock);
return ret;
}
qemu_co_mutex_unlock(&s->lock);
diff --git a/block/vdi.c b/block/vdi.c
index 883046d5a2..523a6409c5 100644
--- a/block/vdi.c
+++ b/block/vdi.c
@@ -114,8 +114,13 @@ void uuid_unparse(const uuid_t uu, char *out);
*/
#define VDI_TEXT "<<< QEMU VM Virtual Disk Image >>>\n"
-/* Unallocated blocks use this index (no need to convert endianness). */
-#define VDI_UNALLOCATED UINT32_MAX
+/* A never-allocated block; semantically arbitrary content. */
+#define VDI_UNALLOCATED 0xffffffffU
+
+/* A discarded (no longer allocated) block; semantically zero-filled. */
+#define VDI_DISCARDED 0xfffffffeU
+
+#define VDI_IS_ALLOCATED(X) ((X) < VDI_DISCARDED)
#if !defined(CONFIG_UUID)
void uuid_generate(uuid_t out)
@@ -307,10 +312,10 @@ static int vdi_check(BlockDriverState *bs, BdrvCheckResult *res)
/* Check block map and value of blocks_allocated. */
for (block = 0; block < s->header.blocks_in_image; block++) {
uint32_t bmap_entry = le32_to_cpu(s->bmap[block]);
- if (bmap_entry != VDI_UNALLOCATED) {
+ if (VDI_IS_ALLOCATED(bmap_entry)) {
if (bmap_entry < s->header.blocks_in_image) {
blocks_allocated++;
- if (bmap[bmap_entry] == VDI_UNALLOCATED) {
+ if (!VDI_IS_ALLOCATED(bmap[bmap_entry])) {
bmap[bmap_entry] = bmap_entry;
} else {
fprintf(stderr, "ERROR: block index %" PRIu32
@@ -472,7 +477,7 @@ static int vdi_is_allocated(BlockDriverState *bs, int64_t sector_num,
n_sectors = nb_sectors;
}
*pnum = n_sectors;
- return bmap_entry != VDI_UNALLOCATED;
+ return VDI_IS_ALLOCATED(bmap_entry);
}
static void vdi_aio_cancel(BlockDriverAIOCB *blockacb)
@@ -603,7 +608,7 @@ static void vdi_aio_read_cb(void *opaque, int ret)
/* prepare next AIO request */
acb->n_sectors = n_sectors;
bmap_entry = le32_to_cpu(s->bmap[block_index]);
- if (bmap_entry == VDI_UNALLOCATED) {
+ if (!VDI_IS_ALLOCATED(bmap_entry)) {
/* Block not allocated, return zeros, no need to wait. */
memset(acb->buf, 0, n_sectors * SECTOR_SIZE);
ret = vdi_schedule_bh(vdi_aio_rw_bh, acb);
@@ -685,7 +690,7 @@ static void vdi_aio_write_cb(void *opaque, int ret)
if (acb->header_modified) {
VdiHeader *header = acb->block_buffer;
logout("now writing modified header\n");
- assert(acb->bmap_first != VDI_UNALLOCATED);
+ assert(VDI_IS_ALLOCATED(acb->bmap_first));
*header = s->header;
vdi_header_to_le(header);
acb->header_modified = 0;
@@ -699,7 +704,7 @@ static void vdi_aio_write_cb(void *opaque, int ret)
goto done;
}
return;
- } else if (acb->bmap_first != VDI_UNALLOCATED) {
+ } else if (VDI_IS_ALLOCATED(acb->bmap_first)) {
/* One or more new blocks were allocated. */
uint64_t offset;
uint32_t bmap_first;
@@ -749,7 +754,7 @@ static void vdi_aio_write_cb(void *opaque, int ret)
/* prepare next AIO request */
acb->n_sectors = n_sectors;
bmap_entry = le32_to_cpu(s->bmap[block_index]);
- if (bmap_entry == VDI_UNALLOCATED) {
+ if (!VDI_IS_ALLOCATED(bmap_entry)) {
/* Allocate new block and write to it. */
uint64_t offset;
uint8_t *block;
diff --git a/block/vmdk.c b/block/vmdk.c
index e93828eeae..985006e203 100644
--- a/block/vmdk.c
+++ b/block/vmdk.c
@@ -212,8 +212,10 @@ static uint32_t vmdk_read_cid(BlockDriverState *bs, int parent)
const char *p_name, *cid_str;
size_t cid_str_size;
BDRVVmdkState *s = bs->opaque;
+ int ret;
- if (bdrv_pread(bs->file, s->desc_offset, desc, DESC_SIZE) != DESC_SIZE) {
+ ret = bdrv_pread(bs->file, s->desc_offset, desc, DESC_SIZE);
+ if (ret < 0) {
return 0;
}
@@ -225,6 +227,7 @@ static uint32_t vmdk_read_cid(BlockDriverState *bs, int parent)
cid_str_size = sizeof("CID");
}
+ desc[DESC_SIZE - 1] = '\0';
p_name = strstr(desc, cid_str);
if (p_name != NULL) {
p_name += cid_str_size;
@@ -239,13 +242,19 @@ static int vmdk_write_cid(BlockDriverState *bs, uint32_t cid)
char desc[DESC_SIZE], tmp_desc[DESC_SIZE];
char *p_name, *tmp_str;
BDRVVmdkState *s = bs->opaque;
+ int ret;
- memset(desc, 0, sizeof(desc));
- if (bdrv_pread(bs->file, s->desc_offset, desc, DESC_SIZE) != DESC_SIZE) {
- return -EIO;
+ ret = bdrv_pread(bs->file, s->desc_offset, desc, DESC_SIZE);
+ if (ret < 0) {
+ return ret;
}
+ desc[DESC_SIZE - 1] = '\0';
tmp_str = strstr(desc, "parentCID");
+ if (tmp_str == NULL) {
+ return -EINVAL;
+ }
+
pstrcpy(tmp_desc, sizeof(tmp_desc), tmp_str);
p_name = strstr(desc, "CID");
if (p_name != NULL) {
@@ -254,9 +263,11 @@ static int vmdk_write_cid(BlockDriverState *bs, uint32_t cid)
pstrcat(desc, sizeof(desc), tmp_desc);
}
- if (bdrv_pwrite_sync(bs->file, s->desc_offset, desc, DESC_SIZE) < 0) {
- return -EIO;
+ ret = bdrv_pwrite_sync(bs->file, s->desc_offset, desc, DESC_SIZE);
+ if (ret < 0) {
+ return ret;
}
+
return 0;
}
@@ -1109,7 +1120,10 @@ static int vmdk_write(BlockDriverState *bs, int64_t sector_num,
/* update CID on the first write every time the virtual disk is
* opened */
if (!s->cid_updated) {
- vmdk_write_cid(bs, time(NULL));
+ ret = vmdk_write_cid(bs, time(NULL));
+ if (ret < 0) {
+ return ret;
+ }
s->cid_updated = true;
}
}