void qmp_block_stream(bool has_job_id, const char *job_id, const char *device, bool has_base, const char *base, bool has_backing_file, const char *backing_file, bool has_speed, int64_t speed, bool has_on_error, BlockdevOnError on_error, Error **errp) { BlockDriverState *bs; BlockDriverState *base_bs = NULL; AioContext *aio_context; Error *local_err = NULL; const char *base_name = NULL; if (!has_on_error) { on_error = BLOCKDEV_ON_ERROR_REPORT; } bs = qmp_get_root_bs(device, errp); if (!bs) { return; } aio_context = bdrv_get_aio_context(bs); aio_context_acquire(aio_context); if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_STREAM, errp)) { goto out; } if (has_base) { base_bs = bdrv_find_backing_image(bs, base); if (base_bs == NULL) { error_setg(errp, QERR_BASE_NOT_FOUND, base); goto out; } assert(bdrv_get_aio_context(base_bs) == aio_context); base_name = base; } /* if we are streaming the entire chain, the result will have no backing * file, and specifying one is therefore an error */ if (base_bs == NULL && has_backing_file) { error_setg(errp, "backing file specified, but streaming the " "entire chain"); goto out; } /* backing_file string overrides base bs filename */ base_name = has_backing_file ? backing_file : base_name; stream_start(has_job_id ? job_id : NULL, bs, base_bs, base_name, has_speed ? speed : 0, on_error, block_job_cb, bs, &local_err); if (local_err) { error_propagate(errp, local_err); goto out; } trace_qmp_block_stream(bs, bs->job); out: aio_context_release(aio_context); }