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);
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);
+ }
}
/*
*
* 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);
}
/*
*/
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;
* 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) */
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'")
# 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'})
# 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')
# 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])
# 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')
# 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')
# 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)