diff --git a/blockdev.c b/blockdev.c index b5d4d6919f0e7b896bbccb0fa608f0fc233dedbc..ded13268f70be50d7ed4cda608ee21062b3e6cc7 100644 --- a/blockdev.c +++ b/blockdev.c @@ -2932,6 +2932,7 @@ static void block_job_cb(void *opaque, int ret) void qmp_block_stream(bool has_job_id, const char *job_id, const char *device, bool has_base, const char *base, + bool has_base_node, const char *base_node, bool has_backing_file, const char *backing_file, bool has_speed, int64_t speed, bool has_on_error, BlockdevOnError on_error, @@ -2955,6 +2956,12 @@ void qmp_block_stream(bool has_job_id, const char *job_id, const char *device, aio_context = bdrv_get_aio_context(bs); aio_context_acquire(aio_context); + if (has_base && has_base_node) { + error_setg(errp, "'base' and 'base-node' cannot be specified " + "at the same time"); + goto out; + } + if (has_base) { base_bs = bdrv_find_backing_image(bs, base); if (base_bs == NULL) { @@ -2965,6 +2972,20 @@ void qmp_block_stream(bool has_job_id, const char *job_id, const char *device, base_name = base; } + if (has_base_node) { + base_bs = bdrv_lookup_bs(NULL, base_node, errp); + if (!base_bs) { + goto out; + } + if (bs == base_bs || !bdrv_chain_contains(bs, base_bs)) { + error_setg(errp, "Node '%s' is not a backing image of '%s'", + base_node, device); + goto out; + } + assert(bdrv_get_aio_context(base_bs) == aio_context); + base_name = base_bs->filename; + } + /* Check for op blockers in the whole chain between bs and base */ for (iter = bs; iter && iter != base_bs; iter = backing_bs(iter)) { if (bdrv_op_is_blocked(iter, BLOCK_OP_TYPE_STREAM, errp)) { diff --git a/docs/qmp-commands.txt b/docs/qmp-commands.txt index a4732a570c8a23756a6eb8e55bf9f409af157a6f..6afa87298d99ab0e2819ee3de83955a36ec4dddc 100644 --- a/docs/qmp-commands.txt +++ b/docs/qmp-commands.txt @@ -750,8 +750,11 @@ Arguments: - "job-id": Identifier for the newly-created block job. If omitted, the device name will be used. (json-string, optional) - "device": The device name or node-name of a root node (json-string) -- "base": The file name of the backing image above which copying starts - (json-string, optional) +- "base": The file name of the backing image above which copying starts. + It cannot be set if 'base-node' is also set (json-string, optional) +- "base-node": the node name of the backing image above which copying starts. + It cannot be set if 'base' is also set. + (json-string, optional) (Since 2.8) - "backing-file": The backing file string to write into the active layer. This filename is not validated. diff --git a/hmp.c b/hmp.c index 00af4230bff59aa47c7c602010f68870403a0826..b5e3f54534c43e635c9414558f9bad3da5da2f1e 100644 --- a/hmp.c +++ b/hmp.c @@ -1571,7 +1571,7 @@ void hmp_block_stream(Monitor *mon, const QDict *qdict) int64_t speed = qdict_get_try_int(qdict, "speed", 0); qmp_block_stream(false, NULL, device, base != NULL, base, false, NULL, - qdict_haskey(qdict, "speed"), speed, + false, NULL, qdict_haskey(qdict, "speed"), speed, true, BLOCKDEV_ON_ERROR_REPORT, &error); hmp_handle_error(mon, &error); diff --git a/qapi/block-core.json b/qapi/block-core.json index d7a029cfd5fbf347b51e0e79849d145b45724c63..3592a9d7ad649eca201156776d1710f5e1aee4f6 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -1484,7 +1484,11 @@ # # @device: the device or node name of the top image # -# @base: #optional the common backing file name +# @base: #optional the common backing file name. +# It cannot be set if @base-node is also set. +# +# @base-node: #optional the node name of the backing file. +# It cannot be set if @base is also set. (Since 2.8) # # @backing-file: #optional The backing file string to write into the top # image. This filename is not validated. @@ -1511,7 +1515,7 @@ ## { 'command': 'block-stream', 'data': { '*job-id': 'str', 'device': 'str', '*base': 'str', - '*backing-file': 'str', '*speed': 'int', + '*base-node': 'str', '*backing-file': 'str', '*speed': 'int', '*on-error': 'BlockdevOnError' } } ##