diff --git a/block.c b/block.c index 3c36af5e76df..b663204f3f38 100644 --- a/block.c +++ b/block.c @@ -588,21 +588,20 @@ BlockDriver *bdrv_probe_all(const uint8_t *buf, int buf_size, return drv; } -static int find_image_format(BdrvChild *file, const char *filename, +static int find_image_format(BlockBackend *file, const char *filename, BlockDriver **pdrv, Error **errp) { - BlockDriverState *bs = file->bs; BlockDriver *drv; uint8_t buf[BLOCK_PROBE_BUF_SIZE]; int ret = 0; /* Return the raw BlockDriver * to scsi-generic devices or empty drives */ - if (bdrv_is_sg(bs) || !bdrv_is_inserted(bs) || bdrv_getlength(bs) == 0) { + if (blk_is_sg(file) || !blk_is_inserted(file) || blk_getlength(file) == 0) { *pdrv = &bdrv_raw; return ret; } - ret = bdrv_pread(file, 0, buf, sizeof(buf)); + ret = blk_pread(file, 0, buf, sizeof(buf)); if (ret < 0) { error_setg_errno(errp, -ret, "Could not read image for determining its " "format"); @@ -926,6 +925,95 @@ static void bdrv_assign_node_name(BlockDriverState *bs, g_free(gen_node_name); } +static int bdrv_open_driver(BlockDriverState *bs, BlockDriver *drv, + const char *node_name, QDict *options, + int open_flags, Error **errp) +{ + Error *local_err = NULL; + int ret; + + bdrv_assign_node_name(bs, node_name, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return -EINVAL; + } + + bs->drv = drv; + bs->read_only = !(bs->open_flags & BDRV_O_RDWR); + bs->opaque = g_malloc0(drv->instance_size); + + if (drv->bdrv_file_open) { + assert(!drv->bdrv_needs_filename || bs->filename[0]); + ret = drv->bdrv_file_open(bs, options, open_flags, &local_err); + } else if (drv->bdrv_open) { + ret = drv->bdrv_open(bs, options, open_flags, &local_err); + } else { + ret = 0; + } + + if (ret < 0) { + if (local_err) { + error_propagate(errp, local_err); + } else if (bs->filename[0]) { + error_setg_errno(errp, -ret, "Could not open '%s'", bs->filename); + } else { + error_setg_errno(errp, -ret, "Could not open image"); + } + goto free_and_fail; + } + + ret = refresh_total_sectors(bs, bs->total_sectors); + if (ret < 0) { + error_setg_errno(errp, -ret, "Could not refresh total sector count"); + goto free_and_fail; + } + + bdrv_refresh_limits(bs, &local_err); + if (local_err) { + error_propagate(errp, local_err); + ret = -EINVAL; + goto free_and_fail; + } + + assert(bdrv_opt_mem_align(bs) != 0); + assert(bdrv_min_mem_align(bs) != 0); + assert(is_power_of_2(bs->bl.request_alignment)); + + return 0; + +free_and_fail: + /* FIXME Close bs first if already opened*/ + g_free(bs->opaque); + bs->opaque = NULL; + bs->drv = NULL; + return ret; +} + +BlockDriverState *bdrv_new_open_driver(BlockDriver *drv, const char *node_name, + int flags, Error **errp) +{ + BlockDriverState *bs; + int ret; + + bs = bdrv_new(); + bs->open_flags = flags; + bs->explicit_options = qdict_new(); + bs->options = qdict_new(); + bs->opaque = NULL; + + update_options_from_flags(bs->options, flags); + + ret = bdrv_open_driver(bs, drv, node_name, bs->options, flags, errp); + if (ret < 0) { + QDECREF(bs->explicit_options); + QDECREF(bs->options); + bdrv_unref(bs); + return NULL; + } + + return bs; +} + QemuOptsList bdrv_runtime_opts = { .name = "bdrv_common", .head = QTAILQ_HEAD_INITIALIZER(bdrv_runtime_opts.head), @@ -974,7 +1062,7 @@ QemuOptsList bdrv_runtime_opts = { * * Removes all processed options from *options. */ -static int bdrv_open_common(BlockDriverState *bs, BdrvChild *file, +static int bdrv_open_common(BlockDriverState *bs, BlockBackend *file, QDict *options, Error **errp) { int ret, open_flags; @@ -1005,7 +1093,7 @@ static int bdrv_open_common(BlockDriverState *bs, BdrvChild *file, assert(drv != NULL); if (file != NULL) { - filename = file->bs->filename; + filename = blk_bs(file)->filename; } else { filename = qdict_get_try_str(options, "filename"); } @@ -1020,14 +1108,6 @@ static int bdrv_open_common(BlockDriverState *bs, BdrvChild *file, trace_bdrv_open_common(bs, filename ?: "", bs->open_flags, drv->format_name); - node_name = qemu_opt_get(opts, "node-name"); - bdrv_assign_node_name(bs, node_name, &local_err); - if (local_err) { - error_propagate(errp, local_err); - ret = -EINVAL; - goto fail_opts; - } - bs->read_only = !(bs->open_flags & BDRV_O_RDWR); if (use_bdrv_whitelist && !bdrv_is_whitelisted(drv, bs->read_only)) { @@ -1093,62 +1173,19 @@ static int bdrv_open_common(BlockDriverState *bs, BdrvChild *file, } pstrcpy(bs->exact_filename, sizeof(bs->exact_filename), bs->filename); - bs->drv = drv; - bs->opaque = g_malloc0(drv->instance_size); - /* Open the image, either directly or using a protocol */ open_flags = bdrv_open_flags(bs, bs->open_flags); - if (drv->bdrv_file_open) { - assert(file == NULL); - assert(!drv->bdrv_needs_filename || filename != NULL); - ret = drv->bdrv_file_open(bs, options, open_flags, &local_err); - } else { - if (file == NULL) { - error_setg(errp, "Can't use '%s' as a block driver for the " - "protocol level", drv->format_name); - ret = -EINVAL; - goto free_and_fail; - } - bs->file = file; - ret = drv->bdrv_open(bs, options, open_flags, &local_err); - } - - if (ret < 0) { - if (local_err) { - error_propagate(errp, local_err); - } else if (bs->filename[0]) { - error_setg_errno(errp, -ret, "Could not open '%s'", bs->filename); - } else { - error_setg_errno(errp, -ret, "Could not open image"); - } - goto free_and_fail; - } + node_name = qemu_opt_get(opts, "node-name"); - ret = refresh_total_sectors(bs, bs->total_sectors); + assert(!drv->bdrv_file_open || file == NULL); + ret = bdrv_open_driver(bs, drv, node_name, options, open_flags, errp); if (ret < 0) { - error_setg_errno(errp, -ret, "Could not refresh total sector count"); - goto free_and_fail; - } - - bdrv_refresh_limits(bs, &local_err); - if (local_err) { - error_propagate(errp, local_err); - ret = -EINVAL; - goto free_and_fail; + goto fail_opts; } - assert(bdrv_opt_mem_align(bs) != 0); - assert(bdrv_min_mem_align(bs) != 0); - assert(is_power_of_2(bs->bl.request_alignment)); - qemu_opts_del(opts); return 0; -free_and_fail: - bs->file = NULL; - g_free(bs->opaque); - bs->opaque = NULL; - bs->drv = NULL; fail_opts: qemu_opts_del(opts); return ret; @@ -1368,7 +1405,18 @@ void bdrv_unref_child(BlockDriverState *parent, BdrvChild *child) } if (child->bs->inherits_from == parent) { - child->bs->inherits_from = NULL; + BdrvChild *c; + + /* Remove inherits_from only when the last reference between parent and + * child->bs goes away. */ + QLIST_FOREACH(c, &parent->children, next) { + if (c != child && c->bs == child->bs) { + break; + } + } + if (c == NULL) { + child->bs->inherits_from = NULL; + } } bdrv_root_unref_child(child); @@ -1543,28 +1591,12 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options, return ret; } -/* - * Opens a disk image whose options are given as BlockdevRef in another block - * device's options. - * - * If allow_none is true, no image will be opened if filename is false and no - * BlockdevRef is given. NULL will be returned, but errp remains unset. - * - * bdrev_key specifies the key for the image's BlockdevRef in the options QDict. - * That QDict has to be flattened; therefore, if the BlockdevRef is a QDict - * itself, all options starting with "${bdref_key}." are considered part of the - * BlockdevRef. - * - * The BlockdevRef will be removed from the options QDict. - */ -BdrvChild *bdrv_open_child(const char *filename, - QDict *options, const char *bdref_key, - BlockDriverState* parent, - const BdrvChildRole *child_role, - bool allow_none, Error **errp) +static BlockDriverState * +bdrv_open_child_bs(const char *filename, QDict *options, const char *bdref_key, + BlockDriverState *parent, const BdrvChildRole *child_role, + bool allow_none, Error **errp) { - BdrvChild *c = NULL; - BlockDriverState *bs; + BlockDriverState *bs = NULL; QDict *image_options; char *bdref_key_dot; const char *reference; @@ -1591,11 +1623,40 @@ BdrvChild *bdrv_open_child(const char *filename, goto done; } - c = bdrv_attach_child(parent, bs, bdref_key, child_role); - done: qdict_del(options, bdref_key); - return c; + return bs; +} + +/* + * Opens a disk image whose options are given as BlockdevRef in another block + * device's options. + * + * If allow_none is true, no image will be opened if filename is false and no + * BlockdevRef is given. NULL will be returned, but errp remains unset. + * + * bdrev_key specifies the key for the image's BlockdevRef in the options QDict. + * That QDict has to be flattened; therefore, if the BlockdevRef is a QDict + * itself, all options starting with "${bdref_key}." are considered part of the + * BlockdevRef. + * + * The BlockdevRef will be removed from the options QDict. + */ +BdrvChild *bdrv_open_child(const char *filename, + QDict *options, const char *bdref_key, + BlockDriverState *parent, + const BdrvChildRole *child_role, + bool allow_none, Error **errp) +{ + BlockDriverState *bs; + + bs = bdrv_open_child_bs(filename, options, bdref_key, parent, child_role, + allow_none, errp); + if (bs == NULL) { + return NULL; + } + + return bdrv_attach_child(parent, bs, bdref_key, child_role); } static BlockDriverState *bdrv_append_temp_snapshot(BlockDriverState *bs, @@ -1691,7 +1752,7 @@ static BlockDriverState *bdrv_open_inherit(const char *filename, Error **errp) { int ret; - BdrvChild *file = NULL; + BlockBackend *file = NULL; BlockDriverState *bs; BlockDriver *drv = NULL; const char *drvname; @@ -1789,13 +1850,25 @@ static BlockDriverState *bdrv_open_inherit(const char *filename, qdict_del(options, "backing"); } - /* Open image file without format layer */ + /* Open image file without format layer. This BlockBackend is only used for + * probing, the block drivers will do their own bdrv_open_child() for the + * same BDS, which is why we put the node name back into options. */ if ((flags & BDRV_O_PROTOCOL) == 0) { - file = bdrv_open_child(filename, options, "file", bs, - &child_file, true, &local_err); + BlockDriverState *file_bs; + + file_bs = bdrv_open_child_bs(filename, options, "file", bs, + &child_file, true, &local_err); if (local_err) { goto fail; } + if (file_bs != NULL) { + file = blk_new(); + blk_insert_bs(file, file_bs); + bdrv_unref(file_bs); + + qdict_put(options, "file", + qstring_from_str(bdrv_get_node_name(file_bs))); + } } /* Image format probing */ @@ -1835,8 +1908,8 @@ static BlockDriverState *bdrv_open_inherit(const char *filename, goto fail; } - if (file && (bs->file != file)) { - bdrv_unref_child(bs, file); + if (file) { + blk_unref(file); file = NULL; } @@ -1898,8 +1971,9 @@ static BlockDriverState *bdrv_open_inherit(const char *filename, return bs; fail: - if (file != NULL) { - bdrv_unref_child(bs, file); + blk_unref(file); + if (bs->file != NULL) { + bdrv_unref_child(bs, bs->file); } QDECREF(snapshot_options); QDECREF(bs->explicit_options); @@ -2626,8 +2700,9 @@ int bdrv_drop_intermediate(BlockDriverState *active, BlockDriverState *top, /** * Truncate file to 'offset' bytes (needed only for file protocols) */ -int bdrv_truncate(BlockDriverState *bs, int64_t offset) +int bdrv_truncate(BdrvChild *child, int64_t offset) { + BlockDriverState *bs = child->bs; BlockDriver *drv = bs->drv; int ret; if (!drv) diff --git a/block/blkdebug.c b/block/blkdebug.c index d8eee1b9b4f5..6117ce5fca86 100644 --- a/block/blkdebug.c +++ b/block/blkdebug.c @@ -663,7 +663,7 @@ static int64_t blkdebug_getlength(BlockDriverState *bs) static int blkdebug_truncate(BlockDriverState *bs, int64_t offset) { - return bdrv_truncate(bs->file->bs, offset); + return bdrv_truncate(bs->file, offset); } static void blkdebug_refresh_filename(BlockDriverState *bs, QDict *options) diff --git a/block/block-backend.c b/block/block-backend.c index 819f27213a8f..492e71e41f0e 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -1605,7 +1605,7 @@ int blk_truncate(BlockBackend *blk, int64_t offset) return -ENOMEDIUM; } - return bdrv_truncate(blk_bs(blk), offset); + return bdrv_truncate(blk->root, offset); } static void blk_pdiscard_entry(void *opaque) diff --git a/block/bochs.c b/block/bochs.c index 8c9652ebeb55..7dd2ac4f515f 100644 --- a/block/bochs.c +++ b/block/bochs.c @@ -104,6 +104,12 @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags, struct bochs_header bochs; int ret; + bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, + false, errp); + if (!bs->file) { + return -EINVAL; + } + bs->read_only = true; /* no write support yet */ ret = bdrv_pread(bs->file, 0, &bochs, sizeof(bochs)); diff --git a/block/cloop.c b/block/cloop.c index 7b75f7ef7b63..877c9b0d1b2b 100644 --- a/block/cloop.c +++ b/block/cloop.c @@ -66,6 +66,12 @@ static int cloop_open(BlockDriverState *bs, QDict *options, int flags, uint32_t offsets_size, max_compressed_block_size = 1, i; int ret; + bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, + false, errp); + if (!bs->file) { + return -EINVAL; + } + bs->read_only = true; /* read header */ diff --git a/block/crypto.c b/block/crypto.c index 7aa7eb553e07..7cb2ff2946e4 100644 --- a/block/crypto.c +++ b/block/crypto.c @@ -300,6 +300,12 @@ static int block_crypto_open_generic(QCryptoBlockFormat format, QCryptoBlockOpenOptions *open_opts = NULL; unsigned int cflags = 0; + bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, + false, errp); + if (!bs->file) { + return -EINVAL; + } + opts = qemu_opts_create(opts_spec, NULL, 0, &error_abort); qemu_opts_absorb_qdict(opts, options, &local_err); if (local_err) { @@ -383,7 +389,7 @@ static int block_crypto_truncate(BlockDriverState *bs, int64_t offset) offset += payload_offset; - return bdrv_truncate(bs->file->bs, offset); + return bdrv_truncate(bs->file, offset); } static void block_crypto_close(BlockDriverState *bs) diff --git a/block/dmg.c b/block/dmg.c index 58a3ae86c1da..8e387cdfe5d0 100644 --- a/block/dmg.c +++ b/block/dmg.c @@ -413,6 +413,12 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags, int64_t offset; int ret; + bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, + false, errp); + if (!bs->file) { + return -EINVAL; + } + block_module_load_one("dmg-bz2"); bs->read_only = true; diff --git a/block/file-posix.c b/block/file-posix.c index 2134e0ef96ee..4de1abd0235a 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -1591,18 +1591,17 @@ static int raw_create(const char *filename, QemuOpts *opts, Error **errp) #endif } - if (ftruncate(fd, total_size) != 0) { - result = -errno; - error_setg_errno(errp, -result, "Could not resize file"); - goto out_close; - } - switch (prealloc) { #ifdef CONFIG_POSIX_FALLOCATE case PREALLOC_MODE_FALLOC: - /* posix_fallocate() doesn't set errno. */ + /* + * Truncating before posix_fallocate() makes it about twice slower on + * file systems that do not support fallocate(), trying to check if a + * block is allocated before allocating it, so don't do that here. + */ result = -posix_fallocate(fd, 0, total_size); if (result != 0) { + /* posix_fallocate() doesn't set errno. */ error_setg_errno(errp, -result, "Could not preallocate data for the new file"); } @@ -1610,6 +1609,17 @@ static int raw_create(const char *filename, QemuOpts *opts, Error **errp) #endif case PREALLOC_MODE_FULL: { + /* + * Knowing the final size from the beginning could allow the file + * system driver to do less allocations and possibly avoid + * fragmentation of the file. + */ + if (ftruncate(fd, total_size) != 0) { + result = -errno; + error_setg_errno(errp, -result, "Could not resize file"); + goto out_close; + } + int64_t num = 0, left = total_size; buf = g_malloc0(65536); @@ -1636,6 +1646,10 @@ static int raw_create(const char *filename, QemuOpts *opts, Error **errp) break; } case PREALLOC_MODE_OFF: + if (ftruncate(fd, total_size) != 0) { + result = -errno; + error_setg_errno(errp, -result, "Could not resize file"); + } break; default: result = -EINVAL; diff --git a/block/mirror.c b/block/mirror.c index ca8547b1d3a7..3d50857300fd 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -662,7 +662,28 @@ static void coroutine_fn mirror_run(void *opaque) if (s->bdev_length < 0) { ret = s->bdev_length; goto immediate_exit; - } else if (s->bdev_length == 0) { + } + + /* Active commit must resize the base image if its size differs from the + * active layer. */ + if (s->base == blk_bs(s->target)) { + int64_t base_length; + + base_length = blk_getlength(s->target); + if (base_length < 0) { + ret = base_length; + goto immediate_exit; + } + + if (s->bdev_length > base_length) { + ret = blk_truncate(s->target, s->bdev_length); + if (ret < 0) { + goto immediate_exit; + } + } + } + + if (s->bdev_length == 0) { /* Report BLOCK_JOB_READY and wait for complete. */ block_job_event_ready(&s->common); s->synced = true; @@ -1063,9 +1084,7 @@ void commit_active_start(const char *job_id, BlockDriverState *bs, BlockCompletionFunc *cb, void *opaque, Error **errp, bool auto_complete) { - int64_t length, base_length; int orig_base_flags; - int ret; Error *local_err = NULL; orig_base_flags = bdrv_get_flags(base); @@ -1074,31 +1093,6 @@ void commit_active_start(const char *job_id, BlockDriverState *bs, return; } - length = bdrv_getlength(bs); - if (length < 0) { - error_setg_errno(errp, -length, - "Unable to determine length of %s", bs->filename); - goto error_restore_flags; - } - - base_length = bdrv_getlength(base); - if (base_length < 0) { - error_setg_errno(errp, -base_length, - "Unable to determine length of %s", base->filename); - goto error_restore_flags; - } - - if (length > base_length) { - ret = bdrv_truncate(base, length); - if (ret < 0) { - error_setg_errno(errp, -ret, - "Top image %s is larger than base image %s, and " - "resize of base image failed", - bs->filename, base->filename); - goto error_restore_flags; - } - } - mirror_start_job(job_id, bs, creation_flags, base, NULL, speed, 0, 0, MIRROR_LEAVE_BACKING_CHAIN, on_error, on_error, true, cb, opaque, &local_err, diff --git a/block/parallels.c b/block/parallels.c index 2ccefa7d85c3..b2ec09f7e66e 100644 --- a/block/parallels.c +++ b/block/parallels.c @@ -215,7 +215,7 @@ static int64_t allocate_clusters(BlockDriverState *bs, int64_t sector_num, s->data_end << BDRV_SECTOR_BITS, space << BDRV_SECTOR_BITS, 0); } else { - ret = bdrv_truncate(bs->file->bs, + ret = bdrv_truncate(bs->file, (s->data_end + space) << BDRV_SECTOR_BITS); } if (ret < 0) { @@ -449,7 +449,7 @@ static int parallels_check(BlockDriverState *bs, BdrvCheckResult *res, size - res->image_end_offset); res->leaks += count; if (fix & BDRV_FIX_LEAKS) { - ret = bdrv_truncate(bs->file->bs, res->image_end_offset); + ret = bdrv_truncate(bs->file, res->image_end_offset); if (ret < 0) { res->check_errors++; return ret; @@ -581,6 +581,12 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags, Error *local_err = NULL; char *buf; + bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, + false, errp); + if (!bs->file) { + return -EINVAL; + } + ret = bdrv_pread(bs->file, 0, &ph, sizeof(ph)); if (ret < 0) { goto fail; @@ -681,7 +687,7 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags, goto fail_options; } if (!bdrv_has_zero_init(bs->file->bs) || - bdrv_truncate(bs->file->bs, bdrv_getlength(bs->file->bs)) != 0) { + bdrv_truncate(bs->file, bdrv_getlength(bs->file->bs)) != 0) { s->prealloc_mode = PRL_PREALLOC_MODE_FALLOCATE; } @@ -724,7 +730,7 @@ static void parallels_close(BlockDriverState *bs) } if (bs->open_flags & BDRV_O_RDWR) { - bdrv_truncate(bs->file->bs, s->data_end << BDRV_SECTOR_BITS); + bdrv_truncate(bs->file, s->data_end << BDRV_SECTOR_BITS); } g_free(s->bat_dirty_bmap); diff --git a/block/qcow.c b/block/qcow.c index fb738fc50769..038b05ab1bcc 100644 --- a/block/qcow.c +++ b/block/qcow.c @@ -106,6 +106,12 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, QCowHeader header; Error *local_err = NULL; + bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, + false, errp); + if (!bs->file) { + return -EINVAL; + } + ret = bdrv_pread(bs->file, 0, &header, sizeof(header)); if (ret < 0) { goto fail; @@ -467,7 +473,7 @@ static uint64_t get_cluster_offset(BlockDriverState *bs, /* round to cluster size */ cluster_offset = (cluster_offset + s->cluster_size - 1) & ~(s->cluster_size - 1); - bdrv_truncate(bs->file->bs, cluster_offset + s->cluster_size); + bdrv_truncate(bs->file, cluster_offset + s->cluster_size); /* if encrypted, we must initialize the cluster content which won't be written */ if (bs->encrypted && @@ -909,7 +915,7 @@ static int qcow_make_empty(BlockDriverState *bs) if (bdrv_pwrite_sync(bs->file, s->l1_table_offset, s->l1_table, l1_length) < 0) return -1; - ret = bdrv_truncate(bs->file->bs, s->l1_table_offset + l1_length); + ret = bdrv_truncate(bs->file, s->l1_table_offset + l1_length); if (ret < 0) return ret; diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c index 3dbde186120e..9e96f64c8b59 100644 --- a/block/qcow2-refcount.c +++ b/block/qcow2-refcount.c @@ -1734,7 +1734,7 @@ static int check_refblocks(BlockDriverState *bs, BdrvCheckResult *res, goto resize_fail; } - ret = bdrv_truncate(bs->file->bs, offset + s->cluster_size); + ret = bdrv_truncate(bs->file, offset + s->cluster_size); if (ret < 0) { goto resize_fail; } diff --git a/block/qcow2.c b/block/qcow2.c index 3e274bd1ba48..21e61427eb33 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -814,8 +814,8 @@ static int qcow2_update_options(BlockDriverState *bs, QDict *options, return ret; } -static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, - Error **errp) +static int qcow2_do_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) { BDRVQcow2State *s = bs->opaque; unsigned int len, i; @@ -1205,6 +1205,18 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, return ret; } +static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) +{ + bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, + false, errp); + if (!bs->file) { + return -EINVAL; + } + + return qcow2_do_open(bs, options, flags, errp); +} + static void qcow2_refresh_limits(BlockDriverState *bs, Error **errp) { BDRVQcow2State *s = bs->opaque; @@ -1785,7 +1797,7 @@ static void qcow2_invalidate_cache(BlockDriverState *bs, Error **errp) options = qdict_clone_shallow(bs->options); flags &= ~BDRV_O_INACTIVE; - ret = qcow2_open(bs, options, flags, &local_err); + ret = qcow2_do_open(bs, options, flags, &local_err); QDECREF(options); if (local_err) { error_propagate(errp, local_err); @@ -2570,7 +2582,7 @@ qcow2_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset, /* align end of file to a sector boundary to ease reading with sector based I/Os */ cluster_offset = bdrv_getlength(bs->file->bs); - return bdrv_truncate(bs->file->bs, cluster_offset); + return bdrv_truncate(bs->file, cluster_offset); } buf = qemu_blockalign(bs, s->cluster_size); @@ -2784,7 +2796,7 @@ static int make_completely_empty(BlockDriverState *bs) goto fail; } - ret = bdrv_truncate(bs->file->bs, (3 + l1_clusters) * s->cluster_size); + ret = bdrv_truncate(bs->file, (3 + l1_clusters) * s->cluster_size); if (ret < 0) { goto fail; } @@ -3250,7 +3262,11 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, } if (new_size) { - ret = bdrv_truncate(bs, new_size); + BlockBackend *blk = blk_new(); + blk_insert_bs(blk, bs); + ret = blk_truncate(blk, new_size); + blk_unref(blk); + if (ret < 0) { return ret; } diff --git a/block/qed.c b/block/qed.c index 0b62c7799e2a..62a0a09326f9 100644 --- a/block/qed.c +++ b/block/qed.c @@ -415,8 +415,8 @@ static void bdrv_qed_drain(BlockDriverState *bs) } } -static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags, - Error **errp) +static int bdrv_qed_do_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) { BDRVQEDState *s = bs->opaque; QEDHeader le_header; @@ -550,6 +550,18 @@ static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags, return ret; } +static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) +{ + bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, + false, errp); + if (!bs->file) { + return -EINVAL; + } + + return bdrv_qed_do_open(bs, options, flags, errp); +} + static void bdrv_qed_refresh_limits(BlockDriverState *bs, Error **errp) { BDRVQEDState *s = bs->opaque; @@ -1629,7 +1641,7 @@ static void bdrv_qed_invalidate_cache(BlockDriverState *bs, Error **errp) bdrv_qed_close(bs); memset(s, 0, sizeof(BDRVQEDState)); - ret = bdrv_qed_open(bs, NULL, bs->open_flags, &local_err); + ret = bdrv_qed_do_open(bs, NULL, bs->open_flags, &local_err); if (local_err) { error_propagate(errp, local_err); error_prepend(errp, "Could not reopen qed layer: "); diff --git a/block/raw-format.c b/block/raw-format.c index 8404a82e0c3a..ce34d1b1cdee 100644 --- a/block/raw-format.c +++ b/block/raw-format.c @@ -341,7 +341,7 @@ static int raw_truncate(BlockDriverState *bs, int64_t offset) s->size = offset; offset += s->offset; - return bdrv_truncate(bs->file->bs, offset); + return bdrv_truncate(bs->file, offset); } static int raw_media_changed(BlockDriverState *bs) @@ -384,6 +384,12 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags, BDRVRawState *s = bs->opaque; int ret; + bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, + false, errp); + if (!bs->file) { + return -EINVAL; + } + bs->sg = bs->file->bs->sg; bs->supported_write_flags = BDRV_REQ_FUA & bs->file->bs->supported_write_flags; diff --git a/block/replication.c b/block/replication.c index 729dd124999f..eff85c77baf6 100644 --- a/block/replication.c +++ b/block/replication.c @@ -86,6 +86,12 @@ static int replication_open(BlockDriverState *bs, QDict *options, const char *mode; const char *top_id; + bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, + false, errp); + if (!bs->file) { + return -EINVAL; + } + ret = -EINVAL; opts = qemu_opts_create(&replication_runtime_opts, NULL, 0, &error_abort); qemu_opts_absorb_qdict(opts, options, &local_err); diff --git a/block/vdi.c b/block/vdi.c index 0aeb940aa8e0..18b4773aaced 100644 --- a/block/vdi.c +++ b/block/vdi.c @@ -363,6 +363,12 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags, int ret; Error *local_err = NULL; + bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, + false, errp); + if (!bs->file) { + return -EINVAL; + } + logout("\n"); ret = bdrv_read(bs->file, 0, (uint8_t *)&header, 1); diff --git a/block/vhdx-log.c b/block/vhdx-log.c index 02eb1043107b..67a91c0de55e 100644 --- a/block/vhdx-log.c +++ b/block/vhdx-log.c @@ -548,7 +548,7 @@ static int vhdx_log_flush(BlockDriverState *bs, BDRVVHDXState *s, if (new_file_size % (1024*1024)) { /* round up to nearest 1MB boundary */ new_file_size = ((new_file_size >> 20) + 1) << 20; - bdrv_truncate(bs->file->bs, new_file_size); + bdrv_truncate(bs->file, new_file_size); } } qemu_vfree(desc_entries); diff --git a/block/vhdx.c b/block/vhdx.c index 68db9e074e2a..9918ee98ffa4 100644 --- a/block/vhdx.c +++ b/block/vhdx.c @@ -898,6 +898,12 @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags, uint64_t signature; Error *local_err = NULL; + bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, + false, errp); + if (!bs->file) { + return -EINVAL; + } + s->bat = NULL; s->first_visible_write = true; @@ -1165,7 +1171,7 @@ static int vhdx_allocate_block(BlockDriverState *bs, BDRVVHDXState *s, /* per the spec, the address for a block is in units of 1MB */ *new_offset = ROUND_UP(*new_offset, 1024 * 1024); - return bdrv_truncate(bs->file->bs, *new_offset + s->block_size); + return bdrv_truncate(bs->file, *new_offset + s->block_size); } /* diff --git a/block/vmdk.c b/block/vmdk.c index 393c84d8b125..9d68ec5a4eca 100644 --- a/block/vmdk.c +++ b/block/vmdk.c @@ -943,6 +943,12 @@ static int vmdk_open(BlockDriverState *bs, QDict *options, int flags, uint32_t magic; Error *local_err = NULL; + bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, + false, errp); + if (!bs->file) { + return -EINVAL; + } + buf = vmdk_read_desc(bs->file, 0, errp); if (!buf) { return -EINVAL; diff --git a/block/vpc.c b/block/vpc.c index ed6353dbd410..d0df2a1c5440 100644 --- a/block/vpc.c +++ b/block/vpc.c @@ -220,6 +220,12 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags, int disk_type = VHD_DYNAMIC; int ret; + bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, + false, errp); + if (!bs->file) { + return -EINVAL; + } + opts = qemu_opts_create(&vpc_runtime_opts, NULL, 0, &error_abort); qemu_opts_absorb_qdict(opts, options, &local_err); if (local_err) { diff --git a/block/vvfat.c b/block/vvfat.c index c6bf67e8f3e9..7f230be00667 100644 --- a/block/vvfat.c +++ b/block/vvfat.c @@ -2968,6 +2968,7 @@ static void write_target_close(BlockDriverState *bs) { static BlockDriver vvfat_write_target = { .format_name = "vvfat_write_target", + .instance_size = sizeof(void*), .bdrv_co_pwritev = write_target_commit, .bdrv_close = write_target_close, }; @@ -3036,14 +3037,13 @@ static int enable_write_target(BlockDriverState *bs, Error **errp) unlink(s->qcow_filename); #endif - backing = bdrv_new(); + backing = bdrv_new_open_driver(&vvfat_write_target, NULL, BDRV_O_ALLOW_RDWR, + &error_abort); + *(void**) backing->opaque = s; + bdrv_set_backing_hd(s->bs, backing); bdrv_unref(backing); - s->bs->backing->bs->drv = &vvfat_write_target; - s->bs->backing->bs->opaque = g_new(void *, 1); - *(void**)s->bs->backing->bs->opaque = s; - return 0; err: diff --git a/blockdev.c b/blockdev.c index bbf9d4d8f168..2b2f6ceef036 100644 --- a/blockdev.c +++ b/blockdev.c @@ -2858,6 +2858,7 @@ void qmp_block_resize(bool has_device, const char *device, int64_t size, Error **errp) { Error *local_err = NULL; + BlockBackend *blk = NULL; BlockDriverState *bs; AioContext *aio_context; int ret; @@ -2888,10 +2889,13 @@ void qmp_block_resize(bool has_device, const char *device, goto out; } + blk = blk_new(); + blk_insert_bs(blk, bs); + /* complete all in-flight operations before resizing the device */ bdrv_drain_all(); - ret = bdrv_truncate(bs, size); + ret = blk_truncate(blk, size); switch (ret) { case 0: break; @@ -2913,6 +2917,7 @@ void qmp_block_resize(bool has_device, const char *device, } out: + blk_unref(blk); aio_context_release(aio_context); } diff --git a/include/block/block.h b/include/block/block.h index 4e81f2069b57..bde5ebda18c1 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -215,6 +215,8 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options, const char *bdref_key, Error **errp); BlockDriverState *bdrv_open(const char *filename, const char *reference, QDict *options, int flags, Error **errp); +BlockDriverState *bdrv_new_open_driver(BlockDriver *drv, const char *node_name, + int flags, Error **errp); BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue, BlockDriverState *bs, QDict *options, int flags); @@ -253,7 +255,7 @@ BlockDriverState *bdrv_find_backing_image(BlockDriverState *bs, const char *backing_file); int bdrv_get_backing_file_depth(BlockDriverState *bs); void bdrv_refresh_filename(BlockDriverState *bs); -int bdrv_truncate(BlockDriverState *bs, int64_t offset); +int bdrv_truncate(BdrvChild *child, int64_t offset); int64_t bdrv_nb_sectors(BlockDriverState *bs); int64_t bdrv_getlength(BlockDriverState *bs); int64_t bdrv_get_allocated_file_size(BlockDriverState *bs); diff --git a/tests/qemu-iotests/030 b/tests/qemu-iotests/030 index 54db54a1ea48..0d472d5f2716 100755 --- a/tests/qemu-iotests/030 +++ b/tests/qemu-iotests/030 @@ -547,11 +547,14 @@ class TestEIO(TestErrors): while not completed: for event in self.vm.get_qmp_events(wait=True): if event['event'] == 'BLOCK_JOB_ERROR': + error = True self.assert_qmp(event, 'data/device', 'drive0') self.assert_qmp(event, 'data/operation', 'read') result = self.vm.qmp('query-block-jobs') + if result == {'return': []}: + # Job finished too quickly + continue self.assert_qmp(result, 'return[0]/paused', False) - error = True elif event['event'] == 'BLOCK_JOB_COMPLETED': self.assertTrue(error, 'job completed unexpectedly') self.assert_qmp(event, 'data/type', 'stream') diff --git a/tests/qemu-iotests/051.out b/tests/qemu-iotests/051.out index 42bf4164ca0b..7524c62025ad 100644 --- a/tests/qemu-iotests/051.out +++ b/tests/qemu-iotests/051.out @@ -225,7 +225,7 @@ Testing: -drive driver=nbd QEMU_PROG: -drive driver=nbd: NBD server address missing Testing: -drive driver=raw -QEMU_PROG: -drive driver=raw: Can't use 'raw' as a block driver for the protocol level +QEMU_PROG: -drive driver=raw: A block device must be specified for "file" Testing: -drive file.driver=file QEMU_PROG: -drive file.driver=file: The 'file' block driver requires a file name @@ -234,7 +234,7 @@ Testing: -drive file.driver=nbd QEMU_PROG: -drive file.driver=nbd: NBD server address missing Testing: -drive file.driver=raw -QEMU_PROG: -drive file.driver=raw: Can't use 'raw' as a block driver for the protocol level +QEMU_PROG: -drive file.driver=raw: A block device must be specified for "file" Testing: -drive foo=bar QEMU_PROG: -drive foo=bar: Must specify either driver or file diff --git a/tests/qemu-iotests/051.pc.out b/tests/qemu-iotests/051.pc.out index f8047a2e4582..e206ad6c29f7 100644 --- a/tests/qemu-iotests/051.pc.out +++ b/tests/qemu-iotests/051.pc.out @@ -323,7 +323,7 @@ Testing: -drive driver=nbd QEMU_PROG: -drive driver=nbd: NBD server address missing Testing: -drive driver=raw -QEMU_PROG: -drive driver=raw: Can't use 'raw' as a block driver for the protocol level +QEMU_PROG: -drive driver=raw: A block device must be specified for "file" Testing: -drive file.driver=file QEMU_PROG: -drive file.driver=file: The 'file' block driver requires a file name @@ -332,7 +332,7 @@ Testing: -drive file.driver=nbd QEMU_PROG: -drive file.driver=nbd: NBD server address missing Testing: -drive file.driver=raw -QEMU_PROG: -drive file.driver=raw: Can't use 'raw' as a block driver for the protocol level +QEMU_PROG: -drive file.driver=raw: A block device must be specified for "file" Testing: -drive foo=bar QEMU_PROG: -drive foo=bar: Must specify either driver or file diff --git a/tests/qemu-iotests/137 b/tests/qemu-iotests/137 index e5e30de2faf4..eb91e517d7d4 100755 --- a/tests/qemu-iotests/137 +++ b/tests/qemu-iotests/137 @@ -39,7 +39,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 . ./common.qemu _supported_fmt qcow2 -_supported_proto generic +_supported_proto file _supported_os Linux diff --git a/tests/qemu-iotests/175 b/tests/qemu-iotests/175 new file mode 100755 index 000000000000..ca56e827cde2 --- /dev/null +++ b/tests/qemu-iotests/175 @@ -0,0 +1,61 @@ +#!/bin/bash +# +# Test creating raw image preallocation mode +# +# Copyright (C) 2017 Nir Soffer +# +# 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 . +# + +# creator +owner=nirsof@gmail.com + +seq=`basename $0` +echo "QA output created by $seq" + +here=`pwd` +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 + +_supported_fmt raw +_supported_proto file +_supported_os Linux + +size=1m + +echo +echo "== creating image with default preallocation ==" +_make_test_img $size | _filter_imgfmt +stat -c "size=%s, blocks=%b" $TEST_IMG + +for mode in off full falloc; do + echo + echo "== creating image with preallocation $mode ==" + IMGOPTS=preallocation=$mode _make_test_img $size | _filter_imgfmt + stat -c "size=%s, blocks=%b" $TEST_IMG +done + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/175.out b/tests/qemu-iotests/175.out new file mode 100644 index 000000000000..76c02c6a573d --- /dev/null +++ b/tests/qemu-iotests/175.out @@ -0,0 +1,18 @@ +QA output created by 175 + +== creating image with default preallocation == +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 +size=1048576, blocks=0 + +== creating image with preallocation off == +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 preallocation=off +size=1048576, blocks=0 + +== creating image with preallocation full == +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 preallocation=full +size=1048576, blocks=2048 + +== creating image with preallocation falloc == +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 preallocation=falloc +size=1048576, blocks=2048 + *** done diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc index a3d904fc22b0..08065dceae63 100644 --- a/tests/qemu-iotests/common.rc +++ b/tests/qemu-iotests/common.rc @@ -172,7 +172,7 @@ _make_test_img() # Start an NBD server on the image file, which is what we'll be talking to if [ $IMGPROTO = "nbd" ]; then - eval "$QEMU_NBD -v -t -b 127.0.0.1 -p 10810 -f $IMGFMT $TEST_IMG_FILE &" + eval "$QEMU_NBD -v -t -b 127.0.0.1 -p 10810 -f $IMGFMT $TEST_IMG_FILE >/dev/null &" sleep 1 # FIXME: qemu-nbd needs to be listening before we continue fi } @@ -379,6 +379,18 @@ _supported_proto() _notrun "not suitable for this image protocol: $IMGPROTO" } +# tests whether $IMGPROTO is specified as an unsupported image protocol for a test +# +_unsupported_proto() +{ + for f; do + if [ "$f" = "$IMGPROTO" ]; then + _notrun "not suitable for this image protocol: $IMGPROTO" + return + fi + done +} + # tests whether the host OS is one of the supported OSes for a test # _supported_os() diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group index 985b9a6a36cb..1f4bf031854f 100644 --- a/tests/qemu-iotests/group +++ b/tests/qemu-iotests/group @@ -167,3 +167,4 @@ 172 auto 173 rw auto 174 auto +175 auto quick diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c index b132e39097b4..f6dfd087461a 100644 --- a/tests/test-blockjob-txn.c +++ b/tests/test-blockjob-txn.c @@ -96,7 +96,10 @@ static BlockJob *test_block_job_start(unsigned int iterations, char job_id[24]; data = g_new0(TestBlockJobCBData, 1); - bs = bdrv_new(); + + bs = bdrv_open("null-co://", NULL, NULL, 0, &error_abort); + g_assert_nonnull(bs); + snprintf(job_id, sizeof(job_id), "job%u", counter++); s = block_job_create(job_id, &test_block_job_driver, bs, 0, BLOCK_JOB_DEFAULT, test_block_job_cb, @@ -242,6 +245,7 @@ static void test_pair_jobs_fail_cancel_race(void) int main(int argc, char **argv) { qemu_init_main_loop(&error_abort); + bdrv_init(); g_test_init(&argc, &argv, NULL); g_test_add_func("/single/success", test_single_job_success); diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c index 60b78a334272..068c9e419bd3 100644 --- a/tests/test-blockjob.c +++ b/tests/test-blockjob.c @@ -54,7 +54,10 @@ static BlockJob *do_test_id(BlockBackend *blk, const char *id, static BlockBackend *create_blk(const char *name) { BlockBackend *blk = blk_new(); - BlockDriverState *bs = bdrv_new(); + BlockDriverState *bs; + + bs = bdrv_open("null-co://", NULL, NULL, 0, &error_abort); + g_assert_nonnull(bs); blk_insert_bs(blk, bs); bdrv_unref(bs); @@ -140,6 +143,7 @@ static void test_job_ids(void) int main(int argc, char **argv) { qemu_init_main_loop(&error_abort); + bdrv_init(); g_test_init(&argc, &argv, NULL); g_test_add_func("/blockjob/ids", test_job_ids);