[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PULL 13/24] block: Allow changing bs->file on reopen
From: |
Kevin Wolf |
Subject: |
[PULL 13/24] block: Allow changing bs->file on reopen |
Date: |
Wed, 30 Jun 2021 18:01:55 +0200 |
From: Alberto Garcia <berto@igalia.com>
When the x-blockdev-reopen was added it allowed reconfiguring the
graph by replacing backing files, but changing the 'file' option was
forbidden. Because of this restriction some operations are not
possible, notably inserting and removing block filters.
This patch adds support for replacing the 'file' option. This is
similar to replacing the backing file and the user is likewise
responsible for the correctness of the resulting graph, otherwise this
can lead to data corruption.
Signed-off-by: Alberto Garcia <berto@igalia.com>
[vsementsov: bdrv_reopen_parse_file_or_backing() is modified a lot]
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
Message-Id: <20210610120537.196183-9-vsementsov@virtuozzo.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
include/block/block.h | 1 +
block.c | 78 +++++++++++++++++++++++++++++-------------
tests/qemu-iotests/245 | 23 +++++++------
3 files changed, 67 insertions(+), 35 deletions(-)
diff --git a/include/block/block.h b/include/block/block.h
index d28022e761..7ec77ecb1a 100644
--- a/include/block/block.h
+++ b/include/block/block.h
@@ -209,6 +209,7 @@ typedef struct BDRVReopenState {
BlockdevDetectZeroesOptions detect_zeroes;
bool backing_missing;
BlockDriverState *old_backing_bs; /* keep pointer for permissions update */
+ BlockDriverState *old_file_bs; /* keep pointer for permissions update */
QDict *options;
QDict *explicit_options;
void *opaque;
diff --git a/block.c b/block.c
index ca11078cd2..acd35cb0cb 100644
--- a/block.c
+++ b/block.c
@@ -92,7 +92,7 @@ static void bdrv_remove_filter_or_cow_child(BlockDriverState
*bs,
static int bdrv_reopen_prepare(BDRVReopenState *reopen_state,
BlockReopenQueue *queue,
- Transaction *set_backings_tran, Error **errp);
+ Transaction *change_child_tran, Error **errp);
static void bdrv_reopen_commit(BDRVReopenState *reopen_state);
static void bdrv_reopen_abort(BDRVReopenState *reopen_state);
@@ -4148,6 +4148,10 @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue,
Error **errp)
refresh_list = bdrv_topological_dfs(refresh_list, found,
state->old_backing_bs);
}
+ if (state->old_file_bs) {
+ refresh_list = bdrv_topological_dfs(refresh_list, found,
+ state->old_file_bs);
+ }
}
/*
@@ -4240,64 +4244,81 @@ int bdrv_reopen_set_read_only(BlockDriverState *bs,
bool read_only,
*
* Return 0 on success, otherwise return < 0 and set @errp.
*/
-static int bdrv_reopen_parse_backing(BDRVReopenState *reopen_state,
- Transaction *set_backings_tran,
- Error **errp)
+static int bdrv_reopen_parse_file_or_backing(BDRVReopenState *reopen_state,
+ bool is_backing, Transaction
*tran,
+ Error **errp)
{
BlockDriverState *bs = reopen_state->bs;
- BlockDriverState *new_backing_bs;
+ BlockDriverState *new_child_bs;
+ BlockDriverState *old_child_bs = is_backing ? child_bs(bs->backing) :
+ child_bs(bs->file);
+ const char *child_name = is_backing ? "backing" : "file";
QObject *value;
const char *str;
- value = qdict_get(reopen_state->options, "backing");
+ value = qdict_get(reopen_state->options, child_name);
if (value == NULL) {
return 0;
}
switch (qobject_type(value)) {
case QTYPE_QNULL:
- new_backing_bs = NULL;
+ assert(is_backing); /* The 'file' option does not allow a null value */
+ new_child_bs = NULL;
break;
case QTYPE_QSTRING:
str = qstring_get_str(qobject_to(QString, value));
- new_backing_bs = bdrv_lookup_bs(NULL, str, errp);
- if (new_backing_bs == NULL) {
+ new_child_bs = bdrv_lookup_bs(NULL, str, errp);
+ if (new_child_bs == NULL) {
return -EINVAL;
- } else if (bdrv_recurse_has_child(new_backing_bs, bs)) {
- error_setg(errp, "Making '%s' a backing file of '%s' "
- "would create a cycle", str, bs->node_name);
+ } else if (bdrv_recurse_has_child(new_child_bs, bs)) {
+ error_setg(errp, "Making '%s' a %s child of '%s' would create a "
+ "cycle", str, child_name, bs->node_name);
return -EINVAL;
}
break;
default:
- /* 'backing' does not allow any other data type */
+ /*
+ * The options QDict has been flattened, so 'backing' and 'file'
+ * do not allow any other data type here.
+ */
g_assert_not_reached();
}
- if (bs->backing) {
- if (bdrv_skip_implicit_filters(bs->backing->bs) == new_backing_bs) {
+ if (old_child_bs == new_child_bs) {
+ return 0;
+ }
+
+ if (old_child_bs) {
+ if (bdrv_skip_implicit_filters(old_child_bs) == new_child_bs) {
return 0;
}
- if (bs->backing->bs->implicit) {
- error_setg(errp, "Cannot change backing link if '%s' has "
- "an implicit backing file", bs->node_name);
+ if (old_child_bs->implicit) {
+ error_setg(errp, "Cannot replace implicit %s child of %s",
+ child_name, bs->node_name);
return -EPERM;
}
}
- if (bs->drv->is_filter && !bs->backing) {
+ if (bs->drv->is_filter && !old_child_bs) {
/*
* Filters always have a file or a backing child, so we are trying to
* change wrong child
*/
error_setg(errp, "'%s' is a %s filter node that does not support a "
- "backing child", bs->node_name, bs->drv->format_name);
+ "%s child", bs->node_name, bs->drv->format_name,
child_name);
return -EINVAL;
}
- reopen_state->old_backing_bs = bs->backing ? bs->backing->bs : NULL;
- return bdrv_set_backing_noperm(bs, new_backing_bs, set_backings_tran,
errp);
+ if (is_backing) {
+ reopen_state->old_backing_bs = old_child_bs;
+ } else {
+ reopen_state->old_file_bs = old_child_bs;
+ }
+
+ return bdrv_set_file_or_backing_noperm(bs, new_child_bs, is_backing,
+ tran, errp);
}
/*
@@ -4319,7 +4340,7 @@ static int bdrv_reopen_parse_backing(BDRVReopenState
*reopen_state,
*/
static int bdrv_reopen_prepare(BDRVReopenState *reopen_state,
BlockReopenQueue *queue,
- Transaction *set_backings_tran, Error **errp)
+ Transaction *change_child_tran, Error **errp)
{
int ret = -1;
int old_flags;
@@ -4439,12 +4460,21 @@ static int bdrv_reopen_prepare(BDRVReopenState
*reopen_state,
* either a reference to an existing node (using its node name)
* or NULL to simply detach the current backing file.
*/
- ret = bdrv_reopen_parse_backing(reopen_state, set_backings_tran, errp);
+ ret = bdrv_reopen_parse_file_or_backing(reopen_state, true,
+ change_child_tran, errp);
if (ret < 0) {
goto error;
}
qdict_del(reopen_state->options, "backing");
+ /* Allow changing the 'file' option. In this case NULL is not allowed */
+ ret = bdrv_reopen_parse_file_or_backing(reopen_state, false,
+ change_child_tran, errp);
+ if (ret < 0) {
+ goto error;
+ }
+ qdict_del(reopen_state->options, "file");
+
/* 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) */
diff --git a/tests/qemu-iotests/245 b/tests/qemu-iotests/245
index c7d671366a..d955e0dfd3 100755
--- a/tests/qemu-iotests/245
+++ b/tests/qemu-iotests/245
@@ -146,8 +146,8 @@ class TestBlockdevReopen(iotests.QMPTestCase):
self.reopen(opts, {'driver': 'raw'}, "Cannot change the option
'driver'")
self.reopen(opts, {'driver': ''}, "Invalid parameter ''")
self.reopen(opts, {'driver': None}, "Invalid parameter type for
'driver', expected: string")
- self.reopen(opts, {'file': 'not-found'}, "Cannot change the option
'file'")
- self.reopen(opts, {'file': ''}, "Cannot change the option 'file'")
+ self.reopen(opts, {'file': 'not-found'}, "Cannot find device='' nor
node-name='not-found'")
+ self.reopen(opts, {'file': ''}, "Cannot find device='' nor
node-name=''")
self.reopen(opts, {'file': None}, "Invalid parameter type for 'file',
expected: BlockdevRef")
self.reopen(opts, {'file.node-name': 'newname'}, "Cannot change the
option 'node-name'")
self.reopen(opts, {'file.driver': 'host_device'}, "Cannot change the
option 'driver'")
@@ -443,7 +443,7 @@ class TestBlockdevReopen(iotests.QMPTestCase):
# Illegal operation: hd2 is a child of hd1
self.reopen(opts[2], {'backing': 'hd1'},
- "Making 'hd1' a backing file of 'hd2' would create a
cycle")
+ "Making 'hd1' a backing child of 'hd2' would create a
cycle")
# hd2 <- hd0, hd2 <- hd1
self.reopen(opts[0], {'backing': 'hd2'})
@@ -454,8 +454,9 @@ class TestBlockdevReopen(iotests.QMPTestCase):
# More illegal operations
self.reopen(opts[2], {'backing': 'hd1'},
- "Making 'hd1' a backing file of 'hd2' would create a
cycle")
- self.reopen(opts[2], {'file': 'hd0-file'}, "Cannot change the option
'file'")
+ "Making 'hd1' a backing child of 'hd2' would create a
cycle")
+ self.reopen(opts[2], {'file': 'hd0-file'},
+ "Permission conflict on node 'hd0-file': permissions
'write, resize' are both required by node 'hd0' (uses node 'hd0-file' as 'file'
child) and unshared by node 'hd2' (uses node 'hd0-file' as 'file' child).")
result = self.vm.qmp('blockdev-del', conv_keys = True, node_name =
'hd2')
self.assert_qmp(result, 'error/class', 'GenericError')
@@ -497,18 +498,18 @@ class TestBlockdevReopen(iotests.QMPTestCase):
# Illegal: hd2 is backed by hd1
self.reopen(opts[1], {'backing': 'hd2'},
- "Making 'hd2' a backing file of 'hd1' would create a
cycle")
+ "Making 'hd2' a backing child of 'hd1' would create a
cycle")
# hd1 <- hd0 <- hd2
self.reopen(opts[2], {'backing': 'hd0'})
# Illegal: hd2 is backed by hd0, which is backed by hd1
self.reopen(opts[1], {'backing': 'hd2'},
- "Making 'hd2' a backing file of 'hd1' would create a
cycle")
+ "Making 'hd2' a backing child of 'hd1' would create a
cycle")
# Illegal: hd1 cannot point to itself
self.reopen(opts[1], {'backing': 'hd1'},
- "Making 'hd1' a backing file of 'hd1' would create a
cycle")
+ "Making 'hd1' a backing child of 'hd1' would create a
cycle")
# Remove all backing files
self.reopen(opts[0])
@@ -530,7 +531,7 @@ class TestBlockdevReopen(iotests.QMPTestCase):
# Illegal: hd0 is a child of the blkverify node
self.reopen(opts[0], {'backing': 'bv'},
- "Making 'bv' a backing file of 'hd0' would create a cycle")
+ "Making 'bv' a backing child of 'hd0' would create a
cycle")
# Delete the blkverify node
result = self.vm.qmp('blockdev-del', conv_keys = True, node_name =
'bv')
@@ -563,7 +564,7 @@ class TestBlockdevReopen(iotests.QMPTestCase):
# You can't make quorum0 a backing file of hd0:
# hd0 is already a child of quorum0.
self.reopen(hd_opts(0), {'backing': 'quorum0'},
- "Making 'quorum0' a backing file of 'hd0' would create a
cycle")
+ "Making 'quorum0' a backing child of 'hd0' would create a
cycle")
# Delete quorum0
result = self.vm.qmp('blockdev-del', conv_keys = True, node_name =
'quorum0')
@@ -969,7 +970,7 @@ class TestBlockdevReopen(iotests.QMPTestCase):
# We can't remove hd1 while the commit job is ongoing
opts['backing'] = None
- self.reopen(opts, {}, "Cannot change backing link if 'hd0' has an
implicit backing file")
+ self.reopen(opts, {}, "Cannot replace implicit backing child of hd0")
# hd2 <- hd0
self.vm.run_job('commit0', auto_finalize = False, auto_dismiss = True)
--
2.31.1
- [PULL 01/24] Prevent compiler warning on block.c, (continued)
- [PULL 01/24] Prevent compiler warning on block.c, Kevin Wolf, 2021/06/30
- [PULL 03/24] block: BDRV_O_NO_IO for backing file on creation, Kevin Wolf, 2021/06/30
- [PULL 02/24] block: Move read-only check during truncation earlier, Kevin Wolf, 2021/06/30
- [PULL 06/24] block: introduce bdrv_remove_file_or_backing_child(), Kevin Wolf, 2021/06/30
- [PULL 08/24] block: bdrv_reopen_parse_backing(): don't check aio context, Kevin Wolf, 2021/06/30
- [PULL 07/24] block: introduce bdrv_set_file_or_backing_noperm(), Kevin Wolf, 2021/06/30
- [PULL 12/24] block: BDRVReopenState: drop replace_backing_bs field, Kevin Wolf, 2021/06/30
- [PULL 09/24] block: bdrv_reopen_parse_backing(): don't check frozen child, Kevin Wolf, 2021/06/30
- [PULL 10/24] block: bdrv_reopen_parse_backing(): simplify handling implicit filters, Kevin Wolf, 2021/06/30
- [PULL 11/24] block: move supports_backing check to bdrv_set_file_or_backing_noperm(), Kevin Wolf, 2021/06/30
- [PULL 13/24] block: Allow changing bs->file on reopen,
Kevin Wolf <=
- [PULL 04/24] block: rename bdrv_replace_child to bdrv_replace_child_tran, Kevin Wolf, 2021/06/30
- [PULL 17/24] block/ssh: add support for sha256 host key fingerprints, Kevin Wolf, 2021/06/30
- [PULL 15/24] introduce QEMU_AUTO_VFREE, Kevin Wolf, 2021/06/30
- [PULL 18/24] vhost: Add Error parameter to vhost_dev_init(), Kevin Wolf, 2021/06/30
- [PULL 16/24] block/commit: use QEMU_AUTO_VFREE, Kevin Wolf, 2021/06/30
- [PULL 22/24] vhost: Distinguish errors in vhost_dev_get_config(), Kevin Wolf, 2021/06/30
- [PULL 23/24] vhost-user-blk: Factor out vhost_user_blk_realize_connect(), Kevin Wolf, 2021/06/30
- [PULL 14/24] iotests: Test replacing files with x-blockdev-reopen, Kevin Wolf, 2021/06/30
- [PULL 20/24] vhost: Return 0/-errno in vhost_dev_init(), Kevin Wolf, 2021/06/30
- [PULL 24/24] vhost-user-blk: Implement reconnection during realize, Kevin Wolf, 2021/06/30