* Set the current 'total_sectors' value
* Return 0 on success, -errno on error.
*/
-static int refresh_total_sectors(BlockDriverState *bs, int64_t hint)
+int refresh_total_sectors(BlockDriverState *bs, int64_t hint)
{
BlockDriver *drv = bs->drv;
}
}
+static BlockdevDetectZeroesOptions bdrv_parse_detect_zeroes(QemuOpts *opts,
+ int open_flags,
+ Error **errp)
+{
+ Error *local_err = NULL;
+ char *value = qemu_opt_get_del(opts, "detect-zeroes");
+ BlockdevDetectZeroesOptions detect_zeroes =
+ qapi_enum_parse(&BlockdevDetectZeroesOptions_lookup, value,
+ BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF, &local_err);
+ g_free(value);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return detect_zeroes;
+ }
+
+ if (detect_zeroes == BLOCKDEV_DETECT_ZEROES_OPTIONS_UNMAP &&
+ !(open_flags & BDRV_O_UNMAP))
+ {
+ error_setg(errp, "setting detect-zeroes to unmap is not allowed "
+ "without setting discard operation to unmap");
+ }
+
+ return detect_zeroes;
+}
+
/**
* Set open flags for a given discard mode
*
*flags &= ~BDRV_O_CACHE_MASK;
assert(qemu_opt_find(opts, BDRV_OPT_CACHE_NO_FLUSH));
- if (qemu_opt_get_bool(opts, BDRV_OPT_CACHE_NO_FLUSH, false)) {
+ if (qemu_opt_get_bool_del(opts, BDRV_OPT_CACHE_NO_FLUSH, false)) {
*flags |= BDRV_O_NO_FLUSH;
}
assert(qemu_opt_find(opts, BDRV_OPT_CACHE_DIRECT));
- if (qemu_opt_get_bool(opts, BDRV_OPT_CACHE_DIRECT, false)) {
+ if (qemu_opt_get_bool_del(opts, BDRV_OPT_CACHE_DIRECT, false)) {
*flags |= BDRV_O_NOCACHE;
}
*flags &= ~BDRV_O_RDWR;
assert(qemu_opt_find(opts, BDRV_OPT_READ_ONLY));
- if (!qemu_opt_get_bool(opts, BDRV_OPT_READ_ONLY, false)) {
+ if (!qemu_opt_get_bool_del(opts, BDRV_OPT_READ_ONLY, false)) {
*flags |= BDRV_O_RDWR;
}
goto out;
}
+ /* Make sure that the node name isn't truncated */
+ if (strlen(node_name) >= sizeof(bs->node_name)) {
+ error_setg(errp, "Node name too long");
+ goto out;
+ }
+
/* copy node name into the bs and insert it into the graph list */
pstrcpy(bs->node_name, sizeof(bs->node_name), node_name);
QTAILQ_INSERT_TAIL(&graph_bdrv_states, bs, node_list);
const char *driver_name = NULL;
const char *node_name = NULL;
const char *discard;
- const char *detect_zeroes;
QemuOpts *opts;
BlockDriver *drv;
Error *local_err = NULL;
}
}
- detect_zeroes = qemu_opt_get(opts, "detect-zeroes");
- if (detect_zeroes) {
- BlockdevDetectZeroesOptions value =
- qapi_enum_parse(&BlockdevDetectZeroesOptions_lookup,
- detect_zeroes,
- BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF,
- &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
- ret = -EINVAL;
- goto fail_opts;
- }
-
- if (value == BLOCKDEV_DETECT_ZEROES_OPTIONS_UNMAP &&
- !(bs->open_flags & BDRV_O_UNMAP))
- {
- error_setg(errp, "setting detect-zeroes to unmap is not allowed "
- "without setting discard operation to unmap");
- ret = -EINVAL;
- goto fail_opts;
- }
-
- bs->detect_zeroes = value;
+ bs->detect_zeroes =
+ bdrv_parse_detect_zeroes(opts, bs->open_flags, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ ret = -EINVAL;
+ goto fail_opts;
}
if (filename != NULL) {
options_obj = qobject_from_json(filename, errp);
if (!options_obj) {
- /* Work around qobject_from_json() lossage TODO fix that */
- if (errp && !*errp) {
- error_setg(errp, "Could not parse the JSON options");
- return NULL;
- }
error_prepend(errp, "Could not parse the JSON options: ");
return NULL;
}
return 0;
}
-#define DEFAULT_PERM_PASSTHROUGH (BLK_PERM_CONSISTENT_READ \
- | BLK_PERM_WRITE \
- | BLK_PERM_WRITE_UNCHANGED \
- | BLK_PERM_RESIZE)
-#define DEFAULT_PERM_UNCHANGED (BLK_PERM_ALL & ~DEFAULT_PERM_PASSTHROUGH)
-
void bdrv_filter_default_perms(BlockDriverState *bs, BdrvChild *c,
const BdrvChildRole *role,
BlockReopenQueue *reopen_queue,
}
assert(num >= 0);
for (i = 0; i < num; i++) {
- child->role->drained_begin(child);
+ bdrv_parent_drained_begin_single(child, true);
}
}
}
}
-static void bdrv_parent_cb_resize(BlockDriverState *bs)
-{
- BdrvChild *c;
- QLIST_FOREACH(c, &bs->parents, next_parent) {
- if (c->role->resize) {
- c->role->resize(c);
- }
- }
-}
-
/*
* Sets the backing file link of a BDS. A new reference is created; callers
* which don't need their own reference any more must call bdrv_unref().
BlockBackend *file = NULL;
BlockDriverState *bs;
BlockDriver *drv = NULL;
+ BdrvChild *child;
const char *drvname;
const char *backing;
Error *local_err = NULL;
}
}
+ /* Remove all children options and references
+ * from bs->options and bs->explicit_options */
+ QLIST_FOREACH(child, &bs->children, next) {
+ char *child_key_dot;
+ child_key_dot = g_strdup_printf("%s.", child->name);
+ qdict_extract_subqdict(bs->explicit_options, NULL, child_key_dot);
+ qdict_extract_subqdict(bs->options, NULL, child_key_dot);
+ qdict_del(bs->explicit_options, child->name);
+ qdict_del(bs->options, child->name);
+ g_free(child_key_dot);
+ }
+
bdrv_refresh_filename(bs);
/* Check if any unknown options were used */
bdrv_parent_cb_change_media(bs, true);
qobject_unref(options);
+ options = NULL;
/* For snapshot=on, create a temporary qcow2 overlay. bs points to the
* temporary snapshot afterwards. */
}
child_key_dot = g_strdup_printf("%s.", child->name);
+ qdict_extract_subqdict(explicit_options, NULL, child_key_dot);
qdict_extract_subqdict(options, &new_child_options, child_key_dot);
g_free(child_key_dot);
*
* Reopens all BDS specified in the queue, with the appropriate
* flags. All devices are prepared for reopen, and failure of any
- * device will cause all device changes to be abandonded, and intermediate
+ * device will cause all device changes to be abandoned, and intermediate
* data cleaned up.
*
* If all devices prepare successfully, then the changes are committed
cleanup:
QSIMPLEQ_FOREACH_SAFE(bs_entry, bs_queue, entry, next) {
- if (ret && bs_entry->prepared) {
- bdrv_reopen_abort(&bs_entry->state);
- } else if (ret) {
+ if (ret) {
+ if (bs_entry->prepared) {
+ bdrv_reopen_abort(&bs_entry->state);
+ }
qobject_unref(bs_entry->state.explicit_options);
+ qobject_unref(bs_entry->state.options);
}
- qobject_unref(bs_entry->state.options);
g_free(bs_entry);
}
g_free(bs_queue);
Error *local_err = NULL;
BlockDriver *drv;
QemuOpts *opts;
- const char *value;
+ QDict *orig_reopen_opts;
+ char *discard = NULL;
bool read_only;
assert(reopen_state != NULL);
assert(reopen_state->bs->drv != NULL);
drv = reopen_state->bs->drv;
+ /* This function and each driver's bdrv_reopen_prepare() remove
+ * entries from reopen_state->options as they are processed, so
+ * we need to make a copy of the original QDict. */
+ orig_reopen_opts = qdict_clone_shallow(reopen_state->options);
+
/* Process generic block layer options */
opts = qemu_opts_create(&bdrv_runtime_opts, NULL, 0, &error_abort);
qemu_opts_absorb_qdict(opts, reopen_state->options, &local_err);
update_flags_from_options(&reopen_state->flags, opts);
- /* node-name and driver must be unchanged. Put them back into the QDict, so
- * that they are checked at the end of this function. */
- value = qemu_opt_get(opts, "node-name");
- if (value) {
- qdict_put_str(reopen_state->options, "node-name", value);
+ discard = qemu_opt_get_del(opts, "discard");
+ if (discard != NULL) {
+ if (bdrv_parse_discard_flags(discard, &reopen_state->flags) != 0) {
+ error_setg(errp, "Invalid discard option");
+ ret = -EINVAL;
+ goto error;
+ }
}
- value = qemu_opt_get(opts, "driver");
- if (value) {
- qdict_put_str(reopen_state->options, "driver", value);
+ reopen_state->detect_zeroes =
+ bdrv_parse_detect_zeroes(opts, reopen_state->flags, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ ret = -EINVAL;
+ goto error;
}
+ /* All other options (including node-name and driver) must be unchanged.
+ * Put them back into the QDict, so that they are checked at the end
+ * of this function. */
+ qemu_opts_to_qdict(opts, reopen_state->options);
+
/* If we are to stay read-only, do not allow permission change
* to r/w. Attempting to set to r/w may fail if either BDRV_O_ALLOW_RDWR is
* not set, or if the BDS still has copy_on_read enabled */
QObject *new = entry->value;
QObject *old = qdict_get(reopen_state->bs->options, entry->key);
+ /* Allow child references (child_name=node_name) as long as they
+ * point to the current child (i.e. everything stays the same). */
+ if (qobject_type(new) == QTYPE_QSTRING) {
+ BdrvChild *child;
+ QLIST_FOREACH(child, &reopen_state->bs->children, next) {
+ if (!strcmp(child->name, entry->key)) {
+ break;
+ }
+ }
+
+ if (child) {
+ const char *str = qobject_get_try_str(new);
+ if (!strcmp(child->bs->node_name, str)) {
+ continue; /* Found child with this name, skip option */
+ }
+ }
+ }
+
/*
* TODO: When using -drive to specify blockdev options, all values
* will be strings; however, when using -blockdev, blockdev-add or
ret = 0;
+ /* Restore the original reopen_state->options QDict */
+ qobject_unref(reopen_state->options);
+ reopen_state->options = qobject_ref(orig_reopen_opts);
+
error:
qemu_opts_del(opts);
+ qobject_unref(orig_reopen_opts);
+ g_free(discard);
return ret;
}
{
BlockDriver *drv;
BlockDriverState *bs;
+ BdrvChild *child;
bool old_can_write, new_can_write;
assert(reopen_state != NULL);
/* set BDS specific flags now */
qobject_unref(bs->explicit_options);
+ qobject_unref(bs->options);
bs->explicit_options = reopen_state->explicit_options;
+ bs->options = reopen_state->options;
bs->open_flags = reopen_state->flags;
bs->read_only = !(reopen_state->flags & BDRV_O_RDWR);
+ bs->detect_zeroes = reopen_state->detect_zeroes;
+
+ /* Remove child references from bs->options and bs->explicit_options.
+ * Child options were already removed in bdrv_reopen_queue_child() */
+ QLIST_FOREACH(child, &bs->children, next) {
+ qdict_del(bs->explicit_options, child->name);
+ qdict_del(bs->options, child->name);
+ }
bdrv_refresh_limits(bs, NULL);
drv->bdrv_reopen_abort(reopen_state);
}
- qobject_unref(reopen_state->explicit_options);
-
bdrv_abort_perm_update(reopen_state->bs);
}
bdrv_drain(bs); /* in case flush left pending I/O */
if (bs->drv) {
- bs->drv->bdrv_close(bs);
+ if (bs->drv->bdrv_close) {
+ bs->drv->bdrv_close(bs);
+ }
bs->drv = NULL;
}
return ret;
}
-/**
- * Truncate file to 'offset' bytes (needed only for file protocols)
- */
-int bdrv_truncate(BdrvChild *child, int64_t offset, PreallocMode prealloc,
- Error **errp)
-{
- BlockDriverState *bs = child->bs;
- BlockDriver *drv = bs->drv;
- int ret;
-
- assert(child->perm & BLK_PERM_RESIZE);
-
- /* if bs->drv == NULL, bs is closed, so there's nothing to do here */
- if (!drv) {
- error_setg(errp, "No medium inserted");
- return -ENOMEDIUM;
- }
- if (offset < 0) {
- error_setg(errp, "Image size cannot be negative");
- return -EINVAL;
- }
-
- if (!drv->bdrv_truncate) {
- if (bs->file && drv->is_filter) {
- return bdrv_truncate(bs->file, offset, prealloc, errp);
- }
- error_setg(errp, "Image format driver does not support resize");
- return -ENOTSUP;
- }
- if (bs->read_only) {
- error_setg(errp, "Image is read-only");
- return -EACCES;
- }
-
- assert(!(bs->open_flags & BDRV_O_INACTIVE));
-
- ret = drv->bdrv_truncate(bs, offset, prealloc, errp);
- if (ret < 0) {
- return ret;
- }
- ret = refresh_total_sectors(bs, offset >> BDRV_SECTOR_BITS);
- if (ret < 0) {
- error_setg_errno(errp, -ret, "Could not refresh total sector count");
- } else {
- offset = bs->total_sectors * BDRV_SECTOR_SIZE;
- }
- bdrv_dirty_bitmap_truncate(bs, offset);
- bdrv_parent_cb_resize(bs);
- atomic_inc(&bs->write_gen);
- return ret;
-}
-
/**
* Length of a allocated file in bytes. Sparse files are counted by actual
* allocated space. Return < 0 if error or unknown.
return bs ? bs->aio_context : qemu_get_aio_context();
}
-AioWait *bdrv_get_aio_wait(BlockDriverState *bs)
-{
- return bs ? &bs->wait : NULL;
-}
-
void bdrv_coroutine_enter(BlockDriverState *bs, Coroutine *co)
{
aio_co_enter(bdrv_get_aio_context(bs), co);
{
const QDictEntry *entry;
QemuOptDesc *desc;
- BdrvChild *child;
bool found_any = false;
- const char *p;
for (entry = qdict_first(bs->options); entry;
entry = qdict_next(bs->options, entry))
{
- /* Exclude options for children */
- QLIST_FOREACH(child, &bs->children, next) {
- if (strstart(qdict_entry_key(entry), child->name, &p)
- && (!*p || *p == '.'))
- {
- break;
- }
- }
- if (child) {
- continue;
- }
-
- /* And exclude all non-driver-specific options */
+ /* Exclude all non-driver-specific options */
for (desc = bdrv_runtime_opts.desc; desc->name; desc++) {
if (!strcmp(qdict_entry_key(entry), desc->name)) {
break;