diff options
-rw-r--r-- | block.c | 150 | ||||
-rw-r--r-- | block/block-backend.c | 2 | ||||
-rw-r--r-- | block/commit.c | 4 | ||||
-rw-r--r-- | block/parallels.c | 2 | ||||
-rw-r--r-- | block/qcow.c | 2 | ||||
-rw-r--r-- | block/qcow2-cache.c | 14 | ||||
-rw-r--r-- | block/qcow2-cluster.c | 76 | ||||
-rw-r--r-- | block/qcow2-refcount.c | 76 | ||||
-rw-r--r-- | block/qcow2-snapshot.c | 20 | ||||
-rw-r--r-- | block/qcow2.c | 486 | ||||
-rw-r--r-- | block/qcow2.h | 26 | ||||
-rw-r--r-- | block/qed.c | 2 | ||||
-rw-r--r-- | block/sheepdog.c | 5 | ||||
-rw-r--r-- | block/vdi.c | 2 | ||||
-rw-r--r-- | block/vhdx.c | 2 | ||||
-rw-r--r-- | block/vmdk.c | 15 | ||||
-rw-r--r-- | block/vpc.c | 2 | ||||
-rw-r--r-- | block/vvfat.c | 8 | ||||
-rw-r--r-- | blockdev.c | 72 | ||||
-rw-r--r-- | include/block/block.h | 9 | ||||
-rw-r--r-- | qemu-io-cmds.c | 90 | ||||
-rw-r--r-- | qemu-io.c | 1 | ||||
-rwxr-xr-x | tests/qemu-iotests/039 | 27 | ||||
-rw-r--r-- | tests/qemu-iotests/039.out | 18 | ||||
-rwxr-xr-x | tests/qemu-iotests/137 | 145 | ||||
-rw-r--r-- | tests/qemu-iotests/137.out | 42 | ||||
-rwxr-xr-x | tests/qemu-iotests/138 | 73 | ||||
-rw-r--r-- | tests/qemu-iotests/138.out | 9 | ||||
-rw-r--r-- | tests/qemu-iotests/group | 2 |
29 files changed, 973 insertions, 409 deletions
@@ -85,8 +85,7 @@ static QLIST_HEAD(, BlockDriver) bdrv_drivers = static int bdrv_open_inherit(BlockDriverState **pbs, const char *filename, const char *reference, QDict *options, int flags, BlockDriverState *parent, - const BdrvChildRole *child_role, - BlockDriver *drv, Error **errp); + const BdrvChildRole *child_role, Error **errp); static void bdrv_dirty_bitmap_truncate(BlockDriverState *bs); /* If non-zero, use only whitelisted block drivers */ @@ -314,13 +313,6 @@ static int bdrv_is_whitelisted(BlockDriver *drv, bool read_only) return 0; } -BlockDriver *bdrv_find_whitelisted_format(const char *format_name, - bool read_only) -{ - BlockDriver *drv = bdrv_find_format(format_name); - return drv && bdrv_is_whitelisted(drv, read_only) ? drv : NULL; -} - typedef struct CreateCo { BlockDriver *drv; char *filename; @@ -997,13 +989,13 @@ static QDict *parse_json_filename(const char *filename, Error **errp) * block driver has been specified explicitly. */ static int bdrv_fill_options(QDict **options, const char **pfilename, - int *flags, BlockDriver *drv, Error **errp) + int *flags, Error **errp) { const char *filename = *pfilename; const char *drvname; bool protocol = *flags & BDRV_O_PROTOCOL; bool parse_filename = false; - BlockDriver *tmp_drv; + BlockDriver *drv = NULL; Error *local_err = NULL; /* Parse json: pseudo-protocol */ @@ -1022,15 +1014,15 @@ static int bdrv_fill_options(QDict **options, const char **pfilename, } drvname = qdict_get_try_str(*options, "driver"); - - /* If the user has explicitly specified the driver, this choice should - * override the BDRV_O_PROTOCOL flag */ - tmp_drv = drv; - if (!tmp_drv && drvname) { - tmp_drv = bdrv_find_format(drvname); - } - if (tmp_drv) { - protocol = tmp_drv->bdrv_file_open; + if (drvname) { + drv = bdrv_find_format(drvname); + if (!drv) { + error_setg(errp, "Unknown driver '%s'", drvname); + return -ENOENT; + } + /* If the user has explicitly specified the driver, this choice should + * override the BDRV_O_PROTOCOL flag */ + protocol = drv->bdrv_file_open; } if (protocol) { @@ -1054,33 +1046,18 @@ static int bdrv_fill_options(QDict **options, const char **pfilename, /* Find the right block driver */ filename = qdict_get_try_str(*options, "filename"); - if (drv) { - if (drvname) { - error_setg(errp, "Driver specified twice"); - return -EINVAL; - } - drvname = drv->format_name; - qdict_put(*options, "driver", qstring_from_str(drvname)); - } else { - if (!drvname && protocol) { - if (filename) { - drv = bdrv_find_protocol(filename, parse_filename, errp); - if (!drv) { - return -EINVAL; - } - - drvname = drv->format_name; - qdict_put(*options, "driver", qstring_from_str(drvname)); - } else { - error_setg(errp, "Must specify either driver or file"); - return -EINVAL; - } - } else if (drvname) { - drv = bdrv_find_format(drvname); + if (!drvname && protocol) { + if (filename) { + drv = bdrv_find_protocol(filename, parse_filename, errp); if (!drv) { - error_setg(errp, "Unknown driver '%s'", drvname); - return -ENOENT; + return -EINVAL; } + + drvname = drv->format_name; + qdict_put(*options, "driver", qstring_from_str(drvname)); + } else { + error_setg(errp, "Must specify either driver or file"); + return -EINVAL; } } @@ -1227,8 +1204,7 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp) assert(bs->backing_hd == NULL); ret = bdrv_open_inherit(&backing_hd, *backing_filename ? backing_filename : NULL, - NULL, options, 0, bs, &child_backing, - NULL, &local_err); + NULL, options, 0, bs, &child_backing, &local_err); if (ret < 0) { bdrv_unref(backing_hd); backing_hd = NULL; @@ -1291,7 +1267,7 @@ BdrvChild *bdrv_open_child(const char *filename, bs = NULL; ret = bdrv_open_inherit(&bs, filename, reference, image_options, 0, - parent, child_role, NULL, errp); + parent, child_role, errp); if (ret < 0) { goto done; } @@ -1385,11 +1361,13 @@ int bdrv_append_temp_snapshot(BlockDriverState *bs, int flags, Error **errp) qstring_from_str("file")); qdict_put(snapshot_options, "file.filename", qstring_from_str(tmp_filename)); + qdict_put(snapshot_options, "driver", + qstring_from_str("qcow2")); bs_snapshot = bdrv_new(); ret = bdrv_open(&bs_snapshot, NULL, NULL, snapshot_options, - flags, &bdrv_qcow2, &local_err); + flags, &local_err); if (ret < 0) { error_propagate(errp, local_err); goto out; @@ -1420,11 +1398,11 @@ out: static int bdrv_open_inherit(BlockDriverState **pbs, const char *filename, const char *reference, QDict *options, int flags, BlockDriverState *parent, - const BdrvChildRole *child_role, - BlockDriver *drv, Error **errp) + const BdrvChildRole *child_role, Error **errp) { int ret; BlockDriverState *file = NULL, *bs; + BlockDriver *drv = NULL; const char *drvname; Error *local_err = NULL; int snapshot_flags = 0; @@ -1474,13 +1452,12 @@ static int bdrv_open_inherit(BlockDriverState **pbs, const char *filename, flags = child_role->inherit_flags(parent->open_flags); } - ret = bdrv_fill_options(&options, &filename, &flags, drv, &local_err); + ret = bdrv_fill_options(&options, &filename, &flags, &local_err); if (local_err) { goto fail; } /* Find the right image format driver */ - drv = NULL; drvname = qdict_get_try_str(options, "driver"); if (drvname) { drv = bdrv_find_format(drvname); @@ -1635,11 +1612,10 @@ close_and_fail: } int bdrv_open(BlockDriverState **pbs, const char *filename, - const char *reference, QDict *options, int flags, - BlockDriver *drv, Error **errp) + const char *reference, QDict *options, int flags, Error **errp) { return bdrv_open_inherit(pbs, filename, reference, options, flags, NULL, - NULL, drv, errp); + NULL, errp); } typedef struct BlockReopenQueueEntry { @@ -1660,6 +1636,9 @@ typedef struct BlockReopenQueueEntry { * * bs is the BlockDriverState to add to the reopen queue. * + * options contains the changed options for the associated bs + * (the BlockReopenQueue takes ownership) + * * flags contains the open flags for the associated bs * * returns a pointer to bs_queue, which is either the newly allocated @@ -1667,18 +1646,28 @@ typedef struct BlockReopenQueueEntry { * */ BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue, - BlockDriverState *bs, int flags) + BlockDriverState *bs, + QDict *options, int flags) { assert(bs != NULL); BlockReopenQueueEntry *bs_entry; BdrvChild *child; + QDict *old_options; if (bs_queue == NULL) { bs_queue = g_new0(BlockReopenQueue, 1); QSIMPLEQ_INIT(bs_queue); } + if (!options) { + options = qdict_new(); + } + + old_options = qdict_clone_shallow(bs->options); + qdict_join(options, old_options, false); + QDECREF(old_options); + /* bdrv_open() masks this flag out */ flags &= ~BDRV_O_PROTOCOL; @@ -1690,13 +1679,15 @@ BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue, } child_flags = child->role->inherit_flags(flags); - bdrv_reopen_queue(bs_queue, child->bs, child_flags); + /* TODO Pass down child flags (backing.*, extents.*, ...) */ + bdrv_reopen_queue(bs_queue, child->bs, NULL, child_flags); } bs_entry = g_new0(BlockReopenQueueEntry, 1); QSIMPLEQ_INSERT_TAIL(bs_queue, bs_entry, entry); bs_entry->state.bs = bs; + bs_entry->state.options = options; bs_entry->state.flags = flags; return bs_queue; @@ -1749,6 +1740,7 @@ cleanup: if (ret && bs_entry->prepared) { bdrv_reopen_abort(&bs_entry->state); } + QDECREF(bs_entry->state.options); g_free(bs_entry); } g_free(bs_queue); @@ -1761,7 +1753,7 @@ int bdrv_reopen(BlockDriverState *bs, int bdrv_flags, Error **errp) { int ret = -1; Error *local_err = NULL; - BlockReopenQueue *queue = bdrv_reopen_queue(NULL, bs, bdrv_flags); + BlockReopenQueue *queue = bdrv_reopen_queue(NULL, bs, NULL, bdrv_flags); ret = bdrv_reopen_multiple(queue, &local_err); if (local_err != NULL) { @@ -1837,6 +1829,26 @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue, goto error; } + /* Options that are not handled are only okay if they are unchanged + * compared to the old state. It is expected that some options are only + * used for the initial open, but not reopen (e.g. filename) */ + if (qdict_size(reopen_state->options)) { + const QDictEntry *entry = qdict_first(reopen_state->options); + + do { + QString *new_obj = qobject_to_qstring(entry->value); + const char *new = qstring_get_str(new_obj); + const char *old = qdict_get_try_str(reopen_state->bs->options, + entry->key); + + if (!old || strcmp(new, old)) { + error_setg(errp, "Cannot change the option '%s'", entry->key); + ret = -EINVAL; + goto error; + } + } while ((entry = qdict_next(reopen_state->options, entry))); + } + ret = 0; error: @@ -3739,7 +3751,6 @@ void bdrv_img_create(const char *filename, const char *fmt, const char *backing_fmt, *backing_file; int64_t size; BlockDriver *drv, *proto_drv; - BlockDriver *backing_drv = NULL; Error *local_err = NULL; int ret = 0; @@ -3813,14 +3824,6 @@ void bdrv_img_create(const char *filename, const char *fmt, } backing_fmt = qemu_opt_get(opts, BLOCK_OPT_BACKING_FMT); - if (backing_fmt) { - backing_drv = bdrv_find_format(backing_fmt); - if (!backing_drv) { - error_setg(errp, "Unknown backing file format '%s'", - backing_fmt); - goto out; - } - } // The size for the image must always be specified, with one exception: // If we are using a backing file, we can obtain the size from there @@ -3831,6 +3834,7 @@ void bdrv_img_create(const char *filename, const char *fmt, char *full_backing = g_new0(char, PATH_MAX); int64_t size; int back_flags; + QDict *backing_options = NULL; bdrv_get_full_backing_filename_from_filename(filename, backing_file, full_backing, PATH_MAX, @@ -3844,9 +3848,15 @@ void bdrv_img_create(const char *filename, const char *fmt, back_flags = flags & ~(BDRV_O_RDWR | BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING); + if (backing_fmt) { + backing_options = qdict_new(); + qdict_put(backing_options, "driver", + qstring_from_str(backing_fmt)); + } + bs = NULL; - ret = bdrv_open(&bs, full_backing, NULL, NULL, back_flags, - backing_drv, &local_err); + ret = bdrv_open(&bs, full_backing, NULL, backing_options, + back_flags, &local_err); g_free(full_backing); if (ret < 0) { goto out; diff --git a/block/block-backend.c b/block/block-backend.c index aee8a12023..c2e873292a 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -126,7 +126,7 @@ BlockBackend *blk_new_open(const char *name, const char *filename, return NULL; } - ret = bdrv_open(&blk->bs, filename, reference, options, flags, NULL, errp); + ret = bdrv_open(&blk->bs, filename, reference, options, flags, errp); if (ret < 0) { blk_unref(blk); return NULL; diff --git a/block/commit.c b/block/commit.c index 7312a5bdc0..d12e26fab6 100644 --- a/block/commit.c +++ b/block/commit.c @@ -236,11 +236,11 @@ void commit_start(BlockDriverState *bs, BlockDriverState *base, /* convert base & overlay_bs to r/w, if necessary */ if (!(orig_base_flags & BDRV_O_RDWR)) { - reopen_queue = bdrv_reopen_queue(reopen_queue, base, + reopen_queue = bdrv_reopen_queue(reopen_queue, base, NULL, orig_base_flags | BDRV_O_RDWR); } if (!(orig_overlay_flags & BDRV_O_RDWR)) { - reopen_queue = bdrv_reopen_queue(reopen_queue, overlay_bs, + reopen_queue = bdrv_reopen_queue(reopen_queue, overlay_bs, NULL, orig_overlay_flags | BDRV_O_RDWR); } if (reopen_queue) { diff --git a/block/parallels.c b/block/parallels.c index 046b56844c..5cd6ec3349 100644 --- a/block/parallels.c +++ b/block/parallels.c @@ -476,7 +476,7 @@ static int parallels_create(const char *filename, QemuOpts *opts, Error **errp) file = NULL; ret = bdrv_open(&file, filename, NULL, NULL, - BDRV_O_RDWR | BDRV_O_PROTOCOL, NULL, &local_err); + BDRV_O_RDWR | BDRV_O_PROTOCOL, &local_err); if (ret < 0) { error_propagate(errp, local_err); return ret; diff --git a/block/qcow.c b/block/qcow.c index 01fba54cef..6e35db1df8 100644 --- a/block/qcow.c +++ b/block/qcow.c @@ -793,7 +793,7 @@ static int qcow_create(const char *filename, QemuOpts *opts, Error **errp) qcow_bs = NULL; ret = bdrv_open(&qcow_bs, filename, NULL, NULL, - BDRV_O_RDWR | BDRV_O_PROTOCOL, NULL, &local_err); + BDRV_O_RDWR | BDRV_O_PROTOCOL, &local_err); if (ret < 0) { error_propagate(errp, local_err); goto cleanup; diff --git a/block/qcow2-cache.c b/block/qcow2-cache.c index 046f5b8e48..7b14c5c5a5 100644 --- a/block/qcow2-cache.c +++ b/block/qcow2-cache.c @@ -55,14 +55,14 @@ struct Qcow2Cache { static inline void *qcow2_cache_get_table_addr(BlockDriverState *bs, Qcow2Cache *c, int table) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; return (uint8_t *) c->table_array + (size_t) table * s->cluster_size; } static inline int qcow2_cache_get_table_idx(BlockDriverState *bs, Qcow2Cache *c, void *table) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; ptrdiff_t table_offset = (uint8_t *) table - (uint8_t *) c->table_array; int idx = table_offset / s->cluster_size; assert(idx >= 0 && idx < c->size && table_offset % s->cluster_size == 0); @@ -73,7 +73,7 @@ static void qcow2_cache_table_release(BlockDriverState *bs, Qcow2Cache *c, int i, int num_tables) { #if QEMU_MADV_DONTNEED != QEMU_MADV_INVALID - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; void *t = qcow2_cache_get_table_addr(bs, c, i); int align = getpagesize(); size_t mem_size = (size_t) s->cluster_size * num_tables; @@ -121,7 +121,7 @@ void qcow2_cache_clean_unused(BlockDriverState *bs, Qcow2Cache *c) Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; Qcow2Cache *c; c = g_new0(Qcow2Cache, 1); @@ -172,7 +172,7 @@ static int qcow2_cache_flush_dependency(BlockDriverState *bs, Qcow2Cache *c) static int qcow2_cache_entry_flush(BlockDriverState *bs, Qcow2Cache *c, int i) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; int ret = 0; if (!c->entries[i].dirty || !c->entries[i].offset) { @@ -229,7 +229,7 @@ static int qcow2_cache_entry_flush(BlockDriverState *bs, Qcow2Cache *c, int i) int qcow2_cache_flush(BlockDriverState *bs, Qcow2Cache *c) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; int result = 0; int ret; int i; @@ -306,7 +306,7 @@ int qcow2_cache_empty(BlockDriverState *bs, Qcow2Cache *c) static int qcow2_cache_do_get(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset, void **table, bool read_from_disk) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; int i; int ret; int lookup_index; diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c index 61309aec56..6ede629efb 100644 --- a/block/qcow2-cluster.c +++ b/block/qcow2-cluster.c @@ -32,7 +32,7 @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size, bool exact_size) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; int new_l1_size2, ret, i; uint64_t *new_l1_table; int64_t old_l1_table_offset, old_l1_size; @@ -148,7 +148,7 @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size, static int l2_load(BlockDriverState *bs, uint64_t l2_offset, uint64_t **l2_table) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; int ret; ret = qcow2_cache_get(bs, s->l2_table_cache, l2_offset, (void**) l2_table); @@ -163,7 +163,7 @@ static int l2_load(BlockDriverState *bs, uint64_t l2_offset, #define L1_ENTRIES_PER_SECTOR (512 / 8) int qcow2_write_l1_entry(BlockDriverState *bs, int l1_index) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; uint64_t buf[L1_ENTRIES_PER_SECTOR] = { 0 }; int l1_start_index; int i, ret; @@ -203,7 +203,7 @@ int qcow2_write_l1_entry(BlockDriverState *bs, int l1_index) static int l2_allocate(BlockDriverState *bs, int l1_index, uint64_t **table) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; uint64_t old_l2_offset; uint64_t *l2_table = NULL; int64_t l2_offset; @@ -298,7 +298,7 @@ fail: * as contiguous. (This allows it, for example, to stop at the first compressed * cluster which may require a different handling) */ -static int count_contiguous_clusters(uint64_t nb_clusters, int cluster_size, +static int count_contiguous_clusters(int nb_clusters, int cluster_size, uint64_t *l2_table, uint64_t stop_flags) { int i; @@ -321,7 +321,7 @@ static int count_contiguous_clusters(uint64_t nb_clusters, int cluster_size, return i; } -static int count_contiguous_free_clusters(uint64_t nb_clusters, uint64_t *l2_table) +static int count_contiguous_free_clusters(int nb_clusters, uint64_t *l2_table) { int i; @@ -339,7 +339,7 @@ static int count_contiguous_free_clusters(uint64_t nb_clusters, uint64_t *l2_tab /* The crypt function is compatible with the linux cryptoloop algorithm for < 4 GB images. NOTE: out_buf == in_buf is supported */ -int qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num, +int qcow2_encrypt_sectors(BDRVQcow2State *s, int64_t sector_num, uint8_t *out_buf, const uint8_t *in_buf, int nb_sectors, bool enc, Error **errp) @@ -387,7 +387,7 @@ static int coroutine_fn copy_sectors(BlockDriverState *bs, uint64_t cluster_offset, int n_start, int n_end) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; QEMUIOVector qiov; struct iovec iov; int n, ret; @@ -469,7 +469,7 @@ out: int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, int *num, uint64_t *cluster_offset) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; unsigned int l2_index; uint64_t l1_index, l2_offset, *l2_table; int l1_bits, c; @@ -495,6 +495,7 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, if (nb_needed > nb_available) { nb_needed = nb_available; } + assert(nb_needed <= INT_MAX); *cluster_offset = 0; @@ -530,6 +531,8 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, l2_index = (offset >> s->cluster_bits) & (s->l2_size - 1); *cluster_offset = be64_to_cpu(l2_table[l2_index]); + + /* nb_needed <= INT_MAX, thus nb_clusters <= INT_MAX, too */ nb_clusters = size_to_clusters(s, nb_needed << 9); ret = qcow2_get_cluster_type(*cluster_offset); @@ -606,7 +609,7 @@ static int get_cluster_table(BlockDriverState *bs, uint64_t offset, uint64_t **new_l2_table, int *new_l2_index) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; unsigned int l2_index; uint64_t l1_index, l2_offset; uint64_t *l2_table = NULL; @@ -680,7 +683,7 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, uint64_t offset, int compressed_size) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; int l2_index, ret; uint64_t *l2_table; int64_t cluster_offset; @@ -725,7 +728,7 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, static int perform_cow(BlockDriverState *bs, QCowL2Meta *m, Qcow2COWRegion *r) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; int ret; if (r->nb_sectors == 0) { @@ -754,7 +757,7 @@ static int perform_cow(BlockDriverState *bs, QCowL2Meta *m, Qcow2COWRegion *r) int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; int i, j = 0, l2_index, ret; uint64_t *old_cluster, *l2_table; uint64_t cluster_offset = m->alloc_offset; @@ -837,7 +840,7 @@ err: * write, but require COW to be performed (this includes yet unallocated space, * which must copy from the backing file) */ -static int count_cow_clusters(BDRVQcowState *s, int nb_clusters, +static int count_cow_clusters(BDRVQcow2State *s, int nb_clusters, uint64_t *l2_table, int l2_index) { int i; @@ -883,7 +886,7 @@ out: static int handle_dependencies(BlockDriverState *bs, uint64_t guest_offset, uint64_t *cur_bytes, QCowL2Meta **m) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; QCowL2Meta *old_alloc; uint64_t bytes = *cur_bytes; @@ -956,11 +959,11 @@ static int handle_dependencies(BlockDriverState *bs, uint64_t guest_offset, static int handle_copied(BlockDriverState *bs, uint64_t guest_offset, uint64_t *host_offset, uint64_t *bytes, QCowL2Meta **m) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; int l2_index; uint64_t cluster_offset; uint64_t *l2_table; - unsigned int nb_clusters; + uint64_t nb_clusters; unsigned int keep_clusters; int ret; @@ -979,6 +982,7 @@ static int handle_copied(BlockDriverState *bs, uint64_t guest_offset, l2_index = offset_to_l2_index(s, guest_offset); nb_clusters = MIN(nb_clusters, s->l2_size - l2_index); + assert(nb_clusters <= INT_MAX); /* Find L2 entry for the first involved cluster */ ret = get_cluster_table(bs, guest_offset, &l2_table, &l2_index); @@ -1061,9 +1065,9 @@ out: * restarted, but the whole request should not be failed. */ static int do_alloc_cluster_offset(BlockDriverState *bs, uint64_t guest_offset, - uint64_t *host_offset, unsigned int *nb_clusters) + uint64_t *host_offset, uint64_t *nb_clusters) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; trace_qcow2_do_alloc_clusters_offset(qemu_coroutine_self(), guest_offset, *host_offset, *nb_clusters); @@ -1079,7 +1083,7 @@ static int do_alloc_cluster_offset(BlockDriverState *bs, uint64_t guest_offset, *host_offset = cluster_offset; return 0; } else { - int ret = qcow2_alloc_clusters_at(bs, *host_offset, *nb_clusters); + int64_t ret = qcow2_alloc_clusters_at(bs, *host_offset, *nb_clusters); if (ret < 0) { return ret; } @@ -1111,11 +1115,11 @@ static int do_alloc_cluster_offset(BlockDriverState *bs, uint64_t guest_offset, static int handle_alloc(BlockDriverState *bs, uint64_t guest_offset, uint64_t *host_offset, uint64_t *bytes, QCowL2Meta **m) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; int l2_index; uint64_t *l2_table; uint64_t entry; - unsigned int nb_clusters; + uint64_t nb_clusters; int ret; uint64_t alloc_cluster_offset; @@ -1133,6 +1137,7 @@ static int handle_alloc(BlockDriverState *bs, uint64_t guest_offset, l2_index = offset_to_l2_index(s, guest_offset); nb_clusters = MIN(nb_clusters, s->l2_size - l2_index); + assert(nb_clusters <= INT_MAX); /* Find L2 entry for the first involved cluster */ ret = get_cluster_table(bs, guest_offset, &l2_table, &l2_index); @@ -1263,7 +1268,7 @@ fail: int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset, int *num, uint64_t *host_offset, QCowL2Meta **m) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; uint64_t start, remaining; uint64_t cluster_offset; uint64_t cur_bytes; @@ -1397,7 +1402,7 @@ static int decompress_buffer(uint8_t *out_buf, int out_buf_size, int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; int ret, csize, nb_csectors, sector_offset; uint64_t coffset; @@ -1426,9 +1431,10 @@ 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, enum qcow2_discard_type type, bool full_discard) + uint64_t nb_clusters, enum qcow2_discard_type type, + bool full_discard) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; uint64_t *l2_table; int l2_index; int ret; @@ -1441,6 +1447,7 @@ static int discard_single_l2(BlockDriverState *bs, uint64_t offset, /* Limit nb_clusters to one L2 table */ nb_clusters = MIN(nb_clusters, s->l2_size - l2_index); + assert(nb_clusters <= INT_MAX); for (i = 0; i < nb_clusters; i++) { uint64_t old_l2_entry; @@ -1501,9 +1508,9 @@ static int discard_single_l2(BlockDriverState *bs, uint64_t offset, int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset, int nb_sectors, enum qcow2_discard_type type, bool full_discard) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; uint64_t end_offset; - unsigned int nb_clusters; + uint64_t nb_clusters; int ret; end_offset = offset + (nb_sectors << BDRV_SECTOR_BITS); @@ -1545,9 +1552,9 @@ fail: * clusters. */ static int zero_single_l2(BlockDriverState *bs, uint64_t offset, - unsigned int nb_clusters) + uint64_t nb_clusters) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; uint64_t *l2_table; int l2_index; int ret; @@ -1560,6 +1567,7 @@ static int zero_single_l2(BlockDriverState *bs, uint64_t offset, /* Limit nb_clusters to one L2 table */ nb_clusters = MIN(nb_clusters, s->l2_size - l2_index); + assert(nb_clusters <= INT_MAX); for (i = 0; i < nb_clusters; i++) { uint64_t old_offset; @@ -1583,8 +1591,8 @@ static int zero_single_l2(BlockDriverState *bs, uint64_t offset, int qcow2_zero_clusters(BlockDriverState *bs, uint64_t offset, int nb_sectors) { - BDRVQcowState *s = bs->opaque; - unsigned int nb_clusters; + BDRVQcow2State *s = bs->opaque; + uint64_t nb_clusters; int ret; /* The zero flag is only supported by version 3 and newer */ @@ -1628,7 +1636,7 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, int64_t l1_entries, BlockDriverAmendStatusCB *status_cb) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; bool is_active_l1 = (l1_table == s->l1_table); uint64_t *l2_table = NULL; int ret; @@ -1815,7 +1823,7 @@ fail: int qcow2_expand_zero_clusters(BlockDriverState *bs, BlockDriverAmendStatusCB *status_cb) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; uint64_t *l1_table = NULL; int64_t l1_entries = 0, visited_l1_entries = 0; int ret; diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c index d8f06458c9..2110839da4 100644 --- a/block/qcow2-refcount.c +++ b/block/qcow2-refcount.c @@ -82,7 +82,7 @@ static Qcow2SetRefcountFunc *const set_refcount_funcs[] = { int qcow2_refcount_init(BlockDriverState *bs) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; unsigned int refcount_table_size2, i; int ret; @@ -116,7 +116,7 @@ int qcow2_refcount_init(BlockDriverState *bs) void qcow2_refcount_close(BlockDriverState *bs) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; g_free(s->refcount_table); } @@ -214,7 +214,7 @@ static int load_refcount_block(BlockDriverState *bs, int64_t refcount_block_offset, void **refcount_block) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; int ret; BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_LOAD); @@ -231,7 +231,7 @@ static int load_refcount_block(BlockDriverState *bs, int qcow2_get_refcount(BlockDriverState *bs, int64_t cluster_index, uint64_t *refcount) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; uint64_t refcount_table_index, block_index; int64_t refcount_block_offset; int ret; @@ -274,7 +274,7 @@ int qcow2_get_refcount(BlockDriverState *bs, int64_t cluster_index, * Rounds the refcount table size up to avoid growing the table for each single * refcount block that is allocated. */ -static unsigned int next_refcount_table_size(BDRVQcowState *s, +static unsigned int next_refcount_table_size(BDRVQcow2State *s, unsigned int min_size) { unsigned int min_clusters = (min_size >> (s->cluster_bits - 3)) + 1; @@ -290,7 +290,7 @@ static unsigned int next_refcount_table_size(BDRVQcowState *s, /* Checks if two offsets are described by the same refcount block */ -static int in_same_refcount_block(BDRVQcowState *s, uint64_t offset_a, +static int in_same_refcount_block(BDRVQcow2State *s, uint64_t offset_a, uint64_t offset_b) { uint64_t block_a = offset_a >> (s->cluster_bits + s->refcount_block_bits); @@ -308,7 +308,7 @@ static int in_same_refcount_block(BDRVQcowState *s, uint64_t offset_a, static int alloc_refcount_block(BlockDriverState *bs, int64_t cluster_index, void **refcount_block) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; unsigned int refcount_table_index; int ret; @@ -605,7 +605,7 @@ fail_block: void qcow2_process_discards(BlockDriverState *bs, int ret) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; Qcow2DiscardRegion *d, *next; QTAILQ_FOREACH_SAFE(d, &s->discards, next, next) { @@ -625,7 +625,7 @@ void qcow2_process_discards(BlockDriverState *bs, int ret) static void update_refcount_discard(BlockDriverState *bs, uint64_t offset, uint64_t length) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; Qcow2DiscardRegion *d, *p, *next; QTAILQ_FOREACH(d, &s->discards, next) { @@ -682,7 +682,7 @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs, bool decrease, enum qcow2_discard_type type) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; int64_t start, last, cluster_offset; void *refcount_block = NULL; int64_t old_table_index = -1; @@ -793,7 +793,7 @@ int qcow2_update_cluster_refcount(BlockDriverState *bs, uint64_t addend, bool decrease, enum qcow2_discard_type type) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; int ret; ret = update_refcount(bs, cluster_index << s->cluster_bits, 1, addend, @@ -815,7 +815,7 @@ int qcow2_update_cluster_refcount(BlockDriverState *bs, /* return < 0 if error */ static int64_t alloc_clusters_noref(BlockDriverState *bs, uint64_t size) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; uint64_t i, nb_clusters, refcount; int ret; @@ -875,10 +875,10 @@ int64_t qcow2_alloc_clusters(BlockDriverState *bs, uint64_t size) return offset; } -int qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset, - int nb_clusters) +int64_t qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset, + int64_t nb_clusters) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; uint64_t cluster_index, refcount; uint64_t i; int ret; @@ -916,7 +916,7 @@ int qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset, contiguous sectors. size must be <= cluster_size */ int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; int64_t offset; size_t free_in_cluster; int ret; @@ -949,11 +949,17 @@ int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size) if (!offset || ROUND_UP(offset, s->cluster_size) != new_cluster) { offset = new_cluster; + free_in_cluster = s->cluster_size; + } else { + free_in_cluster += s->cluster_size; } } assert(offset); ret = update_refcount(bs, offset, size, 1, false, QCOW2_DISCARD_NEVER); + if (ret < 0) { + offset = 0; + } } while (ret == -EAGAIN); if (ret < 0) { return ret; @@ -992,7 +998,7 @@ void qcow2_free_clusters(BlockDriverState *bs, void qcow2_free_any_clusters(BlockDriverState *bs, uint64_t l2_entry, int nb_clusters, enum qcow2_discard_type type) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; switch (qcow2_get_cluster_type(l2_entry)) { case QCOW2_CLUSTER_COMPRESSED: @@ -1036,7 +1042,7 @@ void qcow2_free_any_clusters(BlockDriverState *bs, uint64_t l2_entry, int qcow2_update_snapshot_refcount(BlockDriverState *bs, int64_t l1_table_offset, int l1_size, int addend) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; uint64_t *l1_table, *l2_table, l2_offset, offset, l1_size2, refcount; bool l1_allocated = false; int64_t old_offset, old_l2_offset; @@ -1233,7 +1239,7 @@ fail: /* refcount checking functions */ -static size_t refcount_array_byte_size(BDRVQcowState *s, uint64_t entries) +static size_t refcount_array_byte_size(BDRVQcow2State *s, uint64_t entries) { /* This assertion holds because there is no way we can address more than * 2^(64 - 9) clusters at once (with cluster size 512 = 2^9, and because @@ -1256,10 +1262,10 @@ static size_t refcount_array_byte_size(BDRVQcowState *s, uint64_t entries) * refcount array buffer will be aligned to a cluster boundary, and the newly * allocated area will be zeroed. */ -static int realloc_refcount_array(BDRVQcowState *s, void **array, +static int realloc_refcount_array(BDRVQcow2State *s, void **array, int64_t *size, int64_t new_size) { - size_t old_byte_size, new_byte_size; + int64_t old_byte_size, new_byte_size; void *new_ptr; /* Round to clusters so the array can be directly written to disk */ @@ -1275,13 +1281,17 @@ static int realloc_refcount_array(BDRVQcowState *s, void **array, assert(new_byte_size > 0); + if (new_byte_size > SIZE_MAX) { + return -ENOMEM; + } + new_ptr = g_try_realloc(*array, new_byte_size); if (!new_ptr) { return -ENOMEM; } if (new_byte_size > old_byte_size) { - memset((void *)((uintptr_t)new_ptr + old_byte_size), 0, + memset((char *)new_ptr + old_byte_size, 0, new_byte_size - old_byte_size); } @@ -1304,7 +1314,7 @@ static int inc_refcounts(BlockDriverState *bs, int64_t *refcount_table_size, int64_t offset, int64_t size) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; uint64_t start, last, cluster_offset, k, refcount; int ret; @@ -1357,7 +1367,7 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res, int64_t *refcount_table_size, int64_t l2_offset, int flags) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; uint64_t *l2_table, l2_entry; uint64_t next_contiguous_offset = 0; int i, l2_size, nb_csectors, ret; @@ -1477,7 +1487,7 @@ static int check_refcounts_l1(BlockDriverState *bs, int64_t l1_table_offset, int l1_size, int flags) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; uint64_t *l1_table = NULL, l2_offset, l1_size2; int i, ret; @@ -1554,7 +1564,7 @@ fail: static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode fix) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; uint64_t *l2_table = qemu_blockalign(bs, s->cluster_size); int ret; uint64_t refcount; @@ -1673,7 +1683,7 @@ static int check_refblocks(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode fix, bool *rebuild, void **refcount_table, int64_t *nb_clusters) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; int64_t i, size; int ret; @@ -1776,7 +1786,7 @@ static int calculate_refcounts(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode fix, bool *rebuild, void **refcount_table, int64_t *nb_clusters) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; int64_t i; QCowSnapshot *sn; int ret; @@ -1840,7 +1850,7 @@ static void compare_refcounts(BlockDriverState *bs, BdrvCheckResult *res, int64_t *highest_cluster, void *refcount_table, int64_t nb_clusters) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; int64_t i; uint64_t refcount1, refcount2; int ret; @@ -1917,7 +1927,7 @@ static int64_t alloc_clusters_imrt(BlockDriverState *bs, int64_t *imrt_nb_clusters, int64_t *first_free_cluster) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; int64_t cluster = *first_free_cluster, i; bool first_gap = true; int contiguous_free_clusters; @@ -1987,7 +1997,7 @@ static int rebuild_refcount_structure(BlockDriverState *bs, void **refcount_table, int64_t *nb_clusters) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; int64_t first_free_cluster = 0, reftable_offset = -1, cluster = 0; int64_t refblock_offset, refblock_start, refblock_index; uint32_t reftable_size = 0; @@ -2174,7 +2184,7 @@ fail: int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode fix) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; BdrvCheckResult pre_compare_res; int64_t size, highest_cluster, nb_clusters; void *refcount_table = NULL; @@ -2311,7 +2321,7 @@ fail: int qcow2_check_metadata_overlap(BlockDriverState *bs, int ign, int64_t offset, int64_t size) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; int chk = s->overlap_check & ~ign; int i, j; diff --git a/block/qcow2-snapshot.c b/block/qcow2-snapshot.c index b6f58c13e2..92f4dfc083 100644 --- a/block/qcow2-snapshot.c +++ b/block/qcow2-snapshot.c @@ -29,7 +29,7 @@ void qcow2_free_snapshots(BlockDriverState *bs) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; int i; for(i = 0; i < s->nb_snapshots; i++) { @@ -43,7 +43,7 @@ void qcow2_free_snapshots(BlockDriverState *bs) int qcow2_read_snapshots(BlockDriverState *bs) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; QCowSnapshotHeader h; QCowSnapshotExtraData extra; QCowSnapshot *sn; @@ -136,7 +136,7 @@ fail: /* add at the end of the file a new list of snapshots */ static int qcow2_write_snapshots(BlockDriverState *bs) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; QCowSnapshot *sn; QCowSnapshotHeader h; QCowSnapshotExtraData extra; @@ -278,7 +278,7 @@ fail: static void find_new_snapshot_id(BlockDriverState *bs, char *id_str, int id_str_size) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; QCowSnapshot *sn; int i; unsigned long id, id_max = 0; @@ -296,7 +296,7 @@ static int find_snapshot_by_id_and_name(BlockDriverState *bs, const char *id, const char *name) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; int i; if (id && name) { @@ -338,7 +338,7 @@ static int find_snapshot_by_id_or_name(BlockDriverState *bs, /* if no id is provided, a new one is constructed */ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; QCowSnapshot *new_snapshot_list = NULL; QCowSnapshot *old_snapshot_list = NULL; QCowSnapshot sn1, *sn = &sn1; @@ -461,7 +461,7 @@ fail: /* copy the snapshot 'snapshot_name' into the current disk image */ int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; QCowSnapshot *sn; int i, snapshot_index; int cur_l1_bytes, sn_l1_bytes; @@ -587,7 +587,7 @@ int qcow2_snapshot_delete(BlockDriverState *bs, const char *name, Error **errp) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; QCowSnapshot sn; int snapshot_index, ret; @@ -650,7 +650,7 @@ int qcow2_snapshot_delete(BlockDriverState *bs, int qcow2_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; QEMUSnapshotInfo *sn_tab, *sn_info; QCowSnapshot *sn; int i; @@ -683,7 +683,7 @@ int qcow2_snapshot_load_tmp(BlockDriverState *bs, Error **errp) { int i, snapshot_index; - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; QCowSnapshot *sn; uint64_t *new_l1_table; int new_l1_bytes; diff --git a/block/qcow2.c b/block/qcow2.c index ea34ae2da5..56ad808f6b 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -85,7 +85,7 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, uint64_t end_offset, void **p_feature_table, Error **errp) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; QCowExtension ext; uint64_t offset; int ret; @@ -187,7 +187,7 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, static void cleanup_unknown_header_ext(BlockDriverState *bs) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; Qcow2UnknownHeaderExtension *uext, *next; QLIST_FOREACH_SAFE(uext, &s->unknown_header_ext, next, next) { @@ -249,7 +249,7 @@ static void report_unsupported_feature(BlockDriverState *bs, */ int qcow2_mark_dirty(BlockDriverState *bs) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; uint64_t val; int ret; @@ -282,7 +282,7 @@ int qcow2_mark_dirty(BlockDriverState *bs) */ static int qcow2_mark_clean(BlockDriverState *bs) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; if (s->incompatible_features & QCOW2_INCOMPAT_DIRTY) { int ret; @@ -304,7 +304,7 @@ static int qcow2_mark_clean(BlockDriverState *bs) */ int qcow2_mark_corrupt(BlockDriverState *bs) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; s->incompatible_features |= QCOW2_INCOMPAT_CORRUPT; return qcow2_update_header(bs); @@ -316,7 +316,7 @@ int qcow2_mark_corrupt(BlockDriverState *bs) */ int qcow2_mark_consistent(BlockDriverState *bs) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; if (s->incompatible_features & QCOW2_INCOMPAT_CORRUPT) { int ret = bdrv_flush(bs); @@ -351,7 +351,7 @@ static int qcow2_check(BlockDriverState *bs, BdrvCheckResult *result, static int validate_table_offset(BlockDriverState *bs, uint64_t offset, uint64_t entries, size_t entry_len) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; uint64_t size; /* Use signed INT64_MAX as the maximum even for uint64_t header fields, @@ -490,7 +490,7 @@ static const char *overlap_bool_option_names[QCOW2_OL_MAX_BITNR] = { static void cache_clean_timer_cb(void *opaque) { BlockDriverState *bs = opaque; - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; qcow2_cache_clean_unused(bs, s->l2_table_cache); qcow2_cache_clean_unused(bs, s->refcount_block_cache); timer_mod(s->cache_clean_timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + @@ -499,7 +499,7 @@ static void cache_clean_timer_cb(void *opaque) static void cache_clean_timer_init(BlockDriverState *bs, AioContext *context) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; if (s->cache_clean_interval > 0) { s->cache_clean_timer = aio_timer_new(context, QEMU_CLOCK_VIRTUAL, SCALE_MS, cache_clean_timer_cb, @@ -511,7 +511,7 @@ static void cache_clean_timer_init(BlockDriverState *bs, AioContext *context) static void cache_clean_timer_del(BlockDriverState *bs) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; if (s->cache_clean_timer) { timer_del(s->cache_clean_timer); timer_free(s->cache_clean_timer); @@ -534,7 +534,7 @@ static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts, uint64_t *l2_cache_size, uint64_t *refcount_cache_size, Error **errp) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; uint64_t combined_cache_size; bool l2_cache_size_set, refcount_cache_size_set, combined_cache_size_set; @@ -589,21 +589,244 @@ static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts, } } +typedef struct Qcow2ReopenState { + Qcow2Cache *l2_table_cache; + Qcow2Cache *refcount_block_cache; + bool use_lazy_refcounts; + int overlap_check; + bool discard_passthrough[QCOW2_DISCARD_MAX]; + uint64_t cache_clean_interval; +} Qcow2ReopenState; + +static int qcow2_update_options_prepare(BlockDriverState *bs, + Qcow2ReopenState *r, + QDict *options, int flags, + Error **errp) +{ + BDRVQcow2State *s = bs->opaque; + QemuOpts *opts = NULL; + const char *opt_overlap_check, *opt_overlap_check_template; + int overlap_check_template = 0; + uint64_t l2_cache_size, refcount_cache_size; + int i; + Error *local_err = NULL; + int ret; + + opts = qemu_opts_create(&qcow2_runtime_opts, NULL, 0, &error_abort); + qemu_opts_absorb_qdict(opts, options, &local_err); + if (local_err) { + error_propagate(errp, local_err); + ret = -EINVAL; + goto fail; + } + + /* get L2 table/refcount block cache size from command line options */ + read_cache_sizes(bs, opts, &l2_cache_size, &refcount_cache_size, + &local_err); + if (local_err) { + error_propagate(errp, local_err); + ret = -EINVAL; + goto fail; + } + + l2_cache_size /= s->cluster_size; + if (l2_cache_size < MIN_L2_CACHE_SIZE) { + l2_cache_size = MIN_L2_CACHE_SIZE; + } + if (l2_cache_size > INT_MAX) { + error_setg(errp, "L2 cache size too big"); + ret = -EINVAL; + goto fail; + } + + refcount_cache_size /= s->cluster_size; + if (refcount_cache_size < MIN_REFCOUNT_CACHE_SIZE) { + refcount_cache_size = MIN_REFCOUNT_CACHE_SIZE; + } + if (refcount_cache_size > INT_MAX) { + error_setg(errp, "Refcount cache size too big"); + ret = -EINVAL; + goto fail; + } + + /* alloc new L2 table/refcount block cache, flush old one */ + if (s->l2_table_cache) { + ret = qcow2_cache_flush(bs, s->l2_table_cache); + if (ret) { + error_setg_errno(errp, -ret, "Failed to flush the L2 table cache"); + goto fail; + } + } + + if (s->refcount_block_cache) { + ret = qcow2_cache_flush(bs, s->refcount_block_cache); + if (ret) { + error_setg_errno(errp, -ret, + "Failed to flush the refcount block cache"); + goto fail; + } + } + + r->l2_table_cache = qcow2_cache_create(bs, l2_cache_size); + r->refcount_block_cache = qcow2_cache_create(bs, refcount_cache_size); + if (r->l2_table_cache == NULL || r->refcount_block_cache == NULL) { + error_setg(errp, "Could not allocate metadata caches"); + ret = -ENOMEM; + goto fail; + } + + /* New interval for cache cleanup timer */ + r->cache_clean_interval = + qemu_opt_get_number(opts, QCOW2_OPT_CACHE_CLEAN_INTERVAL, + s->cache_clean_interval); + if (r->cache_clean_interval > UINT_MAX) { + error_setg(errp, "Cache clean interval too big"); + ret = -EINVAL; + goto fail; + } + + /* lazy-refcounts; flush if going from enabled to disabled */ + r->use_lazy_refcounts = qemu_opt_get_bool(opts, QCOW2_OPT_LAZY_REFCOUNTS, + (s->compatible_features & QCOW2_COMPAT_LAZY_REFCOUNTS)); + if (r->use_lazy_refcounts && s->qcow_version < 3) { + error_setg(errp, "Lazy refcounts require a qcow2 image with at least " + "qemu 1.1 compatibility level"); + ret = -EINVAL; + goto fail; + } + + if (s->use_lazy_refcounts && !r->use_lazy_refcounts) { + ret = qcow2_mark_clean(bs); + if (ret < 0) { + error_setg_errno(errp, -ret, "Failed to disable lazy refcounts"); + goto fail; + } + } + + /* Overlap check options */ + opt_overlap_check = qemu_opt_get(opts, QCOW2_OPT_OVERLAP); + opt_overlap_check_template = qemu_opt_get(opts, QCOW2_OPT_OVERLAP_TEMPLATE); + if (opt_overlap_check_template && opt_overlap_check && + strcmp(opt_overlap_check_template, opt_overlap_check)) + { + error_setg(errp, "Conflicting values for qcow2 options '" + QCOW2_OPT_OVERLAP "' ('%s') and '" QCOW2_OPT_OVERLAP_TEMPLATE + "' ('%s')", opt_overlap_check, opt_overlap_check_template); + ret = -EINVAL; + goto fail; + } + if (!opt_overlap_check) { + opt_overlap_check = opt_overlap_check_template ?: "cached"; + } + + if (!strcmp(opt_overlap_check, "none")) { + overlap_check_template = 0; + } else if (!strcmp(opt_overlap_check, "constant")) { + overlap_check_template = QCOW2_OL_CONSTANT; + } else if (!strcmp(opt_overlap_check, "cached")) { + overlap_check_template = QCOW2_OL_CACHED; + } else if (!strcmp(opt_overlap_check, "all")) { + overlap_check_template = QCOW2_OL_ALL; + } else { + error_setg(errp, "Unsupported value '%s' for qcow2 option " + "'overlap-check'. Allowed are any of the following: " + "none, constant, cached, all", opt_overlap_check); + ret = -EINVAL; + goto fail; + } + + r->overlap_check = 0; + for (i = 0; i < QCOW2_OL_MAX_BITNR; i++) { + /* overlap-check defines a template bitmask, but every flag may be + * overwritten through the associated boolean option */ + r->overlap_check |= + qemu_opt_get_bool(opts, overlap_bool_option_names[i], + overlap_check_template & (1 << i)) << i; + } + + r->discard_passthrough[QCOW2_DISCARD_NEVER] = false; + r->discard_passthrough[QCOW2_DISCARD_ALWAYS] = true; + r->discard_passthrough[QCOW2_DISCARD_REQUEST] = + qemu_opt_get_bool(opts, QCOW2_OPT_DISCARD_REQUEST, + flags & BDRV_O_UNMAP); + r->discard_passthrough[QCOW2_DISCARD_SNAPSHOT] = + qemu_opt_get_bool(opts, QCOW2_OPT_DISCARD_SNAPSHOT, true); + r->discard_passthrough[QCOW2_DISCARD_OTHER] = + qemu_opt_get_bool(opts, QCOW2_OPT_DISCARD_OTHER, false); + + ret = 0; +fail: + qemu_opts_del(opts); + opts = NULL; + return ret; +} + +static void qcow2_update_options_commit(BlockDriverState *bs, + Qcow2ReopenState *r) +{ + BDRVQcow2State *s = bs->opaque; + int i; + + if (s->l2_table_cache) { + qcow2_cache_destroy(bs, s->l2_table_cache); + } + if (s->refcount_block_cache) { + qcow2_cache_destroy(bs, s->refcount_block_cache); + } + s->l2_table_cache = r->l2_table_cache; + s->refcount_block_cache = r->refcount_block_cache; + + s->overlap_check = r->overlap_check; + s->use_lazy_refcounts = r->use_lazy_refcounts; + + for (i = 0; i < QCOW2_DISCARD_MAX; i++) { + s->discard_passthrough[i] = r->discard_passthrough[i]; + } + + if (s->cache_clean_interval != r->cache_clean_interval) { + cache_clean_timer_del(bs); + s->cache_clean_interval = r->cache_clean_interval; + cache_clean_timer_init(bs, bdrv_get_aio_context(bs)); + } +} + +static void qcow2_update_options_abort(BlockDriverState *bs, + Qcow2ReopenState *r) +{ + if (r->l2_table_cache) { + qcow2_cache_destroy(bs, r->l2_table_cache); + } + if (r->refcount_block_cache) { + qcow2_cache_destroy(bs, r->refcount_block_cache); + } +} + +static int qcow2_update_options(BlockDriverState *bs, QDict *options, + int flags, Error **errp) +{ + Qcow2ReopenState r = {}; + int ret; + + ret = qcow2_update_options_prepare(bs, &r, options, flags, errp); + if (ret >= 0) { + qcow2_update_options_commit(bs, &r); + } else { + qcow2_update_options_abort(bs, &r); + } + + return ret; +} + static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, Error **errp) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; unsigned int len, i; int ret = 0; QCowHeader header; - QemuOpts *opts = NULL; Error *local_err = NULL; uint64_t ext_end; uint64_t l1_vm_state_index; - const char *opt_overlap_check, *opt_overlap_check_template; - int overlap_check_template = 0; - uint64_t l2_cache_size, refcount_cache_size; - uint64_t cache_clean_interval; ret = bdrv_pread(bs->file, 0, &header, sizeof(header)); if (ret < 0) { @@ -851,61 +1074,11 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, } } - /* get L2 table/refcount block cache size from command line options */ - opts = qemu_opts_create(&qcow2_runtime_opts, NULL, 0, &error_abort); - qemu_opts_absorb_qdict(opts, options, &local_err); - if (local_err) { - error_propagate(errp, local_err); - ret = -EINVAL; - goto fail; - } - - read_cache_sizes(bs, opts, &l2_cache_size, &refcount_cache_size, - &local_err); - if (local_err) { - error_propagate(errp, local_err); - ret = -EINVAL; - goto fail; - } - - l2_cache_size /= s->cluster_size; - if (l2_cache_size < MIN_L2_CACHE_SIZE) { - l2_cache_size = MIN_L2_CACHE_SIZE; - } - if (l2_cache_size > INT_MAX) { - error_setg(errp, "L2 cache size too big"); - ret = -EINVAL; - goto fail; - } - - refcount_cache_size /= s->cluster_size; - if (refcount_cache_size < MIN_REFCOUNT_CACHE_SIZE) { - refcount_cache_size = MIN_REFCOUNT_CACHE_SIZE; - } - if (refcount_cache_size > INT_MAX) { - error_setg(errp, "Refcount cache size too big"); - ret = -EINVAL; - goto fail; - } - - /* alloc L2 table/refcount block cache */ - s->l2_table_cache = qcow2_cache_create(bs, l2_cache_size); - s->refcount_block_cache = qcow2_cache_create(bs, refcount_cache_size); - if (s->l2_table_cache == NULL || s->refcount_block_cache == NULL) { - error_setg(errp, "Could not allocate metadata caches"); - ret = -ENOMEM; - goto fail; - } - - cache_clean_interval = - qemu_opt_get_number(opts, QCOW2_OPT_CACHE_CLEAN_INTERVAL, 0); - if (cache_clean_interval > UINT_MAX) { - error_setg(errp, "Cache clean interval too big"); - ret = -EINVAL; + /* Parse driver-specific options */ + ret = qcow2_update_options(bs, options, flags, errp); + if (ret < 0) { goto fail; } - s->cache_clean_interval = cache_clean_interval; - cache_clean_timer_init(bs, bdrv_get_aio_context(bs)); s->cluster_cache = g_malloc(s->cluster_size); /* one more sector for decompressed data alignment */ @@ -991,70 +1164,6 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, } } - /* Enable lazy_refcounts according to image and command line options */ - s->use_lazy_refcounts = qemu_opt_get_bool(opts, QCOW2_OPT_LAZY_REFCOUNTS, - (s->compatible_features & QCOW2_COMPAT_LAZY_REFCOUNTS)); - - s->discard_passthrough[QCOW2_DISCARD_NEVER] = false; - s->discard_passthrough[QCOW2_DISCARD_ALWAYS] = true; - s->discard_passthrough[QCOW2_DISCARD_REQUEST] = - qemu_opt_get_bool(opts, QCOW2_OPT_DISCARD_REQUEST, - flags & BDRV_O_UNMAP); - s->discard_passthrough[QCOW2_DISCARD_SNAPSHOT] = - qemu_opt_get_bool(opts, QCOW2_OPT_DISCARD_SNAPSHOT, true); - s->discard_passthrough[QCOW2_DISCARD_OTHER] = - qemu_opt_get_bool(opts, QCOW2_OPT_DISCARD_OTHER, false); - - opt_overlap_check = qemu_opt_get(opts, QCOW2_OPT_OVERLAP); - opt_overlap_check_template = qemu_opt_get(opts, QCOW2_OPT_OVERLAP_TEMPLATE); - if (opt_overlap_check_template && opt_overlap_check && - strcmp(opt_overlap_check_template, opt_overlap_check)) - { - error_setg(errp, "Conflicting values for qcow2 options '" - QCOW2_OPT_OVERLAP "' ('%s') and '" QCOW2_OPT_OVERLAP_TEMPLATE - "' ('%s')", opt_overlap_check, opt_overlap_check_template); - ret = -EINVAL; - goto fail; - } - if (!opt_overlap_check) { - opt_overlap_check = opt_overlap_check_template ?: "cached"; - } - - if (!strcmp(opt_overlap_check, "none")) { - overlap_check_template = 0; - } else if (!strcmp(opt_overlap_check, "constant")) { - overlap_check_template = QCOW2_OL_CONSTANT; - } else if (!strcmp(opt_overlap_check, "cached")) { - overlap_check_template = QCOW2_OL_CACHED; - } else if (!strcmp(opt_overlap_check, "all")) { - overlap_check_template = QCOW2_OL_ALL; - } else { - error_setg(errp, "Unsupported value '%s' for qcow2 option " - "'overlap-check'. Allowed are either of the following: " - "none, constant, cached, all", opt_overlap_check); - ret = -EINVAL; - goto fail; - } - - s->overlap_check = 0; - for (i = 0; i < QCOW2_OL_MAX_BITNR; i++) { - /* overlap-check defines a template bitmask, but every flag may be - * overwritten through the associated boolean option */ - s->overlap_check |= - qemu_opt_get_bool(opts, overlap_bool_option_names[i], - overlap_check_template & (1 << i)) << i; - } - - qemu_opts_del(opts); - opts = NULL; - - if (s->use_lazy_refcounts && s->qcow_version < 3) { - error_setg(errp, "Lazy refcounts require a qcow2 image with at least " - "qemu 1.1 compatibility level"); - ret = -EINVAL; - goto fail; - } - #ifdef DEBUG_ALLOC { BdrvCheckResult result = {0}; @@ -1064,7 +1173,6 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, return ret; fail: - qemu_opts_del(opts); g_free(s->unknown_header_fields); cleanup_unknown_header_ext(bs); qcow2_free_snapshots(bs); @@ -1086,14 +1194,14 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, static void qcow2_refresh_limits(BlockDriverState *bs, Error **errp) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; bs->bl.write_zeroes_alignment = s->cluster_sectors; } static int qcow2_set_key(BlockDriverState *bs, const char *key) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; uint8_t keybuf[16]; int len, i; Error *err = NULL; @@ -1126,32 +1234,58 @@ static int qcow2_set_key(BlockDriverState *bs, const char *key) return 0; } -/* We have no actual commit/abort logic for qcow2, but we need to write out any - * unwritten data if we reopen read-only. */ static int qcow2_reopen_prepare(BDRVReopenState *state, BlockReopenQueue *queue, Error **errp) { + Qcow2ReopenState *r; int ret; + r = g_new0(Qcow2ReopenState, 1); + state->opaque = r; + + ret = qcow2_update_options_prepare(state->bs, r, state->options, + state->flags, errp); + if (ret < 0) { + goto fail; + } + + /* We need to write out any unwritten data if we reopen read-only. */ if ((state->flags & BDRV_O_RDWR) == 0) { ret = bdrv_flush(state->bs); if (ret < 0) { - return ret; + goto fail; } ret = qcow2_mark_clean(state->bs); if (ret < 0) { - return ret; + goto fail; } } return 0; + +fail: + qcow2_update_options_abort(state->bs, r); + g_free(r); + return ret; +} + +static void qcow2_reopen_commit(BDRVReopenState *state) +{ + qcow2_update_options_commit(state->bs, state->opaque); + g_free(state->opaque); +} + +static void qcow2_reopen_abort(BDRVReopenState *state) +{ + qcow2_update_options_abort(state->bs, state->opaque); + g_free(state->opaque); } static int64_t coroutine_fn qcow2_co_get_block_status(BlockDriverState *bs, int64_t sector_num, int nb_sectors, int *pnum) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; uint64_t cluster_offset; int index_in_cluster, ret; int64_t status = 0; @@ -1198,7 +1332,7 @@ int qcow2_backing_read1(BlockDriverState *bs, QEMUIOVector *qiov, static coroutine_fn int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num, int remaining_sectors, QEMUIOVector *qiov) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; int index_in_cluster, n1; int ret; int cur_nr_sectors; /* number of sectors in current iteration */ @@ -1360,7 +1494,7 @@ static coroutine_fn int qcow2_co_writev(BlockDriverState *bs, int remaining_sectors, QEMUIOVector *qiov) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; int index_in_cluster; int ret; int cur_nr_sectors; /* number of sectors in current iteration */ @@ -1506,7 +1640,7 @@ fail: static void qcow2_close(BlockDriverState *bs) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; qemu_vfree(s->l1_table); /* else pre-write overlap checks in cache_destroy may crash */ s->l1_table = NULL; @@ -1552,7 +1686,7 @@ static void qcow2_close(BlockDriverState *bs) static void qcow2_invalidate_cache(BlockDriverState *bs, Error **errp) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; int flags = s->flags; QCryptoCipher *cipher = NULL; QDict *options; @@ -1575,7 +1709,7 @@ static void qcow2_invalidate_cache(BlockDriverState *bs, Error **errp) return; } - memset(s, 0, sizeof(BDRVQcowState)); + memset(s, 0, sizeof(BDRVQcow2State)); options = qdict_clone_shallow(bs->options); ret = qcow2_open(bs, options, flags, &local_err); @@ -1622,7 +1756,7 @@ static size_t header_ext_add(char *buf, uint32_t magic, const void *s, */ int qcow2_update_header(BlockDriverState *bs) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; QCowHeader *header; char *buf; size_t buflen = s->cluster_size; @@ -1791,7 +1925,7 @@ fail: static int qcow2_change_backing_file(BlockDriverState *bs, const char *backing_file, const char *backing_fmt) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; pstrcpy(bs->backing_file, sizeof(bs->backing_file), backing_file ?: ""); pstrcpy(bs->backing_format, sizeof(bs->backing_format), backing_fmt ?: ""); @@ -1873,8 +2007,10 @@ static int qcow2_create2(const char *filename, int64_t total_size, QemuOpts *opts, int version, int refcount_order, Error **errp) { - /* Calculate cluster_bits */ int cluster_bits; + QDict *options; + + /* Calculate cluster_bits */ cluster_bits = ctz32(cluster_size); if (cluster_bits < MIN_CLUSTER_BITS || cluster_bits > MAX_CLUSTER_BITS || (1 << cluster_bits) != cluster_size) @@ -1973,7 +2109,7 @@ static int qcow2_create2(const char *filename, int64_t total_size, bs = NULL; ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL, - NULL, &local_err); + &local_err); if (ret < 0) { error_propagate(errp, local_err); return ret; @@ -2032,9 +2168,11 @@ static int qcow2_create2(const char *filename, int64_t total_size, * refcount of the cluster that is occupied by the header and the refcount * table) */ - ret = bdrv_open(&bs, filename, NULL, NULL, + options = qdict_new(); + qdict_put(options, "driver", qstring_from_str("qcow2")); + ret = bdrv_open(&bs, filename, NULL, options, BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH, - &bdrv_qcow2, &local_err); + &local_err); if (ret < 0) { error_propagate(errp, local_err); goto out; @@ -2070,7 +2208,7 @@ static int qcow2_create2(const char *filename, int64_t total_size, /* And if we're supposed to preallocate metadata, do that now */ if (prealloc != PREALLOC_MODE_OFF) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; qemu_co_mutex_lock(&s->lock); ret = preallocate(bs); qemu_co_mutex_unlock(&s->lock); @@ -2084,9 +2222,11 @@ static int qcow2_create2(const char *filename, int64_t total_size, bs = NULL; /* Reopen the image without BDRV_O_NO_FLUSH to flush it before returning */ - ret = bdrv_open(&bs, filename, NULL, NULL, + options = qdict_new(); + qdict_put(options, "driver", qstring_from_str("qcow2")); + ret = bdrv_open(&bs, filename, NULL, options, BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_BACKING, - &bdrv_qcow2, &local_err); + &local_err); if (local_err) { error_propagate(errp, local_err); goto out; @@ -2203,7 +2343,7 @@ static coroutine_fn int qcow2_co_write_zeroes(BlockDriverState *bs, int64_t sector_num, int nb_sectors, BdrvRequestFlags flags) { int ret; - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; /* Emulate misaligned zero writes */ if (sector_num % s->cluster_sectors || nb_sectors % s->cluster_sectors) { @@ -2223,7 +2363,7 @@ static coroutine_fn int qcow2_co_discard(BlockDriverState *bs, int64_t sector_num, int nb_sectors) { int ret; - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; qemu_co_mutex_lock(&s->lock); ret = qcow2_discard_clusters(bs, sector_num << BDRV_SECTOR_BITS, @@ -2234,7 +2374,7 @@ static coroutine_fn int qcow2_co_discard(BlockDriverState *bs, static int qcow2_truncate(BlockDriverState *bs, int64_t offset) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; int64_t new_l1_size; int ret; @@ -2278,7 +2418,7 @@ static int qcow2_truncate(BlockDriverState *bs, int64_t offset) static int qcow2_write_compressed(BlockDriverState *bs, int64_t sector_num, const uint8_t *buf, int nb_sectors) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; z_stream strm; int ret, out_len; uint8_t *out_buf; @@ -2369,7 +2509,7 @@ fail: static int make_completely_empty(BlockDriverState *bs) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; int ret, l1_clusters; int64_t offset; uint64_t *new_reftable = NULL; @@ -2517,7 +2657,7 @@ fail: static int qcow2_make_empty(BlockDriverState *bs) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; uint64_t start_sector; int sector_step = INT_MAX / BDRV_SECTOR_SIZE; int l1_clusters, ret = 0; @@ -2558,7 +2698,7 @@ static int qcow2_make_empty(BlockDriverState *bs) static coroutine_fn int qcow2_co_flush_to_os(BlockDriverState *bs) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; int ret; qemu_co_mutex_lock(&s->lock); @@ -2582,7 +2722,7 @@ static coroutine_fn int qcow2_co_flush_to_os(BlockDriverState *bs) static int qcow2_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; bdi->unallocated_blocks_are_zero = true; bdi->can_write_zeroes_with_unmap = (s->qcow_version >= 3); bdi->cluster_size = s->cluster_size; @@ -2592,7 +2732,7 @@ static int qcow2_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) static ImageInfoSpecific *qcow2_get_specific_info(BlockDriverState *bs) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; ImageInfoSpecific *spec_info = g_new(ImageInfoSpecific, 1); *spec_info = (ImageInfoSpecific){ @@ -2625,7 +2765,7 @@ static ImageInfoSpecific *qcow2_get_specific_info(BlockDriverState *bs) #if 0 static void dump_refcounts(BlockDriverState *bs) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; int64_t nb_clusters, k, k1, size; int refcount; @@ -2646,7 +2786,7 @@ static void dump_refcounts(BlockDriverState *bs) static int qcow2_save_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; int64_t total_sectors = bs->total_sectors; bool zero_beyond_eof = bs->zero_beyond_eof; int ret; @@ -2667,7 +2807,7 @@ static int qcow2_save_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, static int qcow2_load_vmstate(BlockDriverState *bs, uint8_t *buf, int64_t pos, int size) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; bool zero_beyond_eof = bs->zero_beyond_eof; int ret; @@ -2686,7 +2826,7 @@ static int qcow2_load_vmstate(BlockDriverState *bs, uint8_t *buf, static int qcow2_downgrade(BlockDriverState *bs, int target_version, BlockDriverAmendStatusCB *status_cb) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; int current_version = s->qcow_version; int ret; @@ -2750,7 +2890,7 @@ static int qcow2_downgrade(BlockDriverState *bs, int target_version, static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, BlockDriverAmendStatusCB *status_cb) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *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; @@ -2897,7 +3037,7 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, void qcow2_signal_corruption(BlockDriverState *bs, bool fatal, int64_t offset, int64_t size, const char *message_format, ...) { - BDRVQcowState *s = bs->opaque; + BDRVQcow2State *s = bs->opaque; const char *node_name; char *message; va_list ap; @@ -2998,11 +3138,13 @@ static QemuOptsList qcow2_create_opts = { BlockDriver bdrv_qcow2 = { .format_name = "qcow2", - .instance_size = sizeof(BDRVQcowState), + .instance_size = sizeof(BDRVQcow2State), .bdrv_probe = qcow2_probe, .bdrv_open = qcow2_open, .bdrv_close = qcow2_close, .bdrv_reopen_prepare = qcow2_reopen_prepare, + .bdrv_reopen_commit = qcow2_reopen_commit, + .bdrv_reopen_abort = qcow2_reopen_abort, .bdrv_create = qcow2_create, .bdrv_has_zero_init = bdrv_has_zero_init_1, .bdrv_co_get_block_status = qcow2_co_get_block_status, diff --git a/block/qcow2.h b/block/qcow2.h index 71dafd6dc9..d700bf1b62 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -222,7 +222,7 @@ typedef uint64_t Qcow2GetRefcountFunc(const void *refcount_array, typedef void Qcow2SetRefcountFunc(void *refcount_array, uint64_t index, uint64_t value); -typedef struct BDRVQcowState { +typedef struct BDRVQcow2State { int cluster_bits; int cluster_size; int cluster_sectors; @@ -293,7 +293,7 @@ typedef struct BDRVQcowState { * override) */ char *image_backing_file; char *image_backing_format; -} BDRVQcowState; +} BDRVQcow2State; struct QCowAIOCB; @@ -405,28 +405,28 @@ typedef enum QCow2MetadataOverlap { #define REFT_OFFSET_MASK 0xfffffffffffffe00ULL -static inline int64_t start_of_cluster(BDRVQcowState *s, int64_t offset) +static inline int64_t start_of_cluster(BDRVQcow2State *s, int64_t offset) { return offset & ~(s->cluster_size - 1); } -static inline int64_t offset_into_cluster(BDRVQcowState *s, int64_t offset) +static inline int64_t offset_into_cluster(BDRVQcow2State *s, int64_t offset) { return offset & (s->cluster_size - 1); } -static inline int size_to_clusters(BDRVQcowState *s, int64_t size) +static inline uint64_t size_to_clusters(BDRVQcow2State *s, uint64_t size) { return (size + (s->cluster_size - 1)) >> s->cluster_bits; } -static inline int64_t size_to_l1(BDRVQcowState *s, int64_t size) +static inline int64_t size_to_l1(BDRVQcow2State *s, int64_t size) { int shift = s->cluster_bits + s->l2_bits; return (size + (1ULL << shift) - 1) >> shift; } -static inline int offset_to_l2_index(BDRVQcowState *s, int64_t offset) +static inline int offset_to_l2_index(BDRVQcow2State *s, int64_t offset) { return (offset >> s->cluster_bits) & (s->l2_size - 1); } @@ -437,12 +437,12 @@ static inline int64_t align_offset(int64_t offset, int n) return offset; } -static inline int64_t qcow2_vm_state_offset(BDRVQcowState *s) +static inline int64_t qcow2_vm_state_offset(BDRVQcow2State *s) { return (int64_t)s->l1_vm_state_index << (s->cluster_bits + s->l2_bits); } -static inline uint64_t qcow2_max_refcount_clusters(BDRVQcowState *s) +static inline uint64_t qcow2_max_refcount_clusters(BDRVQcow2State *s) { return QCOW_MAX_REFTABLE_SIZE >> s->cluster_bits; } @@ -461,7 +461,7 @@ static inline int qcow2_get_cluster_type(uint64_t l2_entry) } /* Check whether refcounts are eager or lazy */ -static inline bool qcow2_need_accurate_refcounts(BDRVQcowState *s) +static inline bool qcow2_need_accurate_refcounts(BDRVQcow2State *s) { return !(s->incompatible_features & QCOW2_INCOMPAT_DIRTY); } @@ -509,8 +509,8 @@ int qcow2_update_cluster_refcount(BlockDriverState *bs, int64_t cluster_index, enum qcow2_discard_type type); int64_t qcow2_alloc_clusters(BlockDriverState *bs, uint64_t size); -int qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset, - int nb_clusters); +int64_t qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset, + int64_t nb_clusters); int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size); void qcow2_free_clusters(BlockDriverState *bs, int64_t offset, int64_t size, @@ -537,7 +537,7 @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size, int qcow2_write_l1_entry(BlockDriverState *bs, int l1_index); void qcow2_l2_cache_reset(BlockDriverState *bs); int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset); -int qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num, +int qcow2_encrypt_sectors(BDRVQcow2State *s, int64_t sector_num, uint8_t *out_buf, const uint8_t *in_buf, int nb_sectors, bool enc, Error **errp); diff --git a/block/qed.c b/block/qed.c index 954ed007c0..a7ff1d9c41 100644 --- a/block/qed.c +++ b/block/qed.c @@ -583,7 +583,7 @@ static int qed_create(const char *filename, uint32_t cluster_size, bs = NULL; ret = bdrv_open(&bs, filename, NULL, NULL, - BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_PROTOCOL, NULL, + BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_PROTOCOL, &local_err); if (ret < 0) { error_propagate(errp, local_err); diff --git a/block/sheepdog.c b/block/sheepdog.c index 9585beb73e..67ca788d5c 100644 --- a/block/sheepdog.c +++ b/block/sheepdog.c @@ -1554,7 +1554,7 @@ static int sd_prealloc(const char *filename, Error **errp) int ret; ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL, - NULL, errp); + errp); if (ret < 0) { goto out_with_err_set; } @@ -1746,8 +1746,7 @@ static int sd_create(const char *filename, QemuOpts *opts, } bs = NULL; - ret = bdrv_open(&bs, backing_file, NULL, NULL, BDRV_O_PROTOCOL, NULL, - errp); + ret = bdrv_open(&bs, backing_file, NULL, NULL, BDRV_O_PROTOCOL, errp); if (ret < 0) { goto out; } diff --git a/block/vdi.c b/block/vdi.c index 7642ef3597..062a6541f8 100644 --- a/block/vdi.c +++ b/block/vdi.c @@ -764,7 +764,7 @@ static int vdi_create(const char *filename, QemuOpts *opts, Error **errp) goto exit; } ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL, - NULL, &local_err); + &local_err); if (ret < 0) { error_propagate(errp, local_err); goto exit; diff --git a/block/vhdx.c b/block/vhdx.c index f05c7a9de3..d3bb1bd9d0 100644 --- a/block/vhdx.c +++ b/block/vhdx.c @@ -1842,7 +1842,7 @@ static int vhdx_create(const char *filename, QemuOpts *opts, Error **errp) bs = NULL; ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL, - NULL, &local_err); + &local_err); if (ret < 0) { error_propagate(errp, local_err); goto exit; diff --git a/block/vmdk.c b/block/vmdk.c index fbaab67c8f..be0d6401af 100644 --- a/block/vmdk.c +++ b/block/vmdk.c @@ -1324,8 +1324,12 @@ static int vmdk_write_extent(VmdkExtent *extent, int64_t cluster_offset, write_end_sector = DIV_ROUND_UP(write_offset + write_len, BDRV_SECTOR_SIZE); - extent->next_cluster_sector = MAX(extent->next_cluster_sector, - write_end_sector); + if (extent->compressed) { + extent->next_cluster_sector = write_end_sector; + } else { + extent->next_cluster_sector = MAX(extent->next_cluster_sector, + write_end_sector); + } if (ret != write_len) { ret = ret < 0 ? ret : -EIO; @@ -1632,7 +1636,7 @@ static int vmdk_create_extent(const char *filename, int64_t filesize, assert(bs == NULL); ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL, - NULL, &local_err); + &local_err); if (ret < 0) { error_propagate(errp, local_err); goto exit; @@ -1905,8 +1909,7 @@ static int vmdk_create(const char *filename, QemuOpts *opts, Error **errp) ret = -ENOENT; goto exit; } - ret = bdrv_open(&bs, full_backing, NULL, NULL, BDRV_O_NO_BACKING, NULL, - errp); + ret = bdrv_open(&bs, full_backing, NULL, NULL, BDRV_O_NO_BACKING, errp); g_free(full_backing); if (ret != 0) { goto exit; @@ -1977,7 +1980,7 @@ static int vmdk_create(const char *filename, QemuOpts *opts, Error **errp) } assert(new_bs == NULL); ret = bdrv_open(&new_bs, filename, NULL, NULL, - BDRV_O_RDWR | BDRV_O_PROTOCOL, NULL, &local_err); + BDRV_O_RDWR | BDRV_O_PROTOCOL, &local_err); if (ret < 0) { error_propagate(errp, local_err); goto exit; diff --git a/block/vpc.c b/block/vpc.c index 3e385d9fb9..2b3b518d1c 100644 --- a/block/vpc.c +++ b/block/vpc.c @@ -794,7 +794,7 @@ static int vpc_create(const char *filename, QemuOpts *opts, Error **errp) goto out; } ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL, - NULL, &local_err); + &local_err); if (ret < 0) { error_propagate(errp, local_err); goto out; diff --git a/block/vvfat.c b/block/vvfat.c index 206869712e..7ddc962436 100644 --- a/block/vvfat.c +++ b/block/vvfat.c @@ -2926,6 +2926,8 @@ static int enable_write_target(BDRVVVFATState *s, Error **errp) QemuOpts *opts = NULL; int ret; int size = sector2cluster(s, s->sector_count); + QDict *options; + s->used_clusters = calloc(size, 1); array_init(&(s->commits), sizeof(commit_t)); @@ -2956,9 +2958,11 @@ static int enable_write_target(BDRVVVFATState *s, Error **errp) } s->qcow = NULL; - ret = bdrv_open(&s->qcow, s->qcow_filename, NULL, NULL, + options = qdict_new(); + qdict_put(options, "driver", qstring_from_str("qcow")); + ret = bdrv_open(&s->qcow, s->qcow_filename, NULL, options, BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH, - bdrv_qcow, errp); + errp); if (ret < 0) { goto err; } diff --git a/blockdev.c b/blockdev.c index 6b48be60ba..32b04b478c 100644 --- a/blockdev.c +++ b/blockdev.c @@ -1422,9 +1422,8 @@ typedef struct ExternalSnapshotState { static void external_snapshot_prepare(BlkTransactionState *common, Error **errp) { - BlockDriver *drv; int flags, ret; - QDict *options = NULL; + QDict *options; Error *local_err = NULL; bool has_device = false; const char *device; @@ -1459,12 +1458,6 @@ static void external_snapshot_prepare(BlkTransactionState *common, } /* start processing */ - drv = bdrv_find_format(format); - if (!drv) { - error_setg(errp, QERR_INVALID_BLOCK_FORMAT, format); - return; - } - state->old_bs = bdrv_lookup_bs(has_device ? device : NULL, has_node_name ? node_name : NULL, &local_err); @@ -1523,17 +1516,18 @@ static void external_snapshot_prepare(BlkTransactionState *common, } } + options = qdict_new(); if (has_snapshot_node_name) { - options = qdict_new(); qdict_put(options, "node-name", qstring_from_str(snapshot_node_name)); } + qdict_put(options, "driver", qstring_from_str(format)); /* TODO Inherit bs->options or only take explicit options with an * extended QMP command? */ assert(state->new_bs == NULL); ret = bdrv_open(&state->new_bs, new_image_file, NULL, options, - flags | BDRV_O_NO_BACKING, drv, &local_err); + flags | BDRV_O_NO_BACKING, &local_err); /* We will manually add the backing_hd field to the bs later */ if (ret != 0) { error_propagate(errp, local_err); @@ -1895,13 +1889,19 @@ void qmp_block_passwd(bool has_device, const char *device, /* Assumes AioContext is held */ static void qmp_bdrv_open_encrypted(BlockDriverState *bs, const char *filename, - int bdrv_flags, BlockDriver *drv, + int bdrv_flags, const char *format, const char *password, Error **errp) { Error *local_err = NULL; + QDict *options = NULL; int ret; - ret = bdrv_open(&bs, filename, NULL, NULL, bdrv_flags, drv, &local_err); + if (format) { + options = qdict_new(); + qdict_put(options, "driver", qstring_from_str(format)); + } + + ret = bdrv_open(&bs, filename, NULL, options, bdrv_flags, &local_err); if (ret < 0) { error_propagate(errp, local_err); return; @@ -1916,7 +1916,6 @@ void qmp_change_blockdev(const char *device, const char *filename, BlockBackend *blk; BlockDriverState *bs; AioContext *aio_context; - BlockDriver *drv = NULL; int bdrv_flags; Error *err = NULL; @@ -1931,14 +1930,6 @@ void qmp_change_blockdev(const char *device, const char *filename, aio_context = bdrv_get_aio_context(bs); aio_context_acquire(aio_context); - if (format) { - drv = bdrv_find_whitelisted_format(format, bs->read_only); - if (!drv) { - error_setg(errp, QERR_INVALID_BLOCK_FORMAT, format); - goto out; - } - } - eject_device(blk, 0, &err); if (err) { error_propagate(errp, err); @@ -1948,7 +1939,7 @@ void qmp_change_blockdev(const char *device, const char *filename, bdrv_flags = bdrv_is_read_only(bs) ? 0 : BDRV_O_RDWR; bdrv_flags |= bdrv_is_snapshot(bs) ? BDRV_O_SNAPSHOT : 0; - qmp_bdrv_open_encrypted(bs, filename, bdrv_flags, drv, NULL, errp); + qmp_bdrv_open_encrypted(bs, filename, bdrv_flags, format, NULL, errp); out: aio_context_release(aio_context); @@ -2466,7 +2457,7 @@ void qmp_drive_backup(const char *device, const char *target, BlockDriverState *source = NULL; BdrvDirtyBitmap *bmap = NULL; AioContext *aio_context; - BlockDriver *drv = NULL; + QDict *options = NULL; Error *local_err = NULL; int flags; int64_t size; @@ -2506,13 +2497,6 @@ void qmp_drive_backup(const char *device, const char *target, if (!has_format) { format = mode == NEW_IMAGE_MODE_EXISTING ? NULL : bs->drv->format_name; } - if (format) { - drv = bdrv_find_format(format); - if (!drv) { - error_setg(errp, QERR_INVALID_BLOCK_FORMAT, format); - goto out; - } - } /* Early check to avoid creating target */ if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_BACKUP_SOURCE, errp)) { @@ -2540,7 +2524,7 @@ void qmp_drive_backup(const char *device, const char *target, } if (mode != NEW_IMAGE_MODE_EXISTING) { - assert(format && drv); + assert(format); if (source) { bdrv_img_create(target, format, source->filename, source->drv->format_name, NULL, @@ -2556,8 +2540,13 @@ void qmp_drive_backup(const char *device, const char *target, goto out; } + if (format) { + options = qdict_new(); + qdict_put(options, "driver", qstring_from_str(format)); + } + target_bs = NULL; - ret = bdrv_open(&target_bs, target, NULL, NULL, flags, drv, &local_err); + ret = bdrv_open(&target_bs, target, NULL, options, flags, &local_err); if (ret < 0) { error_propagate(errp, local_err); goto out; @@ -2663,9 +2652,8 @@ void qmp_drive_mirror(const char *device, const char *target, BlockDriverState *bs; BlockDriverState *source, *target_bs; AioContext *aio_context; - BlockDriver *drv = NULL; Error *local_err = NULL; - QDict *options = NULL; + QDict *options; int flags; int64_t size; int ret; @@ -2722,13 +2710,6 @@ void qmp_drive_mirror(const char *device, const char *target, if (!has_format) { format = mode == NEW_IMAGE_MODE_EXISTING ? NULL : bs->drv->format_name; } - if (format) { - drv = bdrv_find_format(format); - if (!drv) { - error_setg(errp, QERR_INVALID_BLOCK_FORMAT, format); - goto out; - } - } if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_MIRROR, errp)) { goto out; @@ -2783,7 +2764,7 @@ void qmp_drive_mirror(const char *device, const char *target, && mode != NEW_IMAGE_MODE_EXISTING) { /* create new image w/o backing file */ - assert(format && drv); + assert(format); bdrv_img_create(target, format, NULL, NULL, NULL, size, flags, &local_err, false); } else { @@ -2807,17 +2788,20 @@ void qmp_drive_mirror(const char *device, const char *target, goto out; } + options = qdict_new(); if (has_node_name) { - options = qdict_new(); qdict_put(options, "node-name", qstring_from_str(node_name)); } + if (format) { + qdict_put(options, "driver", qstring_from_str(format)); + } /* Mirroring takes care of copy-on-write using the source's backing * file. */ target_bs = NULL; ret = bdrv_open(&target_bs, target, NULL, options, - flags | BDRV_O_NO_BACKING, drv, &local_err); + flags | BDRV_O_NO_BACKING, &local_err); if (ret < 0) { error_propagate(errp, local_err); goto out; diff --git a/include/block/block.h b/include/block/block.h index 0acd104051..ef67353108 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -147,6 +147,7 @@ typedef QSIMPLEQ_HEAD(BlockReopenQueue, BlockReopenQueueEntry) BlockReopenQueue; typedef struct BDRVReopenState { BlockDriverState *bs; int flags; + QDict *options; void *opaque; } BDRVReopenState; @@ -193,8 +194,6 @@ BlockDriver *bdrv_find_protocol(const char *filename, bool allow_protocol_prefix, Error **errp); BlockDriver *bdrv_find_format(const char *format_name); -BlockDriver *bdrv_find_whitelisted_format(const char *format_name, - bool readonly); int bdrv_create(BlockDriver *drv, const char* filename, QemuOpts *opts, Error **errp); int bdrv_create_file(const char *filename, QemuOpts *opts, Error **errp); @@ -218,10 +217,10 @@ void bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd); int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp); int bdrv_append_temp_snapshot(BlockDriverState *bs, int flags, Error **errp); int bdrv_open(BlockDriverState **pbs, const char *filename, - const char *reference, QDict *options, int flags, - BlockDriver *drv, Error **errp); + const char *reference, QDict *options, int flags, Error **errp); BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue, - BlockDriverState *bs, int flags); + BlockDriverState *bs, + QDict *options, int flags); int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp); int bdrv_reopen(BlockDriverState *bs, int bdrv_flags, Error **errp); int bdrv_reopen_prepare(BDRVReopenState *reopen_state, diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c index 53477e1e17..d6572a8585 100644 --- a/qemu-io-cmds.c +++ b/qemu-io-cmds.c @@ -1979,6 +1979,95 @@ static const cmdinfo_t map_cmd = { .oneline = "prints the allocated areas of a file", }; +static void reopen_help(void) +{ + printf( +"\n" +" Changes the open options of an already opened image\n" +"\n" +" Example:\n" +" 'reopen -o lazy-refcounts=on' - activates lazy refcount writeback on a qcow2 image\n" +"\n" +" -r, -- Reopen the image read-only\n" +" -c, -- Change the cache mode to the given value\n" +" -o, -- Changes block driver options (cf. 'open' command)\n" +"\n"); +} + +static int reopen_f(BlockBackend *blk, int argc, char **argv); + +static QemuOptsList reopen_opts = { + .name = "reopen", + .merge_lists = true, + .head = QTAILQ_HEAD_INITIALIZER(reopen_opts.head), + .desc = { + /* no elements => accept any params */ + { /* end of list */ } + }, +}; + +static const cmdinfo_t reopen_cmd = { + .name = "reopen", + .argmin = 0, + .argmax = -1, + .cfunc = reopen_f, + .args = "[-r] [-c cache] [-o options]", + .oneline = "reopens an image with new options", + .help = reopen_help, +}; + +static int reopen_f(BlockBackend *blk, int argc, char **argv) +{ + BlockDriverState *bs = blk_bs(blk); + QemuOpts *qopts; + QDict *opts; + int c; + int flags = bs->open_flags; + + BlockReopenQueue *brq; + Error *local_err = NULL; + + while ((c = getopt(argc, argv, "c:o:r")) != -1) { + switch (c) { + case 'c': + if (bdrv_parse_cache_flags(optarg, &flags) < 0) { + error_report("Invalid cache option: %s", optarg); + return 0; + } + break; + case 'o': + if (!qemu_opts_parse_noisily(&reopen_opts, optarg, 0)) { + qemu_opts_reset(&reopen_opts); + return 0; + } + break; + case 'r': + flags &= ~BDRV_O_RDWR; + break; + default: + qemu_opts_reset(&reopen_opts); + return qemuio_command_usage(&reopen_cmd); + } + } + + if (optind != argc) { + qemu_opts_reset(&reopen_opts); + return qemuio_command_usage(&reopen_cmd); + } + + qopts = qemu_opts_find(&reopen_opts, NULL); + opts = qopts ? qemu_opts_to_qdict(qopts, NULL) : NULL; + qemu_opts_reset(&reopen_opts); + + brq = bdrv_reopen_queue(NULL, bs, opts, flags); + bdrv_reopen_multiple(brq, &local_err); + if (local_err) { + error_report_err(local_err); + } + + return 0; +} + static int break_f(BlockBackend *blk, int argc, char **argv) { int ret; @@ -2266,6 +2355,7 @@ static void __attribute((constructor)) init_qemuio_commands(void) qemuio_add_command(&discard_cmd); qemuio_add_command(&alloc_cmd); qemuio_add_command(&map_cmd); + qemuio_add_command(&reopen_cmd); qemuio_add_command(&break_cmd); qemuio_add_command(&remove_break_cmd); qemuio_add_command(&resume_cmd); @@ -156,7 +156,6 @@ static int open_f(BlockBackend *blk, int argc, char **argv) break; case 'o': if (!qemu_opts_parse_noisily(&empty_opts, optarg, false)) { - printf("could not parse option list -- %s\n", optarg); qemu_opts_reset(&empty_opts); return 0; } diff --git a/tests/qemu-iotests/039 b/tests/qemu-iotests/039 index 617f3977cc..9e9b379baa 100755 --- a/tests/qemu-iotests/039 +++ b/tests/qemu-iotests/039 @@ -147,6 +147,33 @@ $PYTHON qcow2.py "$TEST_IMG".base dump-header | grep incompatible_features _check_test_img TEST_IMG="$TEST_IMG".base _check_test_img +echo +echo "== Changing lazy_refcounts setting at runtime ==" + +IMGOPTS="compat=1.1,lazy_refcounts=off" +_make_test_img $size + +$QEMU_IO -c "reopen -o lazy-refcounts=on" \ + -c "write -P 0x5a 0 512" \ + -c "sigraise $(kill -l KILL)" "$TEST_IMG" 2>&1 \ + | _filter_qemu_io + +# The dirty bit must be set +$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features +_check_test_img + +IMGOPTS="compat=1.1,lazy_refcounts=on" +_make_test_img $size + +$QEMU_IO -c "reopen -o lazy-refcounts=off" \ + -c "write -P 0x5a 0 512" \ + -c "sigraise $(kill -l KILL)" "$TEST_IMG" 2>&1 \ + | _filter_qemu_io + +# The dirty bit must not be set +$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features +_check_test_img + # success, all done echo "*** done" diff --git a/tests/qemu-iotests/039.out b/tests/qemu-iotests/039.out index d8c5a44519..03a31c5943 100644 --- a/tests/qemu-iotests/039.out +++ b/tests/qemu-iotests/039.out @@ -74,4 +74,22 @@ incompatible_features 0x0 incompatible_features 0x0 No errors were found on the image. No errors were found on the image. + +== Changing lazy_refcounts setting at runtime == +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 +wrote 512/512 bytes at offset 0 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +./common.config: Killed ( exec "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@" ) +incompatible_features 0x1 +ERROR cluster 5 refcount=0 reference=1 +ERROR OFLAG_COPIED data cluster: l2_entry=8000000000050000 refcount=0 + +2 errors were found on the image. +Data may be corrupted, or further writes to the image may corrupt it. +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 +wrote 512/512 bytes at offset 0 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +./common.config: Killed ( exec "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@" ) +incompatible_features 0x0 +No errors were found on the image. *** done diff --git a/tests/qemu-iotests/137 b/tests/qemu-iotests/137 new file mode 100755 index 0000000000..9a6597cf9d --- /dev/null +++ b/tests/qemu-iotests/137 @@ -0,0 +1,145 @@ +#!/bin/bash +# +# Test qcow2 reopen +# +# Copyright (C) 2015 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +# creator +owner=kwolf@redhat.com + +seq="$(basename $0)" +echo "QA output created by $seq" + +here="$PWD" +tmp=/tmp/$$ +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter +. ./common.qemu + +_supported_fmt qcow2 +_supported_proto generic +_supported_os Linux + + +_make_test_img 64M + +echo === Try setting valid values for all options === +echo + +# Try all options and then check that all of the basic I/O operations still +# work on this image. +$QEMU_IO \ + -c "reopen -o lazy-refcounts=on,pass-discard-request=on" \ + -c "reopen -o lazy-refcounts=off,pass-discard-request=off" \ + -c "reopen -o pass-discard-snapshot=on,pass-discard-other=on" \ + -c "reopen -o pass-discard-snapshot=off,pass-discard-other=off" \ + -c "reopen -o overlap-check=all" \ + -c "reopen -o overlap-check=none" \ + -c "reopen -o overlap-check=cached" \ + -c "reopen -o overlap-check=constant" \ + -c "reopen -o overlap-check.template=all" \ + -c "reopen -o overlap-check.template=none" \ + -c "reopen -o overlap-check.template=cached" \ + -c "reopen -o overlap-check.template=constant" \ + -c "reopen -o overlap-check.main-header=on" \ + -c "reopen -o overlap-check.main-header=off" \ + -c "reopen -o overlap-check.active-l1=on" \ + -c "reopen -o overlap-check.active-l1=off" \ + -c "reopen -o overlap-check.active-l2=on" \ + -c "reopen -o overlap-check.active-l2=off" \ + -c "reopen -o overlap-check.refcount-table=on" \ + -c "reopen -o overlap-check.refcount-table=off" \ + -c "reopen -o overlap-check.refcount-block=on" \ + -c "reopen -o overlap-check.refcount-block=off" \ + -c "reopen -o overlap-check.snapshot-table=on" \ + -c "reopen -o overlap-check.snapshot-table=off" \ + -c "reopen -o overlap-check.inactive-l1=on" \ + -c "reopen -o overlap-check.inactive-l1=off" \ + -c "reopen -o overlap-check.inactive-l2=on" \ + -c "reopen -o overlap-check.inactive-l2=off" \ + -c "reopen -o cache-size=1M" \ + -c "reopen -o l2-cache-size=512k" \ + -c "reopen -o refcount-cache-size=128k" \ + -c "reopen -o cache-clean-interval=5" \ + -c "reopen -o cache-clean-interval=0" \ + -c "reopen -o cache-clean-interval=10" \ + \ + -c "write -P 55 0 32M" \ + -c "read -P 55 0 32M" \ + -c "discard 0 32M" \ + -c "write -z 0 32M" \ + -c "read -P 0 0 32M" \ + \ + "$TEST_IMG" | _filter_qemu_io + + +echo +echo === Try setting some invalid values === +echo + +$QEMU_IO \ + -c "reopen -o lazy-refcounts=42" \ + -c "reopen -o cache-size=1M,l2-cache-size=64k,refcount-cache-size=64k" \ + -c "reopen -o cache-size=1M,l2-cache-size=2M" \ + -c "reopen -o cache-size=1M,refcount-cache-size=2M" \ + -c "reopen -o l2-cache-size=256T" \ + -c "reopen -o refcount-cache-size=256T" \ + -c "reopen -o overlap-check=constant,overlap-check.template=all" \ + -c "reopen -o overlap-check=blubb" \ + -c "reopen -o overlap-check.template=blubb" \ + -c "reopen -o cache-clean-interval=-1" \ + "$TEST_IMG" | _filter_qemu_io + +echo +echo === Test transaction semantics === +echo + +# Whether lazy-refcounts was actually enabled can easily be tested: Check if +# the dirty bit is set after a crash +$QEMU_IO \ + -c "reopen -o lazy-refcounts=on,overlap-check=blubb" \ + -c "write -P 0x5a 0 512" \ + -c "sigraise $(kill -l KILL)" \ + "$TEST_IMG" 2>&1 | _filter_qemu_io + +# The dirty bit must not be set +$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features + +# Similarly we can test whether corruption detection has been enabled: +# Create L1/L2, overwrite first entry in refcount block, allocate something. +# Disabling the checks should fail, so the corruption must be detected. +_make_test_img 64M +$QEMU_IO -c "write 0 64k" "$TEST_IMG" | _filter_qemu_io +poke_file "$TEST_IMG" "$((0x20000))" "\x00\x00" +$QEMU_IO \ + -c "reopen -o overlap-check=none,lazy-refcounts=42" \ + -c "write 64k 64k" \ + "$TEST_IMG" 2>&1 | _filter_qemu_io + +# success, all done +echo '*** done' +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/137.out b/tests/qemu-iotests/137.out new file mode 100644 index 0000000000..cf55a41d8a --- /dev/null +++ b/tests/qemu-iotests/137.out @@ -0,0 +1,42 @@ +QA output created by 137 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +=== Try setting valid values for all options === + +wrote 33554432/33554432 bytes at offset 0 +32 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 33554432/33554432 bytes at offset 0 +32 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +discard 33554432/33554432 bytes at offset 0 +32 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 33554432/33554432 bytes at offset 0 +32 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 33554432/33554432 bytes at offset 0 +32 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +=== Try setting some invalid values === + +Parameter 'lazy-refcounts' expects 'on' or 'off' +cache-size, l2-cache-size and refcount-cache-size may not be set the same time +l2-cache-size may not exceed cache-size +refcount-cache-size may not exceed cache-size +L2 cache size too big +L2 cache size too big +Conflicting values for qcow2 options 'overlap-check' ('constant') and 'overlap-check.template' ('all') +Unsupported value 'blubb' for qcow2 option 'overlap-check'. Allowed are any of the following: none, constant, cached, all +Unsupported value 'blubb' for qcow2 option 'overlap-check'. Allowed are any of the following: none, constant, cached, all +Cache clean interval too big + +=== Test transaction semantics === + +Unsupported value 'blubb' for qcow2 option 'overlap-check'. Allowed are any of the following: none, constant, cached, all +wrote 512/512 bytes at offset 0 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +./common.config: Killed ( exec "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@" ) +incompatible_features 0x0 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +wrote 65536/65536 bytes at offset 0 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Parameter 'lazy-refcounts' expects 'on' or 'off' +qcow2: Marking image as corrupt: Preventing invalid write on metadata (overlaps with qcow2_header); further corruption events will be suppressed +write failed: Input/output error +*** done diff --git a/tests/qemu-iotests/138 b/tests/qemu-iotests/138 new file mode 100755 index 0000000000..a5c3464d58 --- /dev/null +++ b/tests/qemu-iotests/138 @@ -0,0 +1,73 @@ +#!/bin/bash +# +# General test case for qcow2's image check +# +# Copyright (C) 2015 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +# creator +owner=mreitz@redhat.com + +seq="$(basename $0)" +echo "QA output created by $seq" + +here="$PWD" +tmp=/tmp/$$ +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +# This tests qocw2-specific low-level functionality +_supported_fmt qcow2 +_supported_proto file +_supported_os Linux + +echo +echo '=== Check on an image with a multiple of 2^32 clusters ===' +echo + +IMGOPTS=$(_optstr_add "$IMGOPTS" "cluster_size=512") \ + _make_test_img 512 + +# Allocate L2 table +$QEMU_IO -c 'write 0 512' "$TEST_IMG" | _filter_qemu_io + +# Put the data cluster at a multiple of 2 TB, resulting in the image apparently +# having a multiple of 2^32 clusters +# (To be more specific: It is at 32 PB) +poke_file "$TEST_IMG" 2048 "\x80\x80\x00\x00\x00\x00\x00\x00" + +# An offset of 32 PB results in qemu-img check having to allocate an in-memory +# refcount table of 128 TB (16 bit refcounts, 512 byte clusters). +# This should be generally too much for any system and thus fail. +# What this test is checking is that the qcow2 driver actually tries to allocate +# such a large amount of memory (and is consequently aborting) instead of having +# truncated the cluster count somewhere (which would result in much less memory +# being allocated and then a segfault occurring). +_check_test_img + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/138.out b/tests/qemu-iotests/138.out new file mode 100644 index 0000000000..3fe911f85a --- /dev/null +++ b/tests/qemu-iotests/138.out @@ -0,0 +1,9 @@ +QA output created by 138 + +=== Check on an image with a multiple of 2^32 clusters === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=512 +wrote 512/512 bytes at offset 0 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-img: Check failed: Cannot allocate memory +*** done diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group index c430b6c234..439b1d237d 100644 --- a/tests/qemu-iotests/group +++ b/tests/qemu-iotests/group @@ -134,3 +134,5 @@ 132 rw auto quick 134 rw auto quick 135 rw auto +137 rw auto +138 rw auto quick |