#include "trace.h"
#include "block/block_int.h"
#include "block/blockjob.h"
+#include "qemu/error-report.h"
#include "qemu/module.h"
+#include "qapi/qmp/qerror.h"
#include "qapi/qmp/qjson.h"
#include "sysemu/block-backend.h"
#include "sysemu/sysemu.h"
static int bdrv_open_inherit(BlockDriverState **pbs, const char *filename,
const char *reference, QDict *options, int flags,
BlockDriverState *parent,
- const BdrvChildRole *child_role,
- BlockDriver *drv, Error **errp);
+ const BdrvChildRole *child_role, Error **errp);
static void bdrv_dirty_bitmap_truncate(BlockDriverState *bs);
/* If non-zero, use only whitelisted block drivers */
return 0;
}
-BlockDriver *bdrv_find_whitelisted_format(const char *format_name,
- bool read_only)
-{
- BlockDriver *drv = bdrv_find_format(format_name);
- return drv && bdrv_is_whitelisted(drv, read_only) ? drv : NULL;
-}
-
typedef struct CreateCo {
BlockDriver *drv;
char *filename;
int ret = 0;
/* Return the raw BlockDriver * to scsi-generic devices or empty drives */
- if (bs->sg || !bdrv_is_inserted(bs) || bdrv_getlength(bs) == 0) {
+ if (bdrv_is_sg(bs) || !bdrv_is_inserted(bs) || bdrv_getlength(bs) == 0) {
*pdrv = &bdrv_raw;
return ret;
}
BlockDriver *drv = bs->drv;
/* Do not attempt drv->bdrv_getlength() on scsi-generic devices */
- if (bs->sg)
+ if (bdrv_is_sg(bs))
return 0;
/* query actual device if possible, otherwise just trust the hint */
assert(bdrv_opt_mem_align(bs) != 0);
assert(bdrv_min_mem_align(bs) != 0);
- assert((bs->request_alignment != 0) || bs->sg);
+ assert((bs->request_alignment != 0) || bdrv_is_sg(bs));
qemu_opts_del(opts);
return 0;
* block driver has been specified explicitly.
*/
static int bdrv_fill_options(QDict **options, const char **pfilename,
- int *flags, BlockDriver *drv, Error **errp)
+ int *flags, Error **errp)
{
const char *filename = *pfilename;
const char *drvname;
bool protocol = *flags & BDRV_O_PROTOCOL;
bool parse_filename = false;
- BlockDriver *tmp_drv;
+ BlockDriver *drv = NULL;
Error *local_err = NULL;
/* Parse json: pseudo-protocol */
}
drvname = qdict_get_try_str(*options, "driver");
-
- /* If the user has explicitly specified the driver, this choice should
- * override the BDRV_O_PROTOCOL flag */
- tmp_drv = drv;
- if (!tmp_drv && drvname) {
- tmp_drv = bdrv_find_format(drvname);
- }
- if (tmp_drv) {
- protocol = tmp_drv->bdrv_file_open;
+ if (drvname) {
+ drv = bdrv_find_format(drvname);
+ if (!drv) {
+ error_setg(errp, "Unknown driver '%s'", drvname);
+ return -ENOENT;
+ }
+ /* If the user has explicitly specified the driver, this choice should
+ * override the BDRV_O_PROTOCOL flag */
+ protocol = drv->bdrv_file_open;
}
if (protocol) {
/* Find the right block driver */
filename = qdict_get_try_str(*options, "filename");
- if (drv) {
- if (drvname) {
- error_setg(errp, "Driver specified twice");
- return -EINVAL;
- }
- drvname = drv->format_name;
- qdict_put(*options, "driver", qstring_from_str(drvname));
- } else {
- if (!drvname && protocol) {
- if (filename) {
- drv = bdrv_find_protocol(filename, parse_filename, errp);
- if (!drv) {
- return -EINVAL;
- }
-
- drvname = drv->format_name;
- qdict_put(*options, "driver", qstring_from_str(drvname));
- } else {
- error_setg(errp, "Must specify either driver or file");
- return -EINVAL;
- }
- } else if (drvname) {
- drv = bdrv_find_format(drvname);
+ if (!drvname && protocol) {
+ if (filename) {
+ drv = bdrv_find_protocol(filename, parse_filename, errp);
if (!drv) {
- error_setg(errp, "Unknown driver '%s'", drvname);
- return -ENOENT;
+ return -EINVAL;
}
+
+ drvname = drv->format_name;
+ qdict_put(*options, "driver", qstring_from_str(drvname));
+ } else {
+ error_setg(errp, "Must specify either driver or file");
+ return -EINVAL;
}
}
return 0;
}
+static BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs,
+ BlockDriverState *child_bs,
+ const BdrvChildRole *child_role)
+{
+ BdrvChild *child = g_new(BdrvChild, 1);
+ *child = (BdrvChild) {
+ .bs = child_bs,
+ .role = child_role,
+ };
+
+ QLIST_INSERT_HEAD(&parent_bs->children, child, next);
+
+ return child;
+}
+
+static void bdrv_detach_child(BdrvChild *child)
+{
+ QLIST_REMOVE(child, next);
+ g_free(child);
+}
+
+void bdrv_unref_child(BlockDriverState *parent, BdrvChild *child)
+{
+ BlockDriverState *child_bs = child->bs;
+
+ if (child->bs->inherits_from == parent) {
+ child->bs->inherits_from = NULL;
+ }
+
+ bdrv_detach_child(child);
+ bdrv_unref(child_bs);
+}
+
void bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd)
{
if (bs->backing_hd) {
assert(bs->backing_blocker);
bdrv_op_unblock_all(bs->backing_hd, bs->backing_blocker);
+ bdrv_detach_child(bs->backing_child);
} else if (backing_hd) {
error_setg(&bs->backing_blocker,
"node is used as backing hd of '%s'",
if (!backing_hd) {
error_free(bs->backing_blocker);
bs->backing_blocker = NULL;
+ bs->backing_child = NULL;
goto out;
}
+ bs->backing_child = bdrv_attach_child(bs, backing_hd, &child_backing);
bs->open_flags &= ~BDRV_O_NO_BACKING;
pstrcpy(bs->backing_file, sizeof(bs->backing_file), backing_hd->filename);
pstrcpy(bs->backing_format, sizeof(bs->backing_format),
assert(bs->backing_hd == NULL);
ret = bdrv_open_inherit(&backing_hd,
*backing_filename ? backing_filename : NULL,
- NULL, options, 0, bs, &child_backing,
- NULL, &local_err);
+ NULL, options, 0, bs, &child_backing, &local_err);
if (ret < 0) {
bdrv_unref(backing_hd);
backing_hd = NULL;
error_free(local_err);
goto free_exit;
}
+
bdrv_set_backing_hd(bs, backing_hd);
free_exit:
* device's options.
*
* If allow_none is true, no image will be opened if filename is false and no
- * BlockdevRef is given. *pbs will remain unchanged and 0 will be returned.
+ * BlockdevRef is given. NULL will be returned, but errp remains unset.
*
* bdrev_key specifies the key for the image's BlockdevRef in the options QDict.
* That QDict has to be flattened; therefore, if the BlockdevRef is a QDict
* BlockdevRef.
*
* The BlockdevRef will be removed from the options QDict.
- *
- * To conform with the behavior of bdrv_open(), *pbs has to be NULL.
*/
-int bdrv_open_image(BlockDriverState **pbs, const char *filename,
- QDict *options, const char *bdref_key,
- BlockDriverState* parent, const BdrvChildRole *child_role,
- bool allow_none, Error **errp)
+BdrvChild *bdrv_open_child(const char *filename,
+ QDict *options, const char *bdref_key,
+ BlockDriverState* parent,
+ const BdrvChildRole *child_role,
+ bool allow_none, Error **errp)
{
+ BdrvChild *c = NULL;
+ BlockDriverState *bs;
QDict *image_options;
int ret;
char *bdref_key_dot;
const char *reference;
- assert(pbs);
- assert(*pbs == NULL);
+ assert(child_role != NULL);
bdref_key_dot = g_strdup_printf("%s.", bdref_key);
qdict_extract_subqdict(options, &image_options, bdref_key_dot);
reference = qdict_get_try_str(options, bdref_key);
if (!filename && !reference && !qdict_size(image_options)) {
- if (allow_none) {
- ret = 0;
- } else {
+ if (!allow_none) {
error_setg(errp, "A block device must be specified for \"%s\"",
bdref_key);
- ret = -EINVAL;
}
QDECREF(image_options);
goto done;
}
- ret = bdrv_open_inherit(pbs, filename, reference, image_options, 0,
- parent, child_role, NULL, errp);
+ bs = NULL;
+ ret = bdrv_open_inherit(&bs, filename, reference, image_options, 0,
+ parent, child_role, errp);
+ if (ret < 0) {
+ goto done;
+ }
+
+ c = bdrv_attach_child(parent, bs, child_role);
done:
qdict_del(options, bdref_key);
- return ret;
+ return c;
+}
+
+/*
+ * This is a version of bdrv_open_child() that returns 0/-EINVAL instead of
+ * a BdrvChild object.
+ *
+ * If allow_none is true, no image will be opened if filename is false and no
+ * BlockdevRef is given. *pbs will remain unchanged and 0 will be returned.
+ *
+ * To conform with the behavior of bdrv_open(), *pbs has to be NULL.
+ */
+int bdrv_open_image(BlockDriverState **pbs, const char *filename,
+ QDict *options, const char *bdref_key,
+ BlockDriverState* parent, const BdrvChildRole *child_role,
+ bool allow_none, Error **errp)
+{
+ Error *local_err = NULL;
+ BdrvChild *c;
+
+ assert(pbs);
+ assert(*pbs == NULL);
+
+ c = bdrv_open_child(filename, options, bdref_key, parent, child_role,
+ allow_none, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return -EINVAL;
+ }
+
+ if (c != NULL) {
+ *pbs = c->bs;
+ }
+
+ return 0;
}
int bdrv_append_temp_snapshot(BlockDriverState *bs, int flags, Error **errp)
QemuOpts *opts = NULL;
QDict *snapshot_options;
BlockDriverState *bs_snapshot;
- Error *local_err;
+ Error *local_err = NULL;
int ret;
/* if snapshot, we create a temporary backing file and open it
qstring_from_str("file"));
qdict_put(snapshot_options, "file.filename",
qstring_from_str(tmp_filename));
+ qdict_put(snapshot_options, "driver",
+ qstring_from_str("qcow2"));
bs_snapshot = bdrv_new();
ret = bdrv_open(&bs_snapshot, NULL, NULL, snapshot_options,
- flags, &bdrv_qcow2, &local_err);
+ flags, &local_err);
if (ret < 0) {
error_propagate(errp, local_err);
goto out;
return ret;
}
-static void bdrv_attach_child(BlockDriverState *parent_bs,
- BlockDriverState *child_bs,
- const BdrvChildRole *child_role)
-{
- BdrvChild *child = g_new(BdrvChild, 1);
- *child = (BdrvChild) {
- .bs = child_bs,
- .role = child_role,
- };
-
- QLIST_INSERT_HEAD(&parent_bs->children, child, next);
-}
-
/*
* Opens a disk image (raw, qcow2, vmdk, ...)
*
static int bdrv_open_inherit(BlockDriverState **pbs, const char *filename,
const char *reference, QDict *options, int flags,
BlockDriverState *parent,
- const BdrvChildRole *child_role,
- BlockDriver *drv, Error **errp)
+ const BdrvChildRole *child_role, Error **errp)
{
int ret;
BlockDriverState *file = NULL, *bs;
+ BlockDriver *drv = NULL;
const char *drvname;
Error *local_err = NULL;
int snapshot_flags = 0;
return -ENODEV;
}
bdrv_ref(bs);
- if (child_role) {
- bdrv_attach_child(parent, bs, child_role);
- }
*pbs = bs;
return 0;
}
flags = child_role->inherit_flags(parent->open_flags);
}
- ret = bdrv_fill_options(&options, &filename, &flags, drv, &local_err);
+ ret = bdrv_fill_options(&options, &filename, &flags, &local_err);
if (local_err) {
goto fail;
}
/* Find the right image format driver */
- drv = NULL;
drvname = qdict_get_try_str(options, "driver");
if (drvname) {
drv = bdrv_find_format(drvname);
goto close_and_fail;
}
- if (child_role) {
- bdrv_attach_child(parent, bs, child_role);
- }
-
QDECREF(options);
*pbs = bs;
return 0;
}
int bdrv_open(BlockDriverState **pbs, const char *filename,
- const char *reference, QDict *options, int flags,
- BlockDriver *drv, Error **errp)
+ const char *reference, QDict *options, int flags, Error **errp)
{
return bdrv_open_inherit(pbs, filename, reference, options, flags, NULL,
- NULL, drv, errp);
+ NULL, errp);
}
typedef struct BlockReopenQueueEntry {
*
* bs is the BlockDriverState to add to the reopen queue.
*
+ * options contains the changed options for the associated bs
+ * (the BlockReopenQueue takes ownership)
+ *
* flags contains the open flags for the associated bs
*
* returns a pointer to bs_queue, which is either the newly allocated
*
*/
BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue,
- BlockDriverState *bs, int flags)
+ BlockDriverState *bs,
+ QDict *options, int flags)
{
assert(bs != NULL);
BlockReopenQueueEntry *bs_entry;
BdrvChild *child;
+ QDict *old_options;
if (bs_queue == NULL) {
bs_queue = g_new0(BlockReopenQueue, 1);
QSIMPLEQ_INIT(bs_queue);
}
+ if (!options) {
+ options = qdict_new();
+ }
+
+ old_options = qdict_clone_shallow(bs->options);
+ qdict_join(options, old_options, false);
+ QDECREF(old_options);
+
/* bdrv_open() masks this flag out */
flags &= ~BDRV_O_PROTOCOL;
}
child_flags = child->role->inherit_flags(flags);
- bdrv_reopen_queue(bs_queue, child->bs, child_flags);
+ /* TODO Pass down child flags (backing.*, extents.*, ...) */
+ bdrv_reopen_queue(bs_queue, child->bs, NULL, child_flags);
}
bs_entry = g_new0(BlockReopenQueueEntry, 1);
QSIMPLEQ_INSERT_TAIL(bs_queue, bs_entry, entry);
bs_entry->state.bs = bs;
+ bs_entry->state.options = options;
bs_entry->state.flags = flags;
return bs_queue;
if (ret && bs_entry->prepared) {
bdrv_reopen_abort(&bs_entry->state);
}
+ QDECREF(bs_entry->state.options);
g_free(bs_entry);
}
g_free(bs_queue);
{
int ret = -1;
Error *local_err = NULL;
- BlockReopenQueue *queue = bdrv_reopen_queue(NULL, bs, bdrv_flags);
+ BlockReopenQueue *queue = bdrv_reopen_queue(NULL, bs, NULL, bdrv_flags);
ret = bdrv_reopen_multiple(queue, &local_err);
if (local_err != NULL) {
goto error;
}
+ /* 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) */
+ if (qdict_size(reopen_state->options)) {
+ const QDictEntry *entry = qdict_first(reopen_state->options);
+
+ do {
+ QString *new_obj = qobject_to_qstring(entry->value);
+ const char *new = qstring_get_str(new_obj);
+ const char *old = qdict_get_try_str(reopen_state->bs->options,
+ entry->key);
+
+ if (!old || strcmp(new, old)) {
+ error_setg(errp, "Cannot change the option '%s'", entry->key);
+ ret = -EINVAL;
+ goto error;
+ }
+ } while ((entry = qdict_next(reopen_state->options, entry)));
+ }
+
ret = 0;
error:
if (bs->job) {
block_job_cancel_sync(bs->job);
}
- bdrv_drain_all(); /* complete I/O */
+ bdrv_drain(bs); /* complete I/O */
bdrv_flush(bs);
- bdrv_drain_all(); /* in case flush left pending I/O */
+ bdrv_drain(bs); /* in case flush left pending I/O */
notifier_list_notify(&bs->close_notifiers, bs);
if (bs->drv) {
BdrvChild *child, *next;
- QLIST_FOREACH_SAFE(child, &bs->children, next, next) {
- if (child->bs->inherits_from == bs) {
- child->bs->inherits_from = NULL;
- }
- QLIST_REMOVE(child, next);
- g_free(child);
- }
+ bs->drv->bdrv_close(bs);
if (bs->backing_hd) {
BlockDriverState *backing_hd = bs->backing_hd;
bdrv_set_backing_hd(bs, NULL);
bdrv_unref(backing_hd);
}
- bs->drv->bdrv_close(bs);
+
+ QLIST_FOREACH_SAFE(child, &bs->children, next, next) {
+ /* TODO Remove bdrv_unref() from drivers' close function and use
+ * bdrv_unref_child() here */
+ if (child->bs->inherits_from == bs) {
+ child->bs->inherits_from = NULL;
+ }
+ bdrv_detach_child(child);
+ }
+
g_free(bs->opaque);
bs->opaque = NULL;
bs->drv = NULL;
/* The contents of 'tmp' will become bs_top, as we are
* swapping bs_new and bs_top contents. */
bdrv_set_backing_hd(bs_top, bs_new);
- bdrv_attach_child(bs_top, bs_new, &child_backing);
}
static void bdrv_delete(BlockDriverState *bs)
error_setg(errp, "Node '%s' is not encrypted",
bdrv_get_device_or_node_name(bs));
} else if (bdrv_set_key(bs, key) < 0) {
- error_set(errp, QERR_INVALID_PASSWORD);
+ error_setg(errp, QERR_INVALID_PASSWORD);
}
} else {
if (bdrv_key_required(bs)) {
void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap)
{
assert(bdrv_dirty_bitmap_enabled(bitmap));
- hbitmap_reset(bitmap->bitmap, 0, bitmap->size);
+ hbitmap_reset_all(bitmap->bitmap);
}
void bdrv_set_dirty(BlockDriverState *bs, int64_t cur_sector,
}
}
-void bdrv_reset_dirty(BlockDriverState *bs, int64_t cur_sector,
- int nr_sectors)
-{
- BdrvDirtyBitmap *bitmap;
- QLIST_FOREACH(bitmap, &bs->dirty_bitmaps, list) {
- if (!bdrv_dirty_bitmap_enabled(bitmap)) {
- continue;
- }
- hbitmap_reset(bitmap->bitmap, cur_sector, nr_sectors);
- }
-}
-
/**
* Advance an HBitmapIter to an arbitrary offset.
*/
const char *backing_fmt, *backing_file;
int64_t size;
BlockDriver *drv, *proto_drv;
- BlockDriver *backing_drv = NULL;
Error *local_err = NULL;
int ret = 0;
}
backing_fmt = qemu_opt_get(opts, BLOCK_OPT_BACKING_FMT);
- if (backing_fmt) {
- backing_drv = bdrv_find_format(backing_fmt);
- if (!backing_drv) {
- error_setg(errp, "Unknown backing file format '%s'",
- backing_fmt);
- goto out;
- }
- }
// The size for the image must always be specified, with one exception:
// If we are using a backing file, we can obtain the size from there
char *full_backing = g_new0(char, PATH_MAX);
int64_t size;
int back_flags;
+ QDict *backing_options = NULL;
bdrv_get_full_backing_filename_from_filename(filename, backing_file,
full_backing, PATH_MAX,
back_flags =
flags & ~(BDRV_O_RDWR | BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING);
+ if (backing_fmt) {
+ backing_options = qdict_new();
+ qdict_put(backing_options, "driver",
+ qstring_from_str(backing_fmt));
+ }
+
bs = NULL;
- ret = bdrv_open(&bs, full_backing, NULL, NULL, back_flags,
- backing_drv, &local_err);
+ ret = bdrv_open(&bs, full_backing, NULL, backing_options,
+ back_flags, &local_err);
g_free(full_backing);
if (ret < 0) {
goto out;
}
if (!quiet) {
- printf("Formatting '%s', fmt=%s", filename, fmt);
+ printf("Formatting '%s', fmt=%s ", filename, fmt);
qemu_opts_print(opts, " ");
puts("");
}
void bdrv_set_aio_context(BlockDriverState *bs, AioContext *new_context)
{
- bdrv_drain_all(); /* ensure there are no in-flight requests */
+ bdrv_drain(bs); /* ensure there are no in-flight requests */
bdrv_detach_aio_context(bs);
return false;
}
-BlockDriverState *check_to_replace_node(const char *node_name, Error **errp)
+BlockDriverState *check_to_replace_node(BlockDriverState *parent_bs,
+ const char *node_name, Error **errp)
{
BlockDriverState *to_replace_bs = bdrv_find_node(node_name);
AioContext *aio_context;
* Another benefit is that this tests exclude backing files which are
* blocked by the backing blockers.
*/
- if (!bdrv_is_first_non_filter(to_replace_bs)) {
+ if (!bdrv_recurse_is_first_non_filter(parent_bs, to_replace_bs)) {
error_setg(errp, "Only top most non filter can be replaced");
to_replace_bs = NULL;
goto out;