summaryrefslogtreecommitdiff
path: root/block
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2014-02-25 10:50:11 +0000
committerPeter Maydell <peter.maydell@linaro.org>2014-02-25 10:50:11 +0000
commite7a1d6c52a3ac6e76e5653c830b2545e0a4043d3 (patch)
treec79235495674ca3dceded77db7224e54419f6f61 /block
parentc58e291591dbc1c846fa152d4792554803405ebb (diff)
parent6141f3bd6904df7cf9519c6444a14a608b9874c4 (diff)
downloadqemu-e7a1d6c52a3ac6e76e5653c830b2545e0a4043d3.tar.gz
Merge remote-tracking branch 'remotes/kevin/tags/for-upstream' into staging
Block patches # gpg: Signature made Fri 21 Feb 2014 21:42:24 GMT using RSA key ID C88F2FD6 # gpg: Good signature from "Kevin Wolf <kwolf@redhat.com>" * remotes/kevin/tags/for-upstream: (54 commits) iotests: Mixed quorum child device specifications quorum: Simplify quorum_open() quorum: Add unit test. quorum: Add quorum_open() and quorum_close(). quorum: Implement recursive .bdrv_recurse_is_first_non_filter in quorum. quorum: Add quorum_co_flush(). quorum: Add quorum_invalidate_cache(). quorum: Add quorum_getlength(). quorum: Add quorum mechanism. quorum: Add quorum_aio_readv. blkverify: Extract qemu_iovec_clone() and qemu_iovec_compare() from blkverify. quorum: Add quorum_aio_writev and its dependencies. quorum: Create BDRVQuorumState and BlkDriver and do init. quorum: Create quorum.c, add QuorumChildRequest and QuorumAIOCB. check-qdict: Test termination of qdict_array_split() check-qdict: Adjust test for qdict_array_split() qdict: Extract non-QDicts in qdict_array_split() qemu-config: Sections must consist of keys qemu-iotests: Check qemu-img command line parsing qemu-img: Allow -o help with incomplete argument list ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'block')
-rw-r--r--block/Makefile.objs1
-rw-r--r--block/blkdebug.c3
-rw-r--r--block/blkverify.c114
-rw-r--r--block/bochs.c3
-rw-r--r--block/cow.c16
-rw-r--r--block/curl.c13
-rw-r--r--block/gluster.c28
-rw-r--r--block/iscsi.c142
-rw-r--r--block/nbd.c43
-rw-r--r--block/parallels.c3
-rw-r--r--block/qcow.c20
-rw-r--r--block/qcow2-cluster.c22
-rw-r--r--block/qcow2.c20
-rw-r--r--block/qed.c24
-rw-r--r--block/quorum.c870
-rw-r--r--block/sheepdog.c7
-rw-r--r--block/vdi.c29
-rw-r--r--block/vhdx.c25
-rw-r--r--block/vmdk.c142
-rw-r--r--block/vpc.c3
-rw-r--r--block/vvfat.c15
21 files changed, 1195 insertions, 348 deletions
diff --git a/block/Makefile.objs b/block/Makefile.objs
index 673aa7c8c9..fd88c03ece 100644
--- a/block/Makefile.objs
+++ b/block/Makefile.objs
@@ -3,6 +3,7 @@ block-obj-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o qcow2-c
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-$(CONFIG_VHDX) += vhdx.o vhdx-endian.o vhdx-log.o
+block-obj-$(CONFIG_QUORUM) += quorum.o
block-obj-y += parallels.o blkdebug.o blkverify.o
block-obj-y += snapshot.o qapi.o
block-obj-$(CONFIG_WIN32) += raw-win32.o win32-aio.o
diff --git a/block/blkdebug.c b/block/blkdebug.c
index ee10013362..380c736101 100644
--- a/block/blkdebug.c
+++ b/block/blkdebug.c
@@ -410,8 +410,9 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags,
s->state = 1;
/* Open the backing file */
+ assert(bs->file == NULL);
ret = bdrv_open_image(&bs->file, qemu_opt_get(opts, "x-image"), options, "image",
- flags, true, false, &local_err);
+ flags | BDRV_O_PROTOCOL, false, &local_err);
if (ret < 0) {
error_propagate(errp, local_err);
goto out;
diff --git a/block/blkverify.c b/block/blkverify.c
index 1563c88324..b98b08bedf 100644
--- a/block/blkverify.c
+++ b/block/blkverify.c
@@ -135,16 +135,18 @@ static int blkverify_open(BlockDriverState *bs, QDict *options, int flags,
}
/* Open the raw file */
+ assert(bs->file == NULL);
ret = bdrv_open_image(&bs->file, qemu_opt_get(opts, "x-raw"), options,
- "raw", flags, true, false, &local_err);
+ "raw", flags | BDRV_O_PROTOCOL, false, &local_err);
if (ret < 0) {
error_propagate(errp, local_err);
goto fail;
}
/* Open the test file */
+ assert(s->test_file == NULL);
ret = bdrv_open_image(&s->test_file, qemu_opt_get(opts, "x-image"), options,
- "test", flags, false, false, &local_err);
+ "test", flags, false, &local_err);
if (ret < 0) {
error_propagate(errp, local_err);
s->test_file = NULL;
@@ -171,110 +173,6 @@ static int64_t blkverify_getlength(BlockDriverState *bs)
return bdrv_getlength(s->test_file);
}
-/**
- * Check that I/O vector contents are identical
- *
- * @a: I/O vector
- * @b: I/O vector
- * @ret: Offset to first mismatching byte or -1 if match
- */
-static ssize_t blkverify_iovec_compare(QEMUIOVector *a, QEMUIOVector *b)
-{
- int i;
- ssize_t offset = 0;
-
- assert(a->niov == b->niov);
- for (i = 0; i < a->niov; i++) {
- size_t len = 0;
- uint8_t *p = (uint8_t *)a->iov[i].iov_base;
- uint8_t *q = (uint8_t *)b->iov[i].iov_base;
-
- assert(a->iov[i].iov_len == b->iov[i].iov_len);
- while (len < a->iov[i].iov_len && *p++ == *q++) {
- len++;
- }
-
- offset += len;
-
- if (len != a->iov[i].iov_len) {
- return offset;
- }
- }
- return -1;
-}
-
-typedef struct {
- int src_index;
- struct iovec *src_iov;
- void *dest_base;
-} IOVectorSortElem;
-
-static int sortelem_cmp_src_base(const void *a, const void *b)
-{
- const IOVectorSortElem *elem_a = a;
- const IOVectorSortElem *elem_b = b;
-
- /* Don't overflow */
- if (elem_a->src_iov->iov_base < elem_b->src_iov->iov_base) {
- return -1;
- } else if (elem_a->src_iov->iov_base > elem_b->src_iov->iov_base) {
- return 1;
- } else {
- return 0;
- }
-}
-
-static int sortelem_cmp_src_index(const void *a, const void *b)
-{
- const IOVectorSortElem *elem_a = a;
- const IOVectorSortElem *elem_b = b;
-
- return elem_a->src_index - elem_b->src_index;
-}
-
-/**
- * Copy contents of I/O vector
- *
- * The relative relationships of overlapping iovecs are preserved. This is
- * necessary to ensure identical semantics in the cloned I/O vector.
- */
-static void blkverify_iovec_clone(QEMUIOVector *dest, const QEMUIOVector *src,
- void *buf)
-{
- IOVectorSortElem sortelems[src->niov];
- void *last_end;
- int i;
-
- /* Sort by source iovecs by base address */
- for (i = 0; i < src->niov; i++) {
- sortelems[i].src_index = i;
- sortelems[i].src_iov = &src->iov[i];
- }
- qsort(sortelems, src->niov, sizeof(sortelems[0]), sortelem_cmp_src_base);
-
- /* Allocate buffer space taking into account overlapping iovecs */
- last_end = NULL;
- for (i = 0; i < src->niov; i++) {
- struct iovec *cur = sortelems[i].src_iov;
- ptrdiff_t rewind = 0;
-
- /* Detect overlap */
- if (last_end && last_end > cur->iov_base) {
- rewind = last_end - cur->iov_base;
- }
-
- sortelems[i].dest_base = buf - rewind;
- buf += cur->iov_len - MIN(rewind, cur->iov_len);
- last_end = MAX(cur->iov_base + cur->iov_len, last_end);
- }
-
- /* Sort by source iovec index and build destination iovec */
- qsort(sortelems, src->niov, sizeof(sortelems[0]), sortelem_cmp_src_index);
- for (i = 0; i < src->niov; i++) {
- qemu_iovec_add(dest, sortelems[i].dest_base, src->iov[i].iov_len);
- }
-}
-
static BlkverifyAIOCB *blkverify_aio_get(BlockDriverState *bs, bool is_write,
int64_t sector_num, QEMUIOVector *qiov,
int nb_sectors,
@@ -338,7 +236,7 @@ static void blkverify_aio_cb(void *opaque, int ret)
static void blkverify_verify_readv(BlkverifyAIOCB *acb)
{
- ssize_t offset = blkverify_iovec_compare(acb->qiov, &acb->raw_qiov);
+ ssize_t offset = qemu_iovec_compare(acb->qiov, &acb->raw_qiov);
if (offset != -1) {
blkverify_err(acb, "contents mismatch in sector %" PRId64,
acb->sector_num + (int64_t)(offset / BDRV_SECTOR_SIZE));
@@ -356,7 +254,7 @@ static BlockDriverAIOCB *blkverify_aio_readv(BlockDriverState *bs,
acb->verify = blkverify_verify_readv;
acb->buf = qemu_blockalign(bs->file, qiov->size);
qemu_iovec_init(&acb->raw_qiov, acb->qiov->niov);
- blkverify_iovec_clone(&acb->raw_qiov, qiov, acb->buf);
+ qemu_iovec_clone(&acb->raw_qiov, qiov, acb->buf);
bdrv_aio_readv(s->test_file, sector_num, qiov, nb_sectors,
blkverify_aio_cb, acb);
diff --git a/block/bochs.c b/block/bochs.c
index 51d9a90577..4d6403f904 100644
--- a/block/bochs.c
+++ b/block/bochs.c
@@ -129,7 +129,8 @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags,
strcmp(bochs.subtype, GROWING_TYPE) ||
((le32_to_cpu(bochs.version) != HEADER_VERSION) &&
(le32_to_cpu(bochs.version) != HEADER_V1))) {
- return -EMEDIUMTYPE;
+ error_setg(errp, "Image not in Bochs format");
+ return -EINVAL;
}
if (le32_to_cpu(bochs.version) == HEADER_V1) {
diff --git a/block/cow.c b/block/cow.c
index 7fc0b12163..30deb88deb 100644
--- a/block/cow.c
+++ b/block/cow.c
@@ -74,7 +74,8 @@ static int cow_open(BlockDriverState *bs, QDict *options, int flags,
}
if (be32_to_cpu(cow_header.magic) != COW_MAGIC) {
- ret = -EMEDIUMTYPE;
+ error_setg(errp, "Image not in COW format");
+ ret = -EINVAL;
goto fail;
}
@@ -82,7 +83,7 @@ static int cow_open(BlockDriverState *bs, QDict *options, int flags,
char version[64];
snprintf(version, sizeof(version),
"COW version %d", cow_header.version);
- qerror_report(QERR_UNKNOWN_BLOCK_FORMAT_FEATURE,
+ error_set(errp, QERR_UNKNOWN_BLOCK_FORMAT_FEATURE,
bs->device_name, "cow", version);
ret = -ENOTSUP;
goto fail;
@@ -346,16 +347,15 @@ static int cow_create(const char *filename, QEMUOptionParameter *options,
ret = bdrv_create_file(filename, options, &local_err);
if (ret < 0) {
- qerror_report_err(local_err);
- error_free(local_err);
+ error_propagate(errp, local_err);
return ret;
}
- ret = bdrv_file_open(&cow_bs, filename, NULL, NULL, BDRV_O_RDWR,
- &local_err);
+ cow_bs = NULL;
+ ret = bdrv_open(&cow_bs, filename, NULL, NULL,
+ BDRV_O_RDWR | BDRV_O_PROTOCOL, NULL, &local_err);
if (ret < 0) {
- qerror_report_err(local_err);
- error_free(local_err);
+ error_propagate(errp, local_err);
return ret;
}
diff --git a/block/curl.c b/block/curl.c
index bb1fc4ae28..3494c6d662 100644
--- a/block/curl.c
+++ b/block/curl.c
@@ -456,30 +456,27 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags,
static int inited = 0;
if (flags & BDRV_O_RDWR) {
- qerror_report(ERROR_CLASS_GENERIC_ERROR,
- "curl block device does not support writes");
+ error_setg(errp, "curl block device does not support writes");
return -EROFS;
}
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
qemu_opts_absorb_qdict(opts, options, &local_err);
if (local_err) {
- qerror_report_err(local_err);
- error_free(local_err);
+ error_propagate(errp, local_err);
goto out_noclean;
}
s->readahead_size = qemu_opt_get_size(opts, "readahead", READ_AHEAD_SIZE);
if ((s->readahead_size & 0x1ff) != 0) {
- fprintf(stderr, "HTTP_READAHEAD_SIZE %zd is not a multiple of 512\n",
- s->readahead_size);
+ error_setg(errp, "HTTP_READAHEAD_SIZE %zd is not a multiple of 512",
+ s->readahead_size);
goto out_noclean;
}
file = qemu_opt_get(opts, "url");
if (file == NULL) {
- qerror_report(ERROR_CLASS_GENERIC_ERROR, "curl block driver requires "
- "an 'url' option");
+ error_setg(errp, "curl block driver requires an 'url' option");
goto out_noclean;
}
diff --git a/block/gluster.c b/block/gluster.c
index 58eab07829..14d390b4c7 100644
--- a/block/gluster.c
+++ b/block/gluster.c
@@ -127,7 +127,7 @@ static int qemu_gluster_parseuri(GlusterConf *gconf, const char *filename)
}
/* transport */
- if (!strcmp(uri->scheme, "gluster")) {
+ if (!uri->scheme || !strcmp(uri->scheme, "gluster")) {
gconf->transport = g_strdup("tcp");
} else if (!strcmp(uri->scheme, "gluster+tcp")) {
gconf->transport = g_strdup("tcp");
@@ -163,7 +163,7 @@ static int qemu_gluster_parseuri(GlusterConf *gconf, const char *filename)
}
gconf->server = g_strdup(qp->p[0].value);
} else {
- gconf->server = g_strdup(uri->server);
+ gconf->server = g_strdup(uri->server ? uri->server : "localhost");
gconf->port = uri->port;
}
@@ -175,7 +175,8 @@ out:
return ret;
}
-static struct glfs *qemu_gluster_init(GlusterConf *gconf, const char *filename)
+static struct glfs *qemu_gluster_init(GlusterConf *gconf, const char *filename,
+ Error **errp)
{
struct glfs *glfs = NULL;
int ret;
@@ -183,8 +184,8 @@ static struct glfs *qemu_gluster_init(GlusterConf *gconf, const char *filename)
ret = qemu_gluster_parseuri(gconf, filename);
if (ret < 0) {
- error_report("Usage: file=gluster[+transport]://[server[:port]]/"
- "volname/image[?socket=...]");
+ error_setg(errp, "Usage: file=gluster[+transport]://[server[:port]]/"
+ "volname/image[?socket=...]");
errno = -ret;
goto out;
}
@@ -211,9 +212,11 @@ static struct glfs *qemu_gluster_init(GlusterConf *gconf, const char *filename)
ret = glfs_init(glfs);
if (ret) {
- error_report("Gluster connection failed for server=%s port=%d "
- "volume=%s image=%s transport=%s", gconf->server, gconf->port,
- gconf->volname, gconf->image, gconf->transport);
+ error_setg_errno(errp, errno,
+ "Gluster connection failed for server=%s port=%d "
+ "volume=%s image=%s transport=%s", gconf->server,
+ gconf->port, gconf->volname, gconf->image,
+ gconf->transport);
goto out;
}
return glfs;
@@ -283,15 +286,14 @@ static int qemu_gluster_open(BlockDriverState *bs, QDict *options,
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
qemu_opts_absorb_qdict(opts, options, &local_err);
if (local_err) {
- qerror_report_err(local_err);
- error_free(local_err);
+ error_propagate(errp, local_err);
ret = -EINVAL;
goto out;
}
filename = qemu_opt_get(opts, "filename");
- s->glfs = qemu_gluster_init(gconf, filename);
+ s->glfs = qemu_gluster_init(gconf, filename, errp);
if (!s->glfs) {
ret = -errno;
goto out;
@@ -389,9 +391,9 @@ static int qemu_gluster_create(const char *filename,
int64_t total_size = 0;
GlusterConf *gconf = g_malloc0(sizeof(GlusterConf));
- glfs = qemu_gluster_init(gconf, filename);
+ glfs = qemu_gluster_init(gconf, filename, errp);
if (!glfs) {
- ret = -errno;
+ ret = -EINVAL;
goto out;
}
diff --git a/block/iscsi.c b/block/iscsi.c
index f8e496f8ef..41ec09709d 100644
--- a/block/iscsi.c
+++ b/block/iscsi.c
@@ -856,7 +856,8 @@ retry:
#endif /* SCSI_SENSE_ASCQ_CAPACITY_DATA_HAS_CHANGED */
-static int parse_chap(struct iscsi_context *iscsi, const char *target)
+static void parse_chap(struct iscsi_context *iscsi, const char *target,
+ Error **errp)
{
QemuOptsList *list;
QemuOpts *opts;
@@ -865,37 +866,35 @@ static int parse_chap(struct iscsi_context *iscsi, const char *target)
list = qemu_find_opts("iscsi");
if (!list) {
- return 0;
+ return;
}
opts = qemu_opts_find(list, target);
if (opts == NULL) {
opts = QTAILQ_FIRST(&list->head);
if (!opts) {
- return 0;
+ return;
}
}
user = qemu_opt_get(opts, "user");
if (!user) {
- return 0;
+ return;
}
password = qemu_opt_get(opts, "password");
if (!password) {
- error_report("CHAP username specified but no password was given");
- return -1;
+ error_setg(errp, "CHAP username specified but no password was given");
+ return;
}
if (iscsi_set_initiator_username_pwd(iscsi, user, password)) {
- error_report("Failed to set initiator username and password");
- return -1;
+ error_setg(errp, "Failed to set initiator username and password");
}
-
- return 0;
}
-static void parse_header_digest(struct iscsi_context *iscsi, const char *target)
+static void parse_header_digest(struct iscsi_context *iscsi, const char *target,
+ Error **errp)
{
QemuOptsList *list;
QemuOpts *opts;
@@ -928,7 +927,7 @@ static void parse_header_digest(struct iscsi_context *iscsi, const char *target)
} else if (!strcmp(digest, "NONE-CRC32C")) {
iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_NONE_CRC32C);
} else {
- error_report("Invalid header-digest setting : %s", digest);
+ error_setg(errp, "Invalid header-digest setting : %s", digest);
}
}
@@ -986,12 +985,11 @@ static void iscsi_nop_timed_event(void *opaque)
}
#endif
-static int iscsi_readcapacity_sync(IscsiLun *iscsilun)
+static void iscsi_readcapacity_sync(IscsiLun *iscsilun, Error **errp)
{
struct scsi_task *task = NULL;
struct scsi_readcapacity10 *rc10 = NULL;
struct scsi_readcapacity16 *rc16 = NULL;
- int ret = 0;
int retries = ISCSI_CMD_RETRIES;
do {
@@ -1006,8 +1004,7 @@ static int iscsi_readcapacity_sync(IscsiLun *iscsilun)
if (task != NULL && task->status == SCSI_STATUS_GOOD) {
rc16 = scsi_datain_unmarshall(task);
if (rc16 == NULL) {
- error_report("iSCSI: Failed to unmarshall readcapacity16 data.");
- ret = -EINVAL;
+ error_setg(errp, "iSCSI: Failed to unmarshall readcapacity16 data.");
} else {
iscsilun->block_size = rc16->block_length;
iscsilun->num_blocks = rc16->returned_lba + 1;
@@ -1021,8 +1018,7 @@ static int iscsi_readcapacity_sync(IscsiLun *iscsilun)
if (task != NULL && task->status == SCSI_STATUS_GOOD) {
rc10 = scsi_datain_unmarshall(task);
if (rc10 == NULL) {
- error_report("iSCSI: Failed to unmarshall readcapacity10 data.");
- ret = -EINVAL;
+ error_setg(errp, "iSCSI: Failed to unmarshall readcapacity10 data.");
} else {
iscsilun->block_size = rc10->block_size;
if (rc10->lba == 0) {
@@ -1035,20 +1031,18 @@ static int iscsi_readcapacity_sync(IscsiLun *iscsilun)
}
break;
default:
- return 0;
+ return;
}
} while (task != NULL && task->status == SCSI_STATUS_CHECK_CONDITION
&& task->sense.key == SCSI_SENSE_UNIT_ATTENTION
&& retries-- > 0);
if (task == NULL || task->status != SCSI_STATUS_GOOD) {
- error_report("iSCSI: failed to send readcapacity10 command.");
- ret = -EINVAL;
+ error_setg(errp, "iSCSI: failed to send readcapacity10 command.");
}
if (task) {
scsi_free_scsi_task(task);
}
- return ret;
}
/* TODO Convert to fine grained options */
@@ -1065,35 +1059,36 @@ static QemuOptsList runtime_opts = {
},
};
-static struct scsi_task *iscsi_do_inquiry(struct iscsi_context *iscsi,
- int lun, int evpd, int pc) {
- int full_size;
- struct scsi_task *task = NULL;
- task = iscsi_inquiry_sync(iscsi, lun, evpd, pc, 64);
+static struct scsi_task *iscsi_do_inquiry(struct iscsi_context *iscsi, int lun,
+ int evpd, int pc, Error **errp)
+{
+ int full_size;
+ struct scsi_task *task = NULL;
+ task = iscsi_inquiry_sync(iscsi, lun, evpd, pc, 64);
+ if (task == NULL || task->status != SCSI_STATUS_GOOD) {
+ goto fail;
+ }
+ full_size = scsi_datain_getfullsize(task);
+ if (full_size > task->datain.size) {
+ scsi_free_scsi_task(task);
+
+ /* we need more data for the full list */
+ task = iscsi_inquiry_sync(iscsi, lun, evpd, pc, full_size);
if (task == NULL || task->status != SCSI_STATUS_GOOD) {
goto fail;
}
- full_size = scsi_datain_getfullsize(task);
- if (full_size > task->datain.size) {
- scsi_free_scsi_task(task);
-
- /* we need more data for the full list */
- task = iscsi_inquiry_sync(iscsi, lun, evpd, pc, full_size);
- if (task == NULL || task->status != SCSI_STATUS_GOOD) {
- goto fail;
- }
- }
+ }
- return task;
+ return task;
fail:
- error_report("iSCSI: Inquiry command failed : %s",
- iscsi_get_error(iscsi));
- if (task) {
- scsi_free_scsi_task(task);
- return NULL;
- }
+ error_setg(errp, "iSCSI: Inquiry command failed : %s",
+ iscsi_get_error(iscsi));
+ if (task) {
+ scsi_free_scsi_task(task);
return NULL;
+ }
+ return NULL;
}
/*
@@ -1119,27 +1114,25 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
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);
+ error_setg(errp, "iSCSI: Invalid BDRV_SECTOR_SIZE. "
+ "BDRV_SECTOR_SIZE(%lld) is not a multiple "
+ "of 512", BDRV_SECTOR_SIZE);
return -EINVAL;
}
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
qemu_opts_absorb_qdict(opts, options, &local_err);
if (local_err) {
- qerror_report_err(local_err);
- error_free(local_err);
+ error_propagate(errp, local_err);
ret = -EINVAL;
goto out;
}
filename = qemu_opt_get(opts, "filename");
-
iscsi_url = iscsi_parse_full_url(iscsi, filename);
if (iscsi_url == NULL) {
- error_report("Failed to parse URL : %s", filename);
+ error_setg(errp, "Failed to parse URL : %s", filename);
ret = -EINVAL;
goto out;
}
@@ -1150,13 +1143,13 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
iscsi = iscsi_create_context(initiator_name);
if (iscsi == NULL) {
- error_report("iSCSI: Failed to create iSCSI context.");
+ error_setg(errp, "iSCSI: Failed to create iSCSI context.");
ret = -ENOMEM;
goto out;
}
if (iscsi_set_targetname(iscsi, iscsi_url->target)) {
- error_report("iSCSI: Failed to set target name.");
+ error_setg(errp, "iSCSI: Failed to set target name.");
ret = -EINVAL;
goto out;
}
@@ -1165,21 +1158,22 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
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");
+ error_setg(errp, "Failed to set initiator username and password");
ret = -EINVAL;
goto out;
}
}
/* check if we got CHAP username/password via the options */
- if (parse_chap(iscsi, iscsi_url->target) != 0) {
- error_report("iSCSI: Failed to set CHAP user/password");
+ parse_chap(iscsi, iscsi_url->target, &local_err);
+ if (local_err != NULL) {
+ error_propagate(errp, local_err);
ret = -EINVAL;
goto out;
}
if (iscsi_set_session_type(iscsi, ISCSI_SESSION_NORMAL) != 0) {
- error_report("iSCSI: Failed to set session type to normal.");
+ error_setg(errp, "iSCSI: Failed to set session type to normal.");
ret = -EINVAL;
goto out;
}
@@ -1187,10 +1181,15 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_NONE_CRC32C);
/* check if we got HEADER_DIGEST via the options */
- parse_header_digest(iscsi, iscsi_url->target);
+ parse_header_digest(iscsi, iscsi_url->target, &local_err);
+ if (local_err != NULL) {
+ error_propagate(errp, local_err);
+ ret = -EINVAL;
+ goto out;
+ }
if (iscsi_full_connect_sync(iscsi, iscsi_url->portal, iscsi_url->lun) != 0) {
- error_report("iSCSI: Failed to connect to LUN : %s",
+ error_setg(errp, "iSCSI: Failed to connect to LUN : %s",
iscsi_get_error(iscsi));
ret = -EINVAL;
goto out;
@@ -1202,14 +1201,14 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
task = iscsi_inquiry_sync(iscsi, iscsilun->lun, 0, 0, 36);
if (task == NULL || task->status != SCSI_STATUS_GOOD) {
- error_report("iSCSI: failed to send inquiry command.");
+ error_setg(errp, "iSCSI: failed to send inquiry command.");
ret = -EINVAL;
goto out;
}
inq = scsi_datain_unmarshall(task);
if (inq == NULL) {
- error_report("iSCSI: Failed to unmarshall inquiry data.");
+ error_setg(errp, "iSCSI: Failed to unmarshall inquiry data.");
ret = -EINVAL;
goto out;
}
@@ -1217,7 +1216,9 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
iscsilun->type = inq->periperal_device_type;
iscsilun->has_write_same = true;
- if ((ret = iscsi_readcapacity_sync(iscsilun)) != 0) {
+ iscsi_readcapacity_sync(iscsilun, &local_err);
+ if (local_err != NULL) {
+ error_propagate(errp, local_err);
goto out;
}
bs->total_sectors = sector_lun2qemu(iscsilun->num_blocks, iscsilun);
@@ -1235,14 +1236,15 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
if (iscsilun->lbpme) {
struct scsi_inquiry_logical_block_provisioning *inq_lbp;
task = iscsi_do_inquiry(iscsilun->iscsi, iscsilun->lun, 1,
- SCSI_INQUIRY_PAGECODE_LOGICAL_BLOCK_PROVISIONING);
+ SCSI_INQUIRY_PAGECODE_LOGICAL_BLOCK_PROVISIONING,
+ errp);
if (task == NULL) {
ret = -EINVAL;
goto out;
}
inq_lbp = scsi_datain_unmarshall(task);
if (inq_lbp == NULL) {
- error_report("iSCSI: failed to unmarshall inquiry datain blob");
+ error_setg(errp, "iSCSI: failed to unmarshall inquiry datain blob");
ret = -EINVAL;
goto out;
}
@@ -1255,14 +1257,14 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
if (iscsilun->lbp.lbpu || iscsilun->lbp.lbpws) {
struct scsi_inquiry_block_limits *inq_bl;
task = iscsi_do_inquiry(iscsilun->iscsi, iscsilun->lun, 1,
- SCSI_INQUIRY_PAGECODE_BLOCK_LIMITS);
+ SCSI_INQUIRY_PAGECODE_BLOCK_LIMITS, errp);
if (task == NULL) {
ret = -EINVAL;
goto out;
}
inq_bl = scsi_datain_unmarshall(task);
if (inq_bl == NULL) {
- error_report("iSCSI: failed to unmarshall inquiry datain blob");
+ error_setg(errp, "iSCSI: failed to unmarshall inquiry datain blob");
ret = -EINVAL;
goto out;
}
@@ -1353,14 +1355,16 @@ static int iscsi_reopen_prepare(BDRVReopenState *state,
static int iscsi_truncate(BlockDriverState *bs, int64_t offset)
{
IscsiLun *iscsilun = bs->opaque;
- int ret = 0;
+ Error *local_err = NULL;
if (iscsilun->type != TYPE_DISK) {
return -ENOTSUP;
}
- if ((ret = iscsi_readcapacity_sync(iscsilun)) != 0) {
- return ret;
+ iscsi_readcapacity_sync(iscsilun, &local_err);
+ if (local_err != NULL) {
+ error_free(local_err);
+ return -EIO;
}
if (offset > iscsi_getlength(bs)) {
diff --git a/block/nbd.c b/block/nbd.c
index abae506f04..55124239df 100644
--- a/block/nbd.c
+++ b/block/nbd.c
@@ -188,31 +188,28 @@ out:
g_free(file);
}
-static int nbd_config(BDRVNBDState *s, QDict *options, char **export)
+static void nbd_config(BDRVNBDState *s, QDict *options, char **export,
+ Error **errp)
{
Error *local_err = NULL;
- if (qdict_haskey(options, "path")) {
- if (qdict_haskey(options, "host")) {
- qerror_report(ERROR_CLASS_GENERIC_ERROR, "path and host may not "
- "be used at the same time.");
- return -EINVAL;
+ if (qdict_haskey(options, "path") == qdict_haskey(options, "host")) {
+ if (qdict_haskey(options, "path")) {
+ error_setg(errp, "path and host may not be used at the same time.");
+ } else {
+ error_setg(errp, "one of path and host must be specified.");
}
- s->client.is_unix = true;
- } else if (qdict_haskey(options, "host")) {
- s->client.is_unix = false;
- } else {
- return -EINVAL;
+ return;
}
+ s->client.is_unix = qdict_haskey(options, "path");
s->socket_opts = qemu_opts_create(&socket_optslist, NULL, 0,
&error_abort);
qemu_opts_absorb_qdict(s->socket_opts, options, &local_err);
if (local_err) {
- qerror_report_err(local_err);
- error_free(local_err);
- return -EINVAL;
+ error_propagate(errp, local_err);
+ return;
}
if (!qemu_opt_get(s->socket_opts, "port")) {
@@ -223,19 +220,17 @@ static int nbd_config(BDRVNBDState *s, QDict *options, char **export)
if (*export) {
qdict_del(options, "export");
}
-
- return 0;
}
-static int nbd_establish_connection(BlockDriverState *bs)
+static int nbd_establish_connection(BlockDriverState *bs, Error **errp)
{
BDRVNBDState *s = bs->opaque;
int sock;
if (s->client.is_unix) {
- sock = unix_socket_outgoing(qemu_opt_get(s->socket_opts, "path"));
+ sock = unix_connect_opts(s->socket_opts, errp, NULL, NULL);
} else {
- sock = tcp_socket_outgoing_opts(s->socket_opts);
+ sock = inet_connect_opts(s->socket_opts, errp, NULL, NULL);
if (sock >= 0) {
socket_set_nodelay(sock);
}
@@ -256,17 +251,19 @@ static int nbd_open(BlockDriverState *bs, QDict *options, int flags,
BDRVNBDState *s = bs->opaque;
char *export = NULL;
int result, sock;
+ Error *local_err = NULL;
/* Pop the config into our state object. Exit if invalid. */
- result = nbd_config(s, options, &export);
- if (result != 0) {
- return result;
+ nbd_config(s, options, &export, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return -EINVAL;
}
/* establish TCP connection, return error if it fails
* TODO: Configurable retry-until-timeout behaviour.
*/
- sock = nbd_establish_connection(bs);
+ sock = nbd_establish_connection(bs, errp);
if (sock < 0) {
return sock;
}
diff --git a/block/parallels.c b/block/parallels.c
index 2121e43204..3f588f58dc 100644
--- a/block/parallels.c
+++ b/block/parallels.c
@@ -85,7 +85,8 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags,
if (memcmp(ph.magic, HEADER_MAGIC, 16) ||
(le32_to_cpu(ph.version) != HEADER_VERSION)) {
- ret = -EMEDIUMTYPE;
+ error_setg(errp, "Image not in Parallels format");
+ ret = -EINVAL;
goto fail;
}
diff --git a/block/qcow.c b/block/qcow.c
index 948b0c5601..1e128becf0 100644
--- a/block/qcow.c
+++ b/block/qcow.c
@@ -113,23 +113,26 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
be64_to_cpus(&header.l1_table_offset);
if (header.magic != QCOW_MAGIC) {
- ret = -EMEDIUMTYPE;
+ error_setg(errp, "Image not in qcow format");
+ ret = -EINVAL;
goto fail;
}
if (header.version != QCOW_VERSION) {
char version[64];
snprintf(version, sizeof(version), "QCOW version %d", header.version);
- qerror_report(QERR_UNKNOWN_BLOCK_FORMAT_FEATURE,
- bs->device_name, "qcow", version);
+ error_set(errp, QERR_UNKNOWN_BLOCK_FORMAT_FEATURE,
+ bs->device_name, "qcow", version);
ret = -ENOTSUP;
goto fail;
}
if (header.size <= 1 || header.cluster_bits < 9) {
+ error_setg(errp, "invalid value in qcow header");
ret = -EINVAL;
goto fail;
}
if (header.crypt_method > QCOW_CRYPT_AES) {
+ error_setg(errp, "invalid encryption method in qcow header");
ret = -EINVAL;
goto fail;
}
@@ -686,16 +689,15 @@ static int qcow_create(const char *filename, QEMUOptionParameter *options,
ret = bdrv_create_file(filename, options, &local_err);
if (ret < 0) {
- qerror_report_err(local_err);
- error_free(local_err);
+ error_propagate(errp, local_err);
return ret;
}
- ret = bdrv_file_open(&qcow_bs, filename, NULL, NULL, BDRV_O_RDWR,
- &local_err);
+ qcow_bs = NULL;
+ ret = bdrv_open(&qcow_bs, filename, NULL, NULL,
+ BDRV_O_RDWR | BDRV_O_PROTOCOL, NULL, &local_err);
if (ret < 0) {
- qerror_report_err(local_err);
- error_free(local_err);
+ error_propagate(errp, local_err);
return ret;
}
diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c
index c57f39dd2b..36c1bed350 100644
--- a/block/qcow2-cluster.c
+++ b/block/qcow2-cluster.c
@@ -1367,13 +1367,31 @@ static int discard_single_l2(BlockDriverState *bs, uint64_t offset,
uint64_t old_offset;
old_offset = be64_to_cpu(l2_table[l2_index + i]);
- if ((old_offset & L2E_OFFSET_MASK) == 0) {
+
+ /*
+ * Make sure that a discarded area reads back as zeroes for v3 images
+ * (we cannot do it for v2 without actually writing a zero-filled
+ * buffer). We can skip the operation if the cluster is already marked
+ * as zero, or if it's unallocated and we don't have a backing file.
+ *
+ * TODO We might want to use bdrv_get_block_status(bs) here, but we're
+ * holding s->lock, so that doesn't work today.
+ */
+ if (old_offset & QCOW_OFLAG_ZERO) {
+ continue;
+ }
+
+ if ((old_offset & L2E_OFFSET_MASK) == 0 && !bs->backing_hd) {
continue;
}
/* First remove L2 entries */
qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table);
- l2_table[l2_index + i] = cpu_to_be64(0);
+ if (s->qcow_version >= 3) {
+ l2_table[l2_index + i] = cpu_to_be64(QCOW_OFLAG_ZERO);
+ } else {
+ l2_table[l2_index + i] = cpu_to_be64(0);
+ }
/* Then decrease the refcount */
qcow2_free_any_clusters(bs, old_offset, 1, type);
diff --git a/block/qcow2.c b/block/qcow2.c
index b1dbdb120e..cfe80befa0 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -449,7 +449,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
if (header.magic != QCOW_MAGIC) {
error_setg(errp, "Image is not in qcow2 format");
- ret = -EMEDIUMTYPE;
+ ret = -EINVAL;
goto fail;
}
if (header.version < 2 || header.version > 3) {
@@ -1493,7 +1493,9 @@ static int qcow2_create2(const char *filename, int64_t total_size,
return ret;
}
- ret = bdrv_file_open(&bs, filename, NULL, NULL, BDRV_O_RDWR, &local_err);
+ bs = NULL;
+ ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL,
+ NULL, &local_err);
if (ret < 0) {
error_propagate(errp, local_err);
return ret;
@@ -1543,7 +1545,8 @@ static int qcow2_create2(const char *filename, int64_t total_size,
goto out;
}
- bdrv_close(bs);
+ bdrv_unref(bs);
+ bs = NULL;
/*
* And now open the image and make it consistent first (i.e. increase the
@@ -1552,7 +1555,7 @@ static int qcow2_create2(const char *filename, int64_t total_size,
*/
BlockDriver* drv = bdrv_find_format("qcow2");
assert(drv != NULL);
- ret = bdrv_open(bs, filename, NULL,
+ ret = bdrv_open(&bs, filename, NULL, NULL,
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH, drv, &local_err);
if (ret < 0) {
error_propagate(errp, local_err);
@@ -1599,10 +1602,11 @@ static int qcow2_create2(const char *filename, int64_t total_size,
}
}
- bdrv_close(bs);
+ bdrv_unref(bs);
+ bs = NULL;
/* Reopen the image without BDRV_O_NO_FLUSH to flush it before returning */
- ret = bdrv_open(bs, filename, NULL,
+ ret = bdrv_open(&bs, filename, NULL, NULL,
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_BACKING,
drv, &local_err);
if (local_err) {
@@ -1612,7 +1616,9 @@ static int qcow2_create2(const char *filename, int64_t total_size,
ret = 0;
out:
- bdrv_unref(bs);
+ if (bs) {
+ bdrv_unref(bs);
+ }
return ret;
}
diff --git a/block/qed.c b/block/qed.c
index b9ca7ac0da..8802ad3845 100644
--- a/block/qed.c
+++ b/block/qed.c
@@ -391,14 +391,15 @@ static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags,
qed_header_le_to_cpu(&le_header, &s->header);
if (s->header.magic != QED_MAGIC) {
- return -EMEDIUMTYPE;
+ error_setg(errp, "Image not in QED format");
+ return -EINVAL;
}
if (s->header.features & ~QED_FEATURE_MASK) {
/* image uses unsupported feature bits */
char buf[64];
snprintf(buf, sizeof(buf), "%" PRIx64,
s->header.features & ~QED_FEATURE_MASK);
- qerror_report(QERR_UNKNOWN_BLOCK_FORMAT_FEATURE,
+ error_set(errp, QERR_UNKNOWN_BLOCK_FORMAT_FEATURE,
bs->device_name, "QED", buf);
return -ENOTSUP;
}
@@ -545,7 +546,8 @@ static void bdrv_qed_close(BlockDriverState *bs)
static int qed_create(const char *filename, uint32_t cluster_size,
uint64_t image_size, uint32_t table_size,
- const char *backing_file, const char *backing_fmt)
+ const char *backing_file, const char *backing_fmt,
+ Error **errp)
{
QEDHeader header = {
.magic = QED_MAGIC,
@@ -562,20 +564,20 @@ static int qed_create(const char *filename, uint32_t cluster_size,
size_t l1_size = header.cluster_size * header.table_size;
Error *local_err = NULL;
int ret = 0;
- BlockDriverState *bs = NULL;
+ BlockDriverState *bs;
ret = bdrv_create_file(filename, NULL, &local_err);
if (ret < 0) {
- qerror_report_err(local_err);
- error_free(local_err);
+ error_propagate(errp, local_err);
return ret;
}
- ret = bdrv_file_open(&bs, filename, NULL, NULL,
- BDRV_O_RDWR | BDRV_O_CACHE_WB, &local_err);
+ bs = NULL;
+ ret = bdrv_open(&bs, filename, NULL, NULL,
+ BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_PROTOCOL, NULL,
+ &local_err);
if (ret < 0) {
- qerror_report_err(local_err);
- error_free(local_err);
+ error_propagate(errp, local_err);
return ret;
}
@@ -665,7 +667,7 @@ static int bdrv_qed_create(const char *filename, QEMUOptionParameter *options,
}
return qed_create(filename, cluster_size, image_size, table_size,
- backing_file, backing_fmt);
+ backing_file, backing_fmt, errp);
}
typedef struct {
diff --git a/block/quorum.c b/block/quorum.c
new file mode 100644
index 0000000000..6c28239718
--- /dev/null
+++ b/block/quorum.c
@@ -0,0 +1,870 @@
+/*
+ * Quorum Block filter
+ *
+ * Copyright (C) 2012-2014 Nodalink, EURL.
+ *
+ * Author:
+ * Benoît Canet <benoit.canet@irqsave.net>
+ *
+ * Based on the design and code of blkverify.c (Copyright (C) 2010 IBM, Corp)
+ * and blkmirror.c (Copyright (C) 2011 Red Hat, Inc).
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+#include "block/block_int.h"
+#include "qapi/qmp/qjson.h"
+
+#define HASH_LENGTH 32
+
+#define QUORUM_OPT_VOTE_THRESHOLD "vote-threshold"
+#define QUORUM_OPT_BLKVERIFY "blkverify"
+
+/* This union holds a vote hash value */
+typedef union QuorumVoteValue {
+ char h[HASH_LENGTH]; /* SHA-256 hash */
+ int64_t l; /* simpler 64 bits hash */
+} QuorumVoteValue;
+
+/* A vote item */
+typedef struct QuorumVoteItem {
+ int index;
+ QLIST_ENTRY(QuorumVoteItem) next;
+} QuorumVoteItem;
+
+/* this structure is a vote version. A version is the set of votes sharing the
+ * same vote value.
+ * The set of votes will be tracked with the items field and its cardinality is
+ * vote_count.
+ */
+typedef struct QuorumVoteVersion {
+ QuorumVoteValue value;
+ int index;
+ int vote_count;
+ QLIST_HEAD(, QuorumVoteItem) items;
+ QLIST_ENTRY(QuorumVoteVersion) next;
+} QuorumVoteVersion;
+
+/* this structure holds a group of vote versions together */
+typedef struct QuorumVotes {
+ QLIST_HEAD(, QuorumVoteVersion) vote_list;
+ bool (*compare)(QuorumVoteValue *a, QuorumVoteValue *b);
+} QuorumVotes;
+
+/* the following structure holds the state of one quorum instance */
+typedef struct BDRVQuorumState {
+ BlockDriverState **bs; /* children BlockDriverStates */
+ int num_children; /* children count */
+ int threshold; /* if less than threshold children reads gave the
+ * same result a quorum error occurs.
+ */
+ bool is_blkverify; /* true if the driver is in blkverify mode
+ * Writes are mirrored on two children devices.
+ * On reads the two children devices' contents are
+ * compared and if a difference is spotted its
+ * location is printed and the code aborts.
+ * It is useful to debug other block drivers by
+ * comparing them with a reference one.
+ */
+} BDRVQuorumState;
+
+typedef struct QuorumAIOCB QuorumAIOCB;
+
+/* Quorum will create one instance of the following structure per operation it
+ * performs on its children.
+ * So for each read/write operation coming from the upper layer there will be
+ * $children_count QuorumChildRequest.
+ */
+typedef struct QuorumChildRequest {
+ BlockDriverAIOCB *aiocb;
+ QEMUIOVector qiov;
+ uint8_t *buf;
+ int ret;
+ QuorumAIOCB *parent;
+} QuorumChildRequest;
+
+/* Quorum will use the following structure to track progress of each read/write
+ * operation received by the upper layer.
+ * This structure hold pointers to the QuorumChildRequest structures instances
+ * used to do operations on each children and track overall progress.
+ */
+struct QuorumAIOCB {
+ BlockDriverAIOCB common;
+
+ /* Request metadata */
+ uint64_t sector_num;
+ int nb_sectors;
+
+ QEMUIOVector *qiov; /* calling IOV */
+
+ QuorumChildRequest *qcrs; /* individual child requests */
+ int count; /* number of completed AIOCB */
+ int success_count; /* number of successfully completed AIOCB */
+
+ QuorumVotes votes;
+
+ bool is_read;
+ int vote_ret;
+};
+
+static void quorum_vote(QuorumAIOCB *acb);
+
+static void quorum_aio_cancel(BlockDriverAIOCB *blockacb)
+{
+ QuorumAIOCB *acb = container_of(blockacb, QuorumAIOCB, common);
+ BDRVQuorumState *s = acb->common.bs->opaque;
+ int i;
+
+ /* cancel all callbacks */
+ for (i = 0; i < s->num_children; i++) {
+ bdrv_aio_cancel(acb->qcrs[i].aiocb);
+ }
+
+ g_free(acb->qcrs);
+ qemu_aio_release(acb);
+}
+
+static AIOCBInfo quorum_aiocb_info = {
+ .aiocb_size = sizeof(QuorumAIOCB),
+ .cancel = quorum_aio_cancel,
+};
+
+static void quorum_aio_finalize(QuorumAIOCB *acb)
+{
+ BDRVQuorumState *s = acb->common.bs->opaque;
+ int i, ret = 0;
+
+ if (acb->vote_ret) {
+ ret = acb->vote_ret;
+ }
+
+ acb->common.cb(acb->common.opaque, ret);
+
+ if (acb->is_read) {
+ for (i = 0; i < s->num_children; i++) {
+ qemu_vfree(acb->qcrs[i].buf);
+ qemu_iovec_destroy(&acb->qcrs[i].qiov);
+ }
+ }
+
+ g_free(acb->qcrs);
+ qemu_aio_release(acb);
+}
+
+static bool quorum_sha256_compare(QuorumVoteValue *a, QuorumVoteValue *b)
+{
+ return !memcmp(a->h, b->h, HASH_LENGTH);
+}
+
+static bool quorum_64bits_compare(QuorumVoteValue *a, QuorumVoteValue *b)
+{
+ return a->l == b->l;
+}
+
+static QuorumAIOCB *quorum_aio_get(BDRVQuorumState *s,
+ BlockDriverState *bs,
+ QEMUIOVector *qiov,
+ uint64_t sector_num,
+ int nb_sectors,
+ BlockDriverCompletionFunc *cb,
+ void *opaque)
+{
+ QuorumAIOCB *acb = qemu_aio_get(&quorum_aiocb_info, bs, cb, opaque);
+ int i;
+
+ acb->common.bs->opaque = s;
+ acb->sector_num = sector_num;
+ acb->nb_sectors = nb_sectors;
+ acb->qiov = qiov;
+ acb->qcrs = g_new0(QuorumChildRequest, s->num_children);
+ acb->count = 0;
+ acb->success_count = 0;
+ acb->votes.compare = quorum_sha256_compare;
+ QLIST_INIT(&acb->votes.vote_list);
+ acb->is_read = false;
+ acb->vote_ret = 0;
+
+ for (i = 0; i < s->num_children; i++) {
+ acb->qcrs[i].buf = NULL;
+ acb->qcrs[i].ret = 0;
+ acb->qcrs[i].parent = acb;
+ }
+
+ return acb;
+}
+
+static void quorum_report_bad(QuorumAIOCB *acb, char *node_name, int ret)
+{
+ QObject *data;
+ assert(node_name);
+ data = qobject_from_jsonf("{ 'ret': %d"
+ ", 'node-name': %s"
+ ", 'sector-num': %" PRId64
+ ", 'sectors-count': %d }",
+ ret, node_name, acb->sector_num, acb->nb_sectors);
+ monitor_protocol_event(QEVENT_QUORUM_REPORT_BAD, data);
+ qobject_decref(data);
+}
+
+static void quorum_report_failure(QuorumAIOCB *acb)
+{
+ QObject *data;
+ const char *reference = acb->common.bs->device_name[0] ?
+ acb->common.bs->device_name :
+ acb->common.bs->node_name;
+ data = qobject_from_jsonf("{ 'reference': %s"
+ ", 'sector-num': %" PRId64
+ ", 'sectors-count': %d }",
+ reference, acb->sector_num, acb->nb_sectors);
+ monitor_protocol_event(QEVENT_QUORUM_FAILURE, data);
+ qobject_decref(data);
+}
+
+static int quorum_vote_error(QuorumAIOCB *acb);
+
+static bool quorum_has_too_much_io_failed(QuorumAIOCB *acb)
+{
+ BDRVQuorumState *s = acb->common.bs->opaque;
+
+ if (acb->success_count < s->threshold) {
+ acb->vote_ret = quorum_vote_error(acb);
+ quorum_report_failure(acb);
+ return true;
+ }
+
+ return false;
+}
+
+static void quorum_aio_cb(void *opaque, int ret)
+{
+ QuorumChildRequest *sacb = opaque;
+ QuorumAIOCB *acb = sacb->parent;
+ BDRVQuorumState *s = acb->common.bs->opaque;
+
+ sacb->ret = ret;
+ acb->count++;
+ if (ret == 0) {
+ acb->success_count++;
+ } else {
+ quorum_report_bad(acb, sacb->aiocb->bs->node_name, ret);
+ }
+ assert(acb->count <= s->num_children);
+ assert(acb->success_count <= s->num_children);
+ if (acb->count < s->num_children) {
+ return;
+ }
+
+ /* Do the vote on read */
+ if (acb->is_read) {
+ quorum_vote(acb);
+ } else {
+ quorum_has_too_much_io_failed(acb);
+ }
+
+ quorum_aio_finalize(acb);
+}
+
+static void quorum_report_bad_versions(BDRVQuorumState *s,
+ QuorumAIOCB *acb,
+ QuorumVoteValue *value)
+{
+ QuorumVoteVersion *version;
+ QuorumVoteItem *item;
+
+ QLIST_FOREACH(version, &acb->votes.vote_list, next) {
+ if (acb->votes.compare(&version->value, value)) {
+ continue;
+ }
+ QLIST_FOREACH(item, &version->items, next) {
+ quorum_report_bad(acb, s->bs[item->index]->node_name, 0);
+ }
+ }
+}
+
+static void quorum_copy_qiov(QEMUIOVector *dest, QEMUIOVector *source)
+{
+ int i;
+ assert(dest->niov == source->niov);
+ assert(dest->size == source->size);
+ for (i = 0; i < source->niov; i++) {
+ assert(dest->iov[i].iov_len == source->iov[i].iov_len);
+ memcpy(dest->iov[i].iov_base,
+ source->iov[i].iov_base,
+ source->iov[i].iov_len);
+ }
+}
+
+static void quorum_count_vote(QuorumVotes *votes,
+ QuorumVoteValue *value,
+ int index)
+{
+ QuorumVoteVersion *v = NULL, *version = NULL;
+ QuorumVoteItem *item;
+
+ /* look if we have something with this hash */
+ QLIST_FOREACH(v, &votes->vote_list, next) {
+ if (votes->compare(&v->value, value)) {
+ version = v;
+ break;
+ }
+ }
+
+ /* It's a version not yet in the list add it */
+ if (!version) {
+ version = g_new0(QuorumVoteVersion, 1);
+ QLIST_INIT(&version->items);
+ memcpy(&version->value, value, sizeof(version->value));
+ version->index = index;
+ version->vote_count = 0;
+ QLIST_INSERT_HEAD(&votes->vote_list, version, next);
+ }
+
+ version->vote_count++;
+
+ item = g_new0(QuorumVoteItem, 1);
+ item->index = index;
+ QLIST_INSERT_HEAD(&version->items, item, next);
+}
+
+static void quorum_free_vote_list(QuorumVotes *votes)
+{
+ QuorumVoteVersion *version, *next_version;
+ QuorumVoteItem *item, *next_item;
+
+ QLIST_FOREACH_SAFE(version, &votes->vote_list, next, next_version) {
+ QLIST_REMOVE(version, next);
+ QLIST_FOREACH_SAFE(item, &version->items, next, next_item) {
+ QLIST_REMOVE(item, next);
+ g_free(item);
+ }
+ g_free(version);
+ }
+}
+
+static int quorum_compute_hash(QuorumAIOCB *acb, int i, QuorumVoteValue *hash)
+{
+ int j, ret;
+ gnutls_hash_hd_t dig;
+ QEMUIOVector *qiov = &acb->qcrs[i].qiov;
+
+ ret = gnutls_hash_init(&dig, GNUTLS_DIG_SHA256);
+
+ if (ret < 0) {
+ return ret;
+ }
+
+ for (j = 0; j < qiov->niov; j++) {
+ ret = gnutls_hash(dig, qiov->iov[j].iov_base, qiov->iov[j].iov_len);
+ if (ret < 0) {
+ break;
+ }
+ }
+
+ gnutls_hash_deinit(dig, (void *) hash);
+ return ret;
+}
+
+static QuorumVoteVersion *quorum_get_vote_winner(QuorumVotes *votes)
+{
+ int max = 0;
+ QuorumVoteVersion *candidate, *winner = NULL;
+
+ QLIST_FOREACH(candidate, &votes->vote_list, next) {
+ if (candidate->vote_count > max) {
+ max = candidate->vote_count;
+ winner = candidate;
+ }
+ }
+
+ return winner;
+}
+
+/* qemu_iovec_compare is handy for blkverify mode because it returns the first
+ * differing byte location. Yet it is handcoded to compare vectors one byte
+ * after another so it does not benefit from the libc SIMD optimizations.
+ * quorum_iovec_compare is written for speed and should be used in the non
+ * blkverify mode of quorum.
+ */
+static bool quorum_iovec_compare(QEMUIOVector *a, QEMUIOVector *b)
+{
+ int i;
+ int result;
+
+ assert(a->niov == b->niov);
+ for (i = 0; i < a->niov; i++) {
+ assert(a->iov[i].iov_len == b->iov[i].iov_len);
+ result = memcmp(a->iov[i].iov_base,
+ b->iov[i].iov_base,
+ a->iov[i].iov_len);
+ if (result) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static void GCC_FMT_ATTR(2, 3) quorum_err(QuorumAIOCB *acb,
+ const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ fprintf(stderr, "quorum: sector_num=%" PRId64 " nb_sectors=%d ",
+ acb->sector_num, acb->nb_sectors);
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+ va_end(ap);
+ exit(1);
+}
+
+static bool quorum_compare(QuorumAIOCB *acb,
+ QEMUIOVector *a,
+ QEMUIOVector *b)
+{
+ BDRVQuorumState *s = acb->common.bs->opaque;
+ ssize_t offset;
+
+ /* This driver will replace blkverify in this particular case */
+ if (s->is_blkverify) {
+ offset = qemu_iovec_compare(a, b);
+ if (offset != -1) {
+ quorum_err(acb, "contents mismatch in sector %" PRId64,
+ acb->sector_num +
+ (uint64_t)(offset / BDRV_SECTOR_SIZE));
+ }
+ return true;
+ }
+
+ return quorum_iovec_compare(a, b);
+}
+
+/* Do a vote to get the error code */
+static int quorum_vote_error(QuorumAIOCB *acb)
+{
+ BDRVQuorumState *s = acb->common.bs->opaque;
+ QuorumVoteVersion *winner = NULL;
+ QuorumVotes error_votes;
+ QuorumVoteValue result_value;
+ int i, ret = 0;
+ bool error = false;
+
+ QLIST_INIT(&error_votes.vote_list);
+ error_votes.compare = quorum_64bits_compare;
+
+ for (i = 0; i < s->num_children; i++) {
+ ret = acb->qcrs[i].ret;
+ if (ret) {
+ error = true;
+ result_value.l = ret;
+ quorum_count_vote(&error_votes, &result_value, i);
+ }
+ }
+
+ if (error) {
+ winner = quorum_get_vote_winner(&error_votes);
+ ret = winner->value.l;
+ }
+
+ quorum_free_vote_list(&error_votes);
+
+ return ret;
+}
+
+static void quorum_vote(QuorumAIOCB *acb)
+{
+ bool quorum = true;
+ int i, j, ret;
+ QuorumVoteValue hash;
+ BDRVQuorumState *s = acb->common.bs->opaque;
+ QuorumVoteVersion *winner;
+
+ if (quorum_has_too_much_io_failed(acb)) {
+ return;
+ }
+
+ /* get the index of the first successful read */
+ for (i = 0; i < s->num_children; i++) {
+ if (!acb->qcrs[i].ret) {
+ break;
+ }
+ }
+
+ assert(i < s->num_children);
+
+ /* compare this read with all other successful reads stopping at quorum
+ * failure
+ */
+ for (j = i + 1; j < s->num_children; j++) {
+ if (acb->qcrs[j].ret) {
+ continue;
+ }
+ quorum = quorum_compare(acb, &acb->qcrs[i].qiov, &acb->qcrs[j].qiov);
+ if (!quorum) {
+ break;
+ }
+ }
+
+ /* Every successful read agrees */
+ if (quorum) {
+ quorum_copy_qiov(acb->qiov, &acb->qcrs[i].qiov);
+ return;
+ }
+
+ /* compute hashes for each successful read, also store indexes */
+ for (i = 0; i < s->num_children; i++) {
+ if (acb->qcrs[i].ret) {
+ continue;
+ }
+ ret = quorum_compute_hash(acb, i, &hash);
+ /* if ever the hash computation failed */
+ if (ret < 0) {
+ acb->vote_ret = ret;
+ goto free_exit;
+ }
+ quorum_count_vote(&acb->votes, &hash, i);
+ }
+
+ /* vote to select the most represented version */
+ winner = quorum_get_vote_winner(&acb->votes);
+
+ /* if the winner count is smaller than threshold the read fails */
+ if (winner->vote_count < s->threshold) {
+ quorum_report_failure(acb);
+ acb->vote_ret = -EIO;
+ goto free_exit;
+ }
+
+ /* we have a winner: copy it */
+ quorum_copy_qiov(acb->qiov, &acb->qcrs[winner->index].qiov);
+
+ /* some versions are bad print them */
+ quorum_report_bad_versions(s, acb, &winner->value);
+
+free_exit:
+ /* free lists */
+ quorum_free_vote_list(&acb->votes);
+}
+
+static BlockDriverAIOCB *quorum_aio_readv(BlockDriverState *bs,
+ int64_t sector_num,
+ QEMUIOVector *qiov,
+ int nb_sectors,
+ BlockDriverCompletionFunc *cb,
+ void *opaque)
+{
+ BDRVQuorumState *s = bs->opaque;
+ QuorumAIOCB *acb = quorum_aio_get(s, bs, qiov, sector_num,
+ nb_sectors, cb, opaque);
+ int i;
+
+ acb->is_read = true;
+
+ for (i = 0; i < s->num_children; i++) {
+ acb->qcrs[i].buf = qemu_blockalign(s->bs[i], qiov->size);
+ qemu_iovec_init(&acb->qcrs[i].qiov, qiov->niov);
+ qemu_iovec_clone(&acb->qcrs[i].qiov, qiov, acb->qcrs[i].buf);
+ }
+
+ for (i = 0; i < s->num_children; i++) {
+ bdrv_aio_readv(s->bs[i], sector_num, &acb->qcrs[i].qiov, nb_sectors,
+ quorum_aio_cb, &acb->qcrs[i]);
+ }
+
+ return &acb->common;
+}
+
+static BlockDriverAIOCB *quorum_aio_writev(BlockDriverState *bs,
+ int64_t sector_num,
+ QEMUIOVector *qiov,
+ int nb_sectors,
+ BlockDriverCompletionFunc *cb,
+ void *opaque)
+{
+ BDRVQuorumState *s = bs->opaque;
+ QuorumAIOCB *acb = quorum_aio_get(s, bs, qiov, sector_num, nb_sectors,
+ cb, opaque);
+ int i;
+
+ for (i = 0; i < s->num_children; i++) {
+ acb->qcrs[i].aiocb = bdrv_aio_writev(s->bs[i], sector_num, qiov,
+ nb_sectors, &quorum_aio_cb,
+ &acb->qcrs[i]);
+ }
+
+ return &acb->common;
+}
+
+static int64_t quorum_getlength(BlockDriverState *bs)
+{
+ BDRVQuorumState *s = bs->opaque;
+ int64_t result;
+ int i;
+
+ /* check that all file have the same length */
+ result = bdrv_getlength(s->bs[0]);
+ if (result < 0) {
+ return result;
+ }
+ for (i = 1; i < s->num_children; i++) {
+ int64_t value = bdrv_getlength(s->bs[i]);
+ if (value < 0) {
+ return value;
+ }
+ if (value != result) {
+ return -EIO;
+ }
+ }
+
+ return result;
+}
+
+static void quorum_invalidate_cache(BlockDriverState *bs)
+{
+ BDRVQuorumState *s = bs->opaque;
+ int i;
+
+ for (i = 0; i < s->num_children; i++) {
+ bdrv_invalidate_cache(s->bs[i]);
+ }
+}
+
+static coroutine_fn int quorum_co_flush(BlockDriverState *bs)
+{
+ BDRVQuorumState *s = bs->opaque;
+ QuorumVoteVersion *winner = NULL;
+ QuorumVotes error_votes;
+ QuorumVoteValue result_value;
+ int i;
+ int result = 0;
+
+ QLIST_INIT(&error_votes.vote_list);
+ error_votes.compare = quorum_64bits_compare;
+
+ for (i = 0; i < s->num_children; i++) {
+ result = bdrv_co_flush(s->bs[i]);
+ result_value.l = result;
+ quorum_count_vote(&error_votes, &result_value, i);
+ }
+
+ winner = quorum_get_vote_winner(&error_votes);
+ result = winner->value.l;
+
+ quorum_free_vote_list(&error_votes);
+
+ return result;
+}
+
+static bool quorum_recurse_is_first_non_filter(BlockDriverState *bs,
+ BlockDriverState *candidate)
+{
+ BDRVQuorumState *s = bs->opaque;
+ int i;
+
+ for (i = 0; i < s->num_children; i++) {
+ bool perm = bdrv_recurse_is_first_non_filter(s->bs[i],
+ candidate);
+ if (perm) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static int quorum_valid_threshold(int threshold, int num_children, Error **errp)
+{
+
+ if (threshold < 1) {
+ error_set(errp, QERR_INVALID_PARAMETER_VALUE,
+ "vote-threshold", "value >= 1");
+ return -ERANGE;
+ }
+
+ if (threshold > num_children) {
+ error_setg(errp, "threshold may not exceed children count");
+ return -ERANGE;
+ }
+
+ return 0;
+}
+
+static QemuOptsList quorum_runtime_opts = {
+ .name = "quorum",
+ .head = QTAILQ_HEAD_INITIALIZER(quorum_runtime_opts.head),
+ .desc = {
+ {
+ .name = QUORUM_OPT_VOTE_THRESHOLD,
+ .type = QEMU_OPT_NUMBER,
+ .help = "The number of vote needed for reaching quorum",
+ },
+ {
+ .name = QUORUM_OPT_BLKVERIFY,
+ .type = QEMU_OPT_BOOL,
+ .help = "Trigger block verify mode if set",
+ },
+ { /* end of list */ }
+ },
+};
+
+static int quorum_open(BlockDriverState *bs, QDict *options, int flags,
+ Error **errp)
+{
+ BDRVQuorumState *s = bs->opaque;
+ Error *local_err = NULL;
+ QemuOpts *opts;
+ bool *opened;
+ QDict *sub = NULL;
+ QList *list = NULL;
+ const QListEntry *lentry;
+ int i;
+ int ret = 0;
+
+ qdict_flatten(options);
+ qdict_extract_subqdict(options, &sub, "children.");
+ qdict_array_split(sub, &list);
+
+ if (qdict_size(sub)) {
+ error_setg(&local_err, "Invalid option children.%s",
+ qdict_first(sub)->key);
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ /* count how many different children are present */
+ s->num_children = qlist_size(list);
+ if (s->num_children < 2) {
+ error_setg(&local_err,
+ "Number of provided children must be greater than 1");
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ opts = qemu_opts_create(&quorum_runtime_opts, NULL, 0, &error_abort);
+ qemu_opts_absorb_qdict(opts, options, &local_err);
+ if (error_is_set(&local_err)) {
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ s->threshold = qemu_opt_get_number(opts, QUORUM_OPT_VOTE_THRESHOLD, 0);
+
+ /* and validate it against s->num_children */
+ ret = quorum_valid_threshold(s->threshold, s->num_children, &local_err);
+ if (ret < 0) {
+ goto exit;
+ }
+
+ /* is the driver in blkverify mode */
+ if (qemu_opt_get_bool(opts, QUORUM_OPT_BLKVERIFY, false) &&
+ s->num_children == 2 && s->threshold == 2) {
+ s->is_blkverify = true;
+ } else if (qemu_opt_get_bool(opts, QUORUM_OPT_BLKVERIFY, false)) {
+ fprintf(stderr, "blkverify mode is set by setting blkverify=on "
+ "and using two files with vote_threshold=2\n");
+ }
+
+ /* allocate the children BlockDriverState array */
+ s->bs = g_new0(BlockDriverState *, s->num_children);
+ opened = g_new0(bool, s->num_children);
+
+ for (i = 0, lentry = qlist_first(list); lentry;
+ lentry = qlist_next(lentry), i++) {
+ QDict *d;
+ QString *string;
+
+ switch (qobject_type(lentry->value))
+ {
+ /* List of options */
+ case QTYPE_QDICT:
+ d = qobject_to_qdict(lentry->value);
+ QINCREF(d);
+ ret = bdrv_open(&s->bs[i], NULL, NULL, d, flags, NULL,
+ &local_err);
+ break;
+
+ /* QMP reference */
+ case QTYPE_QSTRING:
+ string = qobject_to_qstring(lentry->value);
+ ret = bdrv_open(&s->bs[i], NULL, qstring_get_str(string), NULL,
+ flags, NULL, &local_err);
+ break;
+
+ default:
+ error_setg(&local_err, "Specification of child block device %i "
+ "is invalid", i);
+ ret = -EINVAL;
+ }
+
+ if (ret < 0) {
+ goto close_exit;
+ }
+ opened[i] = true;
+ }
+
+ g_free(opened);
+ goto exit;
+
+close_exit:
+ /* cleanup on error */
+ for (i = 0; i < s->num_children; i++) {
+ if (!opened[i]) {
+ continue;
+ }
+ bdrv_unref(s->bs[i]);
+ }
+ g_free(s->bs);
+ g_free(opened);
+exit:
+ /* propagate error */
+ if (error_is_set(&local_err)) {
+ error_propagate(errp, local_err);
+ }
+ QDECREF(list);
+ QDECREF(sub);
+ return ret;
+}
+
+static void quorum_close(BlockDriverState *bs)
+{
+ BDRVQuorumState *s = bs->opaque;
+ int i;
+
+ for (i = 0; i < s->num_children; i++) {
+ bdrv_unref(s->bs[i]);
+ }
+
+ g_free(s->bs);
+}
+
+static BlockDriver bdrv_quorum = {
+ .format_name = "quorum",
+ .protocol_name = "quorum",
+
+ .instance_size = sizeof(BDRVQuorumState),
+
+ .bdrv_file_open = quorum_open,
+ .bdrv_close = quorum_close,
+
+ .authorizations = { true, true },
+
+ .bdrv_co_flush_to_disk = quorum_co_flush,
+
+ .bdrv_getlength = quorum_getlength,
+
+ .bdrv_aio_readv = quorum_aio_readv,
+ .bdrv_aio_writev = quorum_aio_writev,
+ .bdrv_invalidate_cache = quorum_invalidate_cache,
+
+ .bdrv_recurse_is_first_non_filter = quorum_recurse_is_first_non_filter,
+};
+
+static void bdrv_quorum_init(void)
+{
+ bdrv_register(&bdrv_quorum);
+}
+
+block_init(bdrv_quorum_init);
diff --git a/block/sheepdog.c b/block/sheepdog.c
index e6c0376566..f7bd0242e5 100644
--- a/block/sheepdog.c
+++ b/block/sheepdog.c
@@ -1534,7 +1534,8 @@ static int sd_prealloc(const char *filename)
Error *local_err = NULL;
int ret;
- ret = bdrv_file_open(&bs, filename, NULL, NULL, BDRV_O_RDWR, &local_err);
+ ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL,
+ NULL, &local_err);
if (ret < 0) {
qerror_report_err(local_err);
error_free(local_err);
@@ -1695,7 +1696,9 @@ static int sd_create(const char *filename, QEMUOptionParameter *options,
goto out;
}
- ret = bdrv_file_open(&bs, backing_file, NULL, NULL, 0, &local_err);
+ bs = NULL;
+ ret = bdrv_open(&bs, backing_file, NULL, NULL, BDRV_O_PROTOCOL, NULL,
+ &local_err);
if (ret < 0) {
qerror_report_err(local_err);
error_free(local_err);
diff --git a/block/vdi.c b/block/vdi.c
index 2d7490f173..ae49cd83ca 100644
--- a/block/vdi.c
+++ b/block/vdi.c
@@ -395,43 +395,50 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags,
}
if (header.signature != VDI_SIGNATURE) {
- logout("bad vdi signature %08x\n", header.signature);
- ret = -EMEDIUMTYPE;
+ error_setg(errp, "Image not in VDI format (bad signature %08x)", header.signature);
+ ret = -EINVAL;
goto fail;
} else if (header.version != VDI_VERSION_1_1) {
- logout("unsupported version %u.%u\n",
- header.version >> 16, header.version & 0xffff);
+ error_setg(errp, "unsupported VDI image (version %u.%u)",
+ header.version >> 16, header.version & 0xffff);
ret = -ENOTSUP;
goto fail;
} else if (header.offset_bmap % SECTOR_SIZE != 0) {
/* We only support block maps which start on a sector boundary. */
- logout("unsupported block map offset 0x%x B\n", header.offset_bmap);
+ error_setg(errp, "unsupported VDI image (unaligned block map offset "
+ "0x%x)", header.offset_bmap);
ret = -ENOTSUP;
goto fail;
} else if (header.offset_data % SECTOR_SIZE != 0) {
/* We only support data blocks which start on a sector boundary. */
- logout("unsupported data offset 0x%x B\n", header.offset_data);
+ error_setg(errp, "unsupported VDI image (unaligned data offset 0x%x)",
+ header.offset_data);
ret = -ENOTSUP;
goto fail;
} else if (header.sector_size != SECTOR_SIZE) {
- logout("unsupported sector size %u B\n", header.sector_size);
+ error_setg(errp, "unsupported VDI image (sector size %u is not %u)",
+ header.sector_size, SECTOR_SIZE);
ret = -ENOTSUP;
goto fail;
} else if (header.block_size != 1 * MiB) {
- logout("unsupported block size %u B\n", header.block_size);
+ error_setg(errp, "unsupported VDI image (sector size %u is not %u)",
+ header.block_size, 1 * MiB);
ret = -ENOTSUP;
goto fail;
} else if (header.disk_size >
(uint64_t)header.blocks_in_image * header.block_size) {
- logout("unsupported disk size %" PRIu64 " B\n", header.disk_size);
+ error_setg(errp, "unsupported VDI image (disk size %" PRIu64 ", "
+ "image bitmap has room for %" PRIu64 ")",
+ header.disk_size,
+ (uint64_t)header.blocks_in_image * header.block_size);
ret = -ENOTSUP;
goto fail;
} else if (!uuid_is_null(header.uuid_link)) {
- logout("link uuid != 0, unsupported\n");
+ error_setg(errp, "unsupported VDI image (non-NULL link UUID)");
ret = -ENOTSUP;
goto fail;
} else if (!uuid_is_null(header.uuid_parent)) {
- logout("parent uuid != 0, unsupported\n");
+ error_setg(errp, "unsupported VDI image (non-NULL parent UUID)");
ret = -ENOTSUP;
goto fail;
}
diff --git a/block/vhdx.c b/block/vhdx.c
index 55689cf641..5390ba6d0f 100644
--- a/block/vhdx.c
+++ b/block/vhdx.c
@@ -402,9 +402,10 @@ int vhdx_update_headers(BlockDriverState *bs, BDRVVHDXState *s,
}
/* opens the specified header block from the VHDX file header section */
-static int vhdx_parse_header(BlockDriverState *bs, BDRVVHDXState *s)
+static void vhdx_parse_header(BlockDriverState *bs, BDRVVHDXState *s,
+ Error **errp)
{
- int ret = 0;
+ int ret;
VHDXHeader *header1;
VHDXHeader *header2;
bool h1_valid = false;
@@ -462,7 +463,6 @@ static int vhdx_parse_header(BlockDriverState *bs, BDRVVHDXState *s)
} else if (!h1_valid && h2_valid) {
s->curr_header = 1;
} else if (!h1_valid && !h2_valid) {
- ret = -EINVAL;
goto fail;
} else {
/* If both headers are valid, then we choose the active one by the
@@ -473,27 +473,22 @@ static int vhdx_parse_header(BlockDriverState *bs, BDRVVHDXState *s)
} else if (h2_seq > h1_seq) {
s->curr_header = 1;
} else {
- ret = -EINVAL;
goto fail;
}
}
vhdx_region_register(s, s->headers[s->curr_header]->log_offset,
s->headers[s->curr_header]->log_length);
-
- ret = 0;
-
goto exit;
fail:
- qerror_report(ERROR_CLASS_GENERIC_ERROR, "No valid VHDX header found");
+ error_setg_errno(errp, -ret, "No valid VHDX header found");
qemu_vfree(header1);
qemu_vfree(header2);
s->headers[0] = NULL;
s->headers[1] = NULL;
exit:
qemu_vfree(buffer);
- return ret;
}
@@ -878,7 +873,7 @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags,
int ret = 0;
uint32_t i;
uint64_t signature;
-
+ Error *local_err = NULL;
s->bat = NULL;
s->first_visible_write = true;
@@ -901,8 +896,10 @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags,
* header update */
vhdx_guid_generate(&s->session_guid);
- ret = vhdx_parse_header(bs, s);
- if (ret < 0) {
+ vhdx_parse_header(bs, s, &local_err);
+ if (local_err != NULL) {
+ error_propagate(errp, local_err);
+ ret = -EINVAL;
goto fail;
}
@@ -1797,7 +1794,9 @@ static int vhdx_create(const char *filename, QEMUOptionParameter *options,
goto exit;
}
- ret = bdrv_file_open(&bs, filename, NULL, NULL, BDRV_O_RDWR, &local_err);
+ bs = NULL;
+ ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL,
+ NULL, &local_err);
if (ret < 0) {
error_propagate(errp, local_err);
goto exit;
diff --git a/block/vmdk.c b/block/vmdk.c
index ff6f5ee911..83839f9b7a 100644
--- a/block/vmdk.c
+++ b/block/vmdk.c
@@ -526,8 +526,34 @@ static int vmdk_open_vmfs_sparse(BlockDriverState *bs,
return ret;
}
-static int vmdk_open_desc_file(BlockDriverState *bs, int flags,
- uint64_t desc_offset, Error **errp);
+static int vmdk_open_desc_file(BlockDriverState *bs, int flags, char *buf,
+ Error **errp);
+
+static char *vmdk_read_desc(BlockDriverState *file, uint64_t desc_offset,
+ Error **errp)
+{
+ int64_t size;
+ char *buf;
+ int ret;
+
+ size = bdrv_getlength(file);
+ if (size < 0) {
+ error_setg_errno(errp, -size, "Could not access file");
+ return NULL;
+ }
+
+ size = MIN(size, 1 << 20); /* avoid unbounded allocation */
+ buf = g_malloc0(size + 1);
+
+ ret = bdrv_pread(file, desc_offset, buf, size);
+ if (ret < 0) {
+ error_setg_errno(errp, -ret, "Could not read from file");
+ g_free(buf);
+ return NULL;
+ }
+
+ return buf;
+}
static int vmdk_open_vmdk4(BlockDriverState *bs,
BlockDriverState *file,
@@ -546,11 +572,18 @@ static int vmdk_open_vmdk4(BlockDriverState *bs,
error_setg_errno(errp, -ret,
"Could not read header from file '%s'",
file->filename);
+ return -EINVAL;
}
if (header.capacity == 0) {
uint64_t desc_offset = le64_to_cpu(header.desc_offset);
if (desc_offset) {
- return vmdk_open_desc_file(bs, flags, desc_offset << 9, errp);
+ char *buf = vmdk_read_desc(file, desc_offset << 9, errp);
+ if (!buf) {
+ return -EINVAL;
+ }
+ ret = vmdk_open_desc_file(bs, flags, buf, errp);
+ g_free(buf);
+ return ret;
}
}
@@ -609,8 +642,8 @@ static int vmdk_open_vmdk4(BlockDriverState *bs,
char buf[64];
snprintf(buf, sizeof(buf), "VMDK version %d",
le32_to_cpu(header.version));
- qerror_report(QERR_UNKNOWN_BLOCK_FORMAT_FEATURE,
- bs->device_name, "vmdk", buf);
+ error_set(errp, QERR_UNKNOWN_BLOCK_FORMAT_FEATURE,
+ bs->device_name, "vmdk", buf);
return -ENOTSUP;
} else if (le32_to_cpu(header.version) == 3 && (flags & BDRV_O_RDWR)) {
/* VMware KB 2064959 explains that version 3 added support for
@@ -622,7 +655,7 @@ static int vmdk_open_vmdk4(BlockDriverState *bs,
}
if (le32_to_cpu(header.num_gtes_per_gt) > 512) {
- error_report("L2 table size too big");
+ error_setg(errp, "L2 table size too big");
return -EINVAL;
}
@@ -638,8 +671,8 @@ static int vmdk_open_vmdk4(BlockDriverState *bs,
}
if (bdrv_getlength(file) <
le64_to_cpu(header.grain_offset) * BDRV_SECTOR_SIZE) {
- error_report("File truncated, expecting at least %lld bytes",
- le64_to_cpu(header.grain_offset) * BDRV_SECTOR_SIZE);
+ error_setg(errp, "File truncated, expecting at least %lld bytes",
+ le64_to_cpu(header.grain_offset) * BDRV_SECTOR_SIZE);
return -EINVAL;
}
@@ -701,16 +734,12 @@ static int vmdk_parse_description(const char *desc, const char *opt_name,
/* Open an extent file and append to bs array */
static int vmdk_open_sparse(BlockDriverState *bs,
- BlockDriverState *file,
- int flags, Error **errp)
+ BlockDriverState *file, int flags,
+ char *buf, Error **errp)
{
uint32_t magic;
- if (bdrv_pread(file, 0, &magic, sizeof(magic)) != sizeof(magic)) {
- return -EIO;
- }
-
- magic = be32_to_cpu(magic);
+ magic = ldl_be_p(buf);
switch (magic) {
case VMDK3_MAGIC:
return vmdk_open_vmfs_sparse(bs, file, flags, errp);
@@ -719,7 +748,8 @@ static int vmdk_open_sparse(BlockDriverState *bs,
return vmdk_open_vmdk4(bs, file, flags, errp);
break;
default:
- return -EMEDIUMTYPE;
+ error_setg(errp, "Image not in VMDK format");
+ return -EINVAL;
break;
}
}
@@ -776,8 +806,9 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
path_combine(extent_path, sizeof(extent_path),
desc_file_path, fname);
- ret = bdrv_file_open(&extent_file, extent_path, NULL, NULL,
- bs->open_flags, errp);
+ extent_file = NULL;
+ ret = bdrv_open(&extent_file, extent_path, NULL, NULL,
+ bs->open_flags | BDRV_O_PROTOCOL, NULL, errp);
if (ret) {
return ret;
}
@@ -794,8 +825,14 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
extent->flat_start_offset = flat_offset << 9;
} else if (!strcmp(type, "SPARSE") || !strcmp(type, "VMFSSPARSE")) {
/* SPARSE extent and VMFSSPARSE extent are both "COWD" sparse file*/
- ret = vmdk_open_sparse(bs, extent_file, bs->open_flags, errp);
+ char *buf = vmdk_read_desc(extent_file, 0, errp);
+ if (!buf) {
+ ret = -EINVAL;
+ } else {
+ ret = vmdk_open_sparse(bs, extent_file, bs->open_flags, buf, errp);
+ }
if (ret) {
+ g_free(buf);
bdrv_unref(extent_file);
return ret;
}
@@ -818,29 +855,16 @@ next_line:
return 0;
}
-static int vmdk_open_desc_file(BlockDriverState *bs, int flags,
- uint64_t desc_offset, Error **errp)
+static int vmdk_open_desc_file(BlockDriverState *bs, int flags, char *buf,
+ Error **errp)
{
int ret;
- char *buf = NULL;
char ct[128];
BDRVVmdkState *s = bs->opaque;
- int64_t size;
-
- size = bdrv_getlength(bs->file);
- if (size < 0) {
- return -EINVAL;
- }
-
- size = MIN(size, 1 << 20); /* avoid unbounded allocation */
- buf = g_malloc0(size + 1);
- ret = bdrv_pread(bs->file, desc_offset, buf, size);
- if (ret < 0) {
- goto exit;
- }
if (vmdk_parse_description(buf, "createType", ct, sizeof(ct))) {
- ret = -EMEDIUMTYPE;
+ error_setg(errp, "invalid VMDK image descriptor");
+ ret = -EINVAL;
goto exit;
}
if (strcmp(ct, "monolithicFlat") &&
@@ -856,24 +880,37 @@ static int vmdk_open_desc_file(BlockDriverState *bs, int flags,
s->desc_offset = 0;
ret = vmdk_parse_extents(buf, bs, bs->file->filename, errp);
exit:
- g_free(buf);
return ret;
}
static int vmdk_open(BlockDriverState *bs, QDict *options, int flags,
Error **errp)
{
+ char *buf = NULL;
int ret;
BDRVVmdkState *s = bs->opaque;
+ uint32_t magic;
- if (vmdk_open_sparse(bs, bs->file, flags, errp) == 0) {
- s->desc_offset = 0x200;
- } else {
- ret = vmdk_open_desc_file(bs, flags, 0, errp);
- if (ret) {
- goto fail;
- }
+ buf = vmdk_read_desc(bs->file, 0, errp);
+ if (!buf) {
+ return -EINVAL;
}
+
+ magic = ldl_be_p(buf);
+ switch (magic) {
+ case VMDK3_MAGIC:
+ case VMDK4_MAGIC:
+ ret = vmdk_open_sparse(bs, bs->file, flags, buf, errp);
+ s->desc_offset = 0x200;
+ break;
+ default:
+ ret = vmdk_open_desc_file(bs, flags, buf, errp);
+ break;
+ }
+ if (ret) {
+ goto fail;
+ }
+
/* try to open parent images, if exist */
ret = vmdk_parent_open(bs);
if (ret) {
@@ -888,10 +925,11 @@ static int vmdk_open(BlockDriverState *bs, QDict *options, int flags,
QERR_BLOCK_FORMAT_FEATURE_NOT_SUPPORTED,
"vmdk", bs->device_name, "live migration");
migrate_add_blocker(s->migration_blocker);
-
+ g_free(buf);
return 0;
fail:
+ g_free(buf);
g_free(s->create_type);
s->create_type = NULL;
vmdk_free_extents(bs);
@@ -1493,7 +1531,9 @@ static int vmdk_create_extent(const char *filename, int64_t filesize,
goto exit;
}
- ret = bdrv_file_open(&bs, filename, NULL, NULL, BDRV_O_RDWR, &local_err);
+ assert(bs == NULL);
+ ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL,
+ NULL, &local_err);
if (ret < 0) {
error_propagate(errp, local_err);
goto exit;
@@ -1755,10 +1795,10 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options,
goto exit;
}
if (backing_file) {
- BlockDriverState *bs = bdrv_new("");
- ret = bdrv_open(bs, backing_file, NULL, BDRV_O_NO_BACKING, NULL, errp);
+ BlockDriverState *bs = NULL;
+ ret = bdrv_open(&bs, backing_file, NULL, NULL, BDRV_O_NO_BACKING, NULL,
+ errp);
if (ret != 0) {
- bdrv_unref(bs);
goto exit;
}
if (strcmp(bs->drv->format_name, "vmdk")) {
@@ -1831,7 +1871,9 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options,
goto exit;
}
}
- ret = bdrv_file_open(&new_bs, filename, NULL, NULL, BDRV_O_RDWR, &local_err);
+ assert(new_bs == NULL);
+ ret = bdrv_open(&new_bs, filename, NULL, NULL,
+ BDRV_O_RDWR | BDRV_O_PROTOCOL, NULL, &local_err);
if (ret < 0) {
error_setg_errno(errp, -ret, "Could not write description");
goto exit;
diff --git a/block/vpc.c b/block/vpc.c
index 1d326cbf44..82bf2485a5 100644
--- a/block/vpc.c
+++ b/block/vpc.c
@@ -190,7 +190,8 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags,
goto fail;
}
if (strncmp(footer->creator, "conectix", 8)) {
- ret = -EMEDIUMTYPE;
+ error_setg(errp, "invalid VPC image");
+ ret = -EINVAL;
goto fail;
}
disk_type = VHD_FIXED;
diff --git a/block/vvfat.c b/block/vvfat.c
index a19e4ca227..f966ea5da8 100644
--- a/block/vvfat.c
+++ b/block/vvfat.c
@@ -1086,16 +1086,14 @@ DLOG(if (stderr == NULL) {
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
qemu_opts_absorb_qdict(opts, options, &local_err);
if (local_err) {
- qerror_report_err(local_err);
- error_free(local_err);
+ error_propagate(errp, local_err);
ret = -EINVAL;
goto fail;
}
dirname = qemu_opt_get(opts, "dir");
if (!dirname) {
- qerror_report(ERROR_CLASS_GENERIC_ERROR, "vvfat block driver requires "
- "a 'dir' option");
+ error_setg(errp, "vvfat block driver requires a 'dir' option");
ret = -EINVAL;
goto fail;
}
@@ -1135,8 +1133,7 @@ DLOG(if (stderr == NULL) {
case 12:
break;
default:
- qerror_report(ERROR_CLASS_GENERIC_ERROR, "Valid FAT types are only "
- "12, 16 and 32");
+ error_setg(errp, "Valid FAT types are only 12, 16 and 32");
ret = -EINVAL;
goto fail;
}
@@ -2936,15 +2933,13 @@ static int enable_write_target(BDRVVVFATState *s)
goto err;
}
- s->qcow = bdrv_new("");
-
- ret = bdrv_open(s->qcow, s->qcow_filename, NULL,
+ s->qcow = NULL;
+ ret = bdrv_open(&s->qcow, s->qcow_filename, NULL, NULL,
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH, bdrv_qcow,
&local_err);
if (ret < 0) {
qerror_report_err(local_err);
error_free(local_err);
- bdrv_unref(s->qcow);
goto err;
}