From 670df5e3b4b5ef830a7c3c970170dbfa11cbb8d2 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 6 Sep 2013 12:18:47 +0200 Subject: qcow2: Pass discard type to qcow2_discard_clusters() The function will be used internally instead of only being called for guest discard requests. Signed-off-by: Kevin Wolf Reviewed-by: Max Reitz --- block/qcow2-cluster.c | 8 ++++---- block/qcow2.c | 2 +- block/qcow2.h | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) (limited to 'block') diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c index 2d5aa92962..b0d688eb99 100644 --- a/block/qcow2-cluster.c +++ b/block/qcow2-cluster.c @@ -1338,7 +1338,7 @@ int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset) * clusters. */ static int discard_single_l2(BlockDriverState *bs, uint64_t offset, - unsigned int nb_clusters) + unsigned int nb_clusters, enum qcow2_discard_type type) { BDRVQcowState *s = bs->opaque; uint64_t *l2_table; @@ -1367,7 +1367,7 @@ static int discard_single_l2(BlockDriverState *bs, uint64_t offset, l2_table[l2_index + i] = cpu_to_be64(0); /* Then decrease the refcount */ - qcow2_free_any_clusters(bs, old_offset, 1, QCOW2_DISCARD_REQUEST); + qcow2_free_any_clusters(bs, old_offset, 1, type); } ret = qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); @@ -1379,7 +1379,7 @@ static int discard_single_l2(BlockDriverState *bs, uint64_t offset, } int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset, - int nb_sectors) + int nb_sectors, enum qcow2_discard_type type) { BDRVQcowState *s = bs->opaque; uint64_t end_offset; @@ -1402,7 +1402,7 @@ int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset, /* Each L2 table is handled by its own loop iteration */ while (nb_clusters > 0) { - ret = discard_single_l2(bs, offset, nb_clusters); + ret = discard_single_l2(bs, offset, nb_clusters, type); if (ret < 0) { goto fail; } diff --git a/block/qcow2.c b/block/qcow2.c index 578792f0a3..147822e8fd 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -1582,7 +1582,7 @@ static coroutine_fn int qcow2_co_discard(BlockDriverState *bs, qemu_co_mutex_lock(&s->lock); ret = qcow2_discard_clusters(bs, sector_num << BDRV_SECTOR_BITS, - nb_sectors); + nb_sectors, QCOW2_DISCARD_REQUEST); qemu_co_mutex_unlock(&s->lock); return ret; } diff --git a/block/qcow2.h b/block/qcow2.h index 1000239e4c..9c33b98457 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -450,7 +450,7 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m); int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset, - int nb_sectors); + int nb_sectors, enum qcow2_discard_type type); int qcow2_zero_clusters(BlockDriverState *bs, uint64_t offset, int nb_sectors); /* qcow2-snapshot.c functions */ -- cgit v1.2.3 From 1ebf561c11302f4fbe4afdd82758fe053cf1d5fc Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Fri, 6 Sep 2013 12:20:08 +0200 Subject: qcow2: Discard VM state in active L1 after creating snapshot During savevm, the VM state is written to the active L1 of the image and then a snapshot is taken. After that, the VM state isn't needed any more in the active L1 and should be discarded. This is implemented by this patch. The impact of not discarding the VM state is that a snapshot can never become smaller than any previous snapshot (because it would be padded with old VM state), and more importantly that future savevm operations cause unnecessary COWs (with associated flushes), which makes subsequent snapshots much slower. Signed-off-by: Kevin Wolf Reviewed-by: Max Reitz --- block/qcow2-snapshot.c | 7 +++++++ block/qcow2.c | 5 ----- block/qcow2.h | 5 +++++ 3 files changed, 12 insertions(+), 5 deletions(-) (limited to 'block') diff --git a/block/qcow2-snapshot.c b/block/qcow2-snapshot.c index e7e601301a..ffead08ca2 100644 --- a/block/qcow2-snapshot.c +++ b/block/qcow2-snapshot.c @@ -416,6 +416,13 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) g_free(old_snapshot_list); + /* The VM state isn't needed any more in the active L1 table; in fact, it + * hurts by causing expensive COW for the next snapshot. */ + qcow2_discard_clusters(bs, qcow2_vm_state_offset(s), + align_offset(sn->vm_state_size, s->cluster_size) + >> BDRV_SECTOR_BITS, + QCOW2_DISCARD_NEVER); + #ifdef DEBUG_ALLOC { BdrvCheckResult result = {0}; diff --git a/block/qcow2.c b/block/qcow2.c index 147822e8fd..c9e266e22e 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -1757,11 +1757,6 @@ static coroutine_fn int qcow2_co_flush_to_os(BlockDriverState *bs) return 0; } -static int64_t qcow2_vm_state_offset(BDRVQcowState *s) -{ - return (int64_t)s->l1_vm_state_index << (s->cluster_bits + s->l2_bits); -} - static int qcow2_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) { BDRVQcowState *s = bs->opaque; diff --git a/block/qcow2.h b/block/qcow2.h index 9c33b98457..49eed828c5 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -361,6 +361,11 @@ static inline int64_t align_offset(int64_t offset, int n) return offset; } +static inline int64_t qcow2_vm_state_offset(BDRVQcowState *s) +{ + return (int64_t)s->l1_vm_state_index << (s->cluster_bits + s->l2_bits); +} + static inline int qcow2_get_cluster_type(uint64_t l2_entry) { if (l2_entry & QCOW_OFLAG_COMPRESSED) { -- cgit v1.2.3 From 56e023af805215260c71d44f5b5a98087f4920d2 Mon Sep 17 00:00:00 2001 From: Tal Kain Date: Mon, 9 Sep 2013 11:14:55 +0200 Subject: raw-win32.c: Fix incorrect handling behaviour of small block files It is a valid case that the read data's size is smaller than the requested size since there could be files that are smaller than the minimum block size (For ex. when a VMDK disk descriptor file) Signed-off-by: Tal Kain Signed-off-by: Kevin Wolf --- block/raw-win32.c | 1 + 1 file changed, 1 insertion(+) (limited to 'block') diff --git a/block/raw-win32.c b/block/raw-win32.c index d2d2d9f4d4..ff3c5ea0d7 100644 --- a/block/raw-win32.c +++ b/block/raw-win32.c @@ -85,6 +85,7 @@ static size_t handle_aiocb_rw(RawWin32AIOData *aiocb) ret_count = 0; } if (ret_count != len) { + offset += ret_count; break; } offset += len; -- cgit v1.2.3 From e7108feaace8e02b3a4bf010448fc2744f753381 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Tue, 3 Sep 2013 10:09:51 +0200 Subject: qcow2-cache: Empty cache Add a function for emptying a cache, i.e., flushing it and marking all elements invalid. Signed-off-by: Max Reitz Signed-off-by: Kevin Wolf --- block/qcow2-cache.c | 18 ++++++++++++++++++ block/qcow2.h | 2 ++ 2 files changed, 20 insertions(+) (limited to 'block') diff --git a/block/qcow2-cache.c b/block/qcow2-cache.c index 7bcae09a69..40a5a3fc39 100644 --- a/block/qcow2-cache.c +++ b/block/qcow2-cache.c @@ -202,6 +202,24 @@ void qcow2_cache_depends_on_flush(Qcow2Cache *c) c->depends_on_flush = true; } +int qcow2_cache_empty(BlockDriverState *bs, Qcow2Cache *c) +{ + int ret, i; + + ret = qcow2_cache_flush(bs, c); + if (ret < 0) { + return ret; + } + + for (i = 0; i < c->size; i++) { + assert(c->entries[i].ref == 0); + c->entries[i].offset = 0; + c->entries[i].cache_hits = 0; + } + + return 0; +} + static int qcow2_cache_find_entry_to_replace(Qcow2Cache *c) { int i; diff --git a/block/qcow2.h b/block/qcow2.h index 49eed828c5..35c822b8a6 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -478,6 +478,8 @@ int qcow2_cache_set_dependency(BlockDriverState *bs, Qcow2Cache *c, Qcow2Cache *dependency); void qcow2_cache_depends_on_flush(Qcow2Cache *c); +int qcow2_cache_empty(BlockDriverState *bs, Qcow2Cache *c); + int qcow2_cache_get(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset, void **table); int qcow2_cache_get_empty(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset, -- cgit v1.2.3 From 32b6444d23d0ff618d73e5b766600cd258066169 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Tue, 3 Sep 2013 10:09:52 +0200 Subject: qcow2-cluster: Expand zero clusters Add functionality for expanding zero clusters. This is necessary for downgrading the image version to one without zero cluster support. For non-backed images, this function may also just discard zero clusters instead of truly expanding them. Signed-off-by: Max Reitz Signed-off-by: Kevin Wolf --- block/qcow2-cluster.c | 233 +++++++++++++++++++++++++++++++++++++++++++++++++ block/qcow2-refcount.c | 29 +++--- block/qcow2.h | 5 ++ 3 files changed, 253 insertions(+), 14 deletions(-) (limited to 'block') diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c index b0d688eb99..738ff73c1d 100644 --- a/block/qcow2-cluster.c +++ b/block/qcow2-cluster.c @@ -1497,3 +1497,236 @@ fail: return ret; } + +/* + * Expands all zero clusters in a specific L1 table (or deallocates them, for + * non-backed non-pre-allocated zero clusters). + * + * expanded_clusters is a bitmap where every bit corresponds to one cluster in + * the image file; a bit gets set if the corresponding cluster has been used for + * zero expansion (i.e., has been filled with zeroes and is referenced from an + * L2 table). nb_clusters contains the total cluster count of the image file, + * i.e., the number of bits in expanded_clusters. + */ +static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, + int l1_size, uint8_t *expanded_clusters, + uint64_t nb_clusters) +{ + BDRVQcowState *s = bs->opaque; + bool is_active_l1 = (l1_table == s->l1_table); + uint64_t *l2_table = NULL; + int ret; + int i, j; + + if (!is_active_l1) { + /* inactive L2 tables require a buffer to be stored in when loading + * them from disk */ + l2_table = qemu_blockalign(bs, s->cluster_size); + } + + for (i = 0; i < l1_size; i++) { + uint64_t l2_offset = l1_table[i] & L1E_OFFSET_MASK; + bool l2_dirty = false; + + if (!l2_offset) { + /* unallocated */ + continue; + } + + if (is_active_l1) { + /* get active L2 tables from cache */ + ret = qcow2_cache_get(bs, s->l2_table_cache, l2_offset, + (void **)&l2_table); + } else { + /* load inactive L2 tables from disk */ + ret = bdrv_read(bs->file, l2_offset / BDRV_SECTOR_SIZE, + (void *)l2_table, s->cluster_sectors); + } + if (ret < 0) { + goto fail; + } + + for (j = 0; j < s->l2_size; j++) { + uint64_t l2_entry = be64_to_cpu(l2_table[j]); + int64_t offset = l2_entry & L2E_OFFSET_MASK, cluster_index; + int cluster_type = qcow2_get_cluster_type(l2_entry); + + if (cluster_type == QCOW2_CLUSTER_NORMAL) { + cluster_index = offset >> s->cluster_bits; + assert((cluster_index >= 0) && (cluster_index < nb_clusters)); + if (expanded_clusters[cluster_index / 8] & + (1 << (cluster_index % 8))) { + /* Probably a shared L2 table; this cluster was a zero + * cluster which has been expanded, its refcount + * therefore most likely requires an update. */ + ret = qcow2_update_cluster_refcount(bs, cluster_index, 1, + QCOW2_DISCARD_NEVER); + if (ret < 0) { + goto fail; + } + /* Since we just increased the refcount, the COPIED flag may + * no longer be set. */ + l2_table[j] = cpu_to_be64(l2_entry & ~QCOW_OFLAG_COPIED); + l2_dirty = true; + } + continue; + } + else if (qcow2_get_cluster_type(l2_entry) != QCOW2_CLUSTER_ZERO) { + continue; + } + + if (!offset) { + /* not preallocated */ + if (!bs->backing_hd) { + /* not backed; therefore we can simply deallocate the + * cluster */ + l2_table[j] = 0; + l2_dirty = true; + continue; + } + + offset = qcow2_alloc_clusters(bs, s->cluster_size); + if (offset < 0) { + ret = offset; + goto fail; + } + } + + ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_DEFAULT, + offset, s->cluster_size); + if (ret < 0) { + qcow2_free_clusters(bs, offset, s->cluster_size, + QCOW2_DISCARD_ALWAYS); + goto fail; + } + + ret = bdrv_write_zeroes(bs->file, offset / BDRV_SECTOR_SIZE, + s->cluster_sectors); + if (ret < 0) { + qcow2_free_clusters(bs, offset, s->cluster_size, + QCOW2_DISCARD_ALWAYS); + goto fail; + } + + l2_table[j] = cpu_to_be64(offset | QCOW_OFLAG_COPIED); + l2_dirty = true; + + cluster_index = offset >> s->cluster_bits; + assert((cluster_index >= 0) && (cluster_index < nb_clusters)); + expanded_clusters[cluster_index / 8] |= 1 << (cluster_index % 8); + } + + if (is_active_l1) { + if (l2_dirty) { + qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table); + qcow2_cache_depends_on_flush(s->l2_table_cache); + } + ret = qcow2_cache_put(bs, s->l2_table_cache, (void **)&l2_table); + if (ret < 0) { + l2_table = NULL; + goto fail; + } + } else { + if (l2_dirty) { + ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_DEFAULT & + ~(QCOW2_OL_INACTIVE_L2 | QCOW2_OL_ACTIVE_L2), l2_offset, + s->cluster_size); + if (ret < 0) { + goto fail; + } + + ret = bdrv_write(bs->file, l2_offset / BDRV_SECTOR_SIZE, + (void *)l2_table, s->cluster_sectors); + if (ret < 0) { + goto fail; + } + } + } + } + + ret = 0; + +fail: + if (l2_table) { + if (!is_active_l1) { + qemu_vfree(l2_table); + } else { + if (ret < 0) { + qcow2_cache_put(bs, s->l2_table_cache, (void **)&l2_table); + } else { + ret = qcow2_cache_put(bs, s->l2_table_cache, + (void **)&l2_table); + } + } + } + return ret; +} + +/* + * For backed images, expands all zero clusters on the image. For non-backed + * images, deallocates all non-pre-allocated zero clusters (and claims the + * allocation for pre-allocated ones). This is important for downgrading to a + * qcow2 version which doesn't yet support metadata zero clusters. + */ +int qcow2_expand_zero_clusters(BlockDriverState *bs) +{ + BDRVQcowState *s = bs->opaque; + uint64_t *l1_table = NULL; + int cluster_to_sector_bits = s->cluster_bits - BDRV_SECTOR_BITS; + uint64_t nb_clusters; + uint8_t *expanded_clusters; + int ret; + int i, j; + + nb_clusters = (bs->total_sectors + (1 << cluster_to_sector_bits) - 1) + >> cluster_to_sector_bits; + expanded_clusters = g_malloc0((nb_clusters + 7) / 8); + + ret = expand_zero_clusters_in_l1(bs, s->l1_table, s->l1_size, + expanded_clusters, nb_clusters); + if (ret < 0) { + goto fail; + } + + /* Inactive L1 tables may point to active L2 tables - therefore it is + * necessary to flush the L2 table cache before trying to access the L2 + * tables pointed to by inactive L1 entries (else we might try to expand + * zero clusters that have already been expanded); furthermore, it is also + * necessary to empty the L2 table cache, since it may contain tables which + * are now going to be modified directly on disk, bypassing the cache. + * qcow2_cache_empty() does both for us. */ + ret = qcow2_cache_empty(bs, s->l2_table_cache); + if (ret < 0) { + goto fail; + } + + for (i = 0; i < s->nb_snapshots; i++) { + int l1_sectors = (s->snapshots[i].l1_size * sizeof(uint64_t) + + BDRV_SECTOR_SIZE - 1) / BDRV_SECTOR_SIZE; + + l1_table = g_realloc(l1_table, l1_sectors * BDRV_SECTOR_SIZE); + + ret = bdrv_read(bs->file, s->snapshots[i].l1_table_offset / + BDRV_SECTOR_SIZE, (void *)l1_table, l1_sectors); + if (ret < 0) { + goto fail; + } + + for (j = 0; j < s->snapshots[i].l1_size; j++) { + be64_to_cpus(&l1_table[j]); + } + + ret = expand_zero_clusters_in_l1(bs, l1_table, s->snapshots[i].l1_size, + expanded_clusters, nb_clusters); + if (ret < 0) { + goto fail; + } + } + + ret = 0; + +fail: + g_free(expanded_clusters); + g_free(l1_table); + return ret; +} diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c index ba129de478..4264148142 100644 --- a/block/qcow2-refcount.c +++ b/block/qcow2-refcount.c @@ -601,10 +601,10 @@ fail: * If the return value is non-negative, it is the new refcount of the cluster. * If it is negative, it is -errno and indicates an error. */ -static int update_cluster_refcount(BlockDriverState *bs, - int64_t cluster_index, - int addend, - enum qcow2_discard_type type) +int qcow2_update_cluster_refcount(BlockDriverState *bs, + int64_t cluster_index, + int addend, + enum qcow2_discard_type type) { BDRVQcowState *s = bs->opaque; int ret; @@ -733,8 +733,8 @@ int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size) if (free_in_cluster == 0) s->free_byte_offset = 0; if ((offset & (s->cluster_size - 1)) != 0) - update_cluster_refcount(bs, offset >> s->cluster_bits, 1, - QCOW2_DISCARD_NEVER); + qcow2_update_cluster_refcount(bs, offset >> s->cluster_bits, 1, + QCOW2_DISCARD_NEVER); } else { offset = qcow2_alloc_clusters(bs, s->cluster_size); if (offset < 0) { @@ -744,8 +744,8 @@ int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size) if ((cluster_offset + s->cluster_size) == offset) { /* we are lucky: contiguous data */ offset = s->free_byte_offset; - update_cluster_refcount(bs, offset >> s->cluster_bits, 1, - QCOW2_DISCARD_NEVER); + qcow2_update_cluster_refcount(bs, offset >> s->cluster_bits, 1, + QCOW2_DISCARD_NEVER); s->free_byte_offset += size; } else { s->free_byte_offset = offset; @@ -754,8 +754,8 @@ int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size) } /* The cluster refcount was incremented, either by qcow2_alloc_clusters() - * or explicitly by update_cluster_refcount(). Refcount blocks must be - * flushed before the caller's L2 table updates. + * or explicitly by qcow2_update_cluster_refcount(). Refcount blocks must + * be flushed before the caller's L2 table updates. */ qcow2_cache_set_dependency(bs, s->l2_table_cache, s->refcount_block_cache); return offset; @@ -896,8 +896,9 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs, break; } if (addend != 0) { - refcount = update_cluster_refcount(bs, cluster_index, addend, - QCOW2_DISCARD_SNAPSHOT); + refcount = qcow2_update_cluster_refcount(bs, + cluster_index, addend, + QCOW2_DISCARD_SNAPSHOT); } else { refcount = get_refcount(bs, cluster_index); } @@ -936,8 +937,8 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs, if (addend != 0) { - refcount = update_cluster_refcount(bs, l2_offset >> s->cluster_bits, addend, - QCOW2_DISCARD_SNAPSHOT); + refcount = qcow2_update_cluster_refcount(bs, l2_offset >> + s->cluster_bits, addend, QCOW2_DISCARD_SNAPSHOT); } else { refcount = get_refcount(bs, l2_offset >> s->cluster_bits); } diff --git a/block/qcow2.h b/block/qcow2.h index 35c822b8a6..48080fdc03 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -411,6 +411,9 @@ int qcow2_update_header(BlockDriverState *bs); int qcow2_refcount_init(BlockDriverState *bs); void qcow2_refcount_close(BlockDriverState *bs); +int qcow2_update_cluster_refcount(BlockDriverState *bs, int64_t cluster_index, + int addend, enum qcow2_discard_type type); + int64_t qcow2_alloc_clusters(BlockDriverState *bs, int64_t size); int qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset, int nb_clusters); @@ -458,6 +461,8 @@ int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset, int nb_sectors, enum qcow2_discard_type type); int qcow2_zero_clusters(BlockDriverState *bs, uint64_t offset, int nb_sectors); +int qcow2_expand_zero_clusters(BlockDriverState *bs); + /* qcow2-snapshot.c functions */ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info); int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id); -- cgit v1.2.3 From b6481f376bc65894910dd98db3f299d698817106 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Tue, 3 Sep 2013 10:09:53 +0200 Subject: qcow2: Save refcount order in BDRVQcowState Save the image refcount order in BDRVQcowState. This will be relevant for future code supporting different refcount orders than four and also for code that needs to verify a certain refcount order for an opened image. Signed-off-by: Max Reitz Signed-off-by: Kevin Wolf --- block/qcow2.c | 3 ++- block/qcow2.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'block') diff --git a/block/qcow2.c b/block/qcow2.c index c9e266e22e..27203f8f07 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -455,6 +455,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags) ret = -ENOTSUP; goto fail; } + s->refcount_order = header.refcount_order; if (header.cluster_bits < MIN_CLUSTER_BITS || header.cluster_bits > MAX_CLUSTER_BITS) { @@ -1143,7 +1144,7 @@ int qcow2_update_header(BlockDriverState *bs) .incompatible_features = cpu_to_be64(s->incompatible_features), .compatible_features = cpu_to_be64(s->compatible_features), .autoclear_features = cpu_to_be64(s->autoclear_features), - .refcount_order = cpu_to_be32(3 + REFCOUNT_SHIFT), + .refcount_order = cpu_to_be32(s->refcount_order), .header_length = cpu_to_be32(header_length), }; diff --git a/block/qcow2.h b/block/qcow2.h index 48080fdc03..bea6ddb43a 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -199,6 +199,7 @@ typedef struct BDRVQcowState { int flags; int qcow_version; bool use_lazy_refcounts; + int refcount_order; bool discard_passthrough[QCOW2_DISCARD_MAX]; -- cgit v1.2.3 From 9296b3ed7050cc6e0645fbc3b0aea74406d7eeb2 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Tue, 3 Sep 2013 10:09:54 +0200 Subject: qcow2: Implement bdrv_amend_options Implement bdrv_amend_options for compat, size, backing_file, backing_fmt and lazy_refcounts. Downgrading images from compat=1.1 to compat=0.10 is achieved through handling all incompatible flags accordingly, clearing all compatible and autoclear flags and expanding all zero clusters. Signed-off-by: Max Reitz Signed-off-by: Kevin Wolf --- block/qcow2.c | 194 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 194 insertions(+) (limited to 'block') diff --git a/block/qcow2.c b/block/qcow2.c index 27203f8f07..7c9354cadf 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -1820,6 +1820,199 @@ static int qcow2_load_vmstate(BlockDriverState *bs, uint8_t *buf, return ret; } +/* + * Downgrades an image's version. To achieve this, any incompatible features + * have to be removed. + */ +static int qcow2_downgrade(BlockDriverState *bs, int target_version) +{ + BDRVQcowState *s = bs->opaque; + int current_version = s->qcow_version; + int ret; + + if (target_version == current_version) { + return 0; + } else if (target_version > current_version) { + return -EINVAL; + } else if (target_version != 2) { + return -EINVAL; + } + + if (s->refcount_order != 4) { + /* we would have to convert the image to a refcount_order == 4 image + * here; however, since qemu (at the time of writing this) does not + * support anything different than 4 anyway, there is no point in doing + * so right now; however, we should error out (if qemu supports this in + * the future and this code has not been adapted) */ + error_report("qcow2_downgrade: Image refcount orders other than 4 are" + "currently not supported."); + return -ENOTSUP; + } + + /* clear incompatible features */ + if (s->incompatible_features & QCOW2_INCOMPAT_DIRTY) { + ret = qcow2_mark_clean(bs); + if (ret < 0) { + return ret; + } + } + + /* with QCOW2_INCOMPAT_CORRUPT, it is pretty much impossible to get here in + * the first place; if that happens nonetheless, returning -ENOTSUP is the + * best thing to do anyway */ + + if (s->incompatible_features) { + return -ENOTSUP; + } + + /* since we can ignore compatible features, we can set them to 0 as well */ + s->compatible_features = 0; + /* if lazy refcounts have been used, they have already been fixed through + * clearing the dirty flag */ + + /* clearing autoclear features is trivial */ + s->autoclear_features = 0; + + ret = qcow2_expand_zero_clusters(bs); + if (ret < 0) { + return ret; + } + + s->qcow_version = target_version; + ret = qcow2_update_header(bs); + if (ret < 0) { + s->qcow_version = current_version; + return ret; + } + return 0; +} + +static int qcow2_amend_options(BlockDriverState *bs, + QEMUOptionParameter *options) +{ + BDRVQcowState *s = bs->opaque; + int old_version = s->qcow_version, new_version = old_version; + uint64_t new_size = 0; + const char *backing_file = NULL, *backing_format = NULL; + bool lazy_refcounts = s->use_lazy_refcounts; + int ret; + int i; + + for (i = 0; options[i].name; i++) + { + if (!options[i].assigned) { + /* only change explicitly defined options */ + continue; + } + + if (!strcmp(options[i].name, "compat")) { + if (!options[i].value.s) { + /* preserve default */ + } else if (!strcmp(options[i].value.s, "0.10")) { + new_version = 2; + } else if (!strcmp(options[i].value.s, "1.1")) { + new_version = 3; + } else { + fprintf(stderr, "Unknown compatibility level %s.\n", + options[i].value.s); + return -EINVAL; + } + } else if (!strcmp(options[i].name, "preallocation")) { + fprintf(stderr, "Cannot change preallocation mode.\n"); + return -ENOTSUP; + } else if (!strcmp(options[i].name, "size")) { + new_size = options[i].value.n; + } else if (!strcmp(options[i].name, "backing_file")) { + backing_file = options[i].value.s; + } else if (!strcmp(options[i].name, "backing_fmt")) { + backing_format = options[i].value.s; + } else if (!strcmp(options[i].name, "encryption")) { + if ((options[i].value.n != !!s->crypt_method)) { + fprintf(stderr, "Changing the encryption flag is not " + "supported.\n"); + return -ENOTSUP; + } + } else if (!strcmp(options[i].name, "cluster_size")) { + if (options[i].value.n != s->cluster_size) { + fprintf(stderr, "Changing the cluster size is not " + "supported.\n"); + return -ENOTSUP; + } + } else if (!strcmp(options[i].name, "lazy_refcounts")) { + lazy_refcounts = options[i].value.n; + } else { + /* if this assertion fails, this probably means a new option was + * added without having it covered here */ + assert(false); + } + } + + if (new_version != old_version) { + if (new_version > old_version) { + /* Upgrade */ + s->qcow_version = new_version; + ret = qcow2_update_header(bs); + if (ret < 0) { + s->qcow_version = old_version; + return ret; + } + } else { + ret = qcow2_downgrade(bs, new_version); + if (ret < 0) { + return ret; + } + } + } + + if (backing_file || backing_format) { + ret = qcow2_change_backing_file(bs, backing_file ?: bs->backing_file, + backing_format ?: bs->backing_format); + if (ret < 0) { + return ret; + } + } + + if (s->use_lazy_refcounts != lazy_refcounts) { + if (lazy_refcounts) { + if (s->qcow_version < 3) { + fprintf(stderr, "Lazy refcounts only supported with compatibility " + "level 1.1 and above (use compat=1.1 or greater)\n"); + return -EINVAL; + } + s->compatible_features |= QCOW2_COMPAT_LAZY_REFCOUNTS; + ret = qcow2_update_header(bs); + if (ret < 0) { + s->compatible_features &= ~QCOW2_COMPAT_LAZY_REFCOUNTS; + return ret; + } + s->use_lazy_refcounts = true; + } else { + /* make image clean first */ + ret = qcow2_mark_clean(bs); + if (ret < 0) { + return ret; + } + /* now disallow lazy refcounts */ + s->compatible_features &= ~QCOW2_COMPAT_LAZY_REFCOUNTS; + ret = qcow2_update_header(bs); + if (ret < 0) { + s->compatible_features |= QCOW2_COMPAT_LAZY_REFCOUNTS; + return ret; + } + s->use_lazy_refcounts = false; + } + } + + if (new_size) { + ret = bdrv_truncate(bs, new_size); + if (ret < 0) { + return ret; + } + } + + return 0; +} + static QEMUOptionParameter qcow2_create_options[] = { { .name = BLOCK_OPT_SIZE, @@ -1903,6 +2096,7 @@ static BlockDriver bdrv_qcow2 = { .create_options = qcow2_create_options, .bdrv_check = qcow2_check, + .bdrv_amend_options = qcow2_amend_options, }; static void bdrv_qcow2_init(void) -- cgit v1.2.3 From 2ea1dd758c45f8ff12c67ed7934c3ce021eeaf12 Mon Sep 17 00:00:00 2001 From: Wenchao Xia Date: Wed, 11 Sep 2013 14:04:32 +0800 Subject: snapshot: new function bdrv_snapshot_find_by_id_and_name() To make it clear about id and name in searching, add this API to distinguish them. Caller can choose to search by id or name, *errp will be set only for exception. Some code are modified based on Pavel's patch. Signed-off-by: Wenchao Xia Signed-off-by: Pavel Hrdina Reviewed-by: Eric Blake Signed-off-by: Kevin Wolf --- block/snapshot.c | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) (limited to 'block') diff --git a/block/snapshot.c b/block/snapshot.c index 8f61cc0745..a923b386b6 100644 --- a/block/snapshot.c +++ b/block/snapshot.c @@ -48,6 +48,79 @@ int bdrv_snapshot_find(BlockDriverState *bs, QEMUSnapshotInfo *sn_info, return ret; } +/** + * Look up an internal snapshot by @id and @name. + * @bs: block device to search + * @id: unique snapshot ID, or NULL + * @name: snapshot name, or NULL + * @sn_info: location to store information on the snapshot found + * @errp: location to store error, will be set only for exception + * + * This function will traverse snapshot list in @bs to search the matching + * one, @id and @name are the matching condition: + * If both @id and @name are specified, find the first one with id @id and + * name @name. + * If only @id is specified, find the first one with id @id. + * If only @name is specified, find the first one with name @name. + * if none is specified, abort(). + * + * Returns: true when a snapshot is found and @sn_info will be filled, false + * when error or not found. If all operation succeed but no matching one is + * found, @errp will NOT be set. + */ +bool bdrv_snapshot_find_by_id_and_name(BlockDriverState *bs, + const char *id, + const char *name, + QEMUSnapshotInfo *sn_info, + Error **errp) +{ + QEMUSnapshotInfo *sn_tab, *sn; + int nb_sns, i; + bool ret = false; + + assert(id || name); + + nb_sns = bdrv_snapshot_list(bs, &sn_tab); + if (nb_sns < 0) { + error_setg_errno(errp, -nb_sns, "Failed to get a snapshot list"); + return false; + } else if (nb_sns == 0) { + return false; + } + + if (id && name) { + for (i = 0; i < nb_sns; i++) { + sn = &sn_tab[i]; + if (!strcmp(sn->id_str, id) && !strcmp(sn->name, name)) { + *sn_info = *sn; + ret = true; + break; + } + } + } else if (id) { + for (i = 0; i < nb_sns; i++) { + sn = &sn_tab[i]; + if (!strcmp(sn->id_str, id)) { + *sn_info = *sn; + ret = true; + break; + } + } + } else if (name) { + for (i = 0; i < nb_sns; i++) { + sn = &sn_tab[i]; + if (!strcmp(sn->name, name)) { + *sn_info = *sn; + ret = true; + break; + } + } + } + + g_free(sn_tab); + return ret; +} + int bdrv_can_snapshot(BlockDriverState *bs) { BlockDriver *drv = bs->drv; -- cgit v1.2.3 From a89d89d3e65800fa4a8e00de7af0ea8272bef779 Mon Sep 17 00:00:00 2001 From: Wenchao Xia Date: Wed, 11 Sep 2013 14:04:33 +0800 Subject: snapshot: distinguish id and name in snapshot delete Snapshot creation actually already distinguish id and name since it take a structured parameter *sn, but delete can't. Later an accurate delete is needed in qmp_transaction abort and blockdev-snapshot-delete-sync, so change its prototype. Also *errp is added to tip error, but return value is kepted to let caller check what kind of error happens. Existing caller for it are savevm, delvm and qemu-img, they are not impacted by introducing a new function bdrv_snapshot_delete_by_id_or_name(), which check the return value and do the operation again. Before this patch: For qcow2, it search id first then name to find the one to delete. For rbd, it search name. For sheepdog, it does nothing. After this patch: For qcow2, logic is the same by call it twice in caller. For rbd, it always fails in delete with id, but still search for name in second try, no change to user. Some code for *errp is based on Pavel's patch. Signed-off-by: Wenchao Xia Signed-off-by: Pavel Hrdina Signed-off-by: Kevin Wolf --- block/qcow2-snapshot.c | 55 +++++++++++++++++++++++++++++++++-------------- block/qcow2.h | 5 ++++- block/rbd.c | 21 +++++++++++++++++- block/sheepdog.c | 5 ++++- block/snapshot.c | 58 +++++++++++++++++++++++++++++++++++++++++++++++--- 5 files changed, 122 insertions(+), 22 deletions(-) (limited to 'block') diff --git a/block/qcow2-snapshot.c b/block/qcow2-snapshot.c index ffead08ca2..7d144205c3 100644 --- a/block/qcow2-snapshot.c +++ b/block/qcow2-snapshot.c @@ -297,31 +297,47 @@ static void find_new_snapshot_id(BlockDriverState *bs, snprintf(id_str, id_str_size, "%d", id_max + 1); } -static int find_snapshot_by_id(BlockDriverState *bs, const char *id_str) +static int find_snapshot_by_id_and_name(BlockDriverState *bs, + const char *id, + const char *name) { BDRVQcowState *s = bs->opaque; int i; - for(i = 0; i < s->nb_snapshots; i++) { - if (!strcmp(s->snapshots[i].id_str, id_str)) - return i; + if (id && name) { + for (i = 0; i < s->nb_snapshots; i++) { + if (!strcmp(s->snapshots[i].id_str, id) && + !strcmp(s->snapshots[i].name, name)) { + return i; + } + } + } else if (id) { + for (i = 0; i < s->nb_snapshots; i++) { + if (!strcmp(s->snapshots[i].id_str, id)) { + return i; + } + } + } else if (name) { + for (i = 0; i < s->nb_snapshots; i++) { + if (!strcmp(s->snapshots[i].name, name)) { + return i; + } + } } + return -1; } -static int find_snapshot_by_id_or_name(BlockDriverState *bs, const char *name) +static int find_snapshot_by_id_or_name(BlockDriverState *bs, + const char *id_or_name) { - BDRVQcowState *s = bs->opaque; - int i, ret; + int ret; - ret = find_snapshot_by_id(bs, name); - if (ret >= 0) + ret = find_snapshot_by_id_and_name(bs, id_or_name, NULL); + if (ret >= 0) { return ret; - for(i = 0; i < s->nb_snapshots; i++) { - if (!strcmp(s->snapshots[i].name, name)) - return i; } - return -1; + return find_snapshot_by_id_and_name(bs, NULL, id_or_name); } /* if no id is provided, a new one is constructed */ @@ -343,7 +359,7 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) } /* Check that the ID is unique */ - if (find_snapshot_by_id(bs, sn_info->id_str) >= 0) { + if (find_snapshot_by_id_and_name(bs, sn_info->id_str, NULL) >= 0) { return -EEXIST; } @@ -560,15 +576,19 @@ fail: return ret; } -int qcow2_snapshot_delete(BlockDriverState *bs, const char *snapshot_id) +int qcow2_snapshot_delete(BlockDriverState *bs, + const char *snapshot_id, + const char *name, + Error **errp) { BDRVQcowState *s = bs->opaque; QCowSnapshot sn; int snapshot_index, ret; /* Search the snapshot */ - snapshot_index = find_snapshot_by_id_or_name(bs, snapshot_id); + snapshot_index = find_snapshot_by_id_and_name(bs, snapshot_id, name); if (snapshot_index < 0) { + error_setg(errp, "Can't find the snapshot"); return -ENOENT; } sn = s->snapshots[snapshot_index]; @@ -580,6 +600,7 @@ int qcow2_snapshot_delete(BlockDriverState *bs, const char *snapshot_id) s->nb_snapshots--; ret = qcow2_write_snapshots(bs); if (ret < 0) { + error_setg(errp, "Failed to remove snapshot from snapshot list"); return ret; } @@ -597,6 +618,7 @@ int qcow2_snapshot_delete(BlockDriverState *bs, const char *snapshot_id) ret = qcow2_update_snapshot_refcount(bs, sn.l1_table_offset, sn.l1_size, -1); if (ret < 0) { + error_setg(errp, "Failed to free the cluster and L1 table"); return ret; } qcow2_free_clusters(bs, sn.l1_table_offset, sn.l1_size * sizeof(uint64_t), @@ -605,6 +627,7 @@ int qcow2_snapshot_delete(BlockDriverState *bs, const char *snapshot_id) /* must update the copied flag on the current cluster offsets */ ret = qcow2_update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, 0); if (ret < 0) { + error_setg(errp, "Failed to update snapshot status in disk"); return ret; } diff --git a/block/qcow2.h b/block/qcow2.h index bea6ddb43a..c90e5d6c6e 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -467,7 +467,10 @@ int qcow2_expand_zero_clusters(BlockDriverState *bs); /* qcow2-snapshot.c functions */ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info); int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id); -int qcow2_snapshot_delete(BlockDriverState *bs, const char *snapshot_id); +int qcow2_snapshot_delete(BlockDriverState *bs, + const char *snapshot_id, + const char *name, + Error **errp); int qcow2_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab); int qcow2_snapshot_load_tmp(BlockDriverState *bs, const char *snapshot_name); diff --git a/block/rbd.c b/block/rbd.c index e798e19f81..b1ab80ce19 100644 --- a/block/rbd.c +++ b/block/rbd.c @@ -891,12 +891,31 @@ static int qemu_rbd_snap_create(BlockDriverState *bs, } static int qemu_rbd_snap_remove(BlockDriverState *bs, - const char *snapshot_name) + const char *snapshot_id, + const char *snapshot_name, + Error **errp) { BDRVRBDState *s = bs->opaque; int r; + if (!snapshot_name) { + error_setg(errp, "rbd need a valid snapshot name"); + return -EINVAL; + } + + /* If snapshot_id is specified, it must be equal to name, see + qemu_rbd_snap_list() */ + if (snapshot_id && strcmp(snapshot_id, snapshot_name)) { + error_setg(errp, + "rbd do not support snapshot id, it should be NULL or " + "equal to snapshot name"); + return -EINVAL; + } + r = rbd_snap_remove(s->image, snapshot_name); + if (r < 0) { + error_setg_errno(errp, -r, "Failed to remove the snapshot"); + } return r; } diff --git a/block/sheepdog.c b/block/sheepdog.c index f9988d35ba..fe438e0fa2 100644 --- a/block/sheepdog.c +++ b/block/sheepdog.c @@ -2072,7 +2072,10 @@ out: return ret; } -static int sd_snapshot_delete(BlockDriverState *bs, const char *snapshot_id) +static int sd_snapshot_delete(BlockDriverState *bs, + const char *snapshot_id, + const char *name, + Error **errp) { /* FIXME: Delete specified snapshot id. */ return 0; diff --git a/block/snapshot.c b/block/snapshot.c index a923b386b6..82e602fccf 100644 --- a/block/snapshot.c +++ b/block/snapshot.c @@ -182,21 +182,73 @@ int bdrv_snapshot_goto(BlockDriverState *bs, return -ENOTSUP; } -int bdrv_snapshot_delete(BlockDriverState *bs, const char *snapshot_id) +/** + * Delete an internal snapshot by @snapshot_id and @name. + * @bs: block device used in the operation + * @snapshot_id: unique snapshot ID, or NULL + * @name: snapshot name, or NULL + * @errp: location to store error + * + * If both @snapshot_id and @name are specified, delete the first one with + * id @snapshot_id and name @name. + * If only @snapshot_id is specified, delete the first one with id + * @snapshot_id. + * If only @name is specified, delete the first one with name @name. + * if none is specified, return -ENINVAL. + * + * Returns: 0 on success, -errno on failure. If @bs is not inserted, return + * -ENOMEDIUM. If @snapshot_id and @name are both NULL, return -EINVAL. If @bs + * does not support internal snapshot deletion, return -ENOTSUP. If @bs does + * not support parameter @snapshot_id or @name, or one of them is not correctly + * specified, return -EINVAL. If @bs can't find one matching @id and @name, + * return -ENOENT. If @errp != NULL, it will always be filled with error + * message on failure. + */ +int bdrv_snapshot_delete(BlockDriverState *bs, + const char *snapshot_id, + const char *name, + Error **errp) { BlockDriver *drv = bs->drv; if (!drv) { + error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, bdrv_get_device_name(bs)); return -ENOMEDIUM; } + if (!snapshot_id && !name) { + error_setg(errp, "snapshot_id and name are both NULL"); + return -EINVAL; + } if (drv->bdrv_snapshot_delete) { - return drv->bdrv_snapshot_delete(bs, snapshot_id); + return drv->bdrv_snapshot_delete(bs, snapshot_id, name, errp); } if (bs->file) { - return bdrv_snapshot_delete(bs->file, snapshot_id); + return bdrv_snapshot_delete(bs->file, snapshot_id, name, errp); } + error_set(errp, QERR_BLOCK_FORMAT_FEATURE_NOT_SUPPORTED, + drv->format_name, bdrv_get_device_name(bs), + "internal snapshot deletion"); return -ENOTSUP; } +void bdrv_snapshot_delete_by_id_or_name(BlockDriverState *bs, + const char *id_or_name, + Error **errp) +{ + int ret; + Error *local_err = NULL; + + ret = bdrv_snapshot_delete(bs, id_or_name, NULL, &local_err); + if (ret == -ENOENT || ret == -EINVAL) { + error_free(local_err); + local_err = NULL; + ret = bdrv_snapshot_delete(bs, NULL, id_or_name, &local_err); + } + + if (ret < 0) { + error_propagate(errp, local_err); + } +} + int bdrv_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_info) { -- cgit v1.2.3 From 015a1036a74ad29bb6916754911ce25587ff4db3 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Thu, 5 Sep 2013 14:22:29 +0200 Subject: bdrv: Use "Error" for opening images Add an Error ** parameter to BlockDriver.bdrv_open and BlockDriver.bdrv_file_open to allow more specific error messages. Signed-off-by: Max Reitz --- block/blkdebug.c | 3 ++- block/blkverify.c | 3 ++- block/bochs.c | 3 ++- block/cloop.c | 3 ++- block/cow.c | 3 ++- block/curl.c | 3 ++- block/dmg.c | 3 ++- block/gluster.c | 2 +- block/iscsi.c | 5 +++-- block/nbd.c | 3 ++- block/parallels.c | 3 ++- block/qcow.c | 3 ++- block/qcow2.c | 5 +++-- block/qed.c | 5 +++-- block/raw-posix.c | 12 ++++++++---- block/raw-win32.c | 6 ++++-- block/raw_bsd.c | 3 ++- block/rbd.c | 3 ++- block/sheepdog.c | 3 ++- block/snapshot.c | 2 +- block/ssh.c | 3 ++- block/vdi.c | 3 ++- block/vhdx.c | 3 ++- block/vmdk.c | 3 ++- block/vpc.c | 3 ++- block/vvfat.c | 3 ++- 26 files changed, 61 insertions(+), 33 deletions(-) (limited to 'block') diff --git a/block/blkdebug.c b/block/blkdebug.c index 5d33e03608..52d65ffcd4 100644 --- a/block/blkdebug.c +++ b/block/blkdebug.c @@ -350,7 +350,8 @@ static QemuOptsList runtime_opts = { }, }; -static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags) +static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) { BDRVBlkdebugState *s = bs->opaque; QemuOpts *opts; diff --git a/block/blkverify.c b/block/blkverify.c index c4e961eeb1..2093391c8b 100644 --- a/block/blkverify.c +++ b/block/blkverify.c @@ -116,7 +116,8 @@ static QemuOptsList runtime_opts = { }, }; -static int blkverify_open(BlockDriverState *bs, QDict *options, int flags) +static int blkverify_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) { BDRVBlkverifyState *s = bs->opaque; QemuOpts *opts; diff --git a/block/bochs.c b/block/bochs.c index d7078c0775..51d9a90577 100644 --- a/block/bochs.c +++ b/block/bochs.c @@ -108,7 +108,8 @@ static int bochs_probe(const uint8_t *buf, int buf_size, const char *filename) return 0; } -static int bochs_open(BlockDriverState *bs, QDict *options, int flags) +static int bochs_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) { BDRVBochsState *s = bs->opaque; int i; diff --git a/block/cloop.c b/block/cloop.c index 6ea7cf4046..b907023e10 100644 --- a/block/cloop.c +++ b/block/cloop.c @@ -53,7 +53,8 @@ static int cloop_probe(const uint8_t *buf, int buf_size, const char *filename) return 0; } -static int cloop_open(BlockDriverState *bs, QDict *options, int flags) +static int cloop_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) { BDRVCloopState *s = bs->opaque; uint32_t offsets_size, max_compressed_block_size = 1, i; diff --git a/block/cow.c b/block/cow.c index 764b93fae0..3ae1b5cdc9 100644 --- a/block/cow.c +++ b/block/cow.c @@ -58,7 +58,8 @@ static int cow_probe(const uint8_t *buf, int buf_size, const char *filename) return 0; } -static int cow_open(BlockDriverState *bs, QDict *options, int flags) +static int cow_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) { BDRVCowState *s = bs->opaque; struct cow_header_v2 cow_header; diff --git a/block/curl.c b/block/curl.c index ca2cedcec1..5a46f9707c 100644 --- a/block/curl.c +++ b/block/curl.c @@ -395,7 +395,8 @@ static QemuOptsList runtime_opts = { }, }; -static int curl_open(BlockDriverState *bs, QDict *options, int flags) +static int curl_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) { BDRVCURLState *s = bs->opaque; CURLState *state = NULL; diff --git a/block/dmg.c b/block/dmg.c index 3141cb5b88..d5e9b1ff01 100644 --- a/block/dmg.c +++ b/block/dmg.c @@ -92,7 +92,8 @@ static int read_uint32(BlockDriverState *bs, int64_t offset, uint32_t *result) return 0; } -static int dmg_open(BlockDriverState *bs, QDict *options, int flags) +static int dmg_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) { BDRVDMGState *s = bs->opaque; uint64_t info_begin,info_end,last_in_offset,last_out_offset; diff --git a/block/gluster.c b/block/gluster.c index dbb03f4de5..6c7b000db6 100644 --- a/block/gluster.c +++ b/block/gluster.c @@ -288,7 +288,7 @@ static QemuOptsList runtime_opts = { }; static int qemu_gluster_open(BlockDriverState *bs, QDict *options, - int bdrv_flags) + int bdrv_flags, Error **errp) { BDRVGlusterState *s = bs->opaque; int open_flags = O_BINARY; diff --git a/block/iscsi.c b/block/iscsi.c index 813abd8fef..5f26e7379c 100644 --- a/block/iscsi.c +++ b/block/iscsi.c @@ -1046,7 +1046,8 @@ static QemuOptsList runtime_opts = { * We support iscsi url's on the form * iscsi://[%@][:]// */ -static int iscsi_open(BlockDriverState *bs, QDict *options, int flags) +static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) { IscsiLun *iscsilun = bs->opaque; struct iscsi_context *iscsi = NULL; @@ -1260,7 +1261,7 @@ static int iscsi_create(const char *filename, QEMUOptionParameter *options) bs_options = qdict_new(); qdict_put(bs_options, "filename", qstring_from_str(filename)); - ret = iscsi_open(bs, bs_options, 0); + ret = iscsi_open(bs, bs_options, 0, NULL); QDECREF(bs_options); if (ret != 0) { diff --git a/block/nbd.c b/block/nbd.c index 691066f726..c8deeee67f 100644 --- a/block/nbd.c +++ b/block/nbd.c @@ -453,7 +453,8 @@ static void nbd_teardown_connection(BlockDriverState *bs) closesocket(s->sock); } -static int nbd_open(BlockDriverState *bs, QDict *options, int flags) +static int nbd_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) { BDRVNBDState *s = bs->opaque; int result; diff --git a/block/parallels.c b/block/parallels.c index 18b3ac0b28..2121e43204 100644 --- a/block/parallels.c +++ b/block/parallels.c @@ -68,7 +68,8 @@ static int parallels_probe(const uint8_t *buf, int buf_size, const char *filenam return 0; } -static int parallels_open(BlockDriverState *bs, QDict *options, int flags) +static int parallels_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) { BDRVParallelsState *s = bs->opaque; int i; diff --git a/block/qcow.c b/block/qcow.c index 93a993bb44..2a1c9c9e87 100644 --- a/block/qcow.c +++ b/block/qcow.c @@ -92,7 +92,8 @@ static int qcow_probe(const uint8_t *buf, int buf_size, const char *filename) return 0; } -static int qcow_open(BlockDriverState *bs, QDict *options, int flags) +static int qcow_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) { BDRVQcowState *s = bs->opaque; int len, i, shift, ret; diff --git a/block/qcow2.c b/block/qcow2.c index 7c9354cadf..eda085cfd8 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -350,7 +350,8 @@ static QemuOptsList qcow2_runtime_opts = { }, }; -static int qcow2_open(BlockDriverState *bs, QDict *options, int flags) +static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) { BDRVQcowState *s = bs->opaque; int len, i, ret = 0; @@ -1060,7 +1061,7 @@ static void qcow2_invalidate_cache(BlockDriverState *bs) qbool_from_int(s->use_lazy_refcounts)); memset(s, 0, sizeof(BDRVQcowState)); - qcow2_open(bs, options, flags); + qcow2_open(bs, options, flags, NULL); QDECREF(options); diff --git a/block/qed.c b/block/qed.c index 49b3a37ed5..69e1834ff6 100644 --- a/block/qed.c +++ b/block/qed.c @@ -373,7 +373,8 @@ static void bdrv_qed_rebind(BlockDriverState *bs) s->bs = bs; } -static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags) +static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) { BDRVQEDState *s = bs->opaque; QEDHeader le_header; @@ -1547,7 +1548,7 @@ static void bdrv_qed_invalidate_cache(BlockDriverState *bs) bdrv_qed_close(bs); memset(s, 0, sizeof(BDRVQEDState)); - bdrv_qed_open(bs, NULL, bs->open_flags); + bdrv_qed_open(bs, NULL, bs->open_flags, NULL); } static int bdrv_qed_check(BlockDriverState *bs, BdrvCheckResult *result, diff --git a/block/raw-posix.c b/block/raw-posix.c index 1b41ea3356..dcdf45c4ac 100644 --- a/block/raw-posix.c +++ b/block/raw-posix.c @@ -335,7 +335,8 @@ fail: return ret; } -static int raw_open(BlockDriverState *bs, QDict *options, int flags) +static int raw_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) { BDRVRawState *s = bs->opaque; @@ -1331,7 +1332,8 @@ static int check_hdev_writable(BDRVRawState *s) return 0; } -static int hdev_open(BlockDriverState *bs, QDict *options, int flags) +static int hdev_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) { BDRVRawState *s = bs->opaque; int ret; @@ -1565,7 +1567,8 @@ static BlockDriver bdrv_host_device = { }; #ifdef __linux__ -static int floppy_open(BlockDriverState *bs, QDict *options, int flags) +static int floppy_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) { BDRVRawState *s = bs->opaque; int ret; @@ -1686,7 +1689,8 @@ static BlockDriver bdrv_host_floppy = { .bdrv_eject = floppy_eject, }; -static int cdrom_open(BlockDriverState *bs, QDict *options, int flags) +static int cdrom_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) { BDRVRawState *s = bs->opaque; diff --git a/block/raw-win32.c b/block/raw-win32.c index ff3c5ea0d7..80551b957c 100644 --- a/block/raw-win32.c +++ b/block/raw-win32.c @@ -235,7 +235,8 @@ static QemuOptsList raw_runtime_opts = { }, }; -static int raw_open(BlockDriverState *bs, QDict *options, int flags) +static int raw_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) { BDRVRawState *s = bs->opaque; int access_flags; @@ -532,7 +533,8 @@ static int hdev_probe_device(const char *filename) return 0; } -static int hdev_open(BlockDriverState *bs, QDict *options, int flags) +static int hdev_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) { BDRVRawState *s = bs->opaque; int access_flags, create_flags; diff --git a/block/raw_bsd.c b/block/raw_bsd.c index a9060caec4..2418f580cc 100644 --- a/block/raw_bsd.c +++ b/block/raw_bsd.c @@ -135,7 +135,8 @@ static int raw_create(const char *filename, QEMUOptionParameter *options) return bdrv_create_file(filename, options); } -static int raw_open(BlockDriverState *bs, QDict *options, int flags) +static int raw_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) { bs->sg = bs->file->sg; return 0; diff --git a/block/rbd.c b/block/rbd.c index b1ab80ce19..b77f73ca89 100644 --- a/block/rbd.c +++ b/block/rbd.c @@ -446,7 +446,8 @@ static QemuOptsList runtime_opts = { }, }; -static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags) +static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) { BDRVRBDState *s = bs->opaque; char pool[RBD_MAX_POOL_NAME_SIZE]; diff --git a/block/sheepdog.c b/block/sheepdog.c index fe438e0fa2..1cfc6aedd5 100644 --- a/block/sheepdog.c +++ b/block/sheepdog.c @@ -1242,7 +1242,8 @@ static QemuOptsList runtime_opts = { }, }; -static int sd_open(BlockDriverState *bs, QDict *options, int flags) +static int sd_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) { int ret, fd; uint32_t vid = 0; diff --git a/block/snapshot.c b/block/snapshot.c index 82e602fccf..a05c0c0be0 100644 --- a/block/snapshot.c +++ b/block/snapshot.c @@ -170,7 +170,7 @@ int bdrv_snapshot_goto(BlockDriverState *bs, if (bs->file) { drv->bdrv_close(bs); ret = bdrv_snapshot_goto(bs->file, snapshot_id); - open_ret = drv->bdrv_open(bs, NULL, bs->open_flags); + open_ret = drv->bdrv_open(bs, NULL, bs->open_flags, NULL); if (open_ret < 0) { bdrv_unref(bs->file); bs->drv = NULL; diff --git a/block/ssh.c b/block/ssh.c index 27691b4ad5..23cd1f07e5 100644 --- a/block/ssh.c +++ b/block/ssh.c @@ -608,7 +608,8 @@ static int connect_to_ssh(BDRVSSHState *s, QDict *options, return ret; } -static int ssh_file_open(BlockDriverState *bs, QDict *options, int bdrv_flags) +static int ssh_file_open(BlockDriverState *bs, QDict *options, int bdrv_flags, + Error **errp) { BDRVSSHState *s = bs->opaque; int ret; diff --git a/block/vdi.c b/block/vdi.c index 1bf7dc575a..dedbafb539 100644 --- a/block/vdi.c +++ b/block/vdi.c @@ -364,7 +364,8 @@ static int vdi_probe(const uint8_t *buf, int buf_size, const char *filename) return result; } -static int vdi_open(BlockDriverState *bs, QDict *options, int flags) +static int vdi_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) { BDRVVdiState *s = bs->opaque; VdiHeader header; diff --git a/block/vhdx.c b/block/vhdx.c index e9704b1fdc..b8aa49ce4e 100644 --- a/block/vhdx.c +++ b/block/vhdx.c @@ -715,7 +715,8 @@ exit: } -static int vhdx_open(BlockDriverState *bs, QDict *options, int flags) +static int vhdx_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) { BDRVVHDXState *s = bs->opaque; int ret = 0; diff --git a/block/vmdk.c b/block/vmdk.c index fb5b5297ce..d60bf0cdf8 100644 --- a/block/vmdk.c +++ b/block/vmdk.c @@ -806,7 +806,8 @@ exit: return ret; } -static int vmdk_open(BlockDriverState *bs, QDict *options, int flags) +static int vmdk_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) { int ret; BDRVVmdkState *s = bs->opaque; diff --git a/block/vpc.c b/block/vpc.c index fe4f311d50..ed00370bad 100644 --- a/block/vpc.c +++ b/block/vpc.c @@ -155,7 +155,8 @@ static int vpc_probe(const uint8_t *buf, int buf_size, const char *filename) return 0; } -static int vpc_open(BlockDriverState *bs, QDict *options, int flags) +static int vpc_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) { BDRVVPCState *s = bs->opaque; int i; diff --git a/block/vvfat.c b/block/vvfat.c index 0129195e29..2f8be7cf1a 100644 --- a/block/vvfat.c +++ b/block/vvfat.c @@ -1065,7 +1065,8 @@ static void vvfat_parse_filename(const char *filename, QDict *options, qdict_put(options, "rw", qbool_from_int(rw)); } -static int vvfat_open(BlockDriverState *bs, QDict *options, int flags) +static int vvfat_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) { BDRVVVFATState *s = bs->opaque; int cyls, heads, secs; -- cgit v1.2.3 From d5124c00d80b4d948509f2c7f6b54228d9981f75 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Thu, 5 Sep 2013 14:26:05 +0200 Subject: bdrv: Use "Error" for creating images Add an Error ** parameter to BlockDriver.bdrv_create to allow more specific error messages. Signed-off-by: Max Reitz --- block/cow.c | 3 ++- block/gluster.c | 2 +- block/iscsi.c | 3 ++- block/qcow.c | 3 ++- block/qcow2.c | 3 ++- block/qed.c | 3 ++- block/raw-posix.c | 6 ++++-- block/raw-win32.c | 3 ++- block/raw_bsd.c | 3 ++- block/rbd.c | 3 ++- block/sheepdog.c | 3 ++- block/ssh.c | 3 ++- block/vdi.c | 3 ++- block/vmdk.c | 3 ++- block/vpc.c | 3 ++- 15 files changed, 31 insertions(+), 16 deletions(-) (limited to 'block') diff --git a/block/cow.c b/block/cow.c index 3ae1b5cdc9..e252c95601 100644 --- a/block/cow.c +++ b/block/cow.c @@ -295,7 +295,8 @@ static void cow_close(BlockDriverState *bs) { } -static int cow_create(const char *filename, QEMUOptionParameter *options) +static int cow_create(const char *filename, QEMUOptionParameter *options, + Error **errp) { struct cow_header_v2 cow_header; struct stat st; diff --git a/block/gluster.c b/block/gluster.c index 6c7b000db6..256de10ed3 100644 --- a/block/gluster.c +++ b/block/gluster.c @@ -357,7 +357,7 @@ out: } static int qemu_gluster_create(const char *filename, - QEMUOptionParameter *options) + QEMUOptionParameter *options, Error **errp) { struct glfs *glfs; struct glfs_fd *fd; diff --git a/block/iscsi.c b/block/iscsi.c index 5f26e7379c..997b122141 100644 --- a/block/iscsi.c +++ b/block/iscsi.c @@ -1238,7 +1238,8 @@ static int iscsi_has_zero_init(BlockDriverState *bs) return 0; } -static int iscsi_create(const char *filename, QEMUOptionParameter *options) +static int iscsi_create(const char *filename, QEMUOptionParameter *options, + Error **errp) { int ret = 0; int64_t total_size = 0; diff --git a/block/qcow.c b/block/qcow.c index 2a1c9c9e87..0911edff6c 100644 --- a/block/qcow.c +++ b/block/qcow.c @@ -659,7 +659,8 @@ static void qcow_close(BlockDriverState *bs) error_free(s->migration_blocker); } -static int qcow_create(const char *filename, QEMUOptionParameter *options) +static int qcow_create(const char *filename, QEMUOptionParameter *options, + Error **errp) { int header_size, backing_filename_len, l1_size, shift, i; QCowHeader header; diff --git a/block/qcow2.c b/block/qcow2.c index eda085cfd8..43edc5bd2a 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -1468,7 +1468,8 @@ out: return ret; } -static int qcow2_create(const char *filename, QEMUOptionParameter *options) +static int qcow2_create(const char *filename, QEMUOptionParameter *options, + Error **errp) { const char *backing_file = NULL; const char *backing_fmt = NULL; diff --git a/block/qed.c b/block/qed.c index 69e1834ff6..250fa89b0f 100644 --- a/block/qed.c +++ b/block/qed.c @@ -604,7 +604,8 @@ out: return ret; } -static int bdrv_qed_create(const char *filename, QEMUOptionParameter *options) +static int bdrv_qed_create(const char *filename, QEMUOptionParameter *options, + Error **errp) { uint64_t image_size = 0; uint32_t cluster_size = QED_DEFAULT_CLUSTER_SIZE; diff --git a/block/raw-posix.c b/block/raw-posix.c index dcdf45c4ac..3ee5b62509 100644 --- a/block/raw-posix.c +++ b/block/raw-posix.c @@ -1041,7 +1041,8 @@ static int64_t raw_get_allocated_file_size(BlockDriverState *bs) return (int64_t)st.st_blocks * 512; } -static int raw_create(const char *filename, QEMUOptionParameter *options) +static int raw_create(const char *filename, QEMUOptionParameter *options, + Error **errp) { int fd; int result = 0; @@ -1506,7 +1507,8 @@ static coroutine_fn BlockDriverAIOCB *hdev_aio_discard(BlockDriverState *bs, cb, opaque, QEMU_AIO_DISCARD|QEMU_AIO_BLKDEV); } -static int hdev_create(const char *filename, QEMUOptionParameter *options) +static int hdev_create(const char *filename, QEMUOptionParameter *options, + Error **errp) { int fd; int ret = 0; diff --git a/block/raw-win32.c b/block/raw-win32.c index 80551b957c..1e7651be61 100644 --- a/block/raw-win32.c +++ b/block/raw-win32.c @@ -422,7 +422,8 @@ static int64_t raw_get_allocated_file_size(BlockDriverState *bs) return st.st_size; } -static int raw_create(const char *filename, QEMUOptionParameter *options) +static int raw_create(const char *filename, QEMUOptionParameter *options, + Error **errp) { int fd; int64_t total_size = 0; diff --git a/block/raw_bsd.c b/block/raw_bsd.c index 2418f580cc..7d75ad282f 100644 --- a/block/raw_bsd.c +++ b/block/raw_bsd.c @@ -130,7 +130,8 @@ static int raw_has_zero_init(BlockDriverState *bs) return bdrv_has_zero_init(bs->file); } -static int raw_create(const char *filename, QEMUOptionParameter *options) +static int raw_create(const char *filename, QEMUOptionParameter *options, + Error **errp) { return bdrv_create_file(filename, options); } diff --git a/block/rbd.c b/block/rbd.c index b77f73ca89..11086c35c4 100644 --- a/block/rbd.c +++ b/block/rbd.c @@ -287,7 +287,8 @@ static int qemu_rbd_set_conf(rados_t cluster, const char *conf) return ret; } -static int qemu_rbd_create(const char *filename, QEMUOptionParameter *options) +static int qemu_rbd_create(const char *filename, QEMUOptionParameter *options, + Error **errp) { int64_t bytes = 0; int64_t objsize; diff --git a/block/sheepdog.c b/block/sheepdog.c index 1cfc6aedd5..a93a32a2ae 100644 --- a/block/sheepdog.c +++ b/block/sheepdog.c @@ -1438,7 +1438,8 @@ out: return ret; } -static int sd_create(const char *filename, QEMUOptionParameter *options) +static int sd_create(const char *filename, QEMUOptionParameter *options, + Error **errp) { int ret = 0; uint32_t vid = 0, base_vid = 0; diff --git a/block/ssh.c b/block/ssh.c index 23cd1f07e5..aa63c9d20e 100644 --- a/block/ssh.c +++ b/block/ssh.c @@ -651,7 +651,8 @@ static QEMUOptionParameter ssh_create_options[] = { { NULL } }; -static int ssh_create(const char *filename, QEMUOptionParameter *options) +static int ssh_create(const char *filename, QEMUOptionParameter *options, + Error **errp) { int r, ret; Error *local_err = NULL; diff --git a/block/vdi.c b/block/vdi.c index dedbafb539..dcbc27c9cb 100644 --- a/block/vdi.c +++ b/block/vdi.c @@ -645,7 +645,8 @@ static int vdi_co_write(BlockDriverState *bs, return ret; } -static int vdi_create(const char *filename, QEMUOptionParameter *options) +static int vdi_create(const char *filename, QEMUOptionParameter *options, + Error **errp) { int fd; int result = 0; diff --git a/block/vmdk.c b/block/vmdk.c index d60bf0cdf8..a98da086a7 100644 --- a/block/vmdk.c +++ b/block/vmdk.c @@ -1552,7 +1552,8 @@ static int filename_decompose(const char *filename, char *path, char *prefix, return VMDK_OK; } -static int vmdk_create(const char *filename, QEMUOptionParameter *options) +static int vmdk_create(const char *filename, QEMUOptionParameter *options, + Error **errp) { int fd, idx = 0; char desc[BUF_SIZE]; diff --git a/block/vpc.c b/block/vpc.c index ed00370bad..db61274332 100644 --- a/block/vpc.c +++ b/block/vpc.c @@ -684,7 +684,8 @@ static int create_fixed_disk(int fd, uint8_t *buf, int64_t total_size) return ret; } -static int vpc_create(const char *filename, QEMUOptionParameter *options) +static int vpc_create(const char *filename, QEMUOptionParameter *options, + Error **errp) { uint8_t buf[1024]; struct vhd_footer *footer = (struct vhd_footer *) buf; -- cgit v1.2.3 From 34b5d2c68eb4082c288e70fb99c61af8f7b96fde Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Thu, 5 Sep 2013 14:45:29 +0200 Subject: block: Error parameter for open functions Add an Error ** parameter to bdrv_open, bdrv_file_open and associated functions to allow more specific error messages. Signed-off-by: Max Reitz --- block/blkdebug.c | 4 +++- block/blkverify.c | 8 ++++++-- block/cow.c | 5 ++++- block/mirror.c | 5 +++-- block/qcow.c | 5 ++++- block/qcow2.c | 4 ++-- block/qed.c | 6 +++++- block/sheepdog.c | 10 ++++++++-- block/vmdk.c | 11 +++++++++-- block/vvfat.c | 6 +++++- 10 files changed, 49 insertions(+), 15 deletions(-) (limited to 'block') diff --git a/block/blkdebug.c b/block/blkdebug.c index 52d65ffcd4..be948b2fdd 100644 --- a/block/blkdebug.c +++ b/block/blkdebug.c @@ -387,8 +387,10 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags, goto fail; } - ret = bdrv_file_open(&bs->file, filename, NULL, flags); + ret = bdrv_file_open(&bs->file, filename, NULL, flags, &local_err); if (ret < 0) { + qerror_report_err(local_err); + error_free(local_err); goto fail; } diff --git a/block/blkverify.c b/block/blkverify.c index 2093391c8b..bff95d2a45 100644 --- a/block/blkverify.c +++ b/block/blkverify.c @@ -141,8 +141,10 @@ static int blkverify_open(BlockDriverState *bs, QDict *options, int flags, goto fail; } - ret = bdrv_file_open(&bs->file, raw, NULL, flags); + ret = bdrv_file_open(&bs->file, raw, NULL, flags, &local_err); if (ret < 0) { + qerror_report_err(local_err); + error_free(local_err); goto fail; } @@ -154,8 +156,10 @@ static int blkverify_open(BlockDriverState *bs, QDict *options, int flags, } s->test_file = bdrv_new(""); - ret = bdrv_open(s->test_file, filename, NULL, flags, NULL); + ret = bdrv_open(s->test_file, filename, NULL, flags, NULL, &local_err); if (ret < 0) { + qerror_report_err(local_err); + error_free(local_err); bdrv_unref(s->test_file); s->test_file = NULL; goto fail; diff --git a/block/cow.c b/block/cow.c index e252c95601..3a93ed9966 100644 --- a/block/cow.c +++ b/block/cow.c @@ -302,6 +302,7 @@ static int cow_create(const char *filename, QEMUOptionParameter *options, struct stat st; int64_t image_sectors = 0; const char *image_filename = NULL; + Error *local_err = NULL; int ret; BlockDriverState *cow_bs; @@ -320,8 +321,10 @@ static int cow_create(const char *filename, QEMUOptionParameter *options, return ret; } - ret = bdrv_file_open(&cow_bs, filename, NULL, BDRV_O_RDWR); + ret = bdrv_file_open(&cow_bs, filename, NULL, BDRV_O_RDWR, &local_err); if (ret < 0) { + qerror_report_err(local_err); + error_free(local_err); return ret; } diff --git a/block/mirror.c b/block/mirror.c index f61a7799de..6e7a274e43 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -505,14 +505,15 @@ static void mirror_iostatus_reset(BlockJob *job) static void mirror_complete(BlockJob *job, Error **errp) { MirrorBlockJob *s = container_of(job, MirrorBlockJob, common); + Error *local_err = NULL; int ret; - ret = bdrv_open_backing_file(s->target, NULL); + ret = bdrv_open_backing_file(s->target, NULL, &local_err); if (ret < 0) { char backing_filename[PATH_MAX]; bdrv_get_full_backing_filename(s->target, backing_filename, sizeof(backing_filename)); - error_setg_file_open(errp, -ret, backing_filename); + error_propagate(errp, local_err); return; } if (!s->synced) { diff --git a/block/qcow.c b/block/qcow.c index 0911edff6c..396636f7b4 100644 --- a/block/qcow.c +++ b/block/qcow.c @@ -668,6 +668,7 @@ static int qcow_create(const char *filename, QEMUOptionParameter *options, int64_t total_size = 0; const char *backing_file = NULL; int flags = 0; + Error *local_err = NULL; int ret; BlockDriverState *qcow_bs; @@ -688,8 +689,10 @@ static int qcow_create(const char *filename, QEMUOptionParameter *options, return ret; } - ret = bdrv_file_open(&qcow_bs, filename, NULL, BDRV_O_RDWR); + ret = bdrv_file_open(&qcow_bs, filename, NULL, BDRV_O_RDWR, &local_err); if (ret < 0) { + qerror_report_err(local_err); + error_free(local_err); return ret; } diff --git a/block/qcow2.c b/block/qcow2.c index 43edc5bd2a..dabfe8d08b 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -1370,7 +1370,7 @@ static int qcow2_create2(const char *filename, int64_t total_size, return ret; } - ret = bdrv_file_open(&bs, filename, NULL, BDRV_O_RDWR); + ret = bdrv_file_open(&bs, filename, NULL, BDRV_O_RDWR, NULL); if (ret < 0) { return ret; } @@ -1423,7 +1423,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, - BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH, drv); + BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH, drv, NULL); if (ret < 0) { goto out; } diff --git a/block/qed.c b/block/qed.c index 250fa89b0f..f17094cb92 100644 --- a/block/qed.c +++ b/block/qed.c @@ -551,6 +551,7 @@ static int qed_create(const char *filename, uint32_t cluster_size, QEDHeader le_header; uint8_t *l1_table = NULL; size_t l1_size = header.cluster_size * header.table_size; + Error *local_err = NULL; int ret = 0; BlockDriverState *bs = NULL; @@ -559,8 +560,11 @@ static int qed_create(const char *filename, uint32_t cluster_size, return ret; } - ret = bdrv_file_open(&bs, filename, NULL, BDRV_O_RDWR | BDRV_O_CACHE_WB); + ret = bdrv_file_open(&bs, filename, NULL, BDRV_O_RDWR | BDRV_O_CACHE_WB, + &local_err); if (ret < 0) { + qerror_report_err(local_err); + error_free(local_err); return ret; } diff --git a/block/sheepdog.c b/block/sheepdog.c index a93a32a2ae..38fb629650 100644 --- a/block/sheepdog.c +++ b/block/sheepdog.c @@ -1401,10 +1401,13 @@ static int sd_prealloc(const char *filename) uint32_t idx, max_idx; int64_t vdi_size; void *buf = g_malloc0(SD_DATA_OBJ_SIZE); + Error *local_err = NULL; int ret; - ret = bdrv_file_open(&bs, filename, NULL, BDRV_O_RDWR); + ret = bdrv_file_open(&bs, filename, NULL, BDRV_O_RDWR, &local_err); if (ret < 0) { + qerror_report_err(local_err); + error_free(local_err); goto out; } @@ -1449,6 +1452,7 @@ static int sd_create(const char *filename, QEMUOptionParameter *options, char vdi[SD_MAX_VDI_LEN], tag[SD_MAX_VDI_TAG_LEN]; uint32_t snapid; bool prealloc = false; + Error *local_err = NULL; s = g_malloc0(sizeof(BDRVSheepdogState)); @@ -1502,8 +1506,10 @@ static int sd_create(const char *filename, QEMUOptionParameter *options, goto out; } - ret = bdrv_file_open(&bs, backing_file, NULL, 0); + ret = bdrv_file_open(&bs, backing_file, NULL, 0, &local_err); if (ret < 0) { + qerror_report_err(local_err); + error_free(local_err); goto out; } diff --git a/block/vmdk.c b/block/vmdk.c index a98da086a7..96ef1b534e 100644 --- a/block/vmdk.c +++ b/block/vmdk.c @@ -697,6 +697,7 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs, int64_t flat_offset; char extent_path[PATH_MAX]; BlockDriverState *extent_file; + Error *local_err = NULL; while (*p) { /* parse extent line: @@ -726,8 +727,11 @@ 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, bs->open_flags); + ret = bdrv_file_open(&extent_file, extent_path, NULL, bs->open_flags, + &local_err); if (ret) { + qerror_report_err(local_err); + error_free(local_err); return ret; } @@ -1591,6 +1595,7 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options, "ddb.geometry.heads = \"%d\"\n" "ddb.geometry.sectors = \"63\"\n" "ddb.adapterType = \"%s\"\n"; + Error *local_err = NULL; if (filename_decompose(filename, path, prefix, postfix, PATH_MAX)) { return -EINVAL; @@ -1653,8 +1658,10 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options, } if (backing_file) { BlockDriverState *bs = bdrv_new(""); - ret = bdrv_open(bs, backing_file, NULL, 0, NULL); + ret = bdrv_open(bs, backing_file, NULL, 0, NULL, &local_err); if (ret != 0) { + qerror_report_err(local_err); + error_free(local_err); bdrv_unref(bs); return ret; } diff --git a/block/vvfat.c b/block/vvfat.c index 2f8be7cf1a..788d0630fd 100644 --- a/block/vvfat.c +++ b/block/vvfat.c @@ -2910,6 +2910,7 @@ static int enable_write_target(BDRVVVFATState *s) { BlockDriver *bdrv_qcow; QEMUOptionParameter *options; + Error *local_err = NULL; int ret; int size = sector2cluster(s, s->sector_count); s->used_clusters = calloc(size, 1); @@ -2935,8 +2936,11 @@ static int enable_write_target(BDRVVVFATState *s) s->qcow = bdrv_new(""); ret = bdrv_open(s->qcow, s->qcow_filename, NULL, - BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH, bdrv_qcow); + 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; } -- cgit v1.2.3 From cc84d90ff54c025190dbe49ec5fea1268217c5f2 Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Fri, 6 Sep 2013 17:14:26 +0200 Subject: block: Error parameter for create functions Add an Error ** parameter to bdrv_create and its associated functions to allow more specific error messages. Signed-off-by: Max Reitz --- block/cow.c | 4 +++- block/qcow.c | 4 +++- block/qcow2.c | 2 +- block/qed.c | 4 +++- block/raw_bsd.c | 10 +++++++++- block/vvfat.c | 4 +++- 6 files changed, 22 insertions(+), 6 deletions(-) (limited to 'block') diff --git a/block/cow.c b/block/cow.c index 3a93ed9966..909c3e7182 100644 --- a/block/cow.c +++ b/block/cow.c @@ -316,8 +316,10 @@ static int cow_create(const char *filename, QEMUOptionParameter *options, options++; } - ret = bdrv_create_file(filename, options); + ret = bdrv_create_file(filename, options, &local_err); if (ret < 0) { + qerror_report_err(local_err); + error_free(local_err); return ret; } diff --git a/block/qcow.c b/block/qcow.c index 396636f7b4..c470e05f60 100644 --- a/block/qcow.c +++ b/block/qcow.c @@ -684,8 +684,10 @@ static int qcow_create(const char *filename, QEMUOptionParameter *options, options++; } - ret = bdrv_create_file(filename, options); + ret = bdrv_create_file(filename, options, &local_err); if (ret < 0) { + qerror_report_err(local_err); + error_free(local_err); return ret; } diff --git a/block/qcow2.c b/block/qcow2.c index dabfe8d08b..b257347c3a 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -1365,7 +1365,7 @@ static int qcow2_create2(const char *filename, int64_t total_size, uint8_t* refcount_table; int ret; - ret = bdrv_create_file(filename, options); + ret = bdrv_create_file(filename, options, NULL); if (ret < 0) { return ret; } diff --git a/block/qed.c b/block/qed.c index f17094cb92..6c0cba04f3 100644 --- a/block/qed.c +++ b/block/qed.c @@ -555,8 +555,10 @@ static int qed_create(const char *filename, uint32_t cluster_size, int ret = 0; BlockDriverState *bs = NULL; - ret = bdrv_create_file(filename, NULL); + ret = bdrv_create_file(filename, NULL, &local_err); if (ret < 0) { + qerror_report_err(local_err); + error_free(local_err); return ret; } diff --git a/block/raw_bsd.c b/block/raw_bsd.c index 7d75ad282f..d4ace6020b 100644 --- a/block/raw_bsd.c +++ b/block/raw_bsd.c @@ -133,7 +133,15 @@ static int raw_has_zero_init(BlockDriverState *bs) static int raw_create(const char *filename, QEMUOptionParameter *options, Error **errp) { - return bdrv_create_file(filename, options); + Error *local_err = NULL; + int ret; + + ret = bdrv_create_file(filename, options, &local_err); + if (error_is_set(&local_err)) { + qerror_report_err(local_err); + error_free(local_err); + } + return ret; } static int raw_open(BlockDriverState *bs, QDict *options, int flags, diff --git a/block/vvfat.c b/block/vvfat.c index 788d0630fd..3ddaa0bcce 100644 --- a/block/vvfat.c +++ b/block/vvfat.c @@ -2928,8 +2928,10 @@ static int enable_write_target(BDRVVVFATState *s) set_option_parameter_int(options, BLOCK_OPT_SIZE, s->sector_count * 512); set_option_parameter(options, BLOCK_OPT_BACKING_FILE, "fat:"); - ret = bdrv_create(bdrv_qcow, s->qcow_filename, options); + ret = bdrv_create(bdrv_qcow, s->qcow_filename, options, &local_err); if (ret < 0) { + qerror_report_err(local_err); + error_free(local_err); goto err; } -- cgit v1.2.3 From 3ef6c40ad0b350e18c78135ffbdbe209cb479c1f Mon Sep 17 00:00:00 2001 From: Max Reitz Date: Thu, 5 Sep 2013 09:40:43 +0200 Subject: qcow2: Use Error parameter Employ usage of the new Error ** parameter in qcow2_open, qcow2_create and associated functions. Signed-off-by: Max Reitz --- block/qcow2.c | 134 ++++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 88 insertions(+), 46 deletions(-) (limited to 'block') diff --git a/block/qcow2.c b/block/qcow2.c index b257347c3a..318d95d972 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -79,7 +79,8 @@ static int qcow2_probe(const uint8_t *buf, int buf_size, const char *filename) * return 0 upon success, non-0 otherwise */ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, - uint64_t end_offset, void **p_feature_table) + uint64_t end_offset, void **p_feature_table, + Error **errp) { BDRVQcowState *s = bs->opaque; QCowExtension ext; @@ -100,10 +101,10 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, printf("attempting to read extended header in offset %lu\n", offset); #endif - if (bdrv_pread(bs->file, offset, &ext, sizeof(ext)) != sizeof(ext)) { - fprintf(stderr, "qcow2_read_extension: ERROR: " - "pread fail from offset %" PRIu64 "\n", - offset); + ret = bdrv_pread(bs->file, offset, &ext, sizeof(ext)); + if (ret < 0) { + error_setg_errno(errp, -ret, "qcow2_read_extension: ERROR: " + "pread fail from offset %" PRIu64, offset); return 1; } be32_to_cpus(&ext.magic); @@ -113,7 +114,7 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, printf("ext.magic = 0x%x\n", ext.magic); #endif if (ext.len > end_offset - offset) { - error_report("Header extension too large"); + error_setg(errp, "Header extension too large"); return -EINVAL; } @@ -123,14 +124,16 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, case QCOW2_EXT_MAGIC_BACKING_FORMAT: if (ext.len >= sizeof(bs->backing_format)) { - fprintf(stderr, "ERROR: ext_backing_format: len=%u too large" - " (>=%zu)\n", - ext.len, sizeof(bs->backing_format)); + error_setg(errp, "ERROR: ext_backing_format: len=%u too large" + " (>=%zu)", ext.len, sizeof(bs->backing_format)); return 2; } - if (bdrv_pread(bs->file, offset , bs->backing_format, - ext.len) != ext.len) + ret = bdrv_pread(bs->file, offset, bs->backing_format, ext.len); + if (ret < 0) { + error_setg_errno(errp, -ret, "ERROR: ext_backing_format: " + "Could not read format name"); return 3; + } bs->backing_format[ext.len] = '\0'; #ifdef DEBUG_EXT printf("Qcow2: Got format extension %s\n", bs->backing_format); @@ -142,6 +145,8 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, void* feature_table = g_malloc0(ext.len + 2 * sizeof(Qcow2Feature)); ret = bdrv_pread(bs->file, offset , feature_table, ext.len); if (ret < 0) { + error_setg_errno(errp, -ret, "ERROR: ext_feature_table: " + "Could not read table"); return ret; } @@ -161,6 +166,8 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, ret = bdrv_pread(bs->file, offset , uext->data, uext->len); if (ret < 0) { + error_setg_errno(errp, -ret, "ERROR: unknown extension: " + "Could not read data"); return ret; } } @@ -184,8 +191,8 @@ static void cleanup_unknown_header_ext(BlockDriverState *bs) } } -static void GCC_FMT_ATTR(2, 3) report_unsupported(BlockDriverState *bs, - const char *fmt, ...) +static void GCC_FMT_ATTR(3, 4) report_unsupported(BlockDriverState *bs, + Error **errp, const char *fmt, ...) { char msg[64]; va_list ap; @@ -194,17 +201,17 @@ static void GCC_FMT_ATTR(2, 3) report_unsupported(BlockDriverState *bs, vsnprintf(msg, sizeof(msg), fmt, ap); va_end(ap); - qerror_report(QERR_UNKNOWN_BLOCK_FORMAT_FEATURE, - bs->device_name, "qcow2", msg); + error_set(errp, QERR_UNKNOWN_BLOCK_FORMAT_FEATURE, bs->device_name, "qcow2", + msg); } static void report_unsupported_feature(BlockDriverState *bs, - Qcow2Feature *table, uint64_t mask) + Error **errp, Qcow2Feature *table, uint64_t mask) { while (table && table->name[0] != '\0') { if (table->type == QCOW2_FEAT_TYPE_INCOMPATIBLE) { if (mask & (1 << table->bit)) { - report_unsupported(bs, "%.46s",table->name); + report_unsupported(bs, errp, "%.46s", table->name); mask &= ~(1 << table->bit); } } @@ -212,7 +219,8 @@ static void report_unsupported_feature(BlockDriverState *bs, } if (mask) { - report_unsupported(bs, "Unknown incompatible feature: %" PRIx64, mask); + report_unsupported(bs, errp, "Unknown incompatible feature: %" PRIx64, + mask); } } @@ -363,6 +371,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, ret = bdrv_pread(bs->file, 0, &header, sizeof(header)); if (ret < 0) { + error_setg_errno(errp, -ret, "Could not read qcow2 header"); goto fail; } be32_to_cpus(&header.magic); @@ -380,11 +389,12 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, be32_to_cpus(&header.nb_snapshots); if (header.magic != QCOW_MAGIC) { + error_setg(errp, "Image is not in qcow2 format"); ret = -EMEDIUMTYPE; goto fail; } if (header.version < 2 || header.version > 3) { - report_unsupported(bs, "QCOW version %d", header.version); + report_unsupported(bs, errp, "QCOW version %d", header.version); ret = -ENOTSUP; goto fail; } @@ -412,6 +422,8 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, ret = bdrv_pread(bs->file, sizeof(header), s->unknown_header_fields, s->unknown_header_fields_size); if (ret < 0) { + error_setg_errno(errp, -ret, "Could not read unknown qcow2 header " + "fields"); goto fail; } } @@ -430,8 +442,8 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, if (s->incompatible_features & ~QCOW2_INCOMPAT_MASK) { void *feature_table = NULL; qcow2_read_extensions(bs, header.header_length, ext_end, - &feature_table); - report_unsupported_feature(bs, feature_table, + &feature_table, NULL); + report_unsupported_feature(bs, errp, feature_table, s->incompatible_features & ~QCOW2_INCOMPAT_MASK); ret = -ENOTSUP; @@ -442,8 +454,8 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, /* Corrupt images may not be written to unless they are being repaired */ if ((flags & BDRV_O_RDWR) && !(flags & BDRV_O_CHECK)) { - error_report("qcow2: Image is corrupt; cannot be opened " - "read/write."); + error_setg(errp, "qcow2: Image is corrupt; cannot be opened " + "read/write"); ret = -EACCES; goto fail; } @@ -451,7 +463,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, /* Check support for various header values */ if (header.refcount_order != 4) { - report_unsupported(bs, "%d bit reference counts", + report_unsupported(bs, errp, "%d bit reference counts", 1 << header.refcount_order); ret = -ENOTSUP; goto fail; @@ -460,10 +472,13 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, if (header.cluster_bits < MIN_CLUSTER_BITS || header.cluster_bits > MAX_CLUSTER_BITS) { + error_setg(errp, "Unsupported cluster size: 2^%i", header.cluster_bits); ret = -EINVAL; goto fail; } if (header.crypt_method > QCOW_CRYPT_AES) { + error_setg(errp, "Unsupported encryption method: %i", + header.crypt_method); ret = -EINVAL; goto fail; } @@ -492,6 +507,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, l1_vm_state_index = size_to_l1(s, header.size); if (l1_vm_state_index > INT_MAX) { + error_setg(errp, "Image is too big"); ret = -EFBIG; goto fail; } @@ -500,6 +516,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, /* the L1 table must contain at least enough entries to put header.size bytes */ if (s->l1_size < s->l1_vm_state_index) { + error_setg(errp, "L1 table is too small"); ret = -EINVAL; goto fail; } @@ -510,6 +527,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, ret = bdrv_pread(bs->file, s->l1_table_offset, s->l1_table, s->l1_size * sizeof(uint64_t)); if (ret < 0) { + error_setg_errno(errp, -ret, "Could not read L1 table"); goto fail; } for(i = 0;i < s->l1_size; i++) { @@ -530,6 +548,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, ret = qcow2_refcount_init(bs); if (ret != 0) { + error_setg_errno(errp, -ret, "Could not initialize refcount handling"); goto fail; } @@ -537,7 +556,9 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, QTAILQ_INIT(&s->discards); /* read qcow2 extensions */ - if (qcow2_read_extensions(bs, header.header_length, ext_end, NULL)) { + if (qcow2_read_extensions(bs, header.header_length, ext_end, NULL, + &local_err)) { + error_propagate(errp, local_err); ret = -EINVAL; goto fail; } @@ -551,6 +572,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, ret = bdrv_pread(bs->file, header.backing_file_offset, bs->backing_file, len); if (ret < 0) { + error_setg_errno(errp, -ret, "Could not read backing file name"); goto fail; } bs->backing_file[len] = '\0'; @@ -558,6 +580,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, ret = qcow2_read_snapshots(bs); if (ret < 0) { + error_setg_errno(errp, -ret, "Could not read snapshots"); goto fail; } @@ -566,6 +589,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, s->autoclear_features = 0; ret = qcow2_update_header(bs); if (ret < 0) { + error_setg_errno(errp, -ret, "Could not update qcow2 header"); goto fail; } } @@ -580,6 +604,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, ret = qcow2_check(bs, &result, BDRV_FIX_ERRORS); if (ret < 0) { + error_setg_errno(errp, -ret, "Could not repair dirty image"); goto fail; } } @@ -588,8 +613,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, opts = qemu_opts_create_nofail(&qcow2_runtime_opts); qemu_opts_absorb_qdict(opts, options, &local_err); if (error_is_set(&local_err)) { - qerror_report_err(local_err); - error_free(local_err); + error_propagate(errp, local_err); ret = -EINVAL; goto fail; } @@ -610,8 +634,8 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, qemu_opts_del(opts); if (s->use_lazy_refcounts && s->qcow_version < 3) { - qerror_report(ERROR_CLASS_GENERIC_ERROR, "Lazy refcounts require " - "a qcow2 image with at least qemu 1.1 compatibility level"); + error_setg(errp, "Lazy refcounts require a qcow2 image with at least " + "qemu 1.1 compatibility level"); ret = -EINVAL; goto fail; } @@ -1334,7 +1358,8 @@ static int preallocate(BlockDriverState *bs) static int qcow2_create2(const char *filename, int64_t total_size, const char *backing_file, const char *backing_format, int flags, size_t cluster_size, int prealloc, - QEMUOptionParameter *options, int version) + QEMUOptionParameter *options, int version, + Error **errp) { /* Calculate cluster_bits */ int cluster_bits; @@ -1342,9 +1367,8 @@ static int qcow2_create2(const char *filename, int64_t total_size, if (cluster_bits < MIN_CLUSTER_BITS || cluster_bits > MAX_CLUSTER_BITS || (1 << cluster_bits) != cluster_size) { - error_report( - "Cluster size must be a power of two between %d and %dk", - 1 << MIN_CLUSTER_BITS, 1 << (MAX_CLUSTER_BITS - 10)); + error_setg(errp, "Cluster size must be a power of two between %d and " + "%dk", 1 << MIN_CLUSTER_BITS, 1 << (MAX_CLUSTER_BITS - 10)); return -EINVAL; } @@ -1363,15 +1387,18 @@ static int qcow2_create2(const char *filename, int64_t total_size, BlockDriverState* bs; QCowHeader header; uint8_t* refcount_table; + Error *local_err = NULL; int ret; - ret = bdrv_create_file(filename, options, NULL); + ret = bdrv_create_file(filename, options, &local_err); if (ret < 0) { + error_propagate(errp, local_err); return ret; } - ret = bdrv_file_open(&bs, filename, NULL, BDRV_O_RDWR, NULL); + ret = bdrv_file_open(&bs, filename, NULL, BDRV_O_RDWR, &local_err); if (ret < 0) { + error_propagate(errp, local_err); return ret; } @@ -1401,6 +1428,7 @@ static int qcow2_create2(const char *filename, int64_t total_size, ret = bdrv_pwrite(bs, 0, &header, sizeof(header)); if (ret < 0) { + error_setg_errno(errp, -ret, "Could not write qcow2 header"); goto out; } @@ -1410,6 +1438,7 @@ static int qcow2_create2(const char *filename, int64_t total_size, g_free(refcount_table); if (ret < 0) { + error_setg_errno(errp, -ret, "Could not write refcount table"); goto out; } @@ -1423,13 +1452,16 @@ 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, - BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH, drv, NULL); + BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH, drv, &local_err); if (ret < 0) { + error_propagate(errp, local_err); goto out; } ret = qcow2_alloc_clusters(bs, 2 * cluster_size); if (ret < 0) { + error_setg_errno(errp, -ret, "Could not allocate clusters for qcow2 " + "header and refcount table"); goto out; } else if (ret != 0) { @@ -1440,6 +1472,7 @@ static int qcow2_create2(const char *filename, int64_t total_size, /* Okay, now that we have a valid image, let's give it the right size */ ret = bdrv_truncate(bs, total_size * BDRV_SECTOR_SIZE); if (ret < 0) { + error_setg_errno(errp, -ret, "Could not resize image"); goto out; } @@ -1447,6 +1480,8 @@ static int qcow2_create2(const char *filename, int64_t total_size, if (backing_file) { ret = bdrv_change_backing_file(bs, backing_file, backing_format); if (ret < 0) { + error_setg_errno(errp, -ret, "Could not assign backing file '%s' " + "with format '%s'", backing_file, backing_format); goto out; } } @@ -1458,6 +1493,7 @@ static int qcow2_create2(const char *filename, int64_t total_size, ret = preallocate(bs); qemu_co_mutex_unlock(&s->lock); if (ret < 0) { + error_setg_errno(errp, -ret, "Could not preallocate metadata"); goto out; } } @@ -1478,6 +1514,8 @@ static int qcow2_create(const char *filename, QEMUOptionParameter *options, size_t cluster_size = DEFAULT_CLUSTER_SIZE; int prealloc = 0; int version = 3; + Error *local_err = NULL; + int ret; /* Read out options */ while (options && options->name) { @@ -1499,8 +1537,8 @@ static int qcow2_create(const char *filename, QEMUOptionParameter *options, } else if (!strcmp(options->value.s, "metadata")) { prealloc = 1; } else { - fprintf(stderr, "Invalid preallocation mode: '%s'\n", - options->value.s); + error_setg(errp, "Invalid preallocation mode: '%s'", + options->value.s); return -EINVAL; } } else if (!strcmp(options->name, BLOCK_OPT_COMPAT_LEVEL)) { @@ -1511,8 +1549,8 @@ static int qcow2_create(const char *filename, QEMUOptionParameter *options, } else if (!strcmp(options->value.s, "1.1")) { version = 3; } else { - fprintf(stderr, "Invalid compatibility level: '%s'\n", - options->value.s); + error_setg(errp, "Invalid compatibility level: '%s'", + options->value.s); return -EINVAL; } } else if (!strcmp(options->name, BLOCK_OPT_LAZY_REFCOUNTS)) { @@ -1522,19 +1560,23 @@ static int qcow2_create(const char *filename, QEMUOptionParameter *options, } if (backing_file && prealloc) { - fprintf(stderr, "Backing file and preallocation cannot be used at " - "the same time\n"); + error_setg(errp, "Backing file and preallocation cannot be used at " + "the same time"); return -EINVAL; } if (version < 3 && (flags & BLOCK_FLAG_LAZY_REFCOUNTS)) { - fprintf(stderr, "Lazy refcounts only supported with compatibility " - "level 1.1 and above (use compat=1.1 or greater)\n"); + error_setg(errp, "Lazy refcounts only supported with compatibility " + "level 1.1 and above (use compat=1.1 or greater)"); return -EINVAL; } - return qcow2_create2(filename, sectors, backing_file, backing_fmt, flags, - cluster_size, prealloc, options, version); + ret = qcow2_create2(filename, sectors, backing_file, backing_fmt, flags, + cluster_size, prealloc, options, version, &local_err); + if (error_is_set(&local_err)) { + error_propagate(errp, local_err); + } + return ret; } static int qcow2_make_empty(BlockDriverState *bs) -- cgit v1.2.3