if (path_is_absolute(filename)) {
pstrcpy(dest, dest_size, filename);
} else {
- p = strchr(base_path, ':');
- if (p)
- p++;
- else
- p = base_path;
+ const char *protocol_stripped = NULL;
+
+ if (path_has_protocol(base_path)) {
+ protocol_stripped = strchr(base_path, ':');
+ if (protocol_stripped) {
+ protocol_stripped++;
+ }
+ }
+ p = protocol_stripped ?: base_path;
+
p1 = strrchr(base_path, '/');
#ifdef _WIN32
{
}
}
+/*
+ * Helper function for bdrv_parse_filename() implementations to remove optional
+ * protocol prefixes (especially "file:") from a filename and for putting the
+ * stripped filename into the options QDict if there is such a prefix.
+ */
+void bdrv_parse_filename_strip_prefix(const char *filename, const char *prefix,
+ QDict *options)
+{
+ if (strstart(filename, prefix, &filename)) {
+ /* Stripping the explicit protocol prefix may result in a protocol
+ * prefix being (wrongly) detected (if the filename contains a colon) */
+ if (path_has_protocol(filename)) {
+ QString *fat_filename;
+
+ /* This means there is some colon before the first slash; therefore,
+ * this cannot be an absolute path */
+ assert(!path_is_absolute(filename));
+
+ /* And we can thus fix the protocol detection issue by prefixing it
+ * by "./" */
+ fat_filename = qstring_from_str("./");
+ qstring_append(fat_filename, filename);
+
+ assert(!path_has_protocol(qstring_get_str(fat_filename)));
+
+ qdict_put(options, "filename", fat_filename);
+ } else {
+ /* If no protocol prefix was detected, we can use the shortened
+ * filename as-is */
+ qdict_put_str(options, "filename", filename);
+ }
+ }
+}
+
+
+/* Returns whether the image file is opened as read-only. Note that this can
+ * return false and writing to the image file is still not possible because the
+ * image is inactivated. */
bool bdrv_is_read_only(BlockDriverState *bs)
{
return bs->read_only;
}
+/* Returns whether the image file can be written to right now */
+bool bdrv_is_writable(BlockDriverState *bs)
+{
+ return !bdrv_is_read_only(bs) && !(bs->open_flags & BDRV_O_INACTIVE);
+}
+
int bdrv_can_set_read_only(BlockDriverState *bs, bool read_only, Error **errp)
{
/* Do not set read_only if copy_on_read is enabled */
QLIST_INIT(&bs->op_blockers[i]);
}
notifier_with_return_list_init(&bs->before_write_notifiers);
+ qemu_co_mutex_init(&bs->reqs_lock);
+ qemu_mutex_init(&bs->dirty_bitmap_mutex);
bs->refcnt = 1;
bs->aio_context = qemu_get_aio_context();
bdrv_drained_end(bs);
}
+static int bdrv_child_cb_inactivate(BdrvChild *child)
+{
+ BlockDriverState *bs = child->opaque;
+ assert(bs->open_flags & BDRV_O_INACTIVE);
+ return 0;
+}
+
/*
* Returns the options and flags that a temporary snapshot should get, based on
* the originally requested flags (the originally requested image will have
* the parent. */
qdict_copy_default(child_options, parent_options, BDRV_OPT_CACHE_DIRECT);
qdict_copy_default(child_options, parent_options, BDRV_OPT_CACHE_NO_FLUSH);
+ qdict_copy_default(child_options, parent_options, BDRV_OPT_FORCE_SHARE);
/* Inherit the read-only option from the parent if it's not set */
qdict_copy_default(child_options, parent_options, BDRV_OPT_READ_ONLY);
.inherit_options = bdrv_inherited_options,
.drained_begin = bdrv_child_cb_drained_begin,
.drained_end = bdrv_child_cb_drained_end,
+ .inactivate = bdrv_child_cb_inactivate,
};
/*
.inherit_options = bdrv_inherited_fmt_options,
.drained_begin = bdrv_child_cb_drained_begin,
.drained_end = bdrv_child_cb_drained_end,
+ .inactivate = bdrv_child_cb_inactivate,
};
static void bdrv_backing_attach(BdrvChild *c)
* which is only applied on the top level (BlockBackend) */
qdict_copy_default(child_options, parent_options, BDRV_OPT_CACHE_DIRECT);
qdict_copy_default(child_options, parent_options, BDRV_OPT_CACHE_NO_FLUSH);
+ qdict_copy_default(child_options, parent_options, BDRV_OPT_FORCE_SHARE);
/* backing files always opened read-only */
qdict_set_default_str(child_options, BDRV_OPT_READ_ONLY, "on");
.inherit_options = bdrv_backing_options,
.drained_begin = bdrv_child_cb_drained_begin,
.drained_end = bdrv_child_cb_drained_end,
+ .inactivate = bdrv_child_cb_inactivate,
};
static int bdrv_open_flags(BlockDriverState *bs, int flags)
static void update_options_from_flags(QDict *options, int flags)
{
if (!qdict_haskey(options, BDRV_OPT_CACHE_DIRECT)) {
- qdict_put(options, BDRV_OPT_CACHE_DIRECT,
- qbool_from_bool(flags & BDRV_O_NOCACHE));
+ qdict_put_bool(options, BDRV_OPT_CACHE_DIRECT, flags & BDRV_O_NOCACHE);
}
if (!qdict_haskey(options, BDRV_OPT_CACHE_NO_FLUSH)) {
- qdict_put(options, BDRV_OPT_CACHE_NO_FLUSH,
- qbool_from_bool(flags & BDRV_O_NO_FLUSH));
+ qdict_put_bool(options, BDRV_OPT_CACHE_NO_FLUSH,
+ flags & BDRV_O_NO_FLUSH);
}
if (!qdict_haskey(options, BDRV_OPT_READ_ONLY)) {
- qdict_put(options, BDRV_OPT_READ_ONLY,
- qbool_from_bool(!(flags & BDRV_O_RDWR)));
+ qdict_put_bool(options, BDRV_OPT_READ_ONLY, !(flags & BDRV_O_RDWR));
}
}
.type = QEMU_OPT_STRING,
.help = "discard operation (ignore/off, unmap/on)",
},
+ {
+ .name = BDRV_OPT_FORCE_SHARE,
+ .type = QEMU_OPT_BOOL,
+ .help = "always accept other writers (default: off)",
+ },
{ /* end of list */ }
},
};
drv = bdrv_find_format(driver_name);
assert(drv != NULL);
+ bs->force_share = qemu_opt_get_bool(opts, BDRV_OPT_FORCE_SHARE, false);
+
+ if (bs->force_share && (bs->open_flags & BDRV_O_RDWR)) {
+ error_setg(errp,
+ BDRV_OPT_FORCE_SHARE
+ "=on can only be used with read-only images");
+ ret = -EINVAL;
+ goto fail_opts;
+ }
+
if (file != NULL) {
filename = blk_bs(file)->filename;
} else {
goto fail_opts;
}
- assert(bs->copy_on_read == 0); /* bdrv_new() and bdrv_close() make it so */
+ /* bdrv_new() and bdrv_close() make it so */
+ assert(atomic_read(&bs->copy_on_read) == 0);
+
if (bs->open_flags & BDRV_O_COPY_ON_READ) {
if (!bs->read_only) {
bdrv_enable_copy_on_read(bs);
/* Fetch the file name from the options QDict if necessary */
if (protocol && filename) {
if (!qdict_haskey(*options, "filename")) {
- qdict_put(*options, "filename", qstring_from_str(filename));
+ qdict_put_str(*options, "filename", filename);
parse_filename = true;
} else {
error_setg(errp, "Can't specify 'file' and 'filename' options at "
}
drvname = drv->format_name;
- qdict_put(*options, "driver", qstring_from_str(drvname));
+ qdict_put_str(*options, "driver", drvname);
} else {
error_setg(errp, "Must specify either driver or file");
return -EINVAL;
static void bdrv_child_abort_perm_update(BdrvChild *c);
static void bdrv_child_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared);
+static void bdrv_child_perm(BlockDriverState *bs, BlockDriverState *child_bs,
+ BdrvChild *c,
+ const BdrvChildRole *role,
+ uint64_t parent_perm, uint64_t parent_shared,
+ uint64_t *nperm, uint64_t *nshared)
+{
+ if (bs->drv && bs->drv->bdrv_child_perm) {
+ bs->drv->bdrv_child_perm(bs, c, role,
+ parent_perm, parent_shared,
+ nperm, nshared);
+ }
+ if (child_bs && child_bs->force_share) {
+ *nshared = BLK_PERM_ALL;
+ }
+}
+
/*
* Check whether permissions on this node can be changed in a way that
* @cumulative_perms and @cumulative_shared_perms are the new cumulative
/* Write permissions never work with read-only images */
if ((cumulative_perms & (BLK_PERM_WRITE | BLK_PERM_WRITE_UNCHANGED)) &&
- bdrv_is_read_only(bs))
+ !bdrv_is_writable(bs))
{
error_setg(errp, "Block node is read-only");
return -EPERM;
/* Check all children */
QLIST_FOREACH(c, &bs->children, next) {
uint64_t cur_perm, cur_shared;
- drv->bdrv_child_perm(bs, c, c->role,
- cumulative_perms, cumulative_shared_perms,
- &cur_perm, &cur_shared);
+ bdrv_child_perm(bs, c->bs, c, c->role,
+ cumulative_perms, cumulative_shared_perms,
+ &cur_perm, &cur_shared);
ret = bdrv_child_check_perm(c, cur_perm, cur_shared, ignore_children,
errp);
if (ret < 0) {
/* Update all children */
QLIST_FOREACH(c, &bs->children, next) {
uint64_t cur_perm, cur_shared;
- drv->bdrv_child_perm(bs, c, c->role,
- cumulative_perms, cumulative_shared_perms,
- &cur_perm, &cur_shared);
+ bdrv_child_perm(bs, c->bs, c, c->role,
+ cumulative_perms, cumulative_shared_perms,
+ &cur_perm, &cur_shared);
bdrv_child_set_perm(c, cur_perm, cur_shared);
}
}
return g_strdup("another user");
}
-static char *bdrv_perm_names(uint64_t perm)
+char *bdrv_perm_names(uint64_t perm)
{
struct perm_name {
uint64_t perm;
bdrv_filter_default_perms(bs, c, role, perm, shared, &perm, &shared);
/* Format drivers may touch metadata even if the guest doesn't write */
- if (!bdrv_is_read_only(bs)) {
+ if (bdrv_is_writable(bs)) {
perm |= BLK_PERM_WRITE | BLK_PERM_RESIZE;
}
BLK_PERM_WRITE_UNCHANGED;
}
+ if (bs->open_flags & BDRV_O_INACTIVE) {
+ shared |= BLK_PERM_WRITE | BLK_PERM_RESIZE;
+ }
+
*nperm = perm;
*nshared = shared;
}
assert(parent_bs->drv);
assert(bdrv_get_aio_context(parent_bs) == bdrv_get_aio_context(child_bs));
- parent_bs->drv->bdrv_child_perm(parent_bs, NULL, child_role,
- perm, shared_perm, &perm, &shared_perm);
+ bdrv_child_perm(parent_bs, child_bs, NULL, child_role,
+ perm, shared_perm, &perm, &shared_perm);
child = bdrv_root_attach_child(child_bs, child_name, child_role,
perm, shared_perm, parent_bs, errp);
}
if (bs->backing_format[0] != '\0' && !qdict_haskey(options, "driver")) {
- qdict_put(options, "driver", qstring_from_str(bs->backing_format));
+ qdict_put_str(options, "driver", bs->backing_format);
}
backing_hd = bdrv_open_inherit(*backing_filename ? backing_filename : NULL,
ret = -EINVAL;
goto free_exit;
}
+ bdrv_set_aio_context(backing_hd, bdrv_get_aio_context(bs));
/* Hook up the backing file link; drop our reference, bs owns the
* backing_hd reference now */
char *tmp_filename = g_malloc0(PATH_MAX + 1);
int64_t total_size;
QemuOpts *opts = NULL;
- BlockDriverState *bs_snapshot;
+ BlockDriverState *bs_snapshot = NULL;
Error *local_err = NULL;
int ret;
}
/* Prepare options QDict for the temporary file */
- qdict_put(snapshot_options, "file.driver",
- qstring_from_str("file"));
- qdict_put(snapshot_options, "file.filename",
- qstring_from_str(tmp_filename));
- qdict_put(snapshot_options, "driver",
- qstring_from_str("qcow2"));
+ qdict_put_str(snapshot_options, "file.driver", "file");
+ qdict_put_str(snapshot_options, "file.filename", tmp_filename);
+ qdict_put_str(snapshot_options, "driver", "qcow2");
bs_snapshot = bdrv_open(NULL, NULL, snapshot_options, flags, errp);
snapshot_options = NULL;
if (!bs_snapshot) {
- ret = -EINVAL;
goto out;
}
- /* bdrv_append() consumes a strong reference to bs_snapshot (i.e. it will
- * call bdrv_unref() on it), so in order to be able to return one, we have
- * to increase bs_snapshot's refcount here */
+ /* bdrv_append() consumes a strong reference to bs_snapshot
+ * (i.e. it will call bdrv_unref() on it) even on error, so in
+ * order to be able to return one, we have to increase
+ * bs_snapshot's refcount here */
bdrv_ref(bs_snapshot);
bdrv_append(bs_snapshot, bs, &local_err);
if (local_err) {
error_propagate(errp, local_err);
- ret = -EINVAL;
+ bs_snapshot = NULL;
goto out;
}
- g_free(tmp_filename);
- return bs_snapshot;
-
out:
QDECREF(snapshot_options);
g_free(tmp_filename);
- return NULL;
+ return bs_snapshot;
}
/*
goto fail;
}
- qdict_put(options, "file",
- qstring_from_str(bdrv_get_node_name(file_bs)));
+ qdict_put_str(options, "file", bdrv_get_node_name(file_bs));
}
}
* sure to update both bs->options (which has the full effective
* options for bs) and options (which has file.* already removed).
*/
- qdict_put(bs->options, "driver", qstring_from_str(drv->format_name));
- qdict_put(options, "driver", qstring_from_str(drv->format_name));
+ qdict_put_str(bs->options, "driver", drv->format_name);
+ qdict_put_str(options, "driver", drv->format_name);
} else if (!drv) {
error_setg(errp, "Must specify either driver or file");
goto fail;
goto close_and_fail;
}
- if (!bdrv_key_required(bs)) {
- bdrv_parent_cb_change_media(bs, true);
- } else if (!runstate_check(RUN_STATE_PRELAUNCH)
- && !runstate_check(RUN_STATE_INMIGRATE)
- && !runstate_check(RUN_STATE_PAUSED)) { /* HACK */
- error_setg(errp,
- "Guest must be stopped for opening of encrypted image");
- goto close_and_fail;
- }
+ bdrv_parent_cb_change_media(bs, true);
QDECREF(options);
* that they are checked at the end of this function. */
value = qemu_opt_get(opts, "node-name");
if (value) {
- qdict_put(reopen_state->options, "node-name", qstring_from_str(value));
+ qdict_put_str(reopen_state->options, "node-name", value);
}
value = qemu_opt_get(opts, "driver");
if (value) {
- qdict_put(reopen_state->options, "driver", qstring_from_str(value));
+ qdict_put_str(reopen_state->options, "driver", value);
}
/* If we are to stay read-only, do not allow permission change
void bdrv_reopen_commit(BDRVReopenState *reopen_state)
{
BlockDriver *drv;
+ BlockDriverState *bs;
+ bool old_can_write, new_can_write;
assert(reopen_state != NULL);
- drv = reopen_state->bs->drv;
+ bs = reopen_state->bs;
+ drv = bs->drv;
assert(drv != NULL);
+ old_can_write =
+ !bdrv_is_read_only(bs) && !(bdrv_get_flags(bs) & BDRV_O_INACTIVE);
+
/* If there are any driver level actions to take */
if (drv->bdrv_reopen_commit) {
drv->bdrv_reopen_commit(reopen_state);
}
/* set BDS specific flags now */
- QDECREF(reopen_state->bs->explicit_options);
+ QDECREF(bs->explicit_options);
- reopen_state->bs->explicit_options = reopen_state->explicit_options;
- reopen_state->bs->open_flags = reopen_state->flags;
- reopen_state->bs->read_only = !(reopen_state->flags & BDRV_O_RDWR);
+ bs->explicit_options = reopen_state->explicit_options;
+ bs->open_flags = reopen_state->flags;
+ bs->read_only = !(reopen_state->flags & BDRV_O_RDWR);
- bdrv_refresh_limits(reopen_state->bs, NULL);
+ bdrv_refresh_limits(bs, NULL);
+
+ new_can_write =
+ !bdrv_is_read_only(bs) && !(bdrv_get_flags(bs) & BDRV_O_INACTIVE);
+ if (!old_can_write && new_can_write && drv->bdrv_reopen_bitmaps_rw) {
+ Error *local_err = NULL;
+ if (drv->bdrv_reopen_bitmaps_rw(bs, &local_err) < 0) {
+ /* This is not fatal, bitmaps just left read-only, so all following
+ * writes will fail. User can remove read-only bitmaps to unblock
+ * writes.
+ */
+ error_reportf_err(local_err,
+ "%s: Failed to make dirty bitmaps writable: ",
+ bdrv_get_node_name(bs));
+ }
+ }
}
/*
bdrv_flush(bs);
bdrv_drain(bs); /* in case flush left pending I/O */
- bdrv_release_named_dirty_bitmaps(bs);
- assert(QLIST_EMPTY(&bs->dirty_bitmaps));
-
if (bs->drv) {
BdrvChild *child, *next;
g_free(bs->opaque);
bs->opaque = NULL;
- bs->copy_on_read = 0;
+ atomic_set(&bs->copy_on_read, 0);
bs->backing_file[0] = '\0';
bs->backing_format[0] = '\0';
bs->total_sectors = 0;
bs->encrypted = false;
- bs->valid_key = false;
bs->sg = false;
QDECREF(bs->options);
QDECREF(bs->explicit_options);
bs->full_open_options = NULL;
}
+ bdrv_release_named_dirty_bitmaps(bs);
+ assert(QLIST_EMPTY(&bs->dirty_bitmaps));
+
QLIST_FOREACH_SAFE(ban, &bs->aio_notifiers, list, ban_next) {
g_free(ban);
}
/**
* Truncate file to 'offset' bytes (needed only for file protocols)
*/
-int bdrv_truncate(BdrvChild *child, int64_t offset, Error **errp)
+int bdrv_truncate(BdrvChild *child, int64_t offset, PreallocMode prealloc,
+ Error **errp)
{
BlockDriverState *bs = child->bs;
BlockDriver *drv = bs->drv;
return -EACCES;
}
- ret = drv->bdrv_truncate(bs, offset, errp);
+ assert(!(bs->open_flags & BDRV_O_INACTIVE));
+
+ ret = drv->bdrv_truncate(bs, offset, prealloc, errp);
if (ret == 0) {
ret = refresh_total_sectors(bs, offset >> BDRV_SECTOR_BITS);
bdrv_dirty_bitmap_truncate(bs);
bdrv_parent_cb_resize(bs);
- ++bs->write_gen;
+ atomic_inc(&bs->write_gen);
}
return ret;
}
return -ENOTSUP;
}
+/*
+ * bdrv_measure:
+ * @drv: Format driver
+ * @opts: Creation options for new image
+ * @in_bs: Existing image containing data for new image (may be NULL)
+ * @errp: Error object
+ * Returns: A #BlockMeasureInfo (free using qapi_free_BlockMeasureInfo())
+ * or NULL on error
+ *
+ * Calculate file size required to create a new image.
+ *
+ * If @in_bs is given then space for allocated clusters and zero clusters
+ * from that image are included in the calculation. If @opts contains a
+ * backing file that is shared by @in_bs then backing clusters may be omitted
+ * from the calculation.
+ *
+ * If @in_bs is NULL then the calculation includes no allocated clusters
+ * unless a preallocation option is given in @opts.
+ *
+ * Note that @in_bs may use a different BlockDriver from @drv.
+ *
+ * If an error occurs the @errp pointer is set.
+ */
+BlockMeasureInfo *bdrv_measure(BlockDriver *drv, QemuOpts *opts,
+ BlockDriverState *in_bs, Error **errp)
+{
+ if (!drv->bdrv_measure) {
+ error_setg(errp, "Block driver '%s' does not support size measurement",
+ drv->format_name);
+ return NULL;
+ }
+
+ return drv->bdrv_measure(opts, in_bs, errp);
+}
+
/**
* Return number of sectors on success, -errno on error.
*/
return bs->encrypted;
}
-bool bdrv_key_required(BlockDriverState *bs)
-{
- BdrvChild *backing = bs->backing;
-
- if (backing && backing->bs->encrypted && !backing->bs->valid_key) {
- return true;
- }
- return (bs->encrypted && !bs->valid_key);
-}
-
-int bdrv_set_key(BlockDriverState *bs, const char *key)
-{
- int ret;
- if (bs->backing && bs->backing->bs->encrypted) {
- ret = bdrv_set_key(bs->backing->bs, key);
- if (ret < 0)
- return ret;
- if (!bs->encrypted)
- return 0;
- }
- if (!bs->encrypted) {
- return -EINVAL;
- } else if (!bs->drv || !bs->drv->bdrv_set_key) {
- return -ENOMEDIUM;
- }
- ret = bs->drv->bdrv_set_key(bs, key);
- if (ret < 0) {
- bs->valid_key = false;
- } else if (!bs->valid_key) {
- /* call the change callback now, we skipped it on open */
- bs->valid_key = true;
- bdrv_parent_cb_change_media(bs, true);
- }
- return ret;
-}
-
-/*
- * Provide an encryption key for @bs.
- * If @key is non-null:
- * If @bs is not encrypted, fail.
- * Else if the key is invalid, fail.
- * Else set @bs's key to @key, replacing the existing key, if any.
- * If @key is null:
- * If @bs is encrypted and still lacks a key, fail.
- * Else do nothing.
- * On failure, store an error object through @errp if non-null.
- */
-void bdrv_add_key(BlockDriverState *bs, const char *key, Error **errp)
-{
- if (key) {
- if (!bdrv_is_encrypted(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_setg(errp, QERR_INVALID_PASSWORD);
- }
- } else {
- if (bdrv_key_required(bs)) {
- error_set(errp, ERROR_CLASS_DEVICE_ENCRYPTED,
- "'%s' (%s) is encrypted",
- bdrv_get_device_or_node_name(bs),
- bdrv_get_encrypted_filename(bs));
- }
- }
-}
-
const char *bdrv_get_format_name(BlockDriverState *bs)
{
return bs->drv ? bs->drv->format_name : NULL;
return retval;
}
-int bdrv_get_backing_file_depth(BlockDriverState *bs)
-{
- if (!bs->drv) {
- return 0;
- }
-
- if (!bs->backing) {
- return 0;
- }
-
- return 1 + bdrv_get_backing_file_depth(bs->backing->bs);
-}
-
void bdrv_init(void)
{
module_call_init(MODULE_INIT_BLOCK);
void bdrv_invalidate_cache(BlockDriverState *bs, Error **errp)
{
- BdrvChild *child;
+ BdrvChild *child, *parent;
+ uint64_t perm, shared_perm;
Error *local_err = NULL;
int ret;
error_setg_errno(errp, -ret, "Could not refresh total sector count");
return;
}
+
+ /* Update permissions, they may differ for inactive nodes */
+ bdrv_get_cumulative_perm(bs, &perm, &shared_perm);
+ ret = bdrv_check_perm(bs, perm, shared_perm, NULL, &local_err);
+ if (ret < 0) {
+ bs->open_flags |= BDRV_O_INACTIVE;
+ error_propagate(errp, local_err);
+ return;
+ }
+ bdrv_set_perm(bs, perm, shared_perm);
+
+ QLIST_FOREACH(parent, &bs->parents, next_parent) {
+ if (parent->role->activate) {
+ parent->role->activate(parent, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+ }
+ }
}
void bdrv_invalidate_cache_all(Error **errp)
static int bdrv_inactivate_recurse(BlockDriverState *bs,
bool setting_flag)
{
- BdrvChild *child;
+ BdrvChild *child, *parent;
int ret;
if (!setting_flag && bs->drv->bdrv_inactivate) {
}
}
+ if (setting_flag) {
+ uint64_t perm, shared_perm;
+
+ bs->open_flags |= BDRV_O_INACTIVE;
+
+ QLIST_FOREACH(parent, &bs->parents, next_parent) {
+ if (parent->role->inactivate) {
+ ret = parent->role->inactivate(parent);
+ if (ret < 0) {
+ bs->open_flags &= ~BDRV_O_INACTIVE;
+ return ret;
+ }
+ }
+ }
+
+ /* Update permissions, they may differ for inactive nodes */
+ bdrv_get_cumulative_perm(bs, &perm, &shared_perm);
+ bdrv_check_perm(bs, perm, shared_perm, NULL, &error_abort);
+ bdrv_set_perm(bs, perm, shared_perm);
+ }
+
QLIST_FOREACH(child, &bs->children, next) {
ret = bdrv_inactivate_recurse(child->bs, setting_flag);
if (ret < 0) {
}
}
- if (setting_flag) {
- bs->open_flags |= BDRV_O_INACTIVE;
- }
+ /* At this point persistent bitmaps should be already stored by the format
+ * driver */
+ bdrv_release_persistent_dirty_bitmaps(bs);
+
return 0;
}
assert((int) op >= 0 && op < BLOCK_OP_TYPE_MAX);
if (!QLIST_EMPTY(&bs->op_blockers[op])) {
blocker = QLIST_FIRST(&bs->op_blockers[op]);
- if (errp) {
- *errp = error_copy(blocker->reason);
- error_prepend(errp, "Node '%s' is busy: ",
- bdrv_get_device_or_node_name(bs));
- }
+ error_propagate(errp, error_copy(blocker->reason));
+ error_prepend(errp, "Node '%s' is busy: ",
+ bdrv_get_device_or_node_name(bs));
return true;
}
return false;
backing_fmt = qemu_opt_get(opts, BLOCK_OPT_BACKING_FMT);
- // 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
+ /* The size for the image must always be specified, unless we have a backing
+ * file and we have not been forbidden from opening it. */
size = qemu_opt_get_size(opts, BLOCK_OPT_SIZE, 0);
- if (size == -1) {
- if (backing_file) {
- BlockDriverState *bs;
- 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,
- &local_err);
- if (local_err) {
- g_free(full_backing);
- goto out;
- }
+ if (backing_file && !(flags & BDRV_O_NO_BACKING)) {
+ BlockDriverState *bs;
+ char *full_backing = g_new0(char, PATH_MAX);
+ int back_flags;
+ QDict *backing_options = NULL;
+
+ bdrv_get_full_backing_filename_from_filename(filename, backing_file,
+ full_backing, PATH_MAX,
+ &local_err);
+ if (local_err) {
+ g_free(full_backing);
+ goto out;
+ }
- /* backing files always opened read-only */
- back_flags = flags;
- back_flags &= ~(BDRV_O_RDWR | BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING);
+ /* backing files always opened read-only */
+ back_flags = flags;
+ back_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));
- }
+ if (backing_fmt) {
+ backing_options = qdict_new();
+ qdict_put_str(backing_options, "driver", backing_fmt);
+ }
- bs = bdrv_open(full_backing, NULL, backing_options, back_flags,
- &local_err);
- g_free(full_backing);
- if (!bs) {
- goto out;
- }
- size = bdrv_getlength(bs);
- if (size < 0) {
- error_setg_errno(errp, -size, "Could not get size of '%s'",
- backing_file);
- bdrv_unref(bs);
- goto out;
+ bs = bdrv_open(full_backing, NULL, backing_options, back_flags,
+ &local_err);
+ g_free(full_backing);
+ if (!bs && size != -1) {
+ /* Couldn't open BS, but we have a size, so it's nonfatal */
+ warn_reportf_err(local_err,
+ "Could not verify backing image. "
+ "This may become an error in future versions.\n");
+ local_err = NULL;
+ } else if (!bs) {
+ /* Couldn't open bs, do not have size */
+ error_append_hint(&local_err,
+ "Could not open backing image to determine size.\n");
+ goto out;
+ } else {
+ if (size == -1) {
+ /* Opened BS, have no size */
+ size = bdrv_getlength(bs);
+ if (size < 0) {
+ error_setg_errno(errp, -size, "Could not get size of '%s'",
+ backing_file);
+ bdrv_unref(bs);
+ goto out;
+ }
+ qemu_opt_set_number(opts, BLOCK_OPT_SIZE, size, &error_abort);
}
-
- qemu_opt_set_number(opts, BLOCK_OPT_SIZE, size, &error_abort);
-
bdrv_unref(bs);
- } else {
- error_setg(errp, "Image creation needs a size parameter");
- goto out;
}
+ } /* (backing_file && !(flags & BDRV_O_NO_BACKING)) */
+
+ if (size == -1) {
+ error_setg(errp, "Image creation needs a size parameter");
+ goto out;
}
if (!quiet) {
* contain a representation of the filename, therefore the following
* suffices without querying the (exact_)filename of this BDS. */
if (bs->file->bs->full_open_options) {
- qdict_put_obj(opts, "driver",
- QOBJECT(qstring_from_str(drv->format_name)));
+ qdict_put_str(opts, "driver", drv->format_name);
QINCREF(bs->file->bs->full_open_options);
- qdict_put_obj(opts, "file",
- QOBJECT(bs->file->bs->full_open_options));
+ qdict_put(opts, "file", bs->file->bs->full_open_options);
bs->full_open_options = opts;
} else {
opts = qdict_new();
append_open_options(opts, bs);
- qdict_put_obj(opts, "driver",
- QOBJECT(qstring_from_str(drv->format_name)));
+ qdict_put_str(opts, "driver", drv->format_name);
if (bs->exact_filename[0]) {
/* This may not work for all block protocol drivers (some may
* needs some special format of the options QDict, it needs to
* implement the driver-specific bdrv_refresh_filename() function.
*/
- qdict_put_obj(opts, "filename",
- QOBJECT(qstring_from_str(bs->exact_filename)));
+ qdict_put_str(opts, "filename", bs->exact_filename);
}
bs->full_open_options = opts;
parent_bs->drv->bdrv_del_child(parent_bs, child, errp);
}
+
+bool bdrv_can_store_new_dirty_bitmap(BlockDriverState *bs, const char *name,
+ uint32_t granularity, Error **errp)
+{
+ BlockDriver *drv = bs->drv;
+
+ if (!drv) {
+ error_setg_errno(errp, ENOMEDIUM,
+ "Can't store persistent bitmaps to %s",
+ bdrv_get_device_or_node_name(bs));
+ return false;
+ }
+
+ if (!drv->bdrv_can_store_new_dirty_bitmap) {
+ error_setg_errno(errp, ENOTSUP,
+ "Can't store persistent bitmaps to %s",
+ bdrv_get_device_or_node_name(bs));
+ return false;
+ }
+
+ return drv->bdrv_can_store_new_dirty_bitmap(bs, name, granularity, errp);
+}