diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c index 5fb8c6695ad4f8953689c955d5bd3e11b1404f4e..750abe37d4bb7a1a0e7e63f79f71037a8c2b6a86 100644 --- a/block/qcow2-cluster.c +++ b/block/qcow2-cluster.c @@ -515,13 +515,16 @@ static int get_cluster_table(BlockDriverState *bs, uint64_t offset, return ret; } } else { - /* FIXME Order */ - if (l2_offset) - qcow2_free_clusters(bs, l2_offset, s->l2_size * sizeof(uint64_t)); + /* First allocate a new L2 table (and do COW if needed) */ ret = l2_allocate(bs, l1_index, &l2_table); if (ret < 0) { return ret; } + + /* Then decrease the refcount of the old table */ + if (l2_offset) { + qcow2_free_clusters(bs, l2_offset, s->l2_size * sizeof(uint64_t)); + } l2_offset = s->l1_table[l1_index] & ~QCOW_OFLAG_COPIED; } @@ -878,11 +881,11 @@ int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset) BLKDBG_EVENT(bs->file, BLKDBG_READ_COMPRESSED); ret = bdrv_read(bs->file, coffset >> 9, s->cluster_data, nb_csectors); if (ret < 0) { - return -1; + return ret; } if (decompress_buffer(s->cluster_cache, s->cluster_size, s->cluster_data + sector_offset, csize) < 0) { - return -1; + return -EIO; } s->cluster_cache_offset = coffset; } diff --git a/block/qcow2.c b/block/qcow2.c index a1773e4790f35e761aa895cff3ba4905a920d15b..75b8becc0ab2f68db5dbbae9e0d325f12a36797f 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -28,6 +28,7 @@ #include "aes.h" #include "block/qcow2.h" #include "qemu-error.h" +#include "qerror.h" /* Differences with QCOW: @@ -59,7 +60,7 @@ static int qcow2_probe(const uint8_t *buf, int buf_size, const char *filename) if (buf_size >= sizeof(QCowHeader) && be32_to_cpu(cow_header->magic) == QCOW_MAGIC && - be32_to_cpu(cow_header->version) == QCOW_VERSION) + be32_to_cpu(cow_header->version) >= QCOW_VERSION) return 100; else return 0; @@ -163,10 +164,18 @@ static int qcow2_open(BlockDriverState *bs, int flags) be64_to_cpus(&header.snapshots_offset); be32_to_cpus(&header.nb_snapshots); - if (header.magic != QCOW_MAGIC || header.version != QCOW_VERSION) { + if (header.magic != QCOW_MAGIC) { 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, "qcow2", version); + ret = -ENOTSUP; + goto fail; + } if (header.cluster_bits < MIN_CLUSTER_BITS || header.cluster_bits > MAX_CLUSTER_BITS) { ret = -EINVAL; @@ -355,7 +364,7 @@ int qcow2_backing_read1(BlockDriverState *bs, QEMUIOVector *qiov, else n1 = bs->total_sectors - sector_num; - qemu_iovec_memset(qiov, 0, 512 * (nb_sectors - n1)); + qemu_iovec_memset_skip(qiov, 0, 512 * (nb_sectors - n1), 512 * n1); return n1; } @@ -478,10 +487,11 @@ static void qcow2_aio_read_cb(void *opaque, int ret) if (n1 > 0) { BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING_AIO); acb->hd_aiocb = bdrv_aio_readv(bs->backing_hd, acb->sector_num, - &acb->hd_qiov, acb->cur_nr_sectors, - qcow2_aio_read_cb, acb); - if (acb->hd_aiocb == NULL) + &acb->hd_qiov, n1, qcow2_aio_read_cb, acb); + if (acb->hd_aiocb == NULL) { + ret = -EIO; goto done; + } } else { ret = qcow2_schedule_bh(qcow2_aio_read_bh, acb); if (ret < 0) @@ -496,8 +506,10 @@ static void qcow2_aio_read_cb(void *opaque, int ret) } } else if (acb->cluster_offset & QCOW_OFLAG_COMPRESSED) { /* add AIO support for compressed blocks ? */ - if (qcow2_decompress_cluster(bs, acb->cluster_offset) < 0) + ret = qcow2_decompress_cluster(bs, acb->cluster_offset); + if (ret < 0) { goto done; + } qemu_iovec_from_buffer(&acb->hd_qiov, s->cluster_cache + index_in_cluster * 512, diff --git a/block/qed.c b/block/qed.c index 32734486c59e7a47d381e5a13bce6a0eea6b4335..75ae2440ee44ce7c9a69b1fc3095ae112cb21a1d 100644 --- a/block/qed.c +++ b/block/qed.c @@ -14,6 +14,7 @@ #include "trace.h" #include "qed.h" +#include "qerror.h" static void qed_aio_cancel(BlockDriverAIOCB *blockacb) { @@ -311,7 +312,13 @@ static int bdrv_qed_open(BlockDriverState *bs, int flags) return -EINVAL; } if (s->header.features & ~QED_FEATURE_MASK) { - return -ENOTSUP; /* image uses unsupported feature bits */ + /* 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, + bs->device_name, "QED", buf); + return -ENOTSUP; } if (!qed_is_cluster_size_valid(s->header.cluster_size)) { return -EINVAL; diff --git a/blockdev.c b/blockdev.c index ecfadc138fc34808d7492ac1a47eef964677a641..0690cc8bea5b2ac1ad4427a6c042ea00864ec341 100644 --- a/blockdev.c +++ b/blockdev.c @@ -182,6 +182,7 @@ static void drive_uninit(DriveInfo *dinfo) { qemu_opts_del(dinfo->opts); bdrv_delete(dinfo->bdrv); + qemu_free(dinfo->id); QTAILQ_REMOVE(&drives, dinfo, next); qemu_free(dinfo); } @@ -525,7 +526,7 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi) } else if (ro == 1) { if (type != IF_SCSI && type != IF_VIRTIO && type != IF_FLOPPY && type != IF_NONE) { error_report("readonly not supported by this bus type"); - return NULL; + goto err; } } @@ -535,12 +536,19 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi) if (ret < 0) { error_report("could not open disk image %s: %s", file, strerror(-ret)); - return NULL; + goto err; } if (bdrv_key_required(dinfo->bdrv)) autostart = 0; return dinfo; + +err: + bdrv_delete(dinfo->bdrv); + qemu_free(dinfo->id); + QTAILQ_REMOVE(&drives, dinfo, next); + qemu_free(dinfo); + return NULL; } void do_commit(Monitor *mon, const QDict *qdict) diff --git a/cutils.c b/cutils.c index 8d562b28ab0d5fb71c0fa88a44cdcdcd623af2b2..f9a7e3689e04605beebabef940f280e1a8fca572 100644 --- a/cutils.c +++ b/cutils.c @@ -267,6 +267,37 @@ void qemu_iovec_memset(QEMUIOVector *qiov, int c, size_t count) } } +void qemu_iovec_memset_skip(QEMUIOVector *qiov, int c, size_t count, + size_t skip) +{ + int i; + size_t done; + void *iov_base; + uint64_t iov_len; + + done = 0; + for (i = 0; (i < qiov->niov) && (done != count); i++) { + if (skip >= qiov->iov[i].iov_len) { + /* Skip the whole iov */ + skip -= qiov->iov[i].iov_len; + continue; + } else { + /* Skip only part (or nothing) of the iov */ + iov_base = (uint8_t*) qiov->iov[i].iov_base + skip; + iov_len = qiov->iov[i].iov_len - skip; + skip = 0; + } + + if (done + iov_len > count) { + memset(iov_base, c, count - done); + break; + } else { + memset(iov_base, c, iov_len); + } + done += iov_len; + } +} + #ifndef _WIN32 /* Sets a specific flag */ int fcntl_setfl(int fd, int flag) diff --git a/hmp-commands.hx b/hmp-commands.hx index 38e1eb7ebbc84521fc0d724e97f6e6758276fa26..372bef42902054c08e1f4d4c019f42d9449bb1df 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -822,7 +822,7 @@ ETEXI { .name = "snapshot_blkdev", - .args_type = "device:s,snapshot_file:s?,format:s?", + .args_type = "device:B,snapshot_file:s?,format:s?", .params = "device [new-image-file] [format]", .help = "initiates a live snapshot\n\t\t\t" "of device. If a new image file is specified, the\n\t\t\t" diff --git a/qemu-common.h b/qemu-common.h index a4d9c21a39d94c6a7362e9bd677d7a604a86f979..40dad5208cf2a58da92c944b72fb3c4fdad43a93 100644 --- a/qemu-common.h +++ b/qemu-common.h @@ -323,6 +323,8 @@ void qemu_iovec_reset(QEMUIOVector *qiov); void qemu_iovec_to_buffer(QEMUIOVector *qiov, void *buf); void qemu_iovec_from_buffer(QEMUIOVector *qiov, const void *buf, size_t count); void qemu_iovec_memset(QEMUIOVector *qiov, int c, size_t count); +void qemu_iovec_memset_skip(QEMUIOVector *qiov, int c, size_t count, + size_t skip); struct Monitor; typedef struct Monitor Monitor; diff --git a/qemu-img.c b/qemu-img.c index 4a3735811c64a6508760d475583e04d3b77b0f8c..7e3cc4cbd5cc57086703227a4121af11db722e48 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -213,8 +213,9 @@ static BlockDriverState *bdrv_new_open(const char *filename, BlockDriverState *bs; BlockDriver *drv; char password[256]; + int ret; - bs = bdrv_new(""); + bs = bdrv_new("image"); if (fmt) { drv = bdrv_find_format(fmt); @@ -225,10 +226,13 @@ static BlockDriverState *bdrv_new_open(const char *filename, } else { drv = NULL; } - if (bdrv_open(bs, filename, flags, drv) < 0) { - error_report("Could not open '%s'", filename); + + ret = bdrv_open(bs, filename, flags, drv); + if (ret < 0) { + error_report("Could not open '%s': %s", filename, strerror(-ret)); goto fail; } + if (bdrv_is_encrypted(bs)) { printf("Disk image '%s' is encrypted.\n", filename); if (read_password(password, sizeof(password)) < 0) { diff --git a/qerror.c b/qerror.c index 9d0cdeb45cccea8f903d93f46d03dd7847ad427c..485560418bd4284b83c9e2c9a1a5efb977e1a675 100644 --- a/qerror.c +++ b/qerror.c @@ -200,6 +200,11 @@ static const QErrorStringTable qerror_table[] = { .error_fmt = QERR_UNDEFINED_ERROR, .desc = "An undefined error has ocurred", }, + { + .error_fmt = QERR_UNKNOWN_BLOCK_FORMAT_FEATURE, + .desc = "'%(device)' uses a %(format) feature which is not " + "supported by this qemu version: %(feature)", + }, { .error_fmt = QERR_VNC_SERVER_FAILED, .desc = "Could not start VNC server on %(target)", diff --git a/qerror.h b/qerror.h index b0f69dabe520ade0eee541a309f2dcfc66435a6d..f732d45e80bd0092d7c2fd3df6cc0c129f4dc3a2 100644 --- a/qerror.h +++ b/qerror.h @@ -165,6 +165,9 @@ QError *qobject_to_qerror(const QObject *obj); #define QERR_UNDEFINED_ERROR \ "{ 'class': 'UndefinedError', 'data': {} }" +#define QERR_UNKNOWN_BLOCK_FORMAT_FEATURE \ + "{ 'class': 'UnknownBlockFormatFeature', 'data': { 'device': %s, 'format': %s, 'feature': %s } }" + #define QERR_VNC_SERVER_FAILED \ "{ 'class': 'VNCServerFailed', 'data': { 'target': %s } }"