aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--MAINTAINERS22
-rw-r--r--block.c597
-rw-r--r--block/blkdebug.c70
-rw-r--r--block/blklogwrites.c33
-rw-r--r--block/blkverify.c29
-rw-r--r--block/block-backend.c4
-rw-r--r--block/commit.c7
-rw-r--r--block/crypto.c8
-rw-r--r--block/curl.c55
-rw-r--r--block/gluster.c19
-rw-r--r--block/iscsi.c18
-rw-r--r--block/mirror.c3
-rw-r--r--block/nbd-client.c36
-rw-r--r--block/nbd-client.h1
-rw-r--r--block/nbd.c46
-rw-r--r--block/nfs.c54
-rw-r--r--block/null.c32
-rw-r--r--block/nvme.c29
-rw-r--r--block/qapi.c16
-rw-r--r--block/qcow.c14
-rw-r--r--block/qcow2-cluster.c3
-rw-r--r--block/qcow2-snapshot.c5
-rw-r--r--block/qcow2.c89
-rw-r--r--block/qed.c7
-rw-r--r--block/quorum.c71
-rw-r--r--block/raw-format.c11
-rw-r--r--block/rbd.c14
-rw-r--r--block/replication.c10
-rw-r--r--block/sheepdog.c12
-rw-r--r--block/snapshot.c25
-rw-r--r--block/ssh.c12
-rw-r--r--block/throttle.c7
-rw-r--r--block/vhdx-log.c1
-rw-r--r--block/vmdk.c46
-rw-r--r--block/vpc.c7
-rw-r--r--block/vvfat.c12
-rw-r--r--block/vxhs.c11
-rw-r--r--blockdev.c8
-rw-r--r--hmp-commands.hx32
-rw-r--r--hw/block/virtio-blk.c4
-rw-r--r--include/block/block.h16
-rw-r--r--include/block/block_int.h53
-rw-r--r--include/block/nbd.h3
-rw-r--r--include/block/snapshot.h3
-rw-r--r--include/io/channel.h9
-rw-r--r--include/sysemu/block-backend.h2
-rw-r--r--io/channel.c22
-rw-r--r--nbd/client.c52
-rw-r--r--nbd/nbd-internal.h19
-rw-r--r--qemu-img.c64
-rw-r--r--scripts/qemu.py5
-rw-r--r--tests/Makefile.include2
-rwxr-xr-xtests/qemu-iotests/0452
-rw-r--r--tests/qemu-iotests/051.out8
-rw-r--r--tests/qemu-iotests/051.pc.out8
-rwxr-xr-xtests/qemu-iotests/11029
-rw-r--r--tests/qemu-iotests/110.out9
-rwxr-xr-xtests/qemu-iotests/1788
-rw-r--r--tests/qemu-iotests/178.out.qcow224
-rw-r--r--tests/qemu-iotests/206.out56
-rwxr-xr-xtests/qemu-iotests/20710
-rw-r--r--tests/qemu-iotests/207.out18
-rwxr-xr-xtests/qemu-iotests/2105
-rw-r--r--tests/qemu-iotests/210.out28
-rwxr-xr-xtests/qemu-iotests/2119
-rw-r--r--tests/qemu-iotests/211.out26
-rwxr-xr-xtests/qemu-iotests/2125
-rw-r--r--tests/qemu-iotests/212.out44
-rwxr-xr-xtests/qemu-iotests/2135
-rw-r--r--tests/qemu-iotests/213.out46
-rwxr-xr-xtests/qemu-iotests/224139
-rw-r--r--tests/qemu-iotests/224.out18
-rwxr-xr-xtests/qemu-iotests/228239
-rw-r--r--tests/qemu-iotests/228.out84
-rwxr-xr-xtests/qemu-iotests/2325
-rwxr-xr-xtests/qemu-iotests/2377
-rw-r--r--tests/qemu-iotests/237.out54
-rw-r--r--tests/qemu-iotests/common.rc1
-rw-r--r--tests/qemu-iotests/group2
-rw-r--r--tests/qemu-iotests/iotests.py36
-rw-r--r--tests/test-bdrv-drain.c32
-rw-r--r--tests/test-bdrv-graph-mod.c198
-rw-r--r--util/aio-posix.c3
83 files changed, 2191 insertions, 697 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index 7be8c57..8f9f9d7 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1698,7 +1698,7 @@ F: include/scsi/*
F: scsi/*
Block Jobs
-M: Jeff Cody <jcody@redhat.com>
+M: John Snow <jsnow@redhat.com>
L: qemu-block@nongnu.org
S: Supported
F: blockjob.c
@@ -1711,7 +1711,7 @@ F: block/commit.c
F: block/stream.c
F: block/mirror.c
F: qapi/job.json
-T: git https://github.com/codyprime/qemu-kvm-jtc.git block
+T: git https://github.com/jnsnow/qemu.git jobs
Block QAPI, monitor, command line
M: Markus Armbruster <armbru@redhat.com>
@@ -2261,26 +2261,22 @@ F: block/vmdk.c
RBD
M: Josh Durgin <jdurgin@redhat.com>
-M: Jeff Cody <jcody@redhat.com>
L: qemu-block@nongnu.org
S: Supported
F: block/rbd.c
-T: git https://github.com/codyprime/qemu-kvm-jtc.git block
Sheepdog
M: Liu Yuan <namei.unix@gmail.com>
-M: Jeff Cody <jcody@redhat.com>
L: qemu-block@nongnu.org
-S: Supported
+L: sheepdog@lists.wpkg.org
+S: Odd Fixes
F: block/sheepdog.c
-T: git https://github.com/codyprime/qemu-kvm-jtc.git block
VHDX
-M: Jeff Cody <jcody@redhat.com>
+M: Jeff Cody <codyprime@gmail.com>
L: qemu-block@nongnu.org
S: Supported
F: block/vhdx*
-T: git https://github.com/codyprime/qemu-kvm-jtc.git block
VDI
M: Stefan Weil <sw@weilnetz.de>
@@ -2310,34 +2306,26 @@ F: docs/interop/nbd.txt
T: git https://repo.or.cz/qemu/ericb.git nbd
NFS
-M: Jeff Cody <jcody@redhat.com>
M: Peter Lieven <pl@kamp.de>
L: qemu-block@nongnu.org
S: Maintained
F: block/nfs.c
-T: git https://github.com/codyprime/qemu-kvm-jtc.git block
SSH
M: Richard W.M. Jones <rjones@redhat.com>
-M: Jeff Cody <jcody@redhat.com>
L: qemu-block@nongnu.org
S: Supported
F: block/ssh.c
-T: git https://github.com/codyprime/qemu-kvm-jtc.git block
CURL
-M: Jeff Cody <jcody@redhat.com>
L: qemu-block@nongnu.org
S: Supported
F: block/curl.c
-T: git https://github.com/codyprime/qemu-kvm-jtc.git block
GLUSTER
-M: Jeff Cody <jcody@redhat.com>
L: qemu-block@nongnu.org
S: Supported
F: block/gluster.c
-T: git https://github.com/codyprime/qemu-kvm-jtc.git block
Null Block Driver
M: Fam Zheng <fam@euphon.net>
diff --git a/block.c b/block.c
index 4ad0e90..35e78e2 100644
--- a/block.c
+++ b/block.c
@@ -152,53 +152,53 @@ int path_is_absolute(const char *path)
#endif
}
-/* if filename is absolute, just copy it to dest. Otherwise, build a
+/* if filename is absolute, just return its duplicate. Otherwise, build a
path to it by considering it is relative to base_path. URL are
supported. */
-void path_combine(char *dest, int dest_size,
- const char *base_path,
- const char *filename)
+char *path_combine(const char *base_path, const char *filename)
{
+ const char *protocol_stripped = NULL;
const char *p, *p1;
+ char *result;
int len;
- if (dest_size <= 0)
- return;
if (path_is_absolute(filename)) {
- pstrcpy(dest, dest_size, filename);
- } else {
- const char *protocol_stripped = NULL;
+ return g_strdup(filename);
+ }
- if (path_has_protocol(base_path)) {
- protocol_stripped = strchr(base_path, ':');
- if (protocol_stripped) {
- protocol_stripped++;
- }
+ if (path_has_protocol(base_path)) {
+ protocol_stripped = strchr(base_path, ':');
+ if (protocol_stripped) {
+ protocol_stripped++;
}
- p = protocol_stripped ?: base_path;
+ }
+ p = protocol_stripped ?: base_path;
- p1 = strrchr(base_path, '/');
+ p1 = strrchr(base_path, '/');
#ifdef _WIN32
- {
- const char *p2;
- p2 = strrchr(base_path, '\\');
- if (!p1 || p2 > p1)
- p1 = p2;
+ {
+ const char *p2;
+ p2 = strrchr(base_path, '\\');
+ if (!p1 || p2 > p1) {
+ p1 = p2;
}
+ }
#endif
- if (p1)
- p1++;
- else
- p1 = base_path;
- if (p1 > p)
- p = p1;
- len = p - base_path;
- if (len > dest_size - 1)
- len = dest_size - 1;
- memcpy(dest, base_path, len);
- dest[len] = '\0';
- pstrcat(dest, dest_size, filename);
+ if (p1) {
+ p1++;
+ } else {
+ p1 = base_path;
}
+ if (p1 > p) {
+ p = p1;
+ }
+ len = p - base_path;
+
+ result = g_malloc(len + strlen(filename) + 1);
+ memcpy(result, base_path, len);
+ strcpy(result + len, filename);
+
+ return result;
}
/*
@@ -303,30 +303,61 @@ fail:
return -EACCES;
}
-void bdrv_get_full_backing_filename_from_filename(const char *backed,
- const char *backing,
- char *dest, size_t sz,
- Error **errp)
+/*
+ * If @backing is empty, this function returns NULL without setting
+ * @errp. In all other cases, NULL will only be returned with @errp
+ * set.
+ *
+ * Therefore, a return value of NULL without @errp set means that
+ * there is no backing file; if @errp is set, there is one but its
+ * absolute filename cannot be generated.
+ */
+char *bdrv_get_full_backing_filename_from_filename(const char *backed,
+ const char *backing,
+ Error **errp)
{
- if (backing[0] == '\0' || path_has_protocol(backing) ||
- path_is_absolute(backing))
- {
- pstrcpy(dest, sz, backing);
+ if (backing[0] == '\0') {
+ return NULL;
+ } else if (path_has_protocol(backing) || path_is_absolute(backing)) {
+ return g_strdup(backing);
} else if (backed[0] == '\0' || strstart(backed, "json:", NULL)) {
error_setg(errp, "Cannot use relative backing file names for '%s'",
backed);
+ return NULL;
} else {
- path_combine(dest, sz, backed, backing);
+ return path_combine(backed, backing);
}
}
-void bdrv_get_full_backing_filename(BlockDriverState *bs, char *dest, size_t sz,
- Error **errp)
+/*
+ * If @filename is empty or NULL, this function returns NULL without
+ * setting @errp. In all other cases, NULL will only be returned with
+ * @errp set.
+ */
+static char *bdrv_make_absolute_filename(BlockDriverState *relative_to,
+ const char *filename, Error **errp)
{
- char *backed = bs->exact_filename[0] ? bs->exact_filename : bs->filename;
+ char *dir, *full_name;
- bdrv_get_full_backing_filename_from_filename(backed, bs->backing_file,
- dest, sz, errp);
+ if (!filename || filename[0] == '\0') {
+ return NULL;
+ } else if (path_has_protocol(filename) || path_is_absolute(filename)) {
+ return g_strdup(filename);
+ }
+
+ dir = bdrv_dirname(relative_to, errp);
+ if (!dir) {
+ return NULL;
+ }
+
+ full_name = g_strconcat(dir, filename, NULL);
+ g_free(dir);
+ return full_name;
+}
+
+char *bdrv_get_full_backing_filename(BlockDriverState *bs, Error **errp)
+{
+ return bdrv_make_absolute_filename(bs, bs->backing_file, errp);
}
void bdrv_register(BlockDriver *bdrv)
@@ -1004,6 +1035,8 @@ static void bdrv_backing_attach(BdrvChild *c)
"node is used as backing hd of '%s'",
bdrv_get_device_or_node_name(parent));
+ bdrv_refresh_filename(backing_hd);
+
parent->open_flags &= ~BDRV_O_NO_BACKING;
pstrcpy(parent->backing_file, sizeof(parent->backing_file),
backing_hd->filename);
@@ -1413,6 +1446,7 @@ static int bdrv_open_common(BlockDriverState *bs, BlockBackend *file,
}
if (file != NULL) {
+ bdrv_refresh_filename(blk_bs(file));
filename = blk_bs(file)->filename;
} else {
/*
@@ -1954,13 +1988,32 @@ static int bdrv_child_check_perm(BdrvChild *c, BlockReopenQueue *q,
ret = bdrv_check_update_perm(c->bs, q, perm, shared, ignore_children, errp);
g_slist_free(ignore_children);
- return ret;
+ if (ret < 0) {
+ return ret;
+ }
+
+ if (!c->has_backup_perm) {
+ c->has_backup_perm = true;
+ c->backup_perm = c->perm;
+ c->backup_shared_perm = c->shared_perm;
+ }
+ /*
+ * Note: it's OK if c->has_backup_perm was already set, as we can find the
+ * same child twice during check_perm procedure
+ */
+
+ c->perm = perm;
+ c->shared_perm = shared;
+
+ return 0;
}
static void bdrv_child_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared)
{
uint64_t cumulative_perms, cumulative_shared_perms;
+ c->has_backup_perm = false;
+
c->perm = perm;
c->shared_perm = shared;
@@ -1971,6 +2024,12 @@ static void bdrv_child_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared)
static void bdrv_child_abort_perm_update(BdrvChild *c)
{
+ if (c->has_backup_perm) {
+ c->perm = c->backup_perm;
+ c->shared_perm = c->backup_shared_perm;
+ c->has_backup_perm = false;
+ }
+
bdrv_abort_perm_update(c->bs);
}
@@ -2309,8 +2368,6 @@ void bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd,
bdrv_unref(backing_hd);
}
- bdrv_refresh_filename(bs);
-
out:
bdrv_refresh_limits(bs, NULL);
}
@@ -2328,10 +2385,11 @@ out:
int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options,
const char *bdref_key, Error **errp)
{
- char *backing_filename = g_malloc0(PATH_MAX);
+ char *backing_filename = NULL;
char *bdref_key_dot;
const char *reference = NULL;
int ret = 0;
+ bool implicit_backing = false;
BlockDriverState *backing_hd;
QDict *options;
QDict *tmp_parent_options = NULL;
@@ -2362,13 +2420,22 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options,
*/
reference = qdict_get_try_str(parent_options, bdref_key);
if (reference || qdict_haskey(options, "file.filename")) {
- backing_filename[0] = '\0';
+ /* keep backing_filename NULL */
} else if (bs->backing_file[0] == '\0' && qdict_size(options) == 0) {
qobject_unref(options);
goto free_exit;
} else {
- bdrv_get_full_backing_filename(bs, backing_filename, PATH_MAX,
- &local_err);
+ if (qdict_size(options) == 0) {
+ /* If the user specifies options that do not modify the
+ * backing file's behavior, we might still consider it the
+ * implicit backing file. But it's easier this way, and
+ * just specifying some of the backing BDS's options is
+ * only possible with -drive anyway (otherwise the QAPI
+ * schema forces the user to specify everything). */
+ implicit_backing = !strcmp(bs->auto_backing_file, bs->backing_file);
+ }
+
+ backing_filename = bdrv_get_full_backing_filename(bs, &local_err);
if (local_err) {
ret = -EINVAL;
error_propagate(errp, local_err);
@@ -2389,9 +2456,8 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options,
qdict_put_str(options, "driver", bs->backing_format);
}
- backing_hd = bdrv_open_inherit(*backing_filename ? backing_filename : NULL,
- reference, options, 0, bs, &child_backing,
- errp);
+ backing_hd = bdrv_open_inherit(backing_filename, reference, options, 0, bs,
+ &child_backing, errp);
if (!backing_hd) {
bs->open_flags |= BDRV_O_NO_BACKING;
error_prepend(errp, "Could not open backing file: ");
@@ -2400,6 +2466,12 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options,
}
bdrv_set_aio_context(backing_hd, bdrv_get_aio_context(bs));
+ if (implicit_backing) {
+ bdrv_refresh_filename(backing_hd);
+ pstrcpy(bs->auto_backing_file, sizeof(bs->auto_backing_file),
+ backing_hd->filename);
+ }
+
/* Hook up the backing file link; drop our reference, bs owns the
* backing_hd reference now */
bdrv_set_backing_hd(bs, backing_hd, &local_err);
@@ -2839,8 +2911,6 @@ static BlockDriverState *bdrv_open_inherit(const char *filename,
g_free(child_key_dot);
}
- bdrv_refresh_filename(bs);
-
/* Check if any unknown options were used */
if (qdict_size(options) != 0) {
const QDictEntry *entry = qdict_first(options);
@@ -3285,6 +3355,7 @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue,
if (local_err != NULL) {
error_propagate(errp, local_err);
} else {
+ bdrv_refresh_filename(reopen_state->bs);
error_setg(errp, "failed while preparing to reopen image '%s'",
reopen_state->bs->filename);
}
@@ -3542,7 +3613,9 @@ void bdrv_close_all(void)
static bool should_update_child(BdrvChild *c, BlockDriverState *to)
{
- BdrvChild *to_c;
+ GQueue *queue;
+ GHashTable *found;
+ bool ret;
if (c->role->stay_at_node) {
return false;
@@ -3578,14 +3651,43 @@ static bool should_update_child(BdrvChild *c, BlockDriverState *to)
* if A is a child of B, that means we cannot replace A by B there
* because that would create a loop. Silently detaching A from B
* is also not really an option. So overall just leaving A in
- * place there is the most sensible choice. */
- QLIST_FOREACH(to_c, &to->children, next) {
- if (to_c == c) {
- return false;
+ * place there is the most sensible choice.
+ *
+ * We would also create a loop in any cases where @c is only
+ * indirectly referenced by @to. Prevent this by returning false
+ * if @c is found (by breadth-first search) anywhere in the whole
+ * subtree of @to.
+ */
+
+ ret = true;
+ found = g_hash_table_new(NULL, NULL);
+ g_hash_table_add(found, to);
+ queue = g_queue_new();
+ g_queue_push_tail(queue, to);
+
+ while (!g_queue_is_empty(queue)) {
+ BlockDriverState *v = g_queue_pop_head(queue);
+ BdrvChild *c2;
+
+ QLIST_FOREACH(c2, &v->children, next) {
+ if (c2 == c) {
+ ret = false;
+ break;
+ }
+
+ if (g_hash_table_contains(found, c2->bs)) {
+ continue;
+ }
+
+ g_queue_push_tail(queue, c2->bs);
+ g_hash_table_add(found, c2->bs);
}
}
- return true;
+ g_queue_free(queue);
+ g_hash_table_destroy(found);
+
+ return ret;
}
void bdrv_replace_node(BlockDriverState *from, BlockDriverState *to,
@@ -3789,6 +3891,8 @@ int bdrv_change_backing_file(BlockDriverState *bs,
if (ret == 0) {
pstrcpy(bs->backing_file, sizeof(bs->backing_file), backing_file ?: "");
pstrcpy(bs->backing_format, sizeof(bs->backing_format), backing_fmt ?: "");
+ pstrcpy(bs->auto_backing_file, sizeof(bs->auto_backing_file),
+ backing_file ?: "");
}
return ret;
}
@@ -3881,7 +3985,10 @@ int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base,
/* success - we can delete the intermediate states, and link top->base */
/* TODO Check graph modification op blockers (BLK_PERM_GRAPH_MOD) once
* we've figured out how they should work. */
- backing_file_str = backing_file_str ? backing_file_str : base->filename;
+ if (!backing_file_str) {
+ bdrv_refresh_filename(base);
+ backing_file_str = base->filename;
+ }
QLIST_FOREACH_SAFE(c, &top->parents, next_parent, next) {
/* Check whether we are allowed to switch c from top to base */
@@ -4429,16 +4536,6 @@ bool bdrv_can_write_zeroes_with_unmap(BlockDriverState *bs)
return bs->supported_zero_flags & BDRV_REQ_MAY_UNMAP;
}
-const char *bdrv_get_encrypted_filename(BlockDriverState *bs)
-{
- if (bs->backing && bs->backing->bs->encrypted)
- return bs->backing_file;
- else if (bs->encrypted)
- return bs->filename;
- else
- return NULL;
-}
-
void bdrv_get_backing_filename(BlockDriverState *bs,
char *filename, int filename_size)
{
@@ -4547,7 +4644,6 @@ BlockDriverState *bdrv_find_backing_image(BlockDriverState *bs,
int is_protocol = 0;
BlockDriverState *curr_bs = NULL;
BlockDriverState *retval = NULL;
- Error *local_error = NULL;
if (!bs || !bs->drv || !backing_file) {
return NULL;
@@ -4555,7 +4651,6 @@ BlockDriverState *bdrv_find_backing_image(BlockDriverState *bs,
filename_full = g_malloc(PATH_MAX);
backing_file_full = g_malloc(PATH_MAX);
- filename_tmp = g_malloc(PATH_MAX);
is_protocol = path_has_protocol(backing_file);
@@ -4564,41 +4659,43 @@ BlockDriverState *bdrv_find_backing_image(BlockDriverState *bs,
/* If either of the filename paths is actually a protocol, then
* compare unmodified paths; otherwise make paths relative */
if (is_protocol || path_has_protocol(curr_bs->backing_file)) {
+ char *backing_file_full_ret;
+
if (strcmp(backing_file, curr_bs->backing_file) == 0) {
retval = curr_bs->backing->bs;
break;
}
/* Also check against the full backing filename for the image */
- bdrv_get_full_backing_filename(curr_bs, backing_file_full, PATH_MAX,
- &local_error);
- if (local_error == NULL) {
- if (strcmp(backing_file, backing_file_full) == 0) {
+ backing_file_full_ret = bdrv_get_full_backing_filename(curr_bs,
+ NULL);
+ if (backing_file_full_ret) {
+ bool equal = strcmp(backing_file, backing_file_full_ret) == 0;
+ g_free(backing_file_full_ret);
+ if (equal) {
retval = curr_bs->backing->bs;
break;
}
- } else {
- error_free(local_error);
- local_error = NULL;
}
} else {
/* If not an absolute filename path, make it relative to the current
* image's filename path */
- path_combine(filename_tmp, PATH_MAX, curr_bs->filename,
- backing_file);
-
- /* We are going to compare absolute pathnames */
- if (!realpath(filename_tmp, filename_full)) {
+ filename_tmp = bdrv_make_absolute_filename(curr_bs, backing_file,
+ NULL);
+ /* We are going to compare canonicalized absolute pathnames */
+ if (!filename_tmp || !realpath(filename_tmp, filename_full)) {
+ g_free(filename_tmp);
continue;
}
+ g_free(filename_tmp);
/* We need to make sure the backing filename we are comparing against
* is relative to the current image filename (or absolute) */
- path_combine(filename_tmp, PATH_MAX, curr_bs->filename,
- curr_bs->backing_file);
-
- if (!realpath(filename_tmp, backing_file_full)) {
+ filename_tmp = bdrv_get_full_backing_filename(curr_bs, NULL);
+ if (!filename_tmp || !realpath(filename_tmp, backing_file_full)) {
+ g_free(filename_tmp);
continue;
}
+ g_free(filename_tmp);
if (strcmp(backing_file_full, filename_full) == 0) {
retval = curr_bs->backing->bs;
@@ -4609,7 +4706,6 @@ BlockDriverState *bdrv_find_backing_image(BlockDriverState *bs,
g_free(filename_full);
g_free(backing_file_full);
- g_free(filename_tmp);
return retval;
}
@@ -5096,17 +5192,17 @@ void bdrv_img_create(const char *filename, const char *fmt,
size = qemu_opt_get_size(opts, BLOCK_OPT_SIZE, img_size);
if (backing_file && !(flags & BDRV_O_NO_BACKING)) {
BlockDriverState *bs;
- char *full_backing = g_new0(char, PATH_MAX);
+ char *full_backing;
int back_flags;
QDict *backing_options = NULL;
- bdrv_get_full_backing_filename_from_filename(filename, backing_file,
- full_backing, PATH_MAX,
- &local_err);
+ full_backing =
+ bdrv_get_full_backing_filename_from_filename(filename, backing_file,
+ &local_err);
if (local_err) {
- g_free(full_backing);
goto out;
}
+ assert(full_backing);
/* backing files always opened read-only */
back_flags = flags;
@@ -5227,6 +5323,9 @@ void bdrv_detach_aio_context(BlockDriverState *bs)
bdrv_detach_aio_context(child->bs);
}
+ if (bs->quiesce_counter) {
+ aio_enable_external(bs->aio_context);
+ }
bs->aio_context = NULL;
}
@@ -5240,6 +5339,10 @@ void bdrv_attach_aio_context(BlockDriverState *bs,
return;
}
+ if (bs->quiesce_counter) {
+ aio_disable_external(new_context);
+ }
+
bs->aio_context = new_context;
QLIST_FOREACH(child, &bs->children, next) {
@@ -5261,18 +5364,16 @@ void bdrv_attach_aio_context(BlockDriverState *bs,
bs->walking_aio_notifiers = false;
}
+/* The caller must own the AioContext lock for the old AioContext of bs, but it
+ * must not own the AioContext lock for new_context (unless new_context is
+ * the same as the current context of bs). */
void bdrv_set_aio_context(BlockDriverState *bs, AioContext *new_context)
{
- AioContext *ctx = bdrv_get_aio_context(bs);
-
- aio_disable_external(ctx);
- bdrv_parent_drained_begin(bs, NULL, false);
- bdrv_drain(bs); /* ensure there are no in-flight requests */
-
- while (aio_poll(ctx, false)) {
- /* wait for all bottom halves to execute */
+ if (bdrv_get_aio_context(bs) == new_context) {
+ return;
}
+ bdrv_drained_begin(bs);
bdrv_detach_aio_context(bs);
/* This function executes in the old AioContext so acquire the new one in
@@ -5280,8 +5381,7 @@ void bdrv_set_aio_context(BlockDriverState *bs, AioContext *new_context)
*/
aio_context_acquire(new_context);
bdrv_attach_aio_context(bs, new_context);
- bdrv_parent_drained_end(bs, NULL, false);
- aio_enable_external(ctx);
+ bdrv_drained_end(bs);
aio_context_release(new_context);
}
@@ -5435,33 +5535,113 @@ out:
return to_replace_bs;
}
-static bool append_open_options(QDict *d, BlockDriverState *bs)
+/**
+ * Iterates through the list of runtime option keys that are said to
+ * be "strong" for a BDS. An option is called "strong" if it changes
+ * a BDS's data. For example, the null block driver's "size" and
+ * "read-zeroes" options are strong, but its "latency-ns" option is
+ * not.
+ *
+ * If a key returned by this function ends with a dot, all options
+ * starting with that prefix are strong.
+ */
+static const char *const *strong_options(BlockDriverState *bs,
+ const char *const *curopt)
+{
+ static const char *const global_options[] = {
+ "driver", "filename", NULL
+ };
+
+ if (!curopt) {
+ return &global_options[0];
+ }
+
+ curopt++;
+ if (curopt == &global_options[ARRAY_SIZE(global_options) - 1] && bs->drv) {
+ curopt = bs->drv->strong_runtime_opts;
+ }
+
+ return (curopt && *curopt) ? curopt : NULL;
+}
+
+/**
+ * Copies all strong runtime options from bs->options to the given
+ * QDict. The set of strong option keys is determined by invoking
+ * strong_options().
+ *
+ * Returns true iff any strong option was present in bs->options (and
+ * thus copied to the target QDict) with the exception of "filename"
+ * and "driver". The caller is expected to use this value to decide
+ * whether the existence of strong options prevents the generation of
+ * a plain filename.
+ */
+static bool append_strong_runtime_options(QDict *d, BlockDriverState *bs)
{
- const QDictEntry *entry;
- QemuOptDesc *desc;
bool found_any = false;
+ const char *const *option_name = NULL;
- for (entry = qdict_first(bs->options); entry;
- entry = qdict_next(bs->options, entry))
- {
- /* Exclude all non-driver-specific options */
- for (desc = bdrv_runtime_opts.desc; desc->name; desc++) {
- if (!strcmp(qdict_entry_key(entry), desc->name)) {
- break;
+ if (!bs->drv) {
+ return false;
+ }
+
+ while ((option_name = strong_options(bs, option_name))) {
+ bool option_given = false;
+
+ assert(strlen(*option_name) > 0);
+ if ((*option_name)[strlen(*option_name) - 1] != '.') {
+ QObject *entry = qdict_get(bs->options, *option_name);
+ if (!entry) {
+ continue;
+ }
+
+ qdict_put_obj(d, *option_name, qobject_ref(entry));
+ option_given = true;
+ } else {
+ const QDictEntry *entry;
+ for (entry = qdict_first(bs->options); entry;
+ entry = qdict_next(bs->options, entry))
+ {
+ if (strstart(qdict_entry_key(entry), *option_name, NULL)) {
+ qdict_put_obj(d, qdict_entry_key(entry),
+ qobject_ref(qdict_entry_value(entry)));
+ option_given = true;
+ }
}
}
- if (desc->name) {
- continue;
+
+ /* While "driver" and "filename" need to be included in a JSON filename,
+ * their existence does not prohibit generation of a plain filename. */
+ if (!found_any && option_given &&
+ strcmp(*option_name, "driver") && strcmp(*option_name, "filename"))
+ {
+ found_any = true;
}
+ }
- qdict_put_obj(d, qdict_entry_key(entry),
- qobject_ref(qdict_entry_value(entry)));
- found_any = true;
+ if (!qdict_haskey(d, "driver")) {
+ /* Drivers created with bdrv_new_open_driver() may not have a
+ * @driver option. Add it here. */
+ qdict_put_str(d, "driver", bs->drv->format_name);
}
return found_any;
}
+/* Note: This function may return false positives; it may return true
+ * even if opening the backing file specified by bs's image header
+ * would result in exactly bs->backing. */
+static bool bdrv_backing_overridden(BlockDriverState *bs)
+{
+ if (bs->backing) {
+ return strcmp(bs->auto_backing_file,
+ bs->backing->bs->filename);
+ } else {
+ /* No backing BDS, so if the image header reports any backing
+ * file, it must have been suppressed */
+ return bs->auto_backing_file[0] != '\0';
+ }
+}
+
/* Updates the following BDS fields:
* - exact_filename: A filename which may be used for opening a block device
* which (mostly) equals the given BDS (even without any
@@ -5477,92 +5657,108 @@ static bool append_open_options(QDict *d, BlockDriverState *bs)
void bdrv_refresh_filename(BlockDriverState *bs)
{
BlockDriver *drv = bs->drv;
+ BdrvChild *child;
QDict *opts;
+ bool backing_overridden;
+ bool generate_json_filename; /* Whether our default implementation should
+ fill exact_filename (false) or not (true) */
if (!drv) {
return;
}
- /* This BDS's file name will most probably depend on its file's name, so
- * refresh that first */
- if (bs->file) {
- bdrv_refresh_filename(bs->file->bs);
+ /* This BDS's file name may depend on any of its children's file names, so
+ * refresh those first */
+ QLIST_FOREACH(child, &bs->children, next) {
+ bdrv_refresh_filename(child->bs);
+ }
+
+ if (bs->implicit) {
+ /* For implicit nodes, just copy everything from the single child */
+ child = QLIST_FIRST(&bs->children);
+ assert(QLIST_NEXT(child, next) == NULL);
+
+ pstrcpy(bs->exact_filename, sizeof(bs->exact_filename),
+ child->bs->exact_filename);
+ pstrcpy(bs->filename, sizeof(bs->filename), child->bs->filename);
+
+ bs->full_open_options = qobject_ref(child->bs->full_open_options);
+
+ return;
+ }
+
+ backing_overridden = bdrv_backing_overridden(bs);
+
+ if (bs->open_flags & BDRV_O_NO_IO) {
+ /* Without I/O, the backing file does not change anything.
+ * Therefore, in such a case (primarily qemu-img), we can
+ * pretend the backing file has not been overridden even if
+ * it technically has been. */
+ backing_overridden = false;
}
+ /* Gather the options QDict */
+ opts = qdict_new();
+ generate_json_filename = append_strong_runtime_options(opts, bs);
+ generate_json_filename |= backing_overridden;
+
+ if (drv->bdrv_gather_child_options) {
+ /* Some block drivers may not want to present all of their children's
+ * options, or name them differently from BdrvChild.name */
+ drv->bdrv_gather_child_options(bs, opts, backing_overridden);
+ } else {
+ QLIST_FOREACH(child, &bs->children, next) {
+ if (child->role == &child_backing && !backing_overridden) {
+ /* We can skip the backing BDS if it has not been overridden */
+ continue;
+ }
+
+ qdict_put(opts, child->name,
+ qobject_ref(child->bs->full_open_options));
+ }
+
+ if (backing_overridden && !bs->backing) {
+ /* Force no backing file */
+ qdict_put_null(opts, "backing");
+ }
+ }
+
+ qobject_unref(bs->full_open_options);
+ bs->full_open_options = opts;
+
if (drv->bdrv_refresh_filename) {
/* Obsolete information is of no use here, so drop the old file name
* information before refreshing it */
bs->exact_filename[0] = '\0';
- if (bs->full_open_options) {
- qobject_unref(bs->full_open_options);
- bs->full_open_options = NULL;
- }
- opts = qdict_new();
- append_open_options(opts, bs);
- drv->bdrv_refresh_filename(bs, opts);
- qobject_unref(opts);
+ drv->bdrv_refresh_filename(bs);
} else if (bs->file) {
/* Try to reconstruct valid information from the underlying file */
- bool has_open_options;
bs->exact_filename[0] = '\0';
- if (bs->full_open_options) {
- qobject_unref(bs->full_open_options);
- bs->full_open_options = NULL;
- }
-
- opts = qdict_new();
- has_open_options = append_open_options(opts, bs);
- /* If no specific options have been given for this BDS, the filename of
- * the underlying file should suffice for this one as well */
- if (bs->file->bs->exact_filename[0] && !has_open_options) {
+ /*
+ * We can use the underlying file's filename if:
+ * - it has a filename,
+ * - the file is a protocol BDS, and
+ * - opening that file (as this BDS's format) will automatically create
+ * the BDS tree we have right now, that is:
+ * - the user did not significantly change this BDS's behavior with
+ * some explicit (strong) options
+ * - no non-file child of this BDS has been overridden by the user
+ * Both of these conditions are represented by generate_json_filename.
+ */
+ if (bs->file->bs->exact_filename[0] &&
+ bs->file->bs->drv->bdrv_file_open &&
+ !generate_json_filename)
+ {
strcpy(bs->exact_filename, bs->file->bs->exact_filename);
}
- /* Reconstructing the full options QDict is simple for most format block
- * drivers, as long as the full options are known for the underlying
- * file BDS. The full options QDict of that file BDS should somehow
- * 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_str(opts, "driver", drv->format_name);
- qdict_put(opts, "file",
- qobject_ref(bs->file->bs->full_open_options));
-
- bs->full_open_options = opts;
- } else {
- qobject_unref(opts);
- }
- } else if (!bs->full_open_options && qdict_size(bs->options)) {
- /* There is no underlying file BDS (at least referenced by BDS.file),
- * so the full options QDict should be equal to the options given
- * specifically for this block device when it was opened (plus the
- * driver specification).
- * Because those options don't change, there is no need to update
- * full_open_options when it's already set. */
-
- opts = qdict_new();
- append_open_options(opts, bs);
- qdict_put_str(opts, "driver", drv->format_name);
-
- if (bs->exact_filename[0]) {
- /* This may not work for all block protocol drivers (some may
- * require this filename to be parsed), but we have to find some
- * default solution here, so just include it. If some block driver
- * does not support pure options without any filename at all or
- * needs some special format of the options QDict, it needs to
- * implement the driver-specific bdrv_refresh_filename() function.
- */
- qdict_put_str(opts, "filename", bs->exact_filename);
- }
-
- bs->full_open_options = opts;
}
if (bs->exact_filename[0]) {
pstrcpy(bs->filename, sizeof(bs->filename), bs->exact_filename);
- } else if (bs->full_open_options) {
+ } else {
QString *json = qobject_to_json(QOBJECT(bs->full_open_options));
snprintf(bs->filename, sizeof(bs->filename), "json:%s",
qstring_get_str(json));
@@ -5570,6 +5766,33 @@ void bdrv_refresh_filename(BlockDriverState *bs)
}
}
+char *bdrv_dirname(BlockDriverState *bs, Error **errp)
+{
+ BlockDriver *drv = bs->drv;
+
+ if (!drv) {
+ error_setg(errp, "Node '%s' is ejected", bs->node_name);
+ return NULL;
+ }
+
+ if (drv->bdrv_dirname) {
+ return drv->bdrv_dirname(bs, errp);
+ }
+
+ if (bs->file) {
+ return bdrv_dirname(bs->file->bs, errp);
+ }
+
+ bdrv_refresh_filename(bs);
+ if (bs->exact_filename[0] != '\0') {
+ return path_combine(bs->exact_filename, "");
+ }
+
+ error_setg(errp, "Cannot generate a base directory for %s nodes",
+ drv->format_name);
+ return NULL;
+}
+
/*
* Hot add/remove a BDS's child. So the user can take a child offline when
* it is broken and take a new child online
diff --git a/block/blkdebug.c b/block/blkdebug.c
index 0759452..1ea835c 100644
--- a/block/blkdebug.c
+++ b/block/blkdebug.c
@@ -811,51 +811,37 @@ static int64_t blkdebug_getlength(BlockDriverState *bs)
return bdrv_getlength(bs->file->bs);
}
-static void blkdebug_refresh_filename(BlockDriverState *bs, QDict *options)
+static void blkdebug_refresh_filename(BlockDriverState *bs)
{
BDRVBlkdebugState *s = bs->opaque;
- QDict *opts;
const QDictEntry *e;
- bool force_json = false;
-
- for (e = qdict_first(options); e; e = qdict_next(options, e)) {
- if (strcmp(qdict_entry_key(e), "config") &&
- strcmp(qdict_entry_key(e), "x-image"))
- {
- force_json = true;
- break;
- }
- }
+ int ret;
- if (force_json && !bs->file->bs->full_open_options) {
- /* The config file cannot be recreated, so creating a plain filename
- * is impossible */
+ if (!bs->file->bs->exact_filename[0]) {
return;
}
- if (!force_json && bs->file->bs->exact_filename[0]) {
- int ret = snprintf(bs->exact_filename, sizeof(bs->exact_filename),
- "blkdebug:%s:%s", s->config_file ?: "",
- bs->file->bs->exact_filename);
- if (ret >= sizeof(bs->exact_filename)) {
- /* An overflow makes the filename unusable, so do not report any */
- bs->exact_filename[0] = 0;
+ for (e = qdict_first(bs->full_open_options); e;
+ e = qdict_next(bs->full_open_options, e))
+ {
+ /* Real child options are under "image", but "x-image" may
+ * contain a filename */
+ if (strcmp(qdict_entry_key(e), "config") &&
+ strcmp(qdict_entry_key(e), "image") &&
+ strcmp(qdict_entry_key(e), "x-image") &&
+ strcmp(qdict_entry_key(e), "driver"))
+ {
+ return;
}
}
- opts = qdict_new();
- qdict_put_str(opts, "driver", "blkdebug");
-
- qdict_put(opts, "image", qobject_ref(bs->file->bs->full_open_options));
-
- for (e = qdict_first(options); e; e = qdict_next(options, e)) {
- if (strcmp(qdict_entry_key(e), "x-image")) {
- qdict_put_obj(opts, qdict_entry_key(e),
- qobject_ref(qdict_entry_value(e)));
- }
+ ret = snprintf(bs->exact_filename, sizeof(bs->exact_filename),
+ "blkdebug:%s:%s",
+ s->config_file ?: "", bs->file->bs->exact_filename);
+ if (ret >= sizeof(bs->exact_filename)) {
+ /* An overflow makes the filename unusable, so do not report any */
+ bs->exact_filename[0] = 0;
}
-
- bs->full_open_options = opts;
}
static void blkdebug_refresh_limits(BlockDriverState *bs, Error **errp)
@@ -888,6 +874,20 @@ static int blkdebug_reopen_prepare(BDRVReopenState *reopen_state,
return 0;
}
+static const char *const blkdebug_strong_runtime_opts[] = {
+ "config",
+ "inject-error.",
+ "set-state.",
+ "align",
+ "max-transfer",
+ "opt-write-zero",
+ "max-write-zero",
+ "opt-discard",
+ "max-discard",
+
+ NULL
+};
+
static BlockDriver bdrv_blkdebug = {
.format_name = "blkdebug",
.protocol_name = "blkdebug",
@@ -917,6 +917,8 @@ static BlockDriver bdrv_blkdebug = {
= blkdebug_debug_remove_breakpoint,
.bdrv_debug_resume = blkdebug_debug_resume,
.bdrv_debug_is_suspended = blkdebug_debug_is_suspended,
+
+ .strong_runtime_opts = blkdebug_strong_runtime_opts,
};
static void bdrv_blkdebug_init(void)
diff --git a/block/blklogwrites.c b/block/blklogwrites.c
index d2e01bd..eb2b490 100644
--- a/block/blklogwrites.c
+++ b/block/blklogwrites.c
@@ -280,30 +280,6 @@ static int64_t blk_log_writes_getlength(BlockDriverState *bs)
return bdrv_getlength(bs->file->bs);
}
-static void blk_log_writes_refresh_filename(BlockDriverState *bs,
- QDict *options)
-{
- BDRVBlkLogWritesState *s = bs->opaque;
-
- /* bs->file->bs has already been refreshed */
- bdrv_refresh_filename(s->log_file->bs);
-
- if (bs->file->bs->full_open_options
- && s->log_file->bs->full_open_options)
- {
- QDict *opts = qdict_new();
- qdict_put_str(opts, "driver", "blklogwrites");
-
- qobject_ref(bs->file->bs->full_open_options);
- qdict_put(opts, "file", bs->file->bs->full_open_options);
- qobject_ref(s->log_file->bs->full_open_options);
- qdict_put(opts, "log", s->log_file->bs->full_open_options);
- qdict_put_int(opts, "log-sector-size", s->sectorsize);
-
- bs->full_open_options = opts;
- }
-}
-
static void blk_log_writes_child_perm(BlockDriverState *bs, BdrvChild *c,
const BdrvChildRole *role,
BlockReopenQueue *ro_q,
@@ -520,6 +496,13 @@ blk_log_writes_co_pdiscard(BlockDriverState *bs, int64_t offset, int count)
LOG_DISCARD_FLAG, false);
}
+static const char *const blk_log_writes_strong_runtime_opts[] = {
+ "log-append",
+ "log-sector-size",
+
+ NULL
+};
+
static BlockDriver bdrv_blk_log_writes = {
.format_name = "blklogwrites",
.instance_size = sizeof(BDRVBlkLogWritesState),
@@ -527,7 +510,6 @@ static BlockDriver bdrv_blk_log_writes = {
.bdrv_open = blk_log_writes_open,
.bdrv_close = blk_log_writes_close,
.bdrv_getlength = blk_log_writes_getlength,
- .bdrv_refresh_filename = blk_log_writes_refresh_filename,
.bdrv_child_perm = blk_log_writes_child_perm,
.bdrv_refresh_limits = blk_log_writes_refresh_limits,
@@ -539,6 +521,7 @@ static BlockDriver bdrv_blk_log_writes = {
.bdrv_co_block_status = bdrv_co_block_status_from_file,
.is_filter = true,
+ .strong_runtime_opts = blk_log_writes_strong_runtime_opts,
};
static void bdrv_blk_log_writes_init(void)
diff --git a/block/blkverify.c b/block/blkverify.c
index 89bf438..3ff77ff 100644
--- a/block/blkverify.c
+++ b/block/blkverify.c
@@ -281,27 +281,10 @@ static bool blkverify_recurse_is_first_non_filter(BlockDriverState *bs,
return bdrv_recurse_is_first_non_filter(s->test_file->bs, candidate);
}
-static void blkverify_refresh_filename(BlockDriverState *bs, QDict *options)
+static void blkverify_refresh_filename(BlockDriverState *bs)
{
BDRVBlkverifyState *s = bs->opaque;
- /* bs->file->bs has already been refreshed */
- bdrv_refresh_filename(s->test_file->bs);
-
- if (bs->file->bs->full_open_options
- && s->test_file->bs->full_open_options)
- {
- QDict *opts = qdict_new();
- qdict_put_str(opts, "driver", "blkverify");
-
- qdict_put(opts, "raw",
- qobject_ref(bs->file->bs->full_open_options));
- qdict_put(opts, "test",
- qobject_ref(s->test_file->bs->full_open_options));
-
- bs->full_open_options = opts;
- }
-
if (bs->file->bs->exact_filename[0]
&& s->test_file->bs->exact_filename[0])
{
@@ -316,6 +299,15 @@ static void blkverify_refresh_filename(BlockDriverState *bs, QDict *options)
}
}
+static char *blkverify_dirname(BlockDriverState *bs, Error **errp)
+{
+ /* In general, there are two BDSs with different dirnames below this one;
+ * so there is no unique dirname we could return (unless both are equal by
+ * chance). Therefore, to be consistent, just always return NULL. */
+ error_setg(errp, "Cannot generate a base directory for blkverify nodes");
+ return NULL;
+}
+
static BlockDriver bdrv_blkverify = {
.format_name = "blkverify",
.protocol_name = "blkverify",
@@ -327,6 +319,7 @@ static BlockDriver bdrv_blkverify = {
.bdrv_child_perm = bdrv_filter_default_perms,
.bdrv_getlength = blkverify_getlength,
.bdrv_refresh_filename = blkverify_refresh_filename,
+ .bdrv_dirname = blkverify_dirname,
.bdrv_co_preadv = blkverify_co_preadv,
.bdrv_co_pwritev = blkverify_co_pwritev,
diff --git a/block/block-backend.c b/block/block-backend.c
index 6cc2556..edad02a 100644
--- a/block/block-backend.c
+++ b/block/block-backend.c
@@ -1253,12 +1253,12 @@ int blk_make_zero(BlockBackend *blk, BdrvRequestFlags flags)
return bdrv_make_zero(blk->root, flags);
}
-static void blk_inc_in_flight(BlockBackend *blk)
+void blk_inc_in_flight(BlockBackend *blk)
{
atomic_inc(&blk->in_flight);
}
-static void blk_dec_in_flight(BlockBackend *blk)
+void blk_dec_in_flight(BlockBackend *blk)
{
atomic_dec(&blk->in_flight);
aio_wait_kick();
diff --git a/block/commit.c b/block/commit.c
index d500a93..3b46ca7 100644
--- a/block/commit.c
+++ b/block/commit.c
@@ -225,9 +225,8 @@ static int coroutine_fn bdrv_commit_top_preadv(BlockDriverState *bs,
return bdrv_co_preadv(bs->backing, offset, bytes, qiov, flags);
}
-static void bdrv_commit_top_refresh_filename(BlockDriverState *bs, QDict *opts)
+static void bdrv_commit_top_refresh_filename(BlockDriverState *bs)
{
- bdrv_refresh_filename(bs->backing->bs);
pstrcpy(bs->exact_filename, sizeof(bs->exact_filename),
bs->backing->bs->filename);
}
@@ -369,10 +368,12 @@ fail:
if (s->top) {
blk_unref(s->top);
}
+ job_early_fail(&s->common.job);
+ /* commit_top_bs has to be replaced after deleting the block job,
+ * otherwise this would fail because of lack of permissions. */
if (commit_top_bs) {
bdrv_replace_node(commit_top_bs, top, &error_abort);
}
- job_early_fail(&s->common.job);
}
diff --git a/block/crypto.c b/block/crypto.c
index d5b1da6..fd8c7cf 100644
--- a/block/crypto.c
+++ b/block/crypto.c
@@ -619,6 +619,12 @@ block_crypto_get_specific_info_luks(BlockDriverState *bs, Error **errp)
return spec_info;
}
+static const char *const block_crypto_strong_runtime_opts[] = {
+ BLOCK_CRYPTO_OPT_LUKS_KEY_SECRET,
+
+ NULL
+};
+
BlockDriver bdrv_crypto_luks = {
.format_name = "luks",
.instance_size = sizeof(BlockCrypto),
@@ -640,6 +646,8 @@ BlockDriver bdrv_crypto_luks = {
.bdrv_getlength = block_crypto_getlength,
.bdrv_get_info = block_crypto_get_info_luks,
.bdrv_get_specific_info = block_crypto_get_specific_info_luks,
+
+ .strong_runtime_opts = block_crypto_strong_runtime_opts,
};
static void block_crypto_init(void)
diff --git a/block/curl.c b/block/curl.c
index b7ac265..606709f 100644
--- a/block/curl.c
+++ b/block/curl.c
@@ -61,8 +61,6 @@ static CURLMcode __curl_multi_socket_action(CURLM *multi_handle,
#define CURL_NUM_STATES 8
#define CURL_NUM_ACB 8
-#define READ_AHEAD_DEFAULT (256 * 1024)
-#define CURL_TIMEOUT_DEFAULT 5
#define CURL_TIMEOUT_MAX 10000
#define CURL_BLOCK_OPT_URL "url"
@@ -76,6 +74,10 @@ static CURLMcode __curl_multi_socket_action(CURLM *multi_handle,
#define CURL_BLOCK_OPT_PROXY_USERNAME "proxy-username"
#define CURL_BLOCK_OPT_PROXY_PASSWORD_SECRET "proxy-password-secret"
+#define CURL_BLOCK_OPT_READAHEAD_DEFAULT (256 * 1024)
+#define CURL_BLOCK_OPT_SSLVERIFY_DEFAULT true
+#define CURL_BLOCK_OPT_TIMEOUT_DEFAULT 5
+
struct BDRVCURLState;
static bool libcurl_initialized;
@@ -696,7 +698,7 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags,
}
s->readahead_size = qemu_opt_get_size(opts, CURL_BLOCK_OPT_READAHEAD,
- READ_AHEAD_DEFAULT);
+ CURL_BLOCK_OPT_READAHEAD_DEFAULT);
if ((s->readahead_size & 0x1ff) != 0) {
error_setg(errp, "HTTP_READAHEAD_SIZE %zd is not a multiple of 512",
s->readahead_size);
@@ -704,13 +706,14 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags,
}
s->timeout = qemu_opt_get_number(opts, CURL_BLOCK_OPT_TIMEOUT,
- CURL_TIMEOUT_DEFAULT);
+ CURL_BLOCK_OPT_TIMEOUT_DEFAULT);
if (s->timeout > CURL_TIMEOUT_MAX) {
error_setg(errp, "timeout parameter is too large or negative");
goto out_noclean;
}
- s->sslverify = qemu_opt_get_bool(opts, CURL_BLOCK_OPT_SSLVERIFY, true);
+ s->sslverify = qemu_opt_get_bool(opts, CURL_BLOCK_OPT_SSLVERIFY,
+ CURL_BLOCK_OPT_SSLVERIFY_DEFAULT);
cookie = qemu_opt_get(opts, CURL_BLOCK_OPT_COOKIE);
cookie_secret = qemu_opt_get(opts, CURL_BLOCK_OPT_COOKIE_SECRET);
@@ -947,6 +950,36 @@ static int64_t curl_getlength(BlockDriverState *bs)
return s->len;
}
+static void curl_refresh_filename(BlockDriverState *bs)
+{
+ BDRVCURLState *s = bs->opaque;
+
+ /* "readahead" and "timeout" do not change the guest-visible data,
+ * so ignore them */
+ if (s->sslverify != CURL_BLOCK_OPT_SSLVERIFY_DEFAULT ||
+ s->cookie || s->username || s->password || s->proxyusername ||
+ s->proxypassword)
+ {
+ return;
+ }
+
+ pstrcpy(bs->exact_filename, sizeof(bs->exact_filename), s->url);
+}
+
+
+static const char *const curl_strong_runtime_opts[] = {
+ CURL_BLOCK_OPT_URL,
+ CURL_BLOCK_OPT_SSLVERIFY,
+ CURL_BLOCK_OPT_COOKIE,
+ CURL_BLOCK_OPT_COOKIE_SECRET,
+ CURL_BLOCK_OPT_USERNAME,
+ CURL_BLOCK_OPT_PASSWORD_SECRET,
+ CURL_BLOCK_OPT_PROXY_USERNAME,
+ CURL_BLOCK_OPT_PROXY_PASSWORD_SECRET,
+
+ NULL
+};
+
static BlockDriver bdrv_http = {
.format_name = "http",
.protocol_name = "http",
@@ -961,6 +994,9 @@ static BlockDriver bdrv_http = {
.bdrv_detach_aio_context = curl_detach_aio_context,
.bdrv_attach_aio_context = curl_attach_aio_context,
+
+ .bdrv_refresh_filename = curl_refresh_filename,
+ .strong_runtime_opts = curl_strong_runtime_opts,
};
static BlockDriver bdrv_https = {
@@ -977,6 +1013,9 @@ static BlockDriver bdrv_https = {
.bdrv_detach_aio_context = curl_detach_aio_context,
.bdrv_attach_aio_context = curl_attach_aio_context,
+
+ .bdrv_refresh_filename = curl_refresh_filename,
+ .strong_runtime_opts = curl_strong_runtime_opts,
};
static BlockDriver bdrv_ftp = {
@@ -993,6 +1032,9 @@ static BlockDriver bdrv_ftp = {
.bdrv_detach_aio_context = curl_detach_aio_context,
.bdrv_attach_aio_context = curl_attach_aio_context,
+
+ .bdrv_refresh_filename = curl_refresh_filename,
+ .strong_runtime_opts = curl_strong_runtime_opts,
};
static BlockDriver bdrv_ftps = {
@@ -1009,6 +1051,9 @@ static BlockDriver bdrv_ftps = {
.bdrv_detach_aio_context = curl_detach_aio_context,
.bdrv_attach_aio_context = curl_attach_aio_context,
+
+ .bdrv_refresh_filename = curl_refresh_filename,
+ .strong_runtime_opts = curl_strong_runtime_opts,
};
static void curl_block_init(void)
diff --git a/block/gluster.c b/block/gluster.c
index 7289106..af64330 100644
--- a/block/gluster.c
+++ b/block/gluster.c
@@ -1495,6 +1495,21 @@ static int coroutine_fn qemu_gluster_co_block_status(BlockDriverState *bs,
}
+static const char *const gluster_strong_open_opts[] = {
+ GLUSTER_OPT_VOLUME,
+ GLUSTER_OPT_PATH,
+ GLUSTER_OPT_TYPE,
+ GLUSTER_OPT_SERVER_PATTERN,
+ GLUSTER_OPT_HOST,
+ GLUSTER_OPT_PORT,
+ GLUSTER_OPT_TO,
+ GLUSTER_OPT_IPV4,
+ GLUSTER_OPT_IPV6,
+ GLUSTER_OPT_SOCKET,
+
+ NULL
+};
+
static BlockDriver bdrv_gluster = {
.format_name = "gluster",
.protocol_name = "gluster",
@@ -1522,6 +1537,7 @@ static BlockDriver bdrv_gluster = {
#endif
.bdrv_co_block_status = qemu_gluster_co_block_status,
.create_opts = &qemu_gluster_create_opts,
+ .strong_runtime_opts = gluster_strong_open_opts,
};
static BlockDriver bdrv_gluster_tcp = {
@@ -1551,6 +1567,7 @@ static BlockDriver bdrv_gluster_tcp = {
#endif
.bdrv_co_block_status = qemu_gluster_co_block_status,
.create_opts = &qemu_gluster_create_opts,
+ .strong_runtime_opts = gluster_strong_open_opts,
};
static BlockDriver bdrv_gluster_unix = {
@@ -1580,6 +1597,7 @@ static BlockDriver bdrv_gluster_unix = {
#endif
.bdrv_co_block_status = qemu_gluster_co_block_status,
.create_opts = &qemu_gluster_create_opts,
+ .strong_runtime_opts = gluster_strong_open_opts,
};
/* rdma is deprecated (actually never supported for volfile fetch).
@@ -1615,6 +1633,7 @@ static BlockDriver bdrv_gluster_rdma = {
#endif
.bdrv_co_block_status = qemu_gluster_co_block_status,
.create_opts = &qemu_gluster_create_opts,
+ .strong_runtime_opts = gluster_strong_open_opts,
};
static void bdrv_gluster_init(void)
diff --git a/block/iscsi.c b/block/iscsi.c
index ff47320..a0c0084 100644
--- a/block/iscsi.c
+++ b/block/iscsi.c
@@ -2448,6 +2448,20 @@ static QemuOptsList iscsi_create_opts = {
}
};
+static const char *const iscsi_strong_runtime_opts[] = {
+ "transport",
+ "portal",
+ "target",
+ "user",
+ "password",
+ "password-secret",
+ "lun",
+ "initiator-name",
+ "header-digest",
+
+ NULL
+};
+
static BlockDriver bdrv_iscsi = {
.format_name = "iscsi",
.protocol_name = "iscsi",
@@ -2482,6 +2496,8 @@ static BlockDriver bdrv_iscsi = {
.bdrv_detach_aio_context = iscsi_detach_aio_context,
.bdrv_attach_aio_context = iscsi_attach_aio_context,
+
+ .strong_runtime_opts = iscsi_strong_runtime_opts,
};
#if LIBISCSI_API_VERSION >= (20160603)
@@ -2519,6 +2535,8 @@ static BlockDriver bdrv_iser = {
.bdrv_detach_aio_context = iscsi_detach_aio_context,
.bdrv_attach_aio_context = iscsi_attach_aio_context,
+
+ .strong_runtime_opts = iscsi_strong_runtime_opts,
};
#endif
diff --git a/block/mirror.c b/block/mirror.c
index b67b012..726d3c2 100644
--- a/block/mirror.c
+++ b/block/mirror.c
@@ -1431,14 +1431,13 @@ static int coroutine_fn bdrv_mirror_top_pdiscard(BlockDriverState *bs,
NULL, 0);
}
-static void bdrv_mirror_top_refresh_filename(BlockDriverState *bs, QDict *opts)
+static void bdrv_mirror_top_refresh_filename(BlockDriverState *bs)
{
if (bs->backing == NULL) {
/* we can be here after failed bdrv_attach_child in
* bdrv_set_backing_hd */
return;
}
- bdrv_refresh_filename(bs->backing->bs);
pstrcpy(bs->exact_filename, sizeof(bs->exact_filename),
bs->backing->bs->filename);
}
diff --git a/block/nbd-client.c b/block/nbd-client.c
index f0ad54c..bfbaf7e 100644
--- a/block/nbd-client.c
+++ b/block/nbd-client.c
@@ -76,8 +76,18 @@ static coroutine_fn void nbd_connection_entry(void *opaque)
Error *local_err = NULL;
while (!s->quit) {
+ /*
+ * The NBD client can only really be considered idle when it has
+ * yielded from qio_channel_readv_all_eof(), waiting for data. This is
+ * the point where the additional scheduled coroutine entry happens
+ * after nbd_client_attach_aio_context().
+ *
+ * Therefore we keep an additional in_flight reference all the time and
+ * only drop it temporarily here.
+ */
assert(s->reply.handle == 0);
- ret = nbd_receive_reply(s->ioc, &s->reply, &local_err);
+ ret = nbd_receive_reply(s->bs, s->ioc, &s->reply, &local_err);
+
if (local_err) {
trace_nbd_read_reply_entry_fail(ret, error_get_pretty(local_err));
error_free(local_err);
@@ -116,6 +126,8 @@ static coroutine_fn void nbd_connection_entry(void *opaque)
s->quit = true;
nbd_recv_coroutines_wake_all(s);
+ bdrv_dec_in_flight(s->bs);
+
s->connection_co = NULL;
aio_wait_kick();
}
@@ -965,12 +977,30 @@ void nbd_client_detach_aio_context(BlockDriverState *bs)
qio_channel_detach_aio_context(QIO_CHANNEL(client->ioc));
}
+static void nbd_client_attach_aio_context_bh(void *opaque)
+{
+ BlockDriverState *bs = opaque;
+ NBDClientSession *client = nbd_get_client_session(bs);
+
+ /* The node is still drained, so we know the coroutine has yielded in
+ * nbd_read_eof(), the only place where bs->in_flight can reach 0, or it is
+ * entered for the first time. Both places are safe for entering the
+ * coroutine.*/
+ qemu_aio_coroutine_enter(bs->aio_context, client->connection_co);
+ bdrv_dec_in_flight(bs);
+}
+
void nbd_client_attach_aio_context(BlockDriverState *bs,
AioContext *new_context)
{
NBDClientSession *client = nbd_get_client_session(bs);
qio_channel_attach_aio_context(QIO_CHANNEL(client->ioc), new_context);
- aio_co_schedule(new_context, client->connection_co);
+
+ bdrv_inc_in_flight(bs);
+
+ /* Need to wait here for the BH to run because the BH must run while the
+ * node is still drained. */
+ aio_wait_bh_oneshot(new_context, nbd_client_attach_aio_context_bh, bs);
}
void nbd_client_close(BlockDriverState *bs)
@@ -1076,6 +1106,7 @@ static int nbd_client_connect(BlockDriverState *bs,
* kick the reply mechanism. */
qio_channel_set_blocking(QIO_CHANNEL(sioc), false, NULL);
client->connection_co = qemu_coroutine_create(nbd_connection_entry, client);
+ bdrv_inc_in_flight(bs);
nbd_client_attach_aio_context(bs, bdrv_get_aio_context(bs));
logout("Established connection with NBD server\n");
@@ -1108,6 +1139,7 @@ int nbd_client_init(BlockDriverState *bs,
{
NBDClientSession *client = nbd_get_client_session(bs);
+ client->bs = bs;
qemu_co_mutex_init(&client->send_mutex);
qemu_co_queue_init(&client->free_sema);
diff --git a/block/nbd-client.h b/block/nbd-client.h
index d990207..09e0301 100644
--- a/block/nbd-client.h
+++ b/block/nbd-client.h
@@ -35,6 +35,7 @@ typedef struct NBDClientSession {
NBDClientRequest requests[MAX_NBD_REQUESTS];
NBDReply reply;
+ BlockDriverState *bs;
bool quit;
} NBDClientSession;
diff --git a/block/nbd.c b/block/nbd.c
index 9db5ede..2e72df5 100644
--- a/block/nbd.c
+++ b/block/nbd.c
@@ -477,12 +477,9 @@ static void nbd_attach_aio_context(BlockDriverState *bs,
nbd_client_attach_aio_context(bs, new_context);
}
-static void nbd_refresh_filename(BlockDriverState *bs, QDict *options)
+static void nbd_refresh_filename(BlockDriverState *bs)
{
BDRVNBDState *s = bs->opaque;
- QDict *opts = qdict_new();
- QObject *saddr_qdict;
- Visitor *ov;
const char *host = NULL, *port = NULL, *path = NULL;
if (s->saddr->type == SOCKET_ADDRESS_TYPE_INET) {
@@ -495,8 +492,6 @@ static void nbd_refresh_filename(BlockDriverState *bs, QDict *options)
path = s->saddr->u.q_unix.path;
} /* else can't represent as pseudo-filename */
- qdict_put_str(opts, "driver", "nbd");
-
if (path && s->export) {
snprintf(bs->exact_filename, sizeof(bs->exact_filename),
"nbd+unix:///%s?socket=%s", s->export, path);
@@ -510,23 +505,28 @@ static void nbd_refresh_filename(BlockDriverState *bs, QDict *options)
snprintf(bs->exact_filename, sizeof(bs->exact_filename),
"nbd://%s:%s", host, port);
}
+}
- ov = qobject_output_visitor_new(&saddr_qdict);
- visit_type_SocketAddress(ov, NULL, &s->saddr, &error_abort);
- visit_complete(ov, &saddr_qdict);
- visit_free(ov);
- qdict_put_obj(opts, "server", saddr_qdict);
+static char *nbd_dirname(BlockDriverState *bs, Error **errp)
+{
+ /* The generic bdrv_dirname() implementation is able to work out some
+ * directory name for NBD nodes, but that would be wrong. So far there is no
+ * specification for how "export paths" would work, so NBD does not have
+ * directory names. */
+ error_setg(errp, "Cannot generate a base directory for NBD nodes");
+ return NULL;
+}
- if (s->export) {
- qdict_put_str(opts, "export", s->export);
- }
- if (s->tlscredsid) {
- qdict_put_str(opts, "tls-creds", s->tlscredsid);
- }
+static const char *const nbd_strong_runtime_opts[] = {
+ "path",
+ "host",
+ "port",
+ "export",
+ "tls-creds",
+ "server.",
- qdict_flatten(opts);
- bs->full_open_options = opts;
-}
+ NULL
+};
static BlockDriver bdrv_nbd = {
.format_name = "nbd",
@@ -546,6 +546,8 @@ static BlockDriver bdrv_nbd = {
.bdrv_attach_aio_context = nbd_attach_aio_context,
.bdrv_refresh_filename = nbd_refresh_filename,
.bdrv_co_block_status = nbd_client_co_block_status,
+ .bdrv_dirname = nbd_dirname,
+ .strong_runtime_opts = nbd_strong_runtime_opts,
};
static BlockDriver bdrv_nbd_tcp = {
@@ -566,6 +568,8 @@ static BlockDriver bdrv_nbd_tcp = {
.bdrv_attach_aio_context = nbd_attach_aio_context,
.bdrv_refresh_filename = nbd_refresh_filename,
.bdrv_co_block_status = nbd_client_co_block_status,
+ .bdrv_dirname = nbd_dirname,
+ .strong_runtime_opts = nbd_strong_runtime_opts,
};
static BlockDriver bdrv_nbd_unix = {
@@ -586,6 +590,8 @@ static BlockDriver bdrv_nbd_unix = {
.bdrv_attach_aio_context = nbd_attach_aio_context,
.bdrv_refresh_filename = nbd_refresh_filename,
.bdrv_co_block_status = nbd_client_co_block_status,
+ .bdrv_dirname = nbd_dirname,
+ .strong_runtime_opts = nbd_strong_runtime_opts,
};
static void bdrv_nbd_init(void)
diff --git a/block/nfs.c b/block/nfs.c
index eab1a2c..5319036 100644
--- a/block/nfs.c
+++ b/block/nfs.c
@@ -799,14 +799,9 @@ static int nfs_reopen_prepare(BDRVReopenState *state,
return 0;
}
-static void nfs_refresh_filename(BlockDriverState *bs, QDict *options)
+static void nfs_refresh_filename(BlockDriverState *bs)
{
NFSClient *client = bs->opaque;
- QDict *opts = qdict_new();
- QObject *server_qdict;
- Visitor *ov;
-
- qdict_put_str(opts, "driver", "nfs");
if (client->uid && !client->gid) {
snprintf(bs->exact_filename, sizeof(bs->exact_filename),
@@ -824,35 +819,20 @@ static void nfs_refresh_filename(BlockDriverState *bs, QDict *options)
snprintf(bs->exact_filename, sizeof(bs->exact_filename),
"nfs://%s%s", client->server->host, client->path);
}
+}
- ov = qobject_output_visitor_new(&server_qdict);
- visit_type_NFSServer(ov, NULL, &client->server, &error_abort);
- visit_complete(ov, &server_qdict);
- qdict_put_obj(opts, "server", server_qdict);
- qdict_put_str(opts, "path", client->path);
+static char *nfs_dirname(BlockDriverState *bs, Error **errp)
+{
+ NFSClient *client = bs->opaque;
- if (client->uid) {
- qdict_put_int(opts, "user", client->uid);
- }
- if (client->gid) {
- qdict_put_int(opts, "group", client->gid);
- }
- if (client->tcp_syncnt) {
- qdict_put_int(opts, "tcp-syn-cnt", client->tcp_syncnt);
- }
- if (client->readahead) {
- qdict_put_int(opts, "readahead-size", client->readahead);
- }
- if (client->pagecache) {
- qdict_put_int(opts, "page-cache-size", client->pagecache);
- }
- if (client->debug) {
- qdict_put_int(opts, "debug", client->debug);
+ if (client->uid || client->gid) {
+ bdrv_refresh_filename(bs);
+ error_setg(errp, "Cannot generate a base directory for NFS node '%s'",
+ bs->filename);
+ return NULL;
}
- visit_free(ov);
- qdict_flatten(opts);
- bs->full_open_options = opts;
+ return g_strdup_printf("nfs://%s%s/", client->server->host, client->path);
}
#ifdef LIBNFS_FEATURE_PAGECACHE
@@ -864,6 +844,15 @@ static void coroutine_fn nfs_co_invalidate_cache(BlockDriverState *bs,
}
#endif
+static const char *nfs_strong_runtime_opts[] = {
+ "path",
+ "user",
+ "group",
+ "server.",
+
+ NULL
+};
+
static BlockDriver bdrv_nfs = {
.format_name = "nfs",
.protocol_name = "nfs",
@@ -889,6 +878,9 @@ static BlockDriver bdrv_nfs = {
.bdrv_detach_aio_context = nfs_detach_aio_context,
.bdrv_attach_aio_context = nfs_attach_aio_context,
.bdrv_refresh_filename = nfs_refresh_filename,
+ .bdrv_dirname = nfs_dirname,
+
+ .strong_runtime_opts = nfs_strong_runtime_opts,
#ifdef LIBNFS_FEATURE_PAGECACHE
.bdrv_co_invalidate_cache = nfs_co_invalidate_cache,
diff --git a/block/null.c b/block/null.c
index d442d3e..a322929 100644
--- a/block/null.c
+++ b/block/null.c
@@ -239,19 +239,33 @@ static int coroutine_fn null_co_block_status(BlockDriverState *bs,
return ret;
}
-static void null_refresh_filename(BlockDriverState *bs, QDict *opts)
+static void null_refresh_filename(BlockDriverState *bs)
{
- qdict_del(opts, "filename");
-
- if (!qdict_size(opts)) {
- snprintf(bs->exact_filename, sizeof(bs->exact_filename), "%s://",
- bs->drv->format_name);
+ const QDictEntry *e;
+
+ for (e = qdict_first(bs->full_open_options); e;
+ e = qdict_next(bs->full_open_options, e))
+ {
+ /* These options can be ignored */
+ if (strcmp(qdict_entry_key(e), "filename") &&
+ strcmp(qdict_entry_key(e), "driver") &&
+ strcmp(qdict_entry_key(e), NULL_OPT_LATENCY))
+ {
+ return;
+ }
}
- qdict_put_str(opts, "driver", bs->drv->format_name);
- bs->full_open_options = qobject_ref(opts);
+ snprintf(bs->exact_filename, sizeof(bs->exact_filename), "%s://",
+ bs->drv->format_name);
}
+static const char *const null_strong_runtime_opts[] = {
+ BLOCK_OPT_SIZE,
+ NULL_OPT_ZEROES,
+
+ NULL
+};
+
static BlockDriver bdrv_null_co = {
.format_name = "null-co",
.protocol_name = "null-co",
@@ -269,6 +283,7 @@ static BlockDriver bdrv_null_co = {
.bdrv_co_block_status = null_co_block_status,
.bdrv_refresh_filename = null_refresh_filename,
+ .strong_runtime_opts = null_strong_runtime_opts,
};
static BlockDriver bdrv_null_aio = {
@@ -288,6 +303,7 @@ static BlockDriver bdrv_null_aio = {
.bdrv_co_block_status = null_co_block_status,
.bdrv_refresh_filename = null_refresh_filename,
+ .strong_runtime_opts = null_strong_runtime_opts,
};
static void bdrv_null_init(void)
diff --git a/block/nvme.c b/block/nvme.c
index b5952c9..0684bbd 100644
--- a/block/nvme.c
+++ b/block/nvme.c
@@ -82,7 +82,7 @@ typedef volatile struct {
uint8_t reserved1[0xec0];
uint8_t cmd_set_specfic[0x100];
uint32_t doorbells[];
-} QEMU_PACKED NVMeRegs;
+} NVMeRegs;
QEMU_BUILD_BUG_ON(offsetof(NVMeRegs, doorbells) != 0x1000);
@@ -111,6 +111,9 @@ typedef struct {
/* Total size of mapped qiov, accessed under dma_map_lock */
int dma_map_count;
+
+ /* PCI address (required for nvme_refresh_filename()) */
+ char *device;
} BDRVNVMeState;
#define NVME_BLOCK_OPT_DEVICE "device"
@@ -557,6 +560,7 @@ static int nvme_init(BlockDriverState *bs, const char *device, int namespace,
qemu_co_mutex_init(&s->dma_map_lock);
qemu_co_queue_init(&s->dma_flush_queue);
+ s->device = g_strdup(device);
s->nsid = namespace;
s->aio_context = bdrv_get_aio_context(bs);
ret = event_notifier_init(&s->irq_notifier, 0);
@@ -729,6 +733,8 @@ static void nvme_close(BlockDriverState *bs)
event_notifier_cleanup(&s->irq_notifier);
qemu_vfio_pci_unmap_bar(s->vfio, 0, (void *)s->regs, 0, NVME_BAR_SIZE);
qemu_vfio_close(s->vfio);
+
+ g_free(s->device);
}
static int nvme_file_open(BlockDriverState *bs, QDict *options, int flags,
@@ -1053,17 +1059,12 @@ static int nvme_reopen_prepare(BDRVReopenState *reopen_state,
return 0;
}
-static void nvme_refresh_filename(BlockDriverState *bs, QDict *opts)
+static void nvme_refresh_filename(BlockDriverState *bs)
{
- qdict_del(opts, "filename");
-
- if (!qdict_size(opts)) {
- snprintf(bs->exact_filename, sizeof(bs->exact_filename), "%s://",
- bs->drv->format_name);
- }
+ BDRVNVMeState *s = bs->opaque;
- qdict_put_str(opts, "driver", bs->drv->format_name);
- bs->full_open_options = qobject_ref(opts);
+ snprintf(bs->exact_filename, sizeof(bs->exact_filename), "nvme://%s/%i",
+ s->device, s->nsid);
}
static void nvme_refresh_limits(BlockDriverState *bs, Error **errp)
@@ -1136,6 +1137,13 @@ static void nvme_unregister_buf(BlockDriverState *bs, void *host)
qemu_vfio_dma_unmap(s->vfio, host);
}
+static const char *const nvme_strong_runtime_opts[] = {
+ NVME_BLOCK_OPT_DEVICE,
+ NVME_BLOCK_OPT_NAMESPACE,
+
+ NULL
+};
+
static BlockDriver bdrv_nvme = {
.format_name = "nvme",
.protocol_name = "nvme",
@@ -1153,6 +1161,7 @@ static BlockDriver bdrv_nvme = {
.bdrv_refresh_filename = nvme_refresh_filename,
.bdrv_refresh_limits = nvme_refresh_limits,
+ .strong_runtime_opts = nvme_strong_runtime_opts,
.bdrv_detach_aio_context = nvme_detach_aio_context,
.bdrv_attach_aio_context = nvme_attach_aio_context,
diff --git a/block/qapi.c b/block/qapi.c
index 00291f9..6002a76 100644
--- a/block/qapi.c
+++ b/block/qapi.c
@@ -51,6 +51,8 @@ BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk,
return NULL;
}
+ bdrv_refresh_filename(bs);
+
info = g_malloc0(sizeof(*info));
info->file = g_strdup(bs->filename);
info->ro = bs->read_only;
@@ -264,6 +266,8 @@ void bdrv_query_image_info(BlockDriverState *bs,
goto out;
}
+ bdrv_refresh_filename(bs);
+
info = g_new0(ImageInfo, 1);
info->filename = g_strdup(bs->filename);
info->format = g_strdup(bdrv_get_format_name(bs));
@@ -292,18 +296,10 @@ void bdrv_query_image_info(BlockDriverState *bs,
backing_filename = bs->backing_file;
if (backing_filename[0] != '\0') {
- char *backing_filename2 = g_malloc0(PATH_MAX);
+ char *backing_filename2;
info->backing_filename = g_strdup(backing_filename);
info->has_backing_filename = true;
- bdrv_get_full_backing_filename(bs, backing_filename2, PATH_MAX, &err);
- if (err) {
- /* Can't reconstruct the full backing filename, so we must omit
- * this field and apply a Best Effort to this query. */
- g_free(backing_filename2);
- backing_filename2 = NULL;
- error_free(err);
- err = NULL;
- }
+ backing_filename2 = bdrv_get_full_backing_filename(bs, NULL);
/* Always report the full_backing_filename if present, even if it's the
* same as backing_filename. That they are same is useful info. */
diff --git a/block/qcow.c b/block/qcow.c
index 409c700..10d2cf1 100644
--- a/block/qcow.c
+++ b/block/qcow.c
@@ -31,6 +31,7 @@
#include "qemu/module.h"
#include "qemu/option.h"
#include "qemu/bswap.h"
+#include "qemu/cutils.h"
#include <zlib.h>
#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qstring.h"
@@ -295,11 +296,13 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
goto fail;
}
ret = bdrv_pread(bs->file, header.backing_file_offset,
- bs->backing_file, len);
+ bs->auto_backing_file, len);
if (ret < 0) {
goto fail;
}
- bs->backing_file[len] = '\0';
+ bs->auto_backing_file[len] = '\0';
+ pstrcpy(bs->backing_file, sizeof(bs->backing_file),
+ bs->auto_backing_file);
}
/* Disable migration when qcow images are used */
@@ -1170,6 +1173,12 @@ static QemuOptsList qcow_create_opts = {
}
};
+static const char *const qcow_strong_runtime_opts[] = {
+ "encrypt." BLOCK_CRYPTO_OPT_QCOW_KEY_SECRET,
+
+ NULL
+};
+
static BlockDriver bdrv_qcow = {
.format_name = "qcow",
.instance_size = sizeof(BDRVQcowState),
@@ -1193,6 +1202,7 @@ static BlockDriver bdrv_qcow = {
.bdrv_get_info = qcow_get_info,
.create_opts = &qcow_create_opts,
+ .strong_runtime_opts = qcow_strong_runtime_opts,
};
static void bdrv_qcow_init(void)
diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c
index 30eca26..179aa2c 100644
--- a/block/qcow2-cluster.c
+++ b/block/qcow2-cluster.c
@@ -285,6 +285,9 @@ static int l2_allocate(BlockDriverState *bs, int l1_index)
goto fail;
}
+ /* The offset must fit in the offset field of the L1 table entry */
+ assert((l2_offset & L1E_OFFSET_MASK) == l2_offset);
+
/* If we're allocating the table at offset 0 then something is wrong */
if (l2_offset == 0) {
qcow2_signal_corruption(bs, true, -1, -1, "Preventing invalid "
diff --git a/block/qcow2-snapshot.c b/block/qcow2-snapshot.c
index bb6a5b7..20e8472 100644
--- a/block/qcow2-snapshot.c
+++ b/block/qcow2-snapshot.c
@@ -358,11 +358,6 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
/* Generate an ID */
find_new_snapshot_id(bs, sn_info->id_str, sizeof(sn_info->id_str));
- /* Check that the ID is unique */
- if (find_snapshot_by_id_and_name(bs, sn_info->id_str, NULL) >= 0) {
- return -EEXIST;
- }
-
/* Populate sn with passed data */
sn->id_str = g_strdup(sn_info->id_str);
sn->name = g_strdup(sn_info->name);
diff --git a/block/qcow2.c b/block/qcow2.c
index b6d4752..7fb2730 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -1474,13 +1474,15 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options,
goto fail;
}
ret = bdrv_pread(bs->file, header.backing_file_offset,
- bs->backing_file, len);
+ bs->auto_backing_file, len);
if (ret < 0) {
error_setg_errno(errp, -ret, "Could not read backing file name");
goto fail;
}
- bs->backing_file[len] = '\0';
- s->image_backing_file = g_strdup(bs->backing_file);
+ bs->auto_backing_file[len] = '\0';
+ pstrcpy(bs->backing_file, sizeof(bs->backing_file),
+ bs->auto_backing_file);
+ s->image_backing_file = g_strdup(bs->auto_backing_file);
}
/* Internal snapshots */
@@ -2518,6 +2520,8 @@ static int qcow2_change_backing_file(BlockDriverState *bs,
return -EINVAL;
}
+ pstrcpy(bs->auto_backing_file, sizeof(bs->auto_backing_file),
+ backing_file ?: "");
pstrcpy(bs->backing_file, sizeof(bs->backing_file), backing_file ?: "");
pstrcpy(bs->backing_format, sizeof(bs->backing_format), backing_fmt ?: "");
@@ -4224,6 +4228,60 @@ static coroutine_fn int qcow2_co_flush_to_os(BlockDriverState *bs)
return ret;
}
+static ssize_t qcow2_measure_crypto_hdr_init_func(QCryptoBlock *block,
+ size_t headerlen, void *opaque, Error **errp)
+{
+ size_t *headerlenp = opaque;
+
+ /* Stash away the payload size */
+ *headerlenp = headerlen;
+ return 0;
+}
+
+static ssize_t qcow2_measure_crypto_hdr_write_func(QCryptoBlock *block,
+ size_t offset, const uint8_t *buf, size_t buflen,
+ void *opaque, Error **errp)
+{
+ /* Discard the bytes, we're not actually writing to an image */
+ return buflen;
+}
+
+/* Determine the number of bytes for the LUKS payload */
+static bool qcow2_measure_luks_headerlen(QemuOpts *opts, size_t *len,
+ Error **errp)
+{
+ QDict *opts_qdict;
+ QDict *cryptoopts_qdict;
+ QCryptoBlockCreateOptions *cryptoopts;
+ QCryptoBlock *crypto;
+
+ /* Extract "encrypt." options into a qdict */
+ opts_qdict = qemu_opts_to_qdict(opts, NULL);
+ qdict_extract_subqdict(opts_qdict, &cryptoopts_qdict, "encrypt.");
+ qobject_unref(opts_qdict);
+
+ /* Build QCryptoBlockCreateOptions object from qdict */
+ qdict_put_str(cryptoopts_qdict, "format", "luks");
+ cryptoopts = block_crypto_create_opts_init(cryptoopts_qdict, errp);
+ qobject_unref(cryptoopts_qdict);
+ if (!cryptoopts) {
+ return false;
+ }
+
+ /* Fake LUKS creation in order to determine the payload size */
+ crypto = qcrypto_block_create(cryptoopts, "encrypt.",
+ qcow2_measure_crypto_hdr_init_func,
+ qcow2_measure_crypto_hdr_write_func,
+ len, errp);
+ qapi_free_QCryptoBlockCreateOptions(cryptoopts);
+ if (!crypto) {
+ return false;
+ }
+
+ qcrypto_block_free(crypto);
+ return true;
+}
+
static BlockMeasureInfo *qcow2_measure(QemuOpts *opts, BlockDriverState *in_bs,
Error **errp)
{
@@ -4233,11 +4291,13 @@ static BlockMeasureInfo *qcow2_measure(QemuOpts *opts, BlockDriverState *in_bs,
uint64_t virtual_size; /* disk size as seen by guest */
uint64_t refcount_bits;
uint64_t l2_tables;
+ uint64_t luks_payload_size = 0;
size_t cluster_size;
int version;
char *optstr;
PreallocMode prealloc;
bool has_backing_file;
+ bool has_luks;
/* Parse image creation options */
cluster_size = qcow2_opt_get_cluster_size_del(opts, &local_err);
@@ -4267,6 +4327,20 @@ static BlockMeasureInfo *qcow2_measure(QemuOpts *opts, BlockDriverState *in_bs,
has_backing_file = !!optstr;
g_free(optstr);
+ optstr = qemu_opt_get_del(opts, BLOCK_OPT_ENCRYPT_FORMAT);
+ has_luks = optstr && strcmp(optstr, "luks") == 0;
+ g_free(optstr);
+
+ if (has_luks) {
+ size_t headerlen;
+
+ if (!qcow2_measure_luks_headerlen(opts, &headerlen, &local_err)) {
+ goto err;
+ }
+
+ luks_payload_size = ROUND_UP(headerlen, cluster_size);
+ }
+
virtual_size = qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0);
virtual_size = ROUND_UP(virtual_size, cluster_size);
@@ -4337,7 +4411,7 @@ static BlockMeasureInfo *qcow2_measure(QemuOpts *opts, BlockDriverState *in_bs,
info = g_new(BlockMeasureInfo, 1);
info->fully_allocated =
qcow2_calc_prealloc_size(virtual_size, cluster_size,
- ctz32(refcount_bits));
+ ctz32(refcount_bits)) + luks_payload_size;
/* Remove data clusters that are not required. This overestimates the
* required size because metadata needed for the fully allocated file is
@@ -4924,6 +4998,12 @@ static QemuOptsList qcow2_create_opts = {
}
};
+static const char *const qcow2_strong_runtime_opts[] = {
+ "encrypt." BLOCK_CRYPTO_OPT_QCOW_KEY_SECRET,
+
+ NULL
+};
+
BlockDriver bdrv_qcow2 = {
.format_name = "qcow2",
.instance_size = sizeof(BDRVQcow2State),
@@ -4972,6 +5052,7 @@ BlockDriver bdrv_qcow2 = {
.bdrv_inactivate = qcow2_inactivate,
.create_opts = &qcow2_create_opts,
+ .strong_runtime_opts = qcow2_strong_runtime_opts,
.bdrv_co_check = qcow2_co_check,
.bdrv_amend_options = qcow2_amend_options,
diff --git a/block/qed.c b/block/qed.c
index c5e6d6a..89af05d 100644
--- a/block/qed.c
+++ b/block/qed.c
@@ -449,11 +449,14 @@ static int coroutine_fn bdrv_qed_do_open(BlockDriverState *bs, QDict *options,
}
ret = qed_read_string(bs->file, s->header.backing_filename_offset,
- s->header.backing_filename_size, bs->backing_file,
- sizeof(bs->backing_file));
+ s->header.backing_filename_size,
+ bs->auto_backing_file,
+ sizeof(bs->auto_backing_file));
if (ret < 0) {
return ret;
}
+ pstrcpy(bs->backing_file, sizeof(bs->backing_file),
+ bs->auto_backing_file);
if (s->header.features & QED_F_BACKING_FORMAT_NO_PROBE) {
pstrcpy(bs->backing_format, sizeof(bs->backing_format), "raw");
diff --git a/block/quorum.c b/block/quorum.c
index 16b3c80..352f729 100644
--- a/block/quorum.c
+++ b/block/quorum.c
@@ -1065,36 +1065,64 @@ static void quorum_del_child(BlockDriverState *bs, BdrvChild *child,
bdrv_drained_end(bs);
}
-static void quorum_refresh_filename(BlockDriverState *bs, QDict *options)
+static void quorum_gather_child_options(BlockDriverState *bs, QDict *target,
+ bool backing_overridden)
{
BDRVQuorumState *s = bs->opaque;
- QDict *opts;
- QList *children;
+ QList *children_list;
int i;
- for (i = 0; i < s->num_children; i++) {
- bdrv_refresh_filename(s->children[i]->bs);
- if (!s->children[i]->bs->full_open_options) {
- return;
- }
- }
+ /*
+ * The generic implementation for gathering child options in
+ * bdrv_refresh_filename() would use the names of the children
+ * as specified for bdrv_open_child() or bdrv_attach_child(),
+ * which is "children.%u" with %u being a value
+ * (s->next_child_index) that is incremented each time a new child
+ * is added (and never decremented). Since children can be
+ * deleted at runtime, there may be gaps in that enumeration.
+ * When creating a new quorum BDS and specifying the children for
+ * it through runtime options, the enumeration used there may not
+ * have any gaps, though.
+ *
+ * Therefore, we have to create a new gap-less enumeration here
+ * (which we can achieve by simply putting all of the children's
+ * full_open_options into a QList).
+ *
+ * XXX: Note that there are issues with the current child option
+ * structure quorum uses (such as the fact that children do
+ * not really have unique permanent names). Therefore, this
+ * is going to have to change in the future and ideally we
+ * want quorum to be covered by the generic implementation.
+ */
+
+ children_list = qlist_new();
+ qdict_put(target, "children", children_list);
- children = qlist_new();
for (i = 0; i < s->num_children; i++) {
- qlist_append(children,
+ qlist_append(children_list,
qobject_ref(s->children[i]->bs->full_open_options));
}
+}
- opts = qdict_new();
- qdict_put_str(opts, "driver", "quorum");
- qdict_put_int(opts, QUORUM_OPT_VOTE_THRESHOLD, s->threshold);
- qdict_put_bool(opts, QUORUM_OPT_BLKVERIFY, s->is_blkverify);
- qdict_put_bool(opts, QUORUM_OPT_REWRITE, s->rewrite_corrupted);
- qdict_put(opts, "children", children);
-
- bs->full_open_options = opts;
+static char *quorum_dirname(BlockDriverState *bs, Error **errp)
+{
+ /* In general, there are multiple BDSs with different dirnames below this
+ * one; so there is no unique dirname we could return (unless all are equal
+ * by chance, or there is only one). Therefore, to be consistent, just
+ * always return NULL. */
+ error_setg(errp, "Cannot generate a base directory for quorum nodes");
+ return NULL;
}
+static const char *const quorum_strong_runtime_opts[] = {
+ QUORUM_OPT_VOTE_THRESHOLD,
+ QUORUM_OPT_BLKVERIFY,
+ QUORUM_OPT_REWRITE,
+ QUORUM_OPT_READ_PATTERN,
+
+ NULL
+};
+
static BlockDriver bdrv_quorum = {
.format_name = "quorum",
@@ -1102,7 +1130,8 @@ static BlockDriver bdrv_quorum = {
.bdrv_open = quorum_open,
.bdrv_close = quorum_close,
- .bdrv_refresh_filename = quorum_refresh_filename,
+ .bdrv_gather_child_options = quorum_gather_child_options,
+ .bdrv_dirname = quorum_dirname,
.bdrv_co_flush_to_disk = quorum_co_flush,
@@ -1118,6 +1147,8 @@ static BlockDriver bdrv_quorum = {
.is_filter = true,
.bdrv_recurse_is_first_non_filter = quorum_recurse_is_first_non_filter,
+
+ .strong_runtime_opts = quorum_strong_runtime_opts,
};
static void bdrv_quorum_init(void)
diff --git a/block/raw-format.c b/block/raw-format.c
index 6f6dc99..e3e5ba2 100644
--- a/block/raw-format.c
+++ b/block/raw-format.c
@@ -436,6 +436,7 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags,
bs->file->bs->supported_zero_flags);
if (bs->probed && !bdrv_is_read_only(bs)) {
+ bdrv_refresh_filename(bs->file->bs);
fprintf(stderr,
"WARNING: Image format was not specified for '%s' and probing "
"guessed raw.\n"
@@ -531,6 +532,13 @@ static int coroutine_fn raw_co_copy_range_to(BlockDriverState *bs,
read_flags, write_flags);
}
+static const char *const raw_strong_runtime_opts[] = {
+ "offset",
+ "size",
+
+ NULL
+};
+
BlockDriver bdrv_raw = {
.format_name = "raw",
.instance_size = sizeof(BDRVRawState),
@@ -560,7 +568,8 @@ BlockDriver bdrv_raw = {
.bdrv_lock_medium = &raw_lock_medium,
.bdrv_co_ioctl = &raw_co_ioctl,
.create_opts = &raw_create_opts,
- .bdrv_has_zero_init = &raw_has_zero_init
+ .bdrv_has_zero_init = &raw_has_zero_init,
+ .strong_runtime_opts = raw_strong_runtime_opts,
};
static void bdrv_raw_init(void)
diff --git a/block/rbd.c b/block/rbd.c
index 8a1a9f4..0c549c9 100644
--- a/block/rbd.c
+++ b/block/rbd.c
@@ -1228,6 +1228,18 @@ static QemuOptsList qemu_rbd_create_opts = {
}
};
+static const char *const qemu_rbd_strong_runtime_opts[] = {
+ "pool",
+ "image",
+ "conf",
+ "snapshot",
+ "user",
+ "server.",
+ "password-secret",
+
+ NULL
+};
+
static BlockDriver bdrv_rbd = {
.format_name = "rbd",
.instance_size = sizeof(BDRVRBDState),
@@ -1265,6 +1277,8 @@ static BlockDriver bdrv_rbd = {
#ifdef LIBRBD_SUPPORTS_INVALIDATE
.bdrv_co_invalidate_cache = qemu_rbd_co_invalidate_cache,
#endif
+
+ .strong_runtime_opts = qemu_rbd_strong_runtime_opts,
};
static void bdrv_rbd_init(void)
diff --git a/block/replication.c b/block/replication.c
index e70dd95..4c80b54 100644
--- a/block/replication.c
+++ b/block/replication.c
@@ -616,8 +616,6 @@ static void replication_done(void *opaque, int ret)
if (ret == 0) {
s->stage = BLOCK_REPLICATION_DONE;
- /* refresh top bs's filename */
- bdrv_refresh_filename(bs);
s->active_disk = NULL;
s->secondary_disk = NULL;
s->hidden_disk = NULL;
@@ -678,6 +676,13 @@ static void replication_stop(ReplicationState *rs, bool failover, Error **errp)
aio_context_release(aio_context);
}
+static const char *const replication_strong_runtime_opts[] = {
+ REPLICATION_MODE,
+ REPLICATION_TOP_ID,
+
+ NULL
+};
+
BlockDriver bdrv_replication = {
.format_name = "replication",
.instance_size = sizeof(BDRVReplicationState),
@@ -694,6 +699,7 @@ BlockDriver bdrv_replication = {
.bdrv_recurse_is_first_non_filter = replication_recurse_is_first_non_filter,
.has_variable_length = true,
+ .strong_runtime_opts = replication_strong_runtime_opts,
};
static void bdrv_replication_init(void)
diff --git a/block/sheepdog.c b/block/sheepdog.c
index b916ba0..cbdfe9a 100644
--- a/block/sheepdog.c
+++ b/block/sheepdog.c
@@ -3203,6 +3203,15 @@ static QemuOptsList sd_create_opts = {
}
};
+static const char *const sd_strong_runtime_opts[] = {
+ "vdi",
+ "snap-id",
+ "tag",
+ "server.",
+
+ NULL
+};
+
static BlockDriver bdrv_sheepdog = {
.format_name = "sheepdog",
.protocol_name = "sheepdog",
@@ -3238,6 +3247,7 @@ static BlockDriver bdrv_sheepdog = {
.bdrv_attach_aio_context = sd_attach_aio_context,
.create_opts = &sd_create_opts,
+ .strong_runtime_opts = sd_strong_runtime_opts,
};
static BlockDriver bdrv_sheepdog_tcp = {
@@ -3275,6 +3285,7 @@ static BlockDriver bdrv_sheepdog_tcp = {
.bdrv_attach_aio_context = sd_attach_aio_context,
.create_opts = &sd_create_opts,
+ .strong_runtime_opts = sd_strong_runtime_opts,
};
static BlockDriver bdrv_sheepdog_unix = {
@@ -3312,6 +3323,7 @@ static BlockDriver bdrv_sheepdog_unix = {
.bdrv_attach_aio_context = sd_attach_aio_context,
.create_opts = &sd_create_opts,
+ .strong_runtime_opts = sd_strong_runtime_opts,
};
static void bdrv_sheepdog_init(void)
diff --git a/block/snapshot.c b/block/snapshot.c
index 3218a54..f2f48f9 100644
--- a/block/snapshot.c
+++ b/block/snapshot.c
@@ -63,7 +63,7 @@ int bdrv_snapshot_find(BlockDriverState *bs, QEMUSnapshotInfo *sn_info,
}
for (i = 0; i < nb_sns; i++) {
sn = &sn_tab[i];
- if (!strcmp(sn->id_str, name) || !strcmp(sn->name, name)) {
+ if (!strcmp(sn->name, name)) {
*sn_info = *sn;
ret = 0;
break;
@@ -301,26 +301,6 @@ int bdrv_snapshot_delete(BlockDriverState *bs,
return ret;
}
-int bdrv_snapshot_delete_by_id_or_name(BlockDriverState *bs,
- const char *id_or_name,
- Error **errp)
-{
- int ret;
- Error *local_err = NULL;
-
- ret = bdrv_snapshot_delete(bs, id_or_name, NULL, &local_err);
- if (ret == -ENOENT || ret == -EINVAL) {
- error_free(local_err);
- local_err = NULL;
- ret = bdrv_snapshot_delete(bs, NULL, id_or_name, &local_err);
- }
-
- if (ret < 0) {
- error_propagate(errp, local_err);
- }
- return ret;
-}
-
int bdrv_snapshot_list(BlockDriverState *bs,
QEMUSnapshotInfo **psn_info)
{
@@ -448,7 +428,8 @@ int bdrv_all_delete_snapshot(const char *name, BlockDriverState **first_bad_bs,
aio_context_acquire(ctx);
if (bdrv_can_snapshot(bs) &&
bdrv_snapshot_find(bs, snapshot, name) >= 0) {
- ret = bdrv_snapshot_delete_by_id_or_name(bs, name, err);
+ ret = bdrv_snapshot_delete(bs, snapshot->id_str,
+ snapshot->name, err);
}
aio_context_release(ctx);
if (ret < 0) {
diff --git a/block/ssh.c b/block/ssh.c
index bbc513e..190ef95 100644
--- a/block/ssh.c
+++ b/block/ssh.c
@@ -1254,6 +1254,17 @@ static int coroutine_fn ssh_co_truncate(BlockDriverState *bs, int64_t offset,
return ssh_grow_file(s, offset, errp);
}
+static const char *const ssh_strong_runtime_opts[] = {
+ "host",
+ "port",
+ "path",
+ "user",
+ "host_key_check",
+ "server.",
+
+ NULL
+};
+
static BlockDriver bdrv_ssh = {
.format_name = "ssh",
.protocol_name = "ssh",
@@ -1270,6 +1281,7 @@ static BlockDriver bdrv_ssh = {
.bdrv_co_truncate = ssh_co_truncate,
.bdrv_co_flush_to_disk = ssh_co_flush,
.create_opts = &ssh_create_opts,
+ .strong_runtime_opts = ssh_strong_runtime_opts,
};
static void bdrv_ssh_init(void)
diff --git a/block/throttle.c b/block/throttle.c
index 636c976..f64dcc2 100644
--- a/block/throttle.c
+++ b/block/throttle.c
@@ -227,6 +227,12 @@ static void coroutine_fn throttle_co_drain_end(BlockDriverState *bs)
atomic_dec(&tgm->io_limits_disabled);
}
+static const char *const throttle_strong_runtime_opts[] = {
+ QEMU_OPT_THROTTLE_GROUP_NAME,
+
+ NULL
+};
+
static BlockDriver bdrv_throttle = {
.format_name = "throttle",
.instance_size = sizeof(ThrottleGroupMember),
@@ -259,6 +265,7 @@ static BlockDriver bdrv_throttle = {
.bdrv_co_drain_end = throttle_co_drain_end,
.is_filter = true,
+ .strong_runtime_opts = throttle_strong_runtime_opts,
};
static void bdrv_throttle_init(void)
diff --git a/block/vhdx-log.c b/block/vhdx-log.c
index ecd6426..3149ff0 100644
--- a/block/vhdx-log.c
+++ b/block/vhdx-log.c
@@ -803,6 +803,7 @@ int vhdx_parse_log(BlockDriverState *bs, BDRVVHDXState *s, bool *flushed,
if (logs.valid) {
if (bs->read_only) {
+ bdrv_refresh_filename(bs);
ret = -EPERM;
error_setg(errp,
"VHDX image file '%s' opened read-only, but "
diff --git a/block/vmdk.c b/block/vmdk.c
index 4104874..d8c0c50 100644
--- a/block/vmdk.c
+++ b/block/vmdk.c
@@ -27,6 +27,7 @@
#include "qapi/error.h"
#include "block/block_int.h"
#include "sysemu/block-backend.h"
+#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qerror.h"
#include "qemu/error-report.h"
#include "qemu/module.h"
@@ -386,12 +387,14 @@ static int vmdk_parent_open(BlockDriverState *bs)
ret = -EINVAL;
goto out;
}
- if ((end_name - p_name) > sizeof(bs->backing_file) - 1) {
+ if ((end_name - p_name) > sizeof(bs->auto_backing_file) - 1) {
ret = -EINVAL;
goto out;
}
- pstrcpy(bs->backing_file, end_name - p_name + 1, p_name);
+ pstrcpy(bs->auto_backing_file, end_name - p_name + 1, p_name);
+ pstrcpy(bs->backing_file, sizeof(bs->backing_file),
+ bs->auto_backing_file);
}
out:
@@ -479,6 +482,7 @@ static int vmdk_init_tables(BlockDriverState *bs, VmdkExtent *extent,
extent->l1_table,
l1_size);
if (ret < 0) {
+ bdrv_refresh_filename(extent->file->bs);
error_setg_errno(errp, -ret,
"Could not read l1 table from extent '%s'",
extent->file->bs->filename);
@@ -499,6 +503,7 @@ static int vmdk_init_tables(BlockDriverState *bs, VmdkExtent *extent,
extent->l1_backup_table,
l1_size);
if (ret < 0) {
+ bdrv_refresh_filename(extent->file->bs);
error_setg_errno(errp, -ret,
"Could not read l1 backup table from extent '%s'",
extent->file->bs->filename);
@@ -530,6 +535,7 @@ static int vmdk_open_vmfs_sparse(BlockDriverState *bs,
ret = bdrv_pread(file, sizeof(magic), &header, sizeof(header));
if (ret < 0) {
+ bdrv_refresh_filename(file->bs);
error_setg_errno(errp, -ret,
"Could not read header from file '%s'",
file->bs->filename);
@@ -607,6 +613,7 @@ static int vmdk_open_vmdk4(BlockDriverState *bs,
ret = bdrv_pread(file, sizeof(magic), &header, sizeof(header));
if (ret < 0) {
+ bdrv_refresh_filename(file->bs);
error_setg_errno(errp, -ret,
"Could not read header from file '%s'",
file->bs->filename);
@@ -861,13 +868,13 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
if (!path_is_absolute(fname) && !path_has_protocol(fname) &&
!desc_file_path[0])
{
+ bdrv_refresh_filename(bs->file->bs);
error_setg(errp, "Cannot use relative extent paths with VMDK "
"descriptor file '%s'", bs->file->bs->filename);
return -EINVAL;
}
- extent_path = g_malloc0(PATH_MAX);
- path_combine(extent_path, PATH_MAX, desc_file_path, fname);
+ extent_path = path_combine(desc_file_path, fname);
ret = snprintf(extent_opt_prefix, 32, "extents.%d", s->num_extents);
assert(ret < 32);
@@ -2067,16 +2074,16 @@ static int coroutine_fn vmdk_co_do_create(int64_t size,
if (backing_file) {
BlockBackend *backing;
- char *full_backing = g_new0(char, PATH_MAX);
- bdrv_get_full_backing_filename_from_filename(blk_bs(blk)->filename, backing_file,
- full_backing, PATH_MAX,
- &local_err);
+ char *full_backing =
+ bdrv_get_full_backing_filename_from_filename(blk_bs(blk)->filename,
+ backing_file,
+ &local_err);
if (local_err) {
- g_free(full_backing);
error_propagate(errp, local_err);
ret = -ENOENT;
goto exit;
}
+ assert(full_backing);
backing = blk_new_open(full_backing, NULL, NULL,
BDRV_O_NO_BACKING, errp);
@@ -2255,7 +2262,7 @@ static int coroutine_fn vmdk_co_create_opts(const char *filename, QemuOpts *opts
compat6 = qemu_opt_get_bool_del(opts, BLOCK_OPT_COMPAT6, false);
if (strcmp(hw_version, "undefined") == 0) {
g_free(hw_version);
- hw_version = g_strdup("4");
+ hw_version = NULL;
}
fmt = qemu_opt_get_del(opts, BLOCK_OPT_SUBFMT);
zeroed_grain = qemu_opt_get_bool_del(opts, BLOCK_OPT_ZEROED_GRAIN, false);
@@ -2465,6 +2472,7 @@ static ImageInfo *vmdk_get_extent_info(VmdkExtent *extent)
{
ImageInfo *info = g_new0(ImageInfo, 1);
+ bdrv_refresh_filename(extent->file->bs);
*info = (ImageInfo){
.filename = g_strdup(extent->file->bs->filename),
.format = g_strdup(extent->type),
@@ -2596,6 +2604,23 @@ static int vmdk_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
return 0;
}
+static void vmdk_gather_child_options(BlockDriverState *bs, QDict *target,
+ bool backing_overridden)
+{
+ /* No children but file and backing can be explicitly specified (TODO) */
+ qdict_put(target, "file",
+ qobject_ref(bs->file->bs->full_open_options));
+
+ if (backing_overridden) {
+ if (bs->backing) {
+ qdict_put(target, "backing",
+ qobject_ref(bs->backing->bs->full_open_options));
+ } else {
+ qdict_put_null(target, "backing");
+ }
+ }
+}
+
static QemuOptsList vmdk_create_opts = {
.name = "vmdk-create-opts",
.head = QTAILQ_HEAD_INITIALIZER(vmdk_create_opts.head),
@@ -2667,6 +2692,7 @@ static BlockDriver bdrv_vmdk = {
.bdrv_get_specific_info = vmdk_get_specific_info,
.bdrv_refresh_limits = vmdk_refresh_limits,
.bdrv_get_info = vmdk_get_info,
+ .bdrv_gather_child_options = vmdk_gather_child_options,
.supports_backing = true,
.create_opts = &vmdk_create_opts,
diff --git a/block/vpc.c b/block/vpc.c
index 52ab717..a902a4c 100644
--- a/block/vpc.c
+++ b/block/vpc.c
@@ -1218,6 +1218,12 @@ static QemuOptsList vpc_create_opts = {
}
};
+static const char *const vpc_strong_runtime_opts[] = {
+ VPC_OPT_SIZE_CALC,
+
+ NULL
+};
+
static BlockDriver bdrv_vpc = {
.format_name = "vpc",
.instance_size = sizeof(BDRVVPCState),
@@ -1238,6 +1244,7 @@ static BlockDriver bdrv_vpc = {
.create_opts = &vpc_create_opts,
.bdrv_has_zero_init = vpc_has_zero_init,
+ .strong_runtime_opts = vpc_strong_runtime_opts,
};
static void bdrv_vpc_init(void)
diff --git a/block/vvfat.c b/block/vvfat.c
index b7b61ea..5f66787 100644
--- a/block/vvfat.c
+++ b/block/vvfat.c
@@ -3253,6 +3253,16 @@ static void vvfat_close(BlockDriverState *bs)
}
}
+static const char *const vvfat_strong_runtime_opts[] = {
+ "dir",
+ "fat-type",
+ "floppy",
+ "label",
+ "rw",
+
+ NULL
+};
+
static BlockDriver bdrv_vvfat = {
.format_name = "vvfat",
.protocol_name = "fat",
@@ -3267,6 +3277,8 @@ static BlockDriver bdrv_vvfat = {
.bdrv_co_preadv = vvfat_co_preadv,
.bdrv_co_pwritev = vvfat_co_pwritev,
.bdrv_co_block_status = vvfat_co_block_status,
+
+ .strong_runtime_opts = vvfat_strong_runtime_opts,
};
static void bdrv_vvfat_init(void)
diff --git a/block/vxhs.c b/block/vxhs.c
index 0cb0a00..2e18229 100644
--- a/block/vxhs.c
+++ b/block/vxhs.c
@@ -556,6 +556,16 @@ static int64_t vxhs_getlength(BlockDriverState *bs)
return vdisk_size;
}
+static const char *const vxhs_strong_runtime_opts[] = {
+ VXHS_OPT_VDISK_ID,
+ "tls-creds",
+ VXHS_OPT_HOST,
+ VXHS_OPT_PORT,
+ VXHS_OPT_SERVER".",
+
+ NULL
+};
+
static BlockDriver bdrv_vxhs = {
.format_name = "vxhs",
.protocol_name = "vxhs",
@@ -567,6 +577,7 @@ static BlockDriver bdrv_vxhs = {
.bdrv_getlength = vxhs_getlength,
.bdrv_aio_preadv = vxhs_aio_preadv,
.bdrv_aio_pwritev = vxhs_aio_pwritev,
+ .strong_runtime_opts = vxhs_strong_runtime_opts,
};
static void bdrv_vxhs_init(void)
diff --git a/blockdev.c b/blockdev.c
index 8714ad2..7e6bf99 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -1627,6 +1627,7 @@ static void external_snapshot_prepare(BlkActionState *common,
error_setg_errno(errp, -size, "bdrv_getlength failed");
goto out;
}
+ bdrv_refresh_filename(state->old_bs);
bdrv_img_create(new_image_file, format,
state->old_bs->filename,
state->old_bs->drv->format_name,
@@ -3230,6 +3231,7 @@ void qmp_block_stream(bool has_job_id, const char *job_id, const char *device,
goto out;
}
assert(bdrv_get_aio_context(base_bs) == aio_context);
+ bdrv_refresh_filename(base_bs);
base_name = base_bs->filename;
}
@@ -3349,6 +3351,10 @@ void qmp_block_commit(bool has_job_id, const char *job_id, const char *device,
goto out;
}
} else if (has_top && top) {
+ /* This strcmp() is just a shortcut, there is no need to
+ * refresh @bs's filename. If it mismatches,
+ * bdrv_find_backing_image() will do the refresh and may still
+ * return @bs. */
if (strcmp(bs->filename, top) != 0) {
top_bs = bdrv_find_backing_image(bs, top);
}
@@ -3509,6 +3515,7 @@ static BlockJob *do_drive_backup(DriveBackup *backup, JobTxn *txn,
if (backup->mode != NEW_IMAGE_MODE_EXISTING) {
assert(backup->format);
if (source) {
+ bdrv_refresh_filename(source);
bdrv_img_create(backup->target, backup->format, source->filename,
source->drv->format_name, NULL,
size, flags, false, &local_err);
@@ -3889,6 +3896,7 @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp)
break;
case NEW_IMAGE_MODE_ABSOLUTE_PATHS:
/* create new image with backing file */
+ bdrv_refresh_filename(source);
bdrv_img_create(arg->target, format,
source->filename,
source->drv->format_name,
diff --git a/hmp-commands.hx b/hmp-commands.hx
index ba71558..e5fbc2c 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -350,49 +350,57 @@ ETEXI
{
.name = "savevm",
.args_type = "name:s?",
- .params = "[tag|id]",
- .help = "save a VM snapshot. If no tag or id are provided, a new snapshot is created",
+ .params = "tag",
+ .help = "save a VM snapshot. If no tag is provided, a new snapshot is created",
.cmd = hmp_savevm,
},
STEXI
-@item savevm [@var{tag}|@var{id}]
+@item savevm @var{tag}
@findex savevm
Create a snapshot of the whole virtual machine. If @var{tag} is
provided, it is used as human readable identifier. If there is already
-a snapshot with the same tag or ID, it is replaced. More info at
+a snapshot with the same tag, it is replaced. More info at
@ref{vm_snapshots}.
+
+Since 4.0, savevm stopped allowing the snapshot id to be set, accepting
+only @var{tag} as parameter.
ETEXI
{
.name = "loadvm",
.args_type = "name:s",
- .params = "tag|id",
- .help = "restore a VM snapshot from its tag or id",
+ .params = "tag",
+ .help = "restore a VM snapshot from its tag",
.cmd = hmp_loadvm,
.command_completion = loadvm_completion,
},
STEXI
-@item loadvm @var{tag}|@var{id}
+@item loadvm @var{tag}
@findex loadvm
Set the whole virtual machine to the snapshot identified by the tag
-@var{tag} or the unique snapshot ID @var{id}.
+@var{tag}.
+
+Since 4.0, loadvm stopped accepting snapshot id as parameter.
ETEXI
{
.name = "delvm",
.args_type = "name:s",
- .params = "tag|id",
- .help = "delete a VM snapshot from its tag or id",
+ .params = "tag",
+ .help = "delete a VM snapshot from its tag",
.cmd = hmp_delvm,
.command_completion = delvm_completion,
},
STEXI
-@item delvm @var{tag}|@var{id}
+@item delvm @var{tag}
@findex delvm
-Delete the snapshot identified by @var{tag} or @var{id}.
+Delete the snapshot identified by @var{tag}.
+
+Since 4.0, delvm stopped deleting snapshots by snapshot id, accepting
+only @var{tag} as parameter.
ETEXI
{
diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c
index c159a3d..0cc3c59 100644
--- a/hw/block/virtio-blk.c
+++ b/hw/block/virtio-blk.c
@@ -839,6 +839,7 @@ static void virtio_blk_dma_restart_bh(void *opaque)
if (mrb.num_reqs) {
virtio_blk_submit_multireq(s->blk, &mrb);
}
+ blk_dec_in_flight(s->conf.conf.blk);
aio_context_release(blk_get_aio_context(s->conf.conf.blk));
}
@@ -852,8 +853,11 @@ static void virtio_blk_dma_restart_cb(void *opaque, int running,
}
if (!s->bh) {
+ /* FIXME The data plane is not started yet, so these requests are
+ * processed in the main thread. */
s->bh = aio_bh_new(blk_get_aio_context(s->conf.conf.blk),
virtio_blk_dma_restart_bh, s);
+ blk_inc_in_flight(s->conf.conf.blk);
qemu_bh_schedule(s->bh);
}
}
diff --git a/include/block/block.h b/include/block/block.h
index 73357c6..5b5cf86 100644
--- a/include/block/block.h
+++ b/include/block/block.h
@@ -485,21 +485,17 @@ void bdrv_round_to_clusters(BlockDriverState *bs,
int64_t *cluster_offset,
int64_t *cluster_bytes);
-const char *bdrv_get_encrypted_filename(BlockDriverState *bs);
void bdrv_get_backing_filename(BlockDriverState *bs,
char *filename, int filename_size);
-void bdrv_get_full_backing_filename(BlockDriverState *bs,
- char *dest, size_t sz, Error **errp);
-void bdrv_get_full_backing_filename_from_filename(const char *backed,
- const char *backing,
- char *dest, size_t sz,
- Error **errp);
+char *bdrv_get_full_backing_filename(BlockDriverState *bs, Error **errp);
+char *bdrv_get_full_backing_filename_from_filename(const char *backed,
+ const char *backing,
+ Error **errp);
+char *bdrv_dirname(BlockDriverState *bs, Error **errp);
int path_has_protocol(const char *path);
int path_is_absolute(const char *path);
-void path_combine(char *dest, int dest_size,
- const char *base_path,
- const char *filename);
+char *path_combine(const char *base_path, const char *filename);
int bdrv_readv_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos);
int bdrv_writev_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos);
diff --git a/include/block/block_int.h b/include/block/block_int.h
index 0075baf..836d67c 100644
--- a/include/block/block_int.h
+++ b/include/block/block_int.h
@@ -139,7 +139,42 @@ struct BlockDriver {
Error **errp);
int (*bdrv_make_empty)(BlockDriverState *bs);
- void (*bdrv_refresh_filename)(BlockDriverState *bs, QDict *options);
+ /*
+ * Refreshes the bs->exact_filename field. If that is impossible,
+ * bs->exact_filename has to be left empty.
+ */
+ void (*bdrv_refresh_filename)(BlockDriverState *bs);
+
+ /*
+ * Gathers the open options for all children into @target.
+ * A simple format driver (without backing file support) might
+ * implement this function like this:
+ *
+ * QINCREF(bs->file->bs->full_open_options);
+ * qdict_put(target, "file", bs->file->bs->full_open_options);
+ *
+ * If not specified, the generic implementation will simply put
+ * all children's options under their respective name.
+ *
+ * @backing_overridden is true when bs->backing seems not to be
+ * the child that would result from opening bs->backing_file.
+ * Therefore, if it is true, the backing child's options should be
+ * gathered; otherwise, there is no need since the backing child
+ * is the one implied by the image header.
+ *
+ * Note that ideally this function would not be needed. Every
+ * block driver which implements it is probably doing something
+ * shady regarding its runtime option structure.
+ */
+ void (*bdrv_gather_child_options)(BlockDriverState *bs, QDict *target,
+ bool backing_overridden);
+
+ /*
+ * Returns an allocated string which is the directory name of this BDS: It
+ * will be used to make relative filenames absolute by prepending this
+ * function's return value to them.
+ */
+ char *(*bdrv_dirname)(BlockDriverState *bs, Error **errp);
/* aio */
BlockAIOCB *(*bdrv_aio_preadv)(BlockDriverState *bs,
@@ -510,6 +545,13 @@ struct BlockDriver {
void (*bdrv_register_buf)(BlockDriverState *bs, void *host, size_t size);
void (*bdrv_unregister_buf)(BlockDriverState *bs, void *host);
QLIST_ENTRY(BlockDriver) list;
+
+ /* Pointer to a NULL-terminated array of names of strong options
+ * that can be specified for bdrv_open(). A strong option is one
+ * that changes the data of a BDS.
+ * If this pointer is NULL, the array is considered empty.
+ * "filename" and "driver" are always considered strong. */
+ const char *const *strong_runtime_opts;
};
typedef struct BlockLimits {
@@ -662,6 +704,11 @@ struct BdrvChild {
*/
uint64_t shared_perm;
+ /* backup of permissions during permission update procedure */
+ bool has_backup_perm;
+ uint64_t backup_perm;
+ uint64_t backup_shared_perm;
+
QLIST_ENTRY(BdrvChild) next;
QLIST_ENTRY(BdrvChild) next_parent;
};
@@ -697,6 +744,10 @@ struct BlockDriverState {
char filename[PATH_MAX];
char backing_file[PATH_MAX]; /* if non zero, the image is a diff of
this file image */
+ /* The backing filename indicated by the image header; if we ever
+ * open this file, then this is replaced by the resulting BDS's
+ * filename (i.e. after a bdrv_refresh_filename() run). */
+ char auto_backing_file[PATH_MAX];
char backing_format[16]; /* if non-zero and backing_file exists */
QDict *full_open_options;
diff --git a/include/block/nbd.h b/include/block/nbd.h
index 96cfb1d..c6ef1ef 100644
--- a/include/block/nbd.h
+++ b/include/block/nbd.h
@@ -300,7 +300,8 @@ int nbd_receive_export_list(QIOChannel *ioc, QCryptoTLSCreds *tlscreds,
int nbd_init(int fd, QIOChannelSocket *sioc, NBDExportInfo *info,
Error **errp);
int nbd_send_request(QIOChannel *ioc, NBDRequest *request);
-int nbd_receive_reply(QIOChannel *ioc, NBDReply *reply, Error **errp);
+int coroutine_fn nbd_receive_reply(BlockDriverState *bs, QIOChannel *ioc,
+ NBDReply *reply, Error **errp);
int nbd_client(int fd);
int nbd_disconnect(int fd);
int nbd_errno_to_system_errno(int err);
diff --git a/include/block/snapshot.h b/include/block/snapshot.h
index f73d109..b5d5084 100644
--- a/include/block/snapshot.h
+++ b/include/block/snapshot.h
@@ -61,9 +61,6 @@ int bdrv_snapshot_delete(BlockDriverState *bs,
const char *snapshot_id,
const char *name,
Error **errp);
-int bdrv_snapshot_delete_by_id_or_name(BlockDriverState *bs,
- const char *id_or_name,
- Error **errp);
int bdrv_snapshot_list(BlockDriverState *bs,
QEMUSnapshotInfo **psn_info);
int bdrv_snapshot_load_tmp(BlockDriverState *bs,
diff --git a/include/io/channel.h b/include/io/channel.h
index da2f138..59460cb 100644
--- a/include/io/channel.h
+++ b/include/io/channel.h
@@ -739,10 +739,13 @@ void qio_channel_detach_aio_context(QIOChannel *ioc);
* addition, no two coroutine can be waiting on the same condition
* and channel at the same time.
*
- * This must only be called from coroutine context
+ * This must only be called from coroutine context. It is safe to
+ * reenter the coroutine externally while it is waiting; in this
+ * case the function will return even if @condition is not yet
+ * available.
*/
-void qio_channel_yield(QIOChannel *ioc,
- GIOCondition condition);
+void coroutine_fn qio_channel_yield(QIOChannel *ioc,
+ GIOCondition condition);
/**
* qio_channel_wait:
diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h
index 832a4bf..e2066eb 100644
--- a/include/sysemu/block-backend.h
+++ b/include/sysemu/block-backend.h
@@ -156,6 +156,8 @@ int blk_co_pdiscard(BlockBackend *blk, int64_t offset, int bytes);
int blk_co_flush(BlockBackend *blk);
int blk_flush(BlockBackend *blk);
int blk_commit_all(void);
+void blk_inc_in_flight(BlockBackend *blk);
+void blk_dec_in_flight(BlockBackend *blk);
void blk_drain(BlockBackend *blk);
void blk_drain_all(void);
void blk_set_on_error(BlockBackend *blk, BlockdevOnError on_read_error,
diff --git a/io/channel.c b/io/channel.c
index 8dd0684..2a26c2a 100644
--- a/io/channel.c
+++ b/io/channel.c
@@ -400,15 +400,14 @@ off_t qio_channel_io_seek(QIOChannel *ioc,
}
-static void qio_channel_set_aio_fd_handlers(QIOChannel *ioc);
-
static void qio_channel_restart_read(void *opaque)
{
QIOChannel *ioc = opaque;
Coroutine *co = ioc->read_coroutine;
- ioc->read_coroutine = NULL;
- qio_channel_set_aio_fd_handlers(ioc);
+ /* Assert that aio_co_wake() reenters the coroutine directly */
+ assert(qemu_get_current_aio_context() ==
+ qemu_coroutine_get_aio_context(co));
aio_co_wake(co);
}
@@ -417,8 +416,9 @@ static void qio_channel_restart_write(void *opaque)
QIOChannel *ioc = opaque;
Coroutine *co = ioc->write_coroutine;
- ioc->write_coroutine = NULL;
- qio_channel_set_aio_fd_handlers(ioc);
+ /* Assert that aio_co_wake() reenters the coroutine directly */
+ assert(qemu_get_current_aio_context() ==
+ qemu_coroutine_get_aio_context(co));
aio_co_wake(co);
}
@@ -469,6 +469,16 @@ void coroutine_fn qio_channel_yield(QIOChannel *ioc,
}
qio_channel_set_aio_fd_handlers(ioc);
qemu_coroutine_yield();
+
+ /* Allow interrupting the operation by reentering the coroutine other than
+ * through the aio_fd_handlers. */
+ if (condition == G_IO_IN && ioc->read_coroutine) {
+ ioc->read_coroutine = NULL;
+ qio_channel_set_aio_fd_handlers(ioc);
+ } else if (condition == G_IO_OUT && ioc->write_coroutine) {
+ ioc->write_coroutine = NULL;
+ qio_channel_set_aio_fd_handlers(ioc);
+ }
}
diff --git a/nbd/client.c b/nbd/client.c
index 10a52ad..de7da48 100644
--- a/nbd/client.c
+++ b/nbd/client.c
@@ -1387,17 +1387,65 @@ static int nbd_receive_structured_reply_chunk(QIOChannel *ioc,
return 0;
}
+/* nbd_read_eof
+ * Tries to read @size bytes from @ioc.
+ * Returns 1 on success
+ * 0 on eof, when no data was read (errp is not set)
+ * negative errno on failure (errp is set)
+ */
+static inline int coroutine_fn
+nbd_read_eof(BlockDriverState *bs, QIOChannel *ioc, void *buffer, size_t size,
+ Error **errp)
+{
+ bool partial = false;
+
+ assert(size);
+ while (size > 0) {
+ struct iovec iov = { .iov_base = buffer, .iov_len = size };
+ ssize_t len;
+
+ len = qio_channel_readv(ioc, &iov, 1, errp);
+ if (len == QIO_CHANNEL_ERR_BLOCK) {
+ bdrv_dec_in_flight(bs);
+ qio_channel_yield(ioc, G_IO_IN);
+ bdrv_inc_in_flight(bs);
+ continue;
+ } else if (len < 0) {
+ return -EIO;
+ } else if (len == 0) {
+ if (partial) {
+ error_setg(errp,
+ "Unexpected end-of-file before all bytes were read");
+ return -EIO;
+ } else {
+ return 0;
+ }
+ }
+
+ partial = true;
+ size -= len;
+ buffer = (uint8_t*) buffer + len;
+ }
+ return 1;
+}
+
/* nbd_receive_reply
+ *
+ * Decreases bs->in_flight while waiting for a new reply. This yield is where
+ * we wait indefinitely and the coroutine must be able to be safely reentered
+ * for nbd_client_attach_aio_context().
+ *
* Returns 1 on success
* 0 on eof, when no data was read (errp is not set)
* negative errno on failure (errp is set)
*/
-int nbd_receive_reply(QIOChannel *ioc, NBDReply *reply, Error **errp)
+int coroutine_fn nbd_receive_reply(BlockDriverState *bs, QIOChannel *ioc,
+ NBDReply *reply, Error **errp)
{
int ret;
const char *type;
- ret = nbd_read_eof(ioc, &reply->magic, sizeof(reply->magic), errp);
+ ret = nbd_read_eof(bs, ioc, &reply->magic, sizeof(reply->magic), errp);
if (ret <= 0) {
return ret;
}
diff --git a/nbd/nbd-internal.h b/nbd/nbd-internal.h
index 82aa221..049f83d 100644
--- a/nbd/nbd-internal.h
+++ b/nbd/nbd-internal.h
@@ -64,25 +64,6 @@
#define NBD_SET_TIMEOUT _IO(0xab, 9)
#define NBD_SET_FLAGS _IO(0xab, 10)
-/* nbd_read_eof
- * Tries to read @size bytes from @ioc.
- * Returns 1 on success
- * 0 on eof, when no data was read (errp is not set)
- * negative errno on failure (errp is set)
- */
-static inline int nbd_read_eof(QIOChannel *ioc, void *buffer, size_t size,
- Error **errp)
-{
- int ret;
-
- assert(size);
- ret = qio_channel_read_all_eof(ioc, buffer, size, errp);
- if (ret < 0) {
- ret = -EIO;
- }
- return ret;
-}
-
/* nbd_write
* Writes @size bytes to @ioc. Returns 0 on success.
*/
diff --git a/qemu-img.c b/qemu-img.c
index 7853935..660c018 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -503,7 +503,7 @@ static int img_create(int argc, char **argv)
if (qemu_opts_foreach(&qemu_object_opts,
user_creatable_add_opts_foreach,
- NULL, NULL)) {
+ NULL, &error_fatal)) {
goto fail;
}
@@ -753,7 +753,7 @@ static int img_check(int argc, char **argv)
if (qemu_opts_foreach(&qemu_object_opts,
user_creatable_add_opts_foreach,
- NULL, NULL)) {
+ NULL, &error_fatal)) {
return 1;
}
@@ -966,7 +966,7 @@ static int img_commit(int argc, char **argv)
if (qemu_opts_foreach(&qemu_object_opts,
user_creatable_add_opts_foreach,
- NULL, NULL)) {
+ NULL, &error_fatal)) {
return 1;
}
@@ -1349,7 +1349,7 @@ static int img_compare(int argc, char **argv)
if (qemu_opts_foreach(&qemu_object_opts,
user_creatable_add_opts_foreach,
- NULL, NULL)) {
+ NULL, &error_fatal)) {
ret = 2;
goto out4;
}
@@ -2153,7 +2153,7 @@ static int img_convert(int argc, char **argv)
if (qemu_opts_foreach(&qemu_object_opts,
user_creatable_add_opts_foreach,
- NULL, NULL)) {
+ NULL, &error_fatal)) {
goto fail_getopt;
}
@@ -2707,7 +2707,7 @@ static int img_info(int argc, char **argv)
if (qemu_opts_foreach(&qemu_object_opts,
user_creatable_add_opts_foreach,
- NULL, NULL)) {
+ NULL, &error_fatal)) {
return 1;
}
@@ -2784,6 +2784,7 @@ static int get_block_status(BlockDriverState *bs, int64_t offset,
BlockDriverState *file;
bool has_offset;
int64_t map;
+ char *filename = NULL;
/* As an optimization, we could cache the current range of unallocated
* clusters in each file of the chain, and avoid querying the same
@@ -2811,6 +2812,11 @@ static int get_block_status(BlockDriverState *bs, int64_t offset,
has_offset = !!(ret & BDRV_BLOCK_OFFSET_VALID);
+ if (file && has_offset) {
+ bdrv_refresh_filename(file);
+ filename = file->filename;
+ }
+
*e = (MapEntry) {
.start = offset,
.length = bytes,
@@ -2819,8 +2825,8 @@ static int get_block_status(BlockDriverState *bs, int64_t offset,
.offset = map,
.has_offset = has_offset,
.depth = depth,
- .has_filename = file && has_offset,
- .filename = file && has_offset ? file->filename : NULL,
+ .has_filename = filename,
+ .filename = filename,
};
return 0;
@@ -2926,7 +2932,7 @@ static int img_map(int argc, char **argv)
if (qemu_opts_foreach(&qemu_object_opts,
user_creatable_add_opts_foreach,
- NULL, NULL)) {
+ NULL, &error_fatal)) {
return 1;
}
@@ -3075,7 +3081,7 @@ static int img_snapshot(int argc, char **argv)
if (qemu_opts_foreach(&qemu_object_opts,
user_creatable_add_opts_foreach,
- NULL, NULL)) {
+ NULL, &error_fatal)) {
return 1;
}
@@ -3117,11 +3123,18 @@ static int img_snapshot(int argc, char **argv)
break;
case SNAPSHOT_DELETE:
- bdrv_snapshot_delete_by_id_or_name(bs, snapshot_name, &err);
- if (err) {
- error_reportf_err(err, "Could not delete snapshot '%s': ",
- snapshot_name);
+ ret = bdrv_snapshot_find(bs, &sn, snapshot_name);
+ if (ret < 0) {
+ error_report("Could not delete snapshot '%s': snapshot not "
+ "found", snapshot_name);
ret = 1;
+ } else {
+ ret = bdrv_snapshot_delete(bs, sn.id_str, sn.name, &err);
+ if (ret < 0) {
+ error_reportf_err(err, "Could not delete snapshot '%s': ",
+ snapshot_name);
+ ret = 1;
+ }
}
break;
}
@@ -3235,7 +3248,7 @@ static int img_rebase(int argc, char **argv)
if (qemu_opts_foreach(&qemu_object_opts,
user_creatable_add_opts_foreach,
- NULL, NULL)) {
+ NULL, &error_fatal)) {
return 1;
}
@@ -3321,20 +3334,17 @@ static int img_rebase(int argc, char **argv)
qdict_put_bool(options, BDRV_OPT_FORCE_SHARE, true);
}
+ bdrv_refresh_filename(bs);
overlay_filename = bs->exact_filename[0] ? bs->exact_filename
: bs->filename;
- out_real_path = g_malloc(PATH_MAX);
-
- bdrv_get_full_backing_filename_from_filename(overlay_filename,
- out_baseimg,
- out_real_path,
- PATH_MAX,
- &local_err);
+ out_real_path =
+ bdrv_get_full_backing_filename_from_filename(overlay_filename,
+ out_baseimg,
+ &local_err);
if (local_err) {
error_reportf_err(local_err,
"Could not resolve backing filename: ");
ret = -1;
- g_free(out_real_path);
goto out;
}
@@ -3615,7 +3625,7 @@ static int img_resize(int argc, char **argv)
if (qemu_opts_foreach(&qemu_object_opts,
user_creatable_add_opts_foreach,
- NULL, NULL)) {
+ NULL, &error_fatal)) {
return 1;
}
@@ -3859,7 +3869,7 @@ static int img_amend(int argc, char **argv)
if (qemu_opts_foreach(&qemu_object_opts,
user_creatable_add_opts_foreach,
- NULL, NULL)) {
+ NULL, &error_fatal)) {
ret = -1;
goto out_no_progress;
}
@@ -4503,7 +4513,7 @@ static int img_dd(int argc, char **argv)
if (qemu_opts_foreach(&qemu_object_opts,
user_creatable_add_opts_foreach,
- NULL, NULL)) {
+ NULL, &error_fatal)) {
ret = -1;
goto out;
}
@@ -4780,7 +4790,7 @@ static int img_measure(int argc, char **argv)
if (qemu_opts_foreach(&qemu_object_opts,
user_creatable_add_opts_foreach,
- NULL, NULL)) {
+ NULL, &error_fatal)) {
goto out;
}
diff --git a/scripts/qemu.py b/scripts/qemu.py
index 32b00af..f7269ee 100644
--- a/scripts/qemu.py
+++ b/scripts/qemu.py
@@ -144,10 +144,9 @@ class QEMUMachine(object):
return False
# This can be used to add an unused monitor instance.
- def add_monitor_telnet(self, ip, port):
- args = 'tcp:%s:%d,server,nowait,telnet' % (ip, port)
+ def add_monitor_null(self):
self._args.append('-monitor')
- self._args.append(args)
+ self._args.append('null')
def add_fd(self, fd, fdset, opaque, opts=''):
"""
diff --git a/tests/Makefile.include b/tests/Makefile.include
index e60488d..370de59 100644
--- a/tests/Makefile.include
+++ b/tests/Makefile.include
@@ -70,6 +70,7 @@ check-unit-y += tests/test-throttle$(EXESUF)
check-unit-y += tests/test-thread-pool$(EXESUF)
check-unit-y += tests/test-hbitmap$(EXESUF)
check-unit-y += tests/test-bdrv-drain$(EXESUF)
+check-unit-y += tests/test-bdrv-graph-mod$(EXESUF)
check-unit-y += tests/test-blockjob$(EXESUF)
check-unit-y += tests/test-blockjob-txn$(EXESUF)
check-unit-y += tests/test-block-backend$(EXESUF)
@@ -561,6 +562,7 @@ tests/test-aio$(EXESUF): tests/test-aio.o $(test-block-obj-y)
tests/test-aio-multithread$(EXESUF): tests/test-aio-multithread.o $(test-block-obj-y)
tests/test-throttle$(EXESUF): tests/test-throttle.o $(test-block-obj-y)
tests/test-bdrv-drain$(EXESUF): tests/test-bdrv-drain.o $(test-block-obj-y) $(test-util-obj-y)
+tests/test-bdrv-graph-mod$(EXESUF): tests/test-bdrv-graph-mod.o $(test-block-obj-y) $(test-util-obj-y)
tests/test-blockjob$(EXESUF): tests/test-blockjob.o $(test-block-obj-y) $(test-util-obj-y)
tests/test-blockjob-txn$(EXESUF): tests/test-blockjob-txn.o $(test-block-obj-y) $(test-util-obj-y)
tests/test-block-backend$(EXESUF): tests/test-block-backend.o $(test-block-obj-y) $(test-util-obj-y)
diff --git a/tests/qemu-iotests/045 b/tests/qemu-iotests/045
index 55a5d31..d5484a0 100755
--- a/tests/qemu-iotests/045
+++ b/tests/qemu-iotests/045
@@ -132,7 +132,7 @@ class TestSCMFd(iotests.QMPTestCase):
qemu_img('create', '-f', iotests.imgfmt, image0, '128K')
# Add an unused monitor, to verify it works fine when two monitor
# instances present
- self.vm.add_monitor_telnet("0",4445)
+ self.vm.add_monitor_null()
self.vm.launch()
def tearDown(self):
diff --git a/tests/qemu-iotests/051.out b/tests/qemu-iotests/051.out
index 793af2a..b900935 100644
--- a/tests/qemu-iotests/051.out
+++ b/tests/qemu-iotests/051.out
@@ -82,7 +82,7 @@ QEMU X.Y.Z monitor - type 'help' for more information
Testing: -drive file=TEST_DIR/t.qcow2,driver=qcow2,backing.file.filename=TEST_DIR/t.qcow2.orig,if=none,id=drive0 -nodefaults
QEMU X.Y.Z monitor - type 'help' for more information
(qemu) info block
-drive0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2)
+drive0 (NODE_NAME): json:{"backing": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/t.qcow2.orig"}}, "driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/t.qcow2"}} (qcow2)
Removable device: not locked, tray closed
Cache mode: writeback
Backing file: TEST_DIR/t.qcow2.orig (chain depth: 1)
@@ -172,7 +172,7 @@ QEMU_PROG: -drive driver=null-co,cache=invalid_value: invalid cache option
Testing: -drive file=TEST_DIR/t.qcow2,cache=writeback,backing.file.filename=TEST_DIR/t.qcow2.base,backing.cache.no-flush=on,backing.node-name=backing,backing.file.node-name=backing-file,file.node-name=file,if=none,id=drive0 -nodefaults
QEMU X.Y.Z monitor - type 'help' for more information
(qemu) info block
-drive0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2)
+drive0 (NODE_NAME): json:{"backing": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/t.qcow2.base"}}, "driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/t.qcow2"}} (qcow2)
Removable device: not locked, tray closed
Cache mode: writeback
Backing file: TEST_DIR/t.qcow2.base (chain depth: 1)
@@ -192,7 +192,7 @@ backing-file: TEST_DIR/t.qcow2.base (file, read-only)
Testing: -drive file=TEST_DIR/t.qcow2,cache=writethrough,backing.file.filename=TEST_DIR/t.qcow2.base,backing.cache.no-flush=on,backing.node-name=backing,backing.file.node-name=backing-file,file.node-name=file,if=none,id=drive0 -nodefaults
QEMU X.Y.Z monitor - type 'help' for more information
(qemu) info block
-drive0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2)
+drive0 (NODE_NAME): json:{"backing": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/t.qcow2.base"}}, "driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/t.qcow2"}} (qcow2)
Removable device: not locked, tray closed
Cache mode: writethrough
Backing file: TEST_DIR/t.qcow2.base (chain depth: 1)
@@ -212,7 +212,7 @@ backing-file: TEST_DIR/t.qcow2.base (file, read-only)
Testing: -drive file=TEST_DIR/t.qcow2,cache=unsafe,backing.file.filename=TEST_DIR/t.qcow2.base,backing.cache.no-flush=on,backing.node-name=backing,backing.file.node-name=backing-file,file.node-name=file,if=none,id=drive0 -nodefaults
QEMU X.Y.Z monitor - type 'help' for more information
(qemu) info block
-drive0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2)
+drive0 (NODE_NAME): json:{"backing": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/t.qcow2.base"}}, "driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/t.qcow2"}} (qcow2)
Removable device: not locked, tray closed
Cache mode: writeback, ignore flushes
Backing file: TEST_DIR/t.qcow2.base (chain depth: 1)
diff --git a/tests/qemu-iotests/051.pc.out b/tests/qemu-iotests/051.pc.out
index ca64eda..8c5c735 100644
--- a/tests/qemu-iotests/051.pc.out
+++ b/tests/qemu-iotests/051.pc.out
@@ -82,7 +82,7 @@ QEMU X.Y.Z monitor - type 'help' for more information
Testing: -drive file=TEST_DIR/t.qcow2,driver=qcow2,backing.file.filename=TEST_DIR/t.qcow2.orig,if=none,id=drive0 -nodefaults
QEMU X.Y.Z monitor - type 'help' for more information
(qemu) info block
-drive0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2)
+drive0 (NODE_NAME): json:{"backing": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/t.qcow2.orig"}}, "driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/t.qcow2"}} (qcow2)
Removable device: not locked, tray closed
Cache mode: writeback
Backing file: TEST_DIR/t.qcow2.orig (chain depth: 1)
@@ -244,7 +244,7 @@ QEMU_PROG: -drive driver=null-co,cache=invalid_value: invalid cache option
Testing: -drive file=TEST_DIR/t.qcow2,cache=writeback,backing.file.filename=TEST_DIR/t.qcow2.base,backing.cache.no-flush=on,backing.node-name=backing,backing.file.node-name=backing-file,file.node-name=file,if=none,id=drive0 -nodefaults
QEMU X.Y.Z monitor - type 'help' for more information
(qemu) info block
-drive0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2)
+drive0 (NODE_NAME): json:{"backing": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/t.qcow2.base"}}, "driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/t.qcow2"}} (qcow2)
Removable device: not locked, tray closed
Cache mode: writeback
Backing file: TEST_DIR/t.qcow2.base (chain depth: 1)
@@ -264,7 +264,7 @@ backing-file: TEST_DIR/t.qcow2.base (file, read-only)
Testing: -drive file=TEST_DIR/t.qcow2,cache=writethrough,backing.file.filename=TEST_DIR/t.qcow2.base,backing.cache.no-flush=on,backing.node-name=backing,backing.file.node-name=backing-file,file.node-name=file,if=none,id=drive0 -nodefaults
QEMU X.Y.Z monitor - type 'help' for more information
(qemu) info block
-drive0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2)
+drive0 (NODE_NAME): json:{"backing": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/t.qcow2.base"}}, "driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/t.qcow2"}} (qcow2)
Removable device: not locked, tray closed
Cache mode: writethrough
Backing file: TEST_DIR/t.qcow2.base (chain depth: 1)
@@ -284,7 +284,7 @@ backing-file: TEST_DIR/t.qcow2.base (file, read-only)
Testing: -drive file=TEST_DIR/t.qcow2,cache=unsafe,backing.file.filename=TEST_DIR/t.qcow2.base,backing.cache.no-flush=on,backing.node-name=backing,backing.file.node-name=backing-file,file.node-name=file,if=none,id=drive0 -nodefaults
QEMU X.Y.Z monitor - type 'help' for more information
(qemu) info block
-drive0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2)
+drive0 (NODE_NAME): json:{"backing": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/t.qcow2.base"}}, "driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/t.qcow2"}} (qcow2)
Removable device: not locked, tray closed
Cache mode: writeback, ignore flushes
Backing file: TEST_DIR/t.qcow2.base (chain depth: 1)
diff --git a/tests/qemu-iotests/110 b/tests/qemu-iotests/110
index b64b3b2..185ad54 100755
--- a/tests/qemu-iotests/110
+++ b/tests/qemu-iotests/110
@@ -29,6 +29,7 @@ status=1 # failure is the default!
_cleanup()
{
_cleanup_test_img
+ rm -f "$TEST_IMG.copy"
}
trap "_cleanup; exit \$status" 0 1 2 3 15
@@ -60,7 +61,8 @@ echo '=== Non-reconstructable filename ==='
echo
# Across blkdebug without a config file, you cannot reconstruct filenames, so
-# qemu is incapable of knowing the directory of the top image
+# qemu is incapable of knowing the directory of the top image from the filename
+# alone. However, using bdrv_dirname(), it should still work.
TEST_IMG="json:{
'driver': '$IMGFMT',
'file': {
@@ -85,6 +87,31 @@ echo
# omit the image size; it should work anyway
_make_test_img -b "$TEST_IMG_REL.base"
+echo
+echo '=== Nodes without a common directory ==='
+echo
+
+cp "$TEST_IMG" "$TEST_IMG.copy"
+
+# Should inform us that the actual path of the backing file cannot be determined
+TEST_IMG="json:{
+ 'driver': '$IMGFMT',
+ 'file': {
+ 'driver': 'quorum',
+ 'vote-threshold': 1,
+ 'children': [
+ {
+ 'driver': 'file',
+ 'filename': '$TEST_IMG'
+ },
+ {
+ 'driver': 'file',
+ 'filename': '$TEST_IMG.copy'
+ }
+ ]
+ }
+}" _img_info | _filter_img_info
+
# success, all done
echo '*** done'
diff --git a/tests/qemu-iotests/110.out b/tests/qemu-iotests/110.out
index b3584ff..46e6a60 100644
--- a/tests/qemu-iotests/110.out
+++ b/tests/qemu-iotests/110.out
@@ -14,9 +14,16 @@ backing file: t.IMGFMT.base (actual path: TEST_DIR/t.IMGFMT.base)
image: json:{"driver": "IMGFMT", "file": {"set-state.0.event": "read_aio", "image": {"driver": "file", "filename": "TEST_DIR/t.IMGFMT"}, "driver": "blkdebug", "set-state.0.new_state": 42}}
file format: IMGFMT
virtual size: 64M (67108864 bytes)
-backing file: t.IMGFMT.base (cannot determine actual path)
+backing file: t.IMGFMT.base (actual path: TEST_DIR/t.IMGFMT.base)
=== Backing name is always relative to the backed image ===
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=t.IMGFMT.base
+
+=== Nodes without a common directory ===
+
+image: json:{"driver": "IMGFMT", "file": {"children": [{"driver": "file", "filename": "TEST_DIR/t.IMGFMT"}, {"driver": "file", "filename": "TEST_DIR/t.IMGFMT.copy"}], "driver": "quorum", "vote-threshold": 1}}
+file format: IMGFMT
+virtual size: 64M (67108864 bytes)
+backing file: t.IMGFMT.base (cannot determine actual path)
*** done
diff --git a/tests/qemu-iotests/178 b/tests/qemu-iotests/178
index 3f4b4a4..927bf06 100755
--- a/tests/qemu-iotests/178
+++ b/tests/qemu-iotests/178
@@ -142,6 +142,14 @@ for ofmt in human json; do
# The backing file doesn't need to exist :)
$QEMU_IMG measure --output=$ofmt -o backing_file=x \
-f "$fmt" -O "$IMGFMT" "$TEST_IMG"
+
+ echo
+ echo "== $fmt input image and LUKS encryption =="
+ echo
+ $QEMU_IMG measure --output=$ofmt \
+ --object secret,id=sec0,data=base \
+ -o encrypt.format=luks,encrypt.key-secret=sec0,encrypt.iter-time=10 \
+ -f "$fmt" -O "$IMGFMT" "$TEST_IMG"
fi
echo
diff --git a/tests/qemu-iotests/178.out.qcow2 b/tests/qemu-iotests/178.out.qcow2
index d42d4a4..55a8dc9 100644
--- a/tests/qemu-iotests/178.out.qcow2
+++ b/tests/qemu-iotests/178.out.qcow2
@@ -68,6 +68,11 @@ converted image file size in bytes: 458752
required size: 1074135040
fully allocated size: 1074135040
+== qcow2 input image and LUKS encryption ==
+
+required size: 2686976
+fully allocated size: 1076232192
+
== qcow2 input image and preallocation (human) ==
required size: 1074135040
@@ -114,6 +119,11 @@ converted image file size in bytes: 524288
required size: 1074135040
fully allocated size: 1074135040
+== raw input image and LUKS encryption ==
+
+required size: 2686976
+fully allocated size: 1076232192
+
== raw input image and preallocation (human) ==
required size: 1074135040
@@ -205,6 +215,13 @@ converted image file size in bytes: 458752
"fully-allocated": 1074135040
}
+== qcow2 input image and LUKS encryption ==
+
+{
+ "required": 2686976,
+ "fully-allocated": 1076232192
+}
+
== qcow2 input image and preallocation (json) ==
{
@@ -263,6 +280,13 @@ converted image file size in bytes: 524288
"fully-allocated": 1074135040
}
+== raw input image and LUKS encryption ==
+
+{
+ "required": 2686976,
+ "fully-allocated": 1076232192
+}
+
== raw input image and preallocation (json) ==
{
diff --git a/tests/qemu-iotests/206.out b/tests/qemu-iotests/206.out
index 91f4db5..0f1c23b 100644
--- a/tests/qemu-iotests/206.out
+++ b/tests/qemu-iotests/206.out
@@ -1,13 +1,13 @@
=== Successful image creation (defaults) ===
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.qcow2", "size": 0}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.qcow2", "size": 0}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
-{"execute": "blockdev-add", "arguments": {"driver": "file", "filename": "TEST_DIR/PID-t.qcow2", "node_name": "imgfile"}}
+{"execute": "blockdev-add", "arguments": {"driver": "file", "filename": "TEST_DIR/PID-t.qcow2", "node-name": "imgfile"}}
{"return": {}}
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "qcow2", "file": "imgfile", "size": 134217728}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "qcow2", "file": "imgfile", "size": 134217728}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
@@ -24,12 +24,12 @@ Format specific information:
=== Successful image creation (inline blockdev-add, explicit defaults) ===
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.qcow2", "nocow": false, "preallocation": "off", "size": 0}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.qcow2", "nocow": false, "preallocation": "off", "size": 0}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"cluster-size": 65536, "driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/PID-t.qcow2"}, "lazy-refcounts": false, "preallocation": "off", "refcount-bits": 16, "size": 67108864, "version": "v3"}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"cluster-size": 65536, "driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/PID-t.qcow2"}, "lazy-refcounts": false, "preallocation": "off", "refcount-bits": 16, "size": 67108864, "version": "v3"}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
@@ -46,12 +46,12 @@ Format specific information:
=== Successful image creation (v3 non-default options) ===
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.qcow2", "nocow": true, "preallocation": "falloc", "size": 0}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.qcow2", "nocow": true, "preallocation": "falloc", "size": 0}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"cluster-size": 2097152, "driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/PID-t.qcow2"}, "lazy-refcounts": true, "preallocation": "metadata", "refcount-bits": 1, "size": 33554432, "version": "v3"}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"cluster-size": 2097152, "driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/PID-t.qcow2"}, "lazy-refcounts": true, "preallocation": "metadata", "refcount-bits": 1, "size": 33554432, "version": "v3"}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
@@ -68,12 +68,12 @@ Format specific information:
=== Successful image creation (v2 non-default options) ===
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.qcow2", "size": 0}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.qcow2", "size": 0}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"backing-file": "TEST_DIR/PID-t.qcow2.base", "backing-fmt": "qcow2", "cluster-size": 512, "driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/PID-t.qcow2"}, "size": 33554432, "version": "v2"}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"backing-file": "TEST_DIR/PID-t.qcow2.base", "backing-fmt": "qcow2", "cluster-size": 512, "driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/PID-t.qcow2"}, "size": 33554432, "version": "v2"}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
@@ -90,7 +90,7 @@ Format specific information:
=== Successful image creation (encrypted) ===
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "qcow2", "encrypt": {"cipher-alg": "twofish-128", "cipher-mode": "ctr", "format": "luks", "hash-alg": "sha1", "iter-time": 10, "ivgen-alg": "plain64", "ivgen-hash-alg": "md5", "key-secret": "keysec0"}, "file": {"driver": "file", "filename": "TEST_DIR/PID-t.qcow2"}, "size": 33554432}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "qcow2", "encrypt": {"cipher-alg": "twofish-128", "cipher-mode": "ctr", "format": "luks", "hash-alg": "sha1", "iter-time": 10, "ivgen-alg": "plain64", "ivgen-hash-alg": "md5", "key-secret": "keysec0"}, "file": {"driver": "file", "filename": "TEST_DIR/PID-t.qcow2"}, "size": 33554432}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
@@ -144,111 +144,111 @@ Format specific information:
=== Invalid BlockdevRef ===
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "qcow2", "file": "this doesn't exist", "size": 33554432}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "qcow2", "file": "this doesn't exist", "size": 33554432}}}
{"return": {}}
Job failed: Cannot find device=this doesn't exist nor node_name=this doesn't exist
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
=== Invalid sizes ===
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "qcow2", "file": "node0", "size": 1234}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "qcow2", "file": "node0", "size": 1234}}}
{"return": {}}
Job failed: Image size must be a multiple of 512 bytes
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "qcow2", "file": "node0", "size": 18446744073709551104}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "qcow2", "file": "node0", "size": 18446744073709551104}}}
{"return": {}}
Job failed: Could not resize image: Image size cannot be negative
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "qcow2", "file": "node0", "size": 9223372036854775808}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "qcow2", "file": "node0", "size": 9223372036854775808}}}
{"return": {}}
Job failed: Could not resize image: Image size cannot be negative
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "qcow2", "file": "node0", "size": 9223372036854775296}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "qcow2", "file": "node0", "size": 9223372036854775296}}}
{"return": {}}
Job failed: Could not resize image: Failed to grow the L1 table: File too large
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
=== Invalid version ===
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "qcow2", "file": "node0", "size": 67108864, "version": "v1"}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "qcow2", "file": "node0", "size": 67108864, "version": "v1"}}}
{"error": {"class": "GenericError", "desc": "Invalid parameter 'v1'"}}
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "qcow2", "file": "node0", "lazy-refcounts": true, "size": 67108864, "version": "v2"}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "qcow2", "file": "node0", "lazy-refcounts": true, "size": 67108864, "version": "v2"}}}
{"return": {}}
Job failed: Lazy refcounts only supported with compatibility level 1.1 and above (use version=v3 or greater)
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "qcow2", "file": "node0", "refcount-bits": 8, "size": 67108864, "version": "v2"}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "qcow2", "file": "node0", "refcount-bits": 8, "size": 67108864, "version": "v2"}}}
{"return": {}}
Job failed: Different refcount widths than 16 bits require compatibility level 1.1 or above (use version=v3 or greater)
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
=== Invalid backing file options ===
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"backing-file": "/dev/null", "driver": "qcow2", "file": "node0", "preallocation": "full", "size": 67108864}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"backing-file": "/dev/null", "driver": "qcow2", "file": "node0", "preallocation": "full", "size": 67108864}}}
{"return": {}}
Job failed: Backing file and preallocation cannot be used at the same time
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"backing-fmt": "qcow2", "driver": "qcow2", "file": "node0", "size": 67108864}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"backing-fmt": "qcow2", "driver": "qcow2", "file": "node0", "size": 67108864}}}
{"return": {}}
Job failed: Backing format cannot be used without backing file
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
=== Invalid cluster size ===
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"cluster-size": 1234, "driver": "qcow2", "file": "node0", "size": 67108864}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"cluster-size": 1234, "driver": "qcow2", "file": "node0", "size": 67108864}}}
{"return": {}}
Job failed: Cluster size must be a power of two between 512 and 2048k
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"cluster-size": 128, "driver": "qcow2", "file": "node0", "size": 67108864}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"cluster-size": 128, "driver": "qcow2", "file": "node0", "size": 67108864}}}
{"return": {}}
Job failed: Cluster size must be a power of two between 512 and 2048k
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"cluster-size": 4194304, "driver": "qcow2", "file": "node0", "size": 67108864}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"cluster-size": 4194304, "driver": "qcow2", "file": "node0", "size": 67108864}}}
{"return": {}}
Job failed: Cluster size must be a power of two between 512 and 2048k
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"cluster-size": 0, "driver": "qcow2", "file": "node0", "size": 67108864}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"cluster-size": 0, "driver": "qcow2", "file": "node0", "size": 67108864}}}
{"return": {}}
Job failed: Cluster size must be a power of two between 512 and 2048k
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"cluster-size": 512, "driver": "qcow2", "file": "node0", "size": 281474976710656}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"cluster-size": 512, "driver": "qcow2", "file": "node0", "size": 281474976710656}}}
{"return": {}}
Job failed: Could not resize image: Failed to grow the L1 table: File too large
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
=== Invalid refcount width ===
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "qcow2", "file": "node0", "refcount-bits": 128, "size": 67108864}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "qcow2", "file": "node0", "refcount-bits": 128, "size": 67108864}}}
{"return": {}}
Job failed: Refcount width must be a power of two and may not exceed 64 bits
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "qcow2", "file": "node0", "refcount-bits": 0, "size": 67108864}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "qcow2", "file": "node0", "refcount-bits": 0, "size": 67108864}}}
{"return": {}}
Job failed: Refcount width must be a power of two and may not exceed 64 bits
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "qcow2", "file": "node0", "refcount-bits": 7, "size": 67108864}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "qcow2", "file": "node0", "refcount-bits": 7, "size": 67108864}}}
{"return": {}}
Job failed: Refcount width must be a power of two and may not exceed 64 bits
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
diff --git a/tests/qemu-iotests/207 b/tests/qemu-iotests/207
index c617ee7..dfd3c51 100755
--- a/tests/qemu-iotests/207
+++ b/tests/qemu-iotests/207
@@ -27,12 +27,16 @@ import re
iotests.verify_image_format(supported_fmts=['raw'])
iotests.verify_protocol(supported=['ssh'])
-def filter_hash(msg):
- return re.sub('"hash": "[0-9a-f]+"', '"hash": HASH', msg)
+def filter_hash(qmsg):
+ def _filter(key, value):
+ if key == 'hash' and re.match('[0-9a-f]+', value):
+ return 'HASH'
+ return value
+ return iotests.filter_qmp(qmsg, _filter)
def blockdev_create(vm, options):
result = vm.qmp_log('blockdev-create', job_id='job0', options=options,
- filters=[iotests.filter_testfiles, filter_hash])
+ filters=[iotests.filter_qmp_testfiles, filter_hash])
if 'return' in result:
assert result['return'] == {}
diff --git a/tests/qemu-iotests/207.out b/tests/qemu-iotests/207.out
index 45ac7c2..568e861 100644
--- a/tests/qemu-iotests/207.out
+++ b/tests/qemu-iotests/207.out
@@ -1,6 +1,6 @@
=== Successful image creation (defaults) ===
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "ssh", "location": {"path": "TEST_DIR/PID-t.img", "server": {"host": "127.0.0.1", "port": "22"}}, "size": 4194304}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "ssh", "location": {"path": "TEST_DIR/PID-t.img", "server": {"host": "127.0.0.1", "port": "22"}}, "size": 4194304}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
@@ -16,7 +16,7 @@ virtual size: 4.0M (4194304 bytes)
=== Test host-key-check options ===
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "ssh", "location": {"host-key-check": {"mode": "none"}, "path": "TEST_DIR/PID-t.img", "server": {"host": "127.0.0.1", "port": "22"}}, "size": 8388608}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "ssh", "location": {"host-key-check": {"mode": "none"}, "path": "TEST_DIR/PID-t.img", "server": {"host": "127.0.0.1", "port": "22"}}, "size": 8388608}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
@@ -25,7 +25,7 @@ image: json:{"driver": "IMGFMT", "file": {"server.host": "127.0.0.1", "server.po
file format: IMGFMT
virtual size: 8.0M (8388608 bytes)
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "ssh", "location": {"host-key-check": {"mode": "known_hosts"}, "path": "TEST_DIR/PID-t.img", "server": {"host": "127.0.0.1", "port": "22"}}, "size": 4194304}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "ssh", "location": {"host-key-check": {"mode": "known_hosts"}, "path": "TEST_DIR/PID-t.img", "server": {"host": "127.0.0.1", "port": "22"}}, "size": 4194304}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
@@ -34,13 +34,13 @@ image: json:{"driver": "IMGFMT", "file": {"server.host": "127.0.0.1", "server.po
file format: IMGFMT
virtual size: 4.0M (4194304 bytes)
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "ssh", "location": {"host-key-check": {"hash": "wrong", "mode": "hash", "type": "md5"}, "path": "TEST_DIR/PID-t.img", "server": {"host": "127.0.0.1", "port": "22"}}, "size": 2097152}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "ssh", "location": {"host-key-check": {"hash": "wrong", "mode": "hash", "type": "md5"}, "path": "TEST_DIR/PID-t.img", "server": {"host": "127.0.0.1", "port": "22"}}, "size": 2097152}}}
{"return": {}}
Job failed: remote host key does not match host_key_check 'wrong'
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "ssh", "location": {"host-key-check": {"hash": HASH, "mode": "hash", "type": "md5"}, "path": "TEST_DIR/PID-t.img", "server": {"host": "127.0.0.1", "port": "22"}}, "size": 8388608}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "ssh", "location": {"host-key-check": {"hash": "HASH", "mode": "hash", "type": "md5"}, "path": "TEST_DIR/PID-t.img", "server": {"host": "127.0.0.1", "port": "22"}}, "size": 8388608}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
@@ -49,13 +49,13 @@ image: json:{"driver": "IMGFMT", "file": {"server.host": "127.0.0.1", "server.po
file format: IMGFMT
virtual size: 8.0M (8388608 bytes)
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "ssh", "location": {"host-key-check": {"hash": "wrong", "mode": "hash", "type": "sha1"}, "path": "TEST_DIR/PID-t.img", "server": {"host": "127.0.0.1", "port": "22"}}, "size": 2097152}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "ssh", "location": {"host-key-check": {"hash": "wrong", "mode": "hash", "type": "sha1"}, "path": "TEST_DIR/PID-t.img", "server": {"host": "127.0.0.1", "port": "22"}}, "size": 2097152}}}
{"return": {}}
Job failed: remote host key does not match host_key_check 'wrong'
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "ssh", "location": {"host-key-check": {"hash": HASH, "mode": "hash", "type": "sha1"}, "path": "TEST_DIR/PID-t.img", "server": {"host": "127.0.0.1", "port": "22"}}, "size": 4194304}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "ssh", "location": {"host-key-check": {"hash": "HASH", "mode": "hash", "type": "sha1"}, "path": "TEST_DIR/PID-t.img", "server": {"host": "127.0.0.1", "port": "22"}}, "size": 4194304}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
@@ -66,13 +66,13 @@ virtual size: 4.0M (4194304 bytes)
=== Invalid path and user ===
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "ssh", "location": {"host-key-check": {"mode": "none"}, "path": "/this/is/not/an/existing/path", "server": {"host": "127.0.0.1", "port": "22"}}, "size": 4194304}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "ssh", "location": {"host-key-check": {"mode": "none"}, "path": "/this/is/not/an/existing/path", "server": {"host": "127.0.0.1", "port": "22"}}, "size": 4194304}}}
{"return": {}}
Job failed: failed to open remote file '/this/is/not/an/existing/path': Failed opening remote file (libssh2 error code: -31)
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "ssh", "location": {"host-key-check": {"mode": "none"}, "path": "TEST_DIR/PID-t.img", "server": {"host": "127.0.0.1", "port": "22"}, "user": "invalid user"}, "size": 4194304}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "ssh", "location": {"host-key-check": {"mode": "none"}, "path": "TEST_DIR/PID-t.img", "server": {"host": "127.0.0.1", "port": "22"}, "user": "invalid user"}, "size": 4194304}}}
{"return": {}}
Job failed: failed to authenticate using publickey authentication and the identities held by your ssh-agent
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
diff --git a/tests/qemu-iotests/210 b/tests/qemu-iotests/210
index d142841..565e3b7 100755
--- a/tests/qemu-iotests/210
+++ b/tests/qemu-iotests/210
@@ -27,7 +27,8 @@ iotests.verify_image_format(supported_fmts=['luks'])
iotests.verify_protocol(supported=['file'])
def blockdev_create(vm, options):
- result = vm.qmp_log('blockdev-create', job_id='job0', options=options)
+ result = vm.qmp_log('blockdev-create', job_id='job0', options=options,
+ filters=[iotests.filter_qmp_testfiles])
if 'return' in result:
assert result['return'] == {}
@@ -53,7 +54,7 @@ with iotests.FilePath('t.luks') as disk_path, \
'size': 0 })
vm.qmp_log('blockdev-add', driver='file', filename=disk_path,
- node_name='imgfile')
+ node_name='imgfile', filters=[iotests.filter_qmp_testfiles])
blockdev_create(vm, { 'driver': imgfmt,
'file': 'imgfile',
diff --git a/tests/qemu-iotests/210.out b/tests/qemu-iotests/210.out
index 923cb05..a3692ce 100644
--- a/tests/qemu-iotests/210.out
+++ b/tests/qemu-iotests/210.out
@@ -1,13 +1,13 @@
=== Successful image creation (defaults) ===
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.luks", "size": 0}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.luks", "size": 0}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
-{"execute": "blockdev-add", "arguments": {"driver": "file", "filename": "TEST_DIR/PID-t.luks", "node_name": "imgfile"}}
+{"execute": "blockdev-add", "arguments": {"driver": "file", "filename": "TEST_DIR/PID-t.luks", "node-name": "imgfile"}}
{"return": {}}
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "luks", "file": "imgfile", "iter-time": 10, "key-secret": "keysec0", "size": 134217728}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "luks", "file": "imgfile", "iter-time": 10, "key-secret": "keysec0", "size": 134217728}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
@@ -54,12 +54,12 @@ Format specific information:
=== Successful image creation (with non-default options) ===
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.luks", "size": 0}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.luks", "size": 0}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"cipher-alg": "twofish-128", "cipher-mode": "ctr", "driver": "luks", "file": {"driver": "file", "filename": "TEST_DIR/PID-t.luks"}, "hash-alg": "sha1", "iter-time": 10, "ivgen-alg": "plain64", "ivgen-hash-alg": "md5", "key-secret": "keysec0", "size": 67108864}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"cipher-alg": "twofish-128", "cipher-mode": "ctr", "driver": "luks", "file": {"driver": "file", "filename": "TEST_DIR/PID-t.luks"}, "hash-alg": "sha1", "iter-time": 10, "ivgen-alg": "plain64", "ivgen-hash-alg": "md5", "key-secret": "keysec0", "size": 67108864}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
@@ -106,7 +106,7 @@ Format specific information:
=== Invalid BlockdevRef ===
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "luks", "file": "this doesn't exist", "size": 67108864}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "luks", "file": "this doesn't exist", "size": 67108864}}}
{"return": {}}
Job failed: Cannot find device=this doesn't exist nor node_name=this doesn't exist
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
@@ -114,7 +114,7 @@ Job failed: Cannot find device=this doesn't exist nor node_name=this doesn't exi
=== Zero size ===
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "luks", "file": "node0", "iter-time": 10, "key-secret": "keysec0", "size": 0}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "luks", "file": "node0", "iter-time": 10, "key-secret": "keysec0", "size": 0}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
@@ -161,19 +161,19 @@ Format specific information:
=== Invalid sizes ===
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "luks", "file": "node0", "key-secret": "keysec0", "size": 18446744073709551104}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "luks", "file": "node0", "key-secret": "keysec0", "size": 18446744073709551104}}}
{"return": {}}
Job failed: The requested file size is too large
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "luks", "file": "node0", "key-secret": "keysec0", "size": 9223372036854775808}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "luks", "file": "node0", "key-secret": "keysec0", "size": 9223372036854775808}}}
{"return": {}}
Job failed: The requested file size is too large
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "luks", "file": "node0", "key-secret": "keysec0", "size": 9223372036854775296}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "luks", "file": "node0", "key-secret": "keysec0", "size": 9223372036854775296}}}
{"return": {}}
Job failed: The requested file size is too large
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
@@ -181,13 +181,13 @@ Job failed: The requested file size is too large
=== Resize image with invalid sizes ===
-{"execute": "block_resize", "arguments": {"node_name": "node1", "size": 9223372036854775296}}
+{"execute": "block_resize", "arguments": {"node-name": "node1", "size": 9223372036854775296}}
{"error": {"class": "GenericError", "desc": "The requested file size is too large"}}
-{"execute": "block_resize", "arguments": {"node_name": "node1", "size": 9223372036854775808}}
+{"execute": "block_resize", "arguments": {"node-name": "node1", "size": 9223372036854775808}}
{"error": {"class": "GenericError", "desc": "Invalid parameter type for 'size', expected: integer"}}
-{"execute": "block_resize", "arguments": {"node_name": "node1", "size": 18446744073709551104}}
+{"execute": "block_resize", "arguments": {"node-name": "node1", "size": 18446744073709551104}}
{"error": {"class": "GenericError", "desc": "Invalid parameter type for 'size', expected: integer"}}
-{"execute": "block_resize", "arguments": {"node_name": "node1", "size": -9223372036854775808}}
+{"execute": "block_resize", "arguments": {"node-name": "node1", "size": -9223372036854775808}}
{"error": {"class": "GenericError", "desc": "Parameter 'size' expects a >0 size"}}
image: json:{"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_IMG"}, "key-secret": "keysec0"}
file format: IMGFMT
diff --git a/tests/qemu-iotests/211 b/tests/qemu-iotests/211
index 7b7985d..6afc894 100755
--- a/tests/qemu-iotests/211
+++ b/tests/qemu-iotests/211
@@ -27,11 +27,14 @@ iotests.verify_image_format(supported_fmts=['vdi'])
iotests.verify_protocol(supported=['file'])
def blockdev_create(vm, options):
- result = vm.qmp_log('blockdev-create', job_id='job0', options=options)
+ result = vm.qmp_log('blockdev-create', job_id='job0', options=options,
+ filters=[iotests.filter_qmp_testfiles])
if 'return' in result:
assert result['return'] == {}
- vm.run_job('job0')
+ error = vm.run_job('job0')
+ if error and 'Could not allocate bmap' in error:
+ iotests.notrun('Insufficient memory')
iotests.log("")
with iotests.FilePath('t.vdi') as disk_path, \
@@ -51,7 +54,7 @@ with iotests.FilePath('t.vdi') as disk_path, \
'size': 0 })
vm.qmp_log('blockdev-add', driver='file', filename=disk_path,
- node_name='imgfile')
+ node_name='imgfile', filters=[iotests.filter_qmp_testfiles])
blockdev_create(vm, { 'driver': imgfmt,
'file': 'imgfile',
diff --git a/tests/qemu-iotests/211.out b/tests/qemu-iotests/211.out
index eebb0ea..682adc2 100644
--- a/tests/qemu-iotests/211.out
+++ b/tests/qemu-iotests/211.out
@@ -1,13 +1,13 @@
=== Successful image creation (defaults) ===
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.vdi", "size": 0}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.vdi", "size": 0}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
-{"execute": "blockdev-add", "arguments": {"driver": "file", "filename": "TEST_DIR/PID-t.vdi", "node_name": "imgfile"}}
+{"execute": "blockdev-add", "arguments": {"driver": "file", "filename": "TEST_DIR/PID-t.vdi", "node-name": "imgfile"}}
{"return": {}}
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vdi", "file": "imgfile", "size": 134217728}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vdi", "file": "imgfile", "size": 134217728}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
@@ -21,12 +21,12 @@ cluster_size: 1048576
=== Successful image creation (explicit defaults) ===
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.vdi", "size": 0}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.vdi", "size": 0}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vdi", "file": {"driver": "file", "filename": "TEST_DIR/PID-t.vdi"}, "preallocation": "off", "size": 67108864}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vdi", "file": {"driver": "file", "filename": "TEST_DIR/PID-t.vdi"}, "preallocation": "off", "size": 67108864}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
@@ -40,12 +40,12 @@ cluster_size: 1048576
=== Successful image creation (with non-default options) ===
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.vdi", "size": 0}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.vdi", "size": 0}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vdi", "file": {"driver": "file", "filename": "TEST_DIR/PID-t.vdi"}, "preallocation": "metadata", "size": 33554432}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vdi", "file": {"driver": "file", "filename": "TEST_DIR/PID-t.vdi"}, "preallocation": "metadata", "size": 33554432}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
@@ -60,7 +60,7 @@ cluster_size: 1048576
=== Invalid BlockdevRef ===
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vdi", "file": "this doesn't exist", "size": 33554432}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vdi", "file": "this doesn't exist", "size": 33554432}}}
{"return": {}}
Job failed: Cannot find device=this doesn't exist nor node_name=this doesn't exist
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
@@ -68,7 +68,7 @@ Job failed: Cannot find device=this doesn't exist nor node_name=this doesn't exi
=== Zero size ===
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vdi", "file": "node0", "size": 0}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vdi", "file": "node0", "size": 0}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
@@ -80,7 +80,7 @@ cluster_size: 1048576
=== Maximum size ===
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vdi", "file": "node0", "size": 562949819203584}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vdi", "file": "node0", "size": 562949819203584}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
@@ -92,19 +92,19 @@ cluster_size: 1048576
=== Invalid sizes ===
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vdi", "file": "node0", "size": 18446744073709551104}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vdi", "file": "node0", "size": 18446744073709551104}}}
{"return": {}}
Job failed: Unsupported VDI image size (size is 0xfffffffffffffe00, max supported is 0x1fffff8000000)
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vdi", "file": "node0", "size": 9223372036854775808}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vdi", "file": "node0", "size": 9223372036854775808}}}
{"return": {}}
Job failed: Unsupported VDI image size (size is 0x8000000000000000, max supported is 0x1fffff8000000)
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vdi", "file": "node0", "size": 562949819203585}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vdi", "file": "node0", "size": 562949819203585}}}
{"return": {}}
Job failed: Unsupported VDI image size (size is 0x1fffff8000001, max supported is 0x1fffff8000000)
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
diff --git a/tests/qemu-iotests/212 b/tests/qemu-iotests/212
index 95c8810..42b74f2 100755
--- a/tests/qemu-iotests/212
+++ b/tests/qemu-iotests/212
@@ -27,7 +27,8 @@ iotests.verify_image_format(supported_fmts=['parallels'])
iotests.verify_protocol(supported=['file'])
def blockdev_create(vm, options):
- result = vm.qmp_log('blockdev-create', job_id='job0', options=options)
+ result = vm.qmp_log('blockdev-create', job_id='job0', options=options,
+ filters=[iotests.filter_qmp_testfiles])
if 'return' in result:
assert result['return'] == {}
@@ -51,7 +52,7 @@ with iotests.FilePath('t.parallels') as disk_path, \
'size': 0 })
vm.qmp_log('blockdev-add', driver='file', filename=disk_path,
- node_name='imgfile')
+ node_name='imgfile', filters=[iotests.filter_qmp_testfiles])
blockdev_create(vm, { 'driver': imgfmt,
'file': 'imgfile',
diff --git a/tests/qemu-iotests/212.out b/tests/qemu-iotests/212.out
index 01da467..2281072 100644
--- a/tests/qemu-iotests/212.out
+++ b/tests/qemu-iotests/212.out
@@ -1,13 +1,13 @@
=== Successful image creation (defaults) ===
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.parallels", "size": 0}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.parallels", "size": 0}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
-{"execute": "blockdev-add", "arguments": {"driver": "file", "filename": "TEST_DIR/PID-t.parallels", "node_name": "imgfile"}}
+{"execute": "blockdev-add", "arguments": {"driver": "file", "filename": "TEST_DIR/PID-t.parallels", "node-name": "imgfile"}}
{"return": {}}
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "parallels", "file": "imgfile", "size": 134217728}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "parallels", "file": "imgfile", "size": 134217728}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
@@ -18,12 +18,12 @@ virtual size: 128M (134217728 bytes)
=== Successful image creation (explicit defaults) ===
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.parallels", "size": 0}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.parallels", "size": 0}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"cluster-size": 1048576, "driver": "parallels", "file": {"driver": "file", "filename": "TEST_DIR/PID-t.parallels"}, "size": 67108864}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"cluster-size": 1048576, "driver": "parallels", "file": {"driver": "file", "filename": "TEST_DIR/PID-t.parallels"}, "size": 67108864}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
@@ -34,12 +34,12 @@ virtual size: 64M (67108864 bytes)
=== Successful image creation (with non-default options) ===
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.parallels", "size": 0}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.parallels", "size": 0}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"cluster-size": 65536, "driver": "parallels", "file": {"driver": "file", "filename": "TEST_DIR/PID-t.parallels"}, "size": 33554432}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"cluster-size": 65536, "driver": "parallels", "file": {"driver": "file", "filename": "TEST_DIR/PID-t.parallels"}, "size": 33554432}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
@@ -50,7 +50,7 @@ virtual size: 32M (33554432 bytes)
=== Invalid BlockdevRef ===
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "parallels", "file": "this doesn't exist", "size": 33554432}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "parallels", "file": "this doesn't exist", "size": 33554432}}}
{"return": {}}
Job failed: Cannot find device=this doesn't exist nor node_name=this doesn't exist
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
@@ -58,7 +58,7 @@ Job failed: Cannot find device=this doesn't exist nor node_name=this doesn't exi
=== Zero size ===
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "parallels", "file": "node0", "size": 0}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "parallels", "file": "node0", "size": 0}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
@@ -69,7 +69,7 @@ virtual size: 0 (0 bytes)
=== Maximum size ===
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "parallels", "file": "node0", "size": 4503599627369984}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "parallels", "file": "node0", "size": 4503599627369984}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
@@ -80,31 +80,31 @@ virtual size: 4096T (4503599627369984 bytes)
=== Invalid sizes ===
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "parallels", "file": "node0", "size": 1234}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "parallels", "file": "node0", "size": 1234}}}
{"return": {}}
Job failed: Image size must be a multiple of 512 bytes
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "parallels", "file": "node0", "size": 18446744073709551104}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "parallels", "file": "node0", "size": 18446744073709551104}}}
{"return": {}}
Job failed: Image size is too large for this cluster size
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "parallels", "file": "node0", "size": 9223372036854775808}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "parallels", "file": "node0", "size": 9223372036854775808}}}
{"return": {}}
Job failed: Image size is too large for this cluster size
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "parallels", "file": "node0", "size": 9223372036854775296}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "parallels", "file": "node0", "size": 9223372036854775296}}}
{"return": {}}
Job failed: Image size is too large for this cluster size
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "parallels", "file": "node0", "size": 4503599627370497}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "parallels", "file": "node0", "size": 4503599627370497}}}
{"return": {}}
Job failed: Image size is too large for this cluster size
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
@@ -112,43 +112,43 @@ Job failed: Image size is too large for this cluster size
=== Invalid cluster size ===
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"cluster-size": 1234, "driver": "parallels", "file": "node0", "size": 67108864}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"cluster-size": 1234, "driver": "parallels", "file": "node0", "size": 67108864}}}
{"return": {}}
Job failed: Cluster size must be a multiple of 512 bytes
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"cluster-size": 128, "driver": "parallels", "file": "node0", "size": 67108864}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"cluster-size": 128, "driver": "parallels", "file": "node0", "size": 67108864}}}
{"return": {}}
Job failed: Cluster size must be a multiple of 512 bytes
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"cluster-size": 4294967296, "driver": "parallels", "file": "node0", "size": 67108864}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"cluster-size": 4294967296, "driver": "parallels", "file": "node0", "size": 67108864}}}
{"return": {}}
Job failed: Cluster size is too large
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"cluster-size": 9223372036854775808, "driver": "parallels", "file": "node0", "size": 67108864}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"cluster-size": 9223372036854775808, "driver": "parallels", "file": "node0", "size": 67108864}}}
{"return": {}}
Job failed: Cluster size is too large
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"cluster-size": 18446744073709551104, "driver": "parallels", "file": "node0", "size": 67108864}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"cluster-size": 18446744073709551104, "driver": "parallels", "file": "node0", "size": 67108864}}}
{"return": {}}
Job failed: Cluster size is too large
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"cluster-size": 0, "driver": "parallels", "file": "node0", "size": 67108864}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"cluster-size": 0, "driver": "parallels", "file": "node0", "size": 67108864}}}
{"return": {}}
Job failed: Image size is too large for this cluster size
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"cluster-size": 512, "driver": "parallels", "file": "node0", "size": 281474976710656}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"cluster-size": 512, "driver": "parallels", "file": "node0", "size": 281474976710656}}}
{"return": {}}
Job failed: Image size is too large for this cluster size
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
diff --git a/tests/qemu-iotests/213 b/tests/qemu-iotests/213
index 4054439..5604f3c 100755
--- a/tests/qemu-iotests/213
+++ b/tests/qemu-iotests/213
@@ -27,7 +27,8 @@ iotests.verify_image_format(supported_fmts=['vhdx'])
iotests.verify_protocol(supported=['file'])
def blockdev_create(vm, options):
- result = vm.qmp_log('blockdev-create', job_id='job0', options=options)
+ result = vm.qmp_log('blockdev-create', job_id='job0', options=options,
+ filters=[iotests.filter_qmp_testfiles])
if 'return' in result:
assert result['return'] == {}
@@ -51,7 +52,7 @@ with iotests.FilePath('t.vhdx') as disk_path, \
'size': 0 })
vm.qmp_log('blockdev-add', driver='file', filename=disk_path,
- node_name='imgfile')
+ node_name='imgfile', filters=[iotests.filter_qmp_testfiles])
blockdev_create(vm, { 'driver': imgfmt,
'file': 'imgfile',
diff --git a/tests/qemu-iotests/213.out b/tests/qemu-iotests/213.out
index 0c9d65b..169083e 100644
--- a/tests/qemu-iotests/213.out
+++ b/tests/qemu-iotests/213.out
@@ -1,13 +1,13 @@
=== Successful image creation (defaults) ===
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.vhdx", "size": 0}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.vhdx", "size": 0}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
-{"execute": "blockdev-add", "arguments": {"driver": "file", "filename": "TEST_DIR/PID-t.vhdx", "node_name": "imgfile"}}
+{"execute": "blockdev-add", "arguments": {"driver": "file", "filename": "TEST_DIR/PID-t.vhdx", "node-name": "imgfile"}}
{"return": {}}
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vhdx", "file": "imgfile", "size": 134217728}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vhdx", "file": "imgfile", "size": 134217728}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
@@ -19,12 +19,12 @@ cluster_size: 8388608
=== Successful image creation (explicit defaults) ===
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.vhdx", "size": 0}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.vhdx", "size": 0}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"block-size": 8388608, "block-state-zero": true, "driver": "vhdx", "file": {"driver": "file", "filename": "TEST_DIR/PID-t.vhdx"}, "log-size": 1048576, "size": 67108864, "subformat": "dynamic"}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"block-size": 8388608, "block-state-zero": true, "driver": "vhdx", "file": {"driver": "file", "filename": "TEST_DIR/PID-t.vhdx"}, "log-size": 1048576, "size": 67108864, "subformat": "dynamic"}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
@@ -36,12 +36,12 @@ cluster_size: 8388608
=== Successful image creation (with non-default options) ===
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.vhdx", "size": 0}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.vhdx", "size": 0}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"block-size": 268435456, "block-state-zero": false, "driver": "vhdx", "file": {"driver": "file", "filename": "TEST_DIR/PID-t.vhdx"}, "log-size": 8388608, "size": 33554432, "subformat": "fixed"}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"block-size": 268435456, "block-state-zero": false, "driver": "vhdx", "file": {"driver": "file", "filename": "TEST_DIR/PID-t.vhdx"}, "log-size": 8388608, "size": 33554432, "subformat": "fixed"}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
@@ -53,7 +53,7 @@ cluster_size: 268435456
=== Invalid BlockdevRef ===
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vhdx", "file": "this doesn't exist", "size": 33554432}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vhdx", "file": "this doesn't exist", "size": 33554432}}}
{"return": {}}
Job failed: Cannot find device=this doesn't exist nor node_name=this doesn't exist
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
@@ -61,7 +61,7 @@ Job failed: Cannot find device=this doesn't exist nor node_name=this doesn't exi
=== Zero size ===
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vhdx", "file": "node0", "size": 0}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vhdx", "file": "node0", "size": 0}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
@@ -73,7 +73,7 @@ cluster_size: 8388608
=== Maximum size ===
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vhdx", "file": "node0", "size": 70368744177664}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vhdx", "file": "node0", "size": 70368744177664}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
@@ -85,25 +85,25 @@ cluster_size: 67108864
=== Invalid sizes ===
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vhdx", "file": "node0", "size": 18446744073709551104}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vhdx", "file": "node0", "size": 18446744073709551104}}}
{"return": {}}
Job failed: Image size too large; max of 64TB
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vhdx", "file": "node0", "size": 9223372036854775808}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vhdx", "file": "node0", "size": 9223372036854775808}}}
{"return": {}}
Job failed: Image size too large; max of 64TB
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vhdx", "file": "node0", "size": 9223372036854775296}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vhdx", "file": "node0", "size": 9223372036854775296}}}
{"return": {}}
Job failed: Image size too large; max of 64TB
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vhdx", "file": "node0", "size": 70368744177665}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vhdx", "file": "node0", "size": 70368744177665}}}
{"return": {}}
Job failed: Image size too large; max of 64TB
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
@@ -111,31 +111,31 @@ Job failed: Image size too large; max of 64TB
=== Invalid block size ===
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"block-size": 1234567, "driver": "vhdx", "file": "node0", "size": 67108864}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"block-size": 1234567, "driver": "vhdx", "file": "node0", "size": 67108864}}}
{"return": {}}
Job failed: Block size must be a multiple of 1 MB
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"block-size": 128, "driver": "vhdx", "file": "node0", "size": 67108864}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"block-size": 128, "driver": "vhdx", "file": "node0", "size": 67108864}}}
{"return": {}}
Job failed: Block size must be a multiple of 1 MB
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"block-size": 3145728, "driver": "vhdx", "file": "node0", "size": 67108864}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"block-size": 3145728, "driver": "vhdx", "file": "node0", "size": 67108864}}}
{"return": {}}
Job failed: Block size must be a power of two
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"block-size": 536870912, "driver": "vhdx", "file": "node0", "size": 67108864}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"block-size": 536870912, "driver": "vhdx", "file": "node0", "size": 67108864}}}
{"return": {}}
Job failed: Block size must not exceed 268435456
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"block-size": 0, "driver": "vhdx", "file": "node0", "size": 67108864}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"block-size": 0, "driver": "vhdx", "file": "node0", "size": 67108864}}}
{"return": {}}
Job failed: Block size must be a multiple of 1 MB
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
@@ -143,25 +143,25 @@ Job failed: Block size must be a multiple of 1 MB
=== Invalid log size ===
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vhdx", "file": "node0", "log-size": 1234567, "size": 67108864}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vhdx", "file": "node0", "log-size": 1234567, "size": 67108864}}}
{"return": {}}
Job failed: Log size must be a multiple of 1 MB
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vhdx", "file": "node0", "log-size": 128, "size": 67108864}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vhdx", "file": "node0", "log-size": 128, "size": 67108864}}}
{"return": {}}
Job failed: Log size must be a multiple of 1 MB
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vhdx", "file": "node0", "log-size": 4294967296, "size": 67108864}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vhdx", "file": "node0", "log-size": 4294967296, "size": 67108864}}}
{"return": {}}
Job failed: Log size must be smaller than 4 GB
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vhdx", "file": "node0", "log-size": 0, "size": 67108864}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vhdx", "file": "node0", "log-size": 0, "size": 67108864}}}
{"return": {}}
Job failed: Log size must be a multiple of 1 MB
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
diff --git a/tests/qemu-iotests/224 b/tests/qemu-iotests/224
new file mode 100755
index 0000000..b4dfaa6
--- /dev/null
+++ b/tests/qemu-iotests/224
@@ -0,0 +1,139 @@
+#!/usr/bin/env python
+#
+# Test json:{} filenames with qemu-internal BDSs
+# (the one of commit, to be precise)
+#
+# Copyright (C) 2018 Red Hat, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+# Creator/Owner: Max Reitz <mreitz@redhat.com>
+
+import iotests
+from iotests import log, qemu_img, qemu_io_silent, filter_qmp_testfiles, \
+ filter_qmp_imgfmt
+import json
+
+# Need backing file support (for arbitrary backing formats)
+iotests.verify_image_format(supported_fmts=['qcow2', 'qcow', 'qed'])
+iotests.verify_platform(['linux'])
+
+
+# There are two variations of this test:
+# (1) We do not set filter_node_name. In that case, the commit_top
+# driver should not appear anywhere.
+# (2) We do set filter_node_name. In that case, it should appear.
+#
+# This for loop executes both.
+for filter_node_name in False, True:
+ log('')
+ log('--- filter_node_name: %s ---' % filter_node_name)
+ log('')
+
+ with iotests.FilePath('base.img') as base_img_path, \
+ iotests.FilePath('mid.img') as mid_img_path, \
+ iotests.FilePath('top.img') as top_img_path, \
+ iotests.VM() as vm:
+
+ assert qemu_img('create', '-f', iotests.imgfmt,
+ base_img_path, '64M') == 0
+ assert qemu_img('create', '-f', iotests.imgfmt, '-b', base_img_path,
+ mid_img_path) == 0
+ assert qemu_img('create', '-f', iotests.imgfmt, '-b', mid_img_path,
+ top_img_path) == 0
+
+ # Something to commit
+ assert qemu_io_silent(mid_img_path, '-c', 'write -P 1 0 1M') == 0
+
+ vm.launch()
+
+ # Change the bottom-most image's backing file (to null-co://)
+ # to enforce json:{} filenames
+ vm.qmp_log('blockdev-add',
+ node_name='top',
+ driver=iotests.imgfmt,
+ file={
+ 'driver': 'file',
+ 'filename': top_img_path
+ },
+ backing={
+ 'node-name': 'mid',
+ 'driver': iotests.imgfmt,
+ 'file': {
+ 'driver': 'file',
+ 'filename': mid_img_path
+ },
+ 'backing': {
+ 'node-name': 'base',
+ 'driver': iotests.imgfmt,
+ 'file': {
+ 'driver': 'file',
+ 'filename': base_img_path
+ },
+ 'backing': {
+ 'driver': 'null-co'
+ }
+ }
+ },
+ filters=[filter_qmp_testfiles, filter_qmp_imgfmt])
+
+ # As long as block-commit does not accept node names, we have to
+ # get our mid/base filenames here
+ mid_name = vm.node_info('mid')['image']['filename']
+ base_name = vm.node_info('base')['image']['filename']
+
+ assert mid_name[:5] == 'json:'
+ assert base_name[:5] == 'json:'
+
+ # Start the block job
+ if filter_node_name:
+ vm.qmp_log('block-commit',
+ job_id='commit',
+ device='top',
+ filter_node_name='filter_node',
+ top=mid_name,
+ base=base_name,
+ speed=1,
+ filters=[filter_qmp_testfiles, filter_qmp_imgfmt])
+ else:
+ vm.qmp_log('block-commit',
+ job_id='commit',
+ device='top',
+ top=mid_name,
+ base=base_name,
+ speed=1,
+ filters=[filter_qmp_testfiles, filter_qmp_imgfmt])
+
+ vm.qmp_log('job-pause', id='commit')
+
+ # Get and parse top's json:{} filename
+ top_name = vm.node_info('top')['image']['filename']
+
+ vm.shutdown()
+
+ assert top_name[:5] == 'json:'
+ top_options = json.loads(top_name[5:])
+
+ if filter_node_name:
+ # This should be present and set
+ assert top_options['backing']['driver'] == 'commit_top'
+ # And the mid image is commit_top's backing image
+ mid_options = top_options['backing']['backing']
+ else:
+ # The mid image should appear as the immediate backing BDS
+ # of top
+ mid_options = top_options['backing']
+
+ assert mid_options['driver'] == iotests.imgfmt
+ assert mid_options['file']['filename'] == mid_img_path
diff --git a/tests/qemu-iotests/224.out b/tests/qemu-iotests/224.out
new file mode 100644
index 0000000..23374a1
--- /dev/null
+++ b/tests/qemu-iotests/224.out
@@ -0,0 +1,18 @@
+
+--- filter_node_name: False ---
+
+{"execute": "blockdev-add", "arguments": {"backing": {"backing": {"backing": {"driver": "null-co"}, "driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-base.img"}, "node-name": "base"}, "driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-mid.img"}, "node-name": "mid"}, "driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-top.img"}, "node-name": "top"}}
+{"return": {}}
+{"execute": "block-commit", "arguments": {"base": "json:{\"backing\": {\"driver\": \"null-co\"}, \"driver\": \"IMGFMT\", \"file\": {\"driver\": \"file\", \"filename\": \"TEST_DIR/PID-base.img\"}}", "device": "top", "job-id": "commit", "speed": 1, "top": "json:{\"backing\": {\"backing\": {\"driver\": \"null-co\"}, \"driver\": \"IMGFMT\", \"file\": {\"driver\": \"file\", \"filename\": \"TEST_DIR/PID-base.img\"}}, \"driver\": \"IMGFMT\", \"file\": {\"driver\": \"file\", \"filename\": \"TEST_DIR/PID-mid.img\"}}"}}
+{"return": {}}
+{"execute": "job-pause", "arguments": {"id": "commit"}}
+{"return": {}}
+
+--- filter_node_name: True ---
+
+{"execute": "blockdev-add", "arguments": {"backing": {"backing": {"backing": {"driver": "null-co"}, "driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-base.img"}, "node-name": "base"}, "driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-mid.img"}, "node-name": "mid"}, "driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-top.img"}, "node-name": "top"}}
+{"return": {}}
+{"execute": "block-commit", "arguments": {"base": "json:{\"backing\": {\"driver\": \"null-co\"}, \"driver\": \"IMGFMT\", \"file\": {\"driver\": \"file\", \"filename\": \"TEST_DIR/PID-base.img\"}}", "device": "top", "filter-node-name": "filter_node", "job-id": "commit", "speed": 1, "top": "json:{\"backing\": {\"backing\": {\"driver\": \"null-co\"}, \"driver\": \"IMGFMT\", \"file\": {\"driver\": \"file\", \"filename\": \"TEST_DIR/PID-base.img\"}}, \"driver\": \"IMGFMT\", \"file\": {\"driver\": \"file\", \"filename\": \"TEST_DIR/PID-mid.img\"}}"}}
+{"return": {}}
+{"execute": "job-pause", "arguments": {"id": "commit"}}
+{"return": {}}
diff --git a/tests/qemu-iotests/228 b/tests/qemu-iotests/228
new file mode 100755
index 0000000..9a50afd
--- /dev/null
+++ b/tests/qemu-iotests/228
@@ -0,0 +1,239 @@
+#!/usr/bin/env python
+#
+# Test for when a backing file is considered overridden (thus, a
+# json:{} filename is generated for the overlay) and when it is not
+#
+# Copyright (C) 2018 Red Hat, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+# Creator/Owner: Max Reitz <mreitz@redhat.com>
+
+import iotests
+from iotests import log, qemu_img, filter_testfiles, filter_imgfmt, \
+ filter_qmp_testfiles, filter_qmp_imgfmt
+
+# Need backing file and change-backing-file support
+iotests.verify_image_format(supported_fmts=['qcow2', 'qed'])
+iotests.verify_platform(['linux'])
+
+
+def log_node_info(node):
+ log('')
+
+ log('bs->filename: ' + node['image']['filename'],
+ filters=[filter_testfiles, filter_imgfmt])
+ log('bs->backing_file: ' + node['backing_file'],
+ filters=[filter_testfiles, filter_imgfmt])
+
+ if 'backing-image' in node['image']:
+ log('bs->backing->bs->filename: ' +
+ node['image']['backing-image']['filename'],
+ filters=[filter_testfiles, filter_imgfmt])
+ else:
+ log('bs->backing: (none)')
+
+ log('')
+
+
+with iotests.FilePath('base.img') as base_img_path, \
+ iotests.FilePath('top.img') as top_img_path, \
+ iotests.VM() as vm:
+
+ assert qemu_img('create', '-f', iotests.imgfmt, base_img_path, '64M') == 0
+ # Choose a funny way to describe the backing filename
+ assert qemu_img('create', '-f', iotests.imgfmt, '-b',
+ 'file:' + base_img_path, top_img_path) == 0
+
+ vm.launch()
+
+ log('--- Implicit backing file ---')
+ log('')
+
+ vm.qmp_log('blockdev-add',
+ node_name='node0',
+ driver=iotests.imgfmt,
+ file={
+ 'driver': 'file',
+ 'filename': top_img_path
+ },
+ filters=[filter_qmp_testfiles, filter_qmp_imgfmt])
+
+ # Filename should be plain, and the backing filename should not
+ # contain the "file:" prefix
+ log_node_info(vm.node_info('node0'))
+
+ vm.qmp_log('blockdev-del', node_name='node0')
+
+ log('')
+ log('--- change-backing-file ---')
+ log('')
+
+ vm.qmp_log('blockdev-add',
+ node_name='node0',
+ driver=iotests.imgfmt,
+ file={
+ 'driver': 'file',
+ 'filename': top_img_path
+ },
+ filters=[filter_qmp_testfiles, filter_qmp_imgfmt])
+
+ # Changing the backing file to a qemu-reported filename should
+ # result in qemu accepting the corresponding BDS as the implicit
+ # backing BDS (and thus not generate a json:{} filename).
+ # So, first, query the backing filename.
+
+ backing_filename = \
+ vm.node_info('node0')['image']['backing-image']['filename']
+
+ # Next, change the backing file to something different
+
+ vm.qmp_log('change-backing-file',
+ image_node_name='node0',
+ device='node0',
+ backing_file='null-co://',
+ filters=[filter_qmp_testfiles])
+
+ # Now, verify that we get a json:{} filename
+ # (Image header says "null-co://", actual backing file still is
+ # base_img_path)
+
+ log_node_info(vm.node_info('node0'))
+
+ # Change it back
+ # (To get header and backing file in sync)
+
+ vm.qmp_log('change-backing-file',
+ image_node_name='node0',
+ device='node0',
+ backing_file=backing_filename,
+ filters=[filter_qmp_testfiles])
+
+ # And verify that we get our original results
+
+ log_node_info(vm.node_info('node0'))
+
+ # Finally, try a "file:" prefix. While this is actually what we
+ # originally had in the image header, qemu will not reopen the
+ # backing file here, so it cannot verify that this filename
+ # "resolves" to the actual backing BDS's filename and will thus
+ # consider both to be different.
+ # (This may be fixed in the future.)
+
+ vm.qmp_log('change-backing-file',
+ image_node_name='node0',
+ device='node0',
+ backing_file=('file:' + backing_filename),
+ filters=[filter_qmp_testfiles])
+
+ # So now we should get a json:{} filename
+
+ log_node_info(vm.node_info('node0'))
+
+ # Remove and re-attach so we can see that (as in our first try),
+ # opening the image anew helps qemu resolve the header backing
+ # filename.
+
+ vm.qmp_log('blockdev-del', node_name='node0')
+
+ vm.qmp_log('blockdev-add',
+ node_name='node0',
+ driver=iotests.imgfmt,
+ file={
+ 'driver': 'file',
+ 'filename': top_img_path
+ },
+ filters=[filter_qmp_testfiles, filter_qmp_imgfmt])
+
+ log_node_info(vm.node_info('node0'))
+
+ vm.qmp_log('blockdev-del', node_name='node0')
+
+ log('')
+ log('--- Override backing file ---')
+ log('')
+
+ # For this test, we need the plain filename in the image header
+ # (because qemu cannot "canonicalize"/"resolve" the backing
+ # filename unless the backing file is opened implicitly with the
+ # overlay)
+ assert qemu_img('create', '-f', iotests.imgfmt, '-b', base_img_path,
+ top_img_path) == 0
+
+ # You can only reliably override backing options by using a node
+ # reference (or by specifying file.filename, but, well...)
+ vm.qmp_log('blockdev-add', node_name='null', driver='null-co')
+
+ vm.qmp_log('blockdev-add',
+ node_name='node0',
+ driver=iotests.imgfmt,
+ file={
+ 'driver': 'file',
+ 'filename': top_img_path
+ },
+ backing='null',
+ filters=[filter_qmp_testfiles, filter_qmp_imgfmt])
+
+ # Should get a json:{} filename (and bs->backing_file is
+ # null-co://, because that field actually has not much to do
+ # with the header backing filename (except that it is changed by
+ # change-backing-file))
+
+ log_node_info(vm.node_info('node0'))
+
+ # Detach the backing file by reopening the whole thing
+
+ vm.qmp_log('blockdev-del', node_name='node0')
+ vm.qmp_log('blockdev-del', node_name='null')
+
+ vm.qmp_log('blockdev-add',
+ node_name='node0',
+ driver=iotests.imgfmt,
+ file={
+ 'driver': 'file',
+ 'filename': top_img_path
+ },
+ backing=None,
+ filters=[filter_qmp_testfiles, filter_qmp_imgfmt])
+
+ # Should get a json:{} filename (because we overrode the backing
+ # file to not be there)
+
+ log_node_info(vm.node_info('node0'))
+
+ # Open the original backing file
+
+ vm.qmp_log('blockdev-add',
+ node_name='original-backing',
+ driver=iotests.imgfmt,
+ file={
+ 'driver': 'file',
+ 'filename': base_img_path
+ },
+ filters=[filter_qmp_testfiles, filter_qmp_imgfmt])
+
+ # Attach the original backing file to its overlay
+
+ vm.qmp_log('blockdev-snapshot',
+ node='original-backing',
+ overlay='node0')
+
+ # This should give us the original plain result
+
+ log_node_info(vm.node_info('node0'))
+
+ vm.qmp_log('blockdev-del', node_name='node0')
+ vm.qmp_log('blockdev-del', node_name='original-backing')
+
+ vm.shutdown()
diff --git a/tests/qemu-iotests/228.out b/tests/qemu-iotests/228.out
new file mode 100644
index 0000000..4217df2
--- /dev/null
+++ b/tests/qemu-iotests/228.out
@@ -0,0 +1,84 @@
+--- Implicit backing file ---
+
+{"execute": "blockdev-add", "arguments": {"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-top.img"}, "node-name": "node0"}}
+{"return": {}}
+
+bs->filename: TEST_DIR/PID-top.img
+bs->backing_file: TEST_DIR/PID-base.img
+bs->backing->bs->filename: TEST_DIR/PID-base.img
+
+{"execute": "blockdev-del", "arguments": {"node-name": "node0"}}
+{"return": {}}
+
+--- change-backing-file ---
+
+{"execute": "blockdev-add", "arguments": {"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-top.img"}, "node-name": "node0"}}
+{"return": {}}
+{"execute": "change-backing-file", "arguments": {"backing-file": "null-co://", "device": "node0", "image-node-name": "node0"}}
+{"return": {}}
+
+bs->filename: json:{"backing": {"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-base.img"}}, "driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-top.img"}}
+bs->backing_file: null-co://
+bs->backing->bs->filename: TEST_DIR/PID-base.img
+
+{"execute": "change-backing-file", "arguments": {"backing-file": "TEST_DIR/PID-base.img", "device": "node0", "image-node-name": "node0"}}
+{"return": {}}
+
+bs->filename: TEST_DIR/PID-top.img
+bs->backing_file: TEST_DIR/PID-base.img
+bs->backing->bs->filename: TEST_DIR/PID-base.img
+
+{"execute": "change-backing-file", "arguments": {"backing-file": "file:TEST_DIR/PID-base.img", "device": "node0", "image-node-name": "node0"}}
+{"return": {}}
+
+bs->filename: json:{"backing": {"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-base.img"}}, "driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-top.img"}}
+bs->backing_file: file:TEST_DIR/PID-base.img
+bs->backing->bs->filename: TEST_DIR/PID-base.img
+
+{"execute": "blockdev-del", "arguments": {"node-name": "node0"}}
+{"return": {}}
+{"execute": "blockdev-add", "arguments": {"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-top.img"}, "node-name": "node0"}}
+{"return": {}}
+
+bs->filename: TEST_DIR/PID-top.img
+bs->backing_file: TEST_DIR/PID-base.img
+bs->backing->bs->filename: TEST_DIR/PID-base.img
+
+{"execute": "blockdev-del", "arguments": {"node-name": "node0"}}
+{"return": {}}
+
+--- Override backing file ---
+
+{"execute": "blockdev-add", "arguments": {"driver": "null-co", "node-name": "null"}}
+{"return": {}}
+{"execute": "blockdev-add", "arguments": {"backing": "null", "driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-top.img"}, "node-name": "node0"}}
+{"return": {}}
+
+bs->filename: json:{"backing": {"driver": "null-co"}, "driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-top.img"}}
+bs->backing_file: null-co://
+bs->backing->bs->filename: null-co://
+
+{"execute": "blockdev-del", "arguments": {"node-name": "node0"}}
+{"return": {}}
+{"execute": "blockdev-del", "arguments": {"node-name": "null"}}
+{"return": {}}
+{"execute": "blockdev-add", "arguments": {"backing": null, "driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-top.img"}, "node-name": "node0"}}
+{"return": {}}
+
+bs->filename: json:{"backing": null, "driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-top.img"}}
+bs->backing_file: TEST_DIR/PID-base.img
+bs->backing: (none)
+
+{"execute": "blockdev-add", "arguments": {"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-base.img"}, "node-name": "original-backing"}}
+{"return": {}}
+{"execute": "blockdev-snapshot", "arguments": {"node": "original-backing", "overlay": "node0"}}
+{"return": {}}
+
+bs->filename: TEST_DIR/PID-top.img
+bs->backing_file: TEST_DIR/PID-base.img
+bs->backing->bs->filename: TEST_DIR/PID-base.img
+
+{"execute": "blockdev-del", "arguments": {"node-name": "node0"}}
+{"return": {}}
+{"execute": "blockdev-del", "arguments": {"node-name": "original-backing"}}
+{"return": {}}
diff --git a/tests/qemu-iotests/232 b/tests/qemu-iotests/232
index 0708b8b..e48bc8f 100755
--- a/tests/qemu-iotests/232
+++ b/tests/qemu-iotests/232
@@ -29,7 +29,6 @@ status=1 # failure is the default!
_cleanup()
{
_cleanup_test_img
- rm -f $TEST_IMG.snap
}
trap "_cleanup; exit \$status" 0 1 2 3 15
@@ -70,6 +69,10 @@ size=128M
_make_test_img $size
+if [ -n "$TEST_IMG_FILE" ]; then
+ TEST_IMG=$TEST_IMG_FILE
+fi
+
echo
echo "=== -drive with read-write image: read-only/auto-read-only combinations ==="
echo
diff --git a/tests/qemu-iotests/237 b/tests/qemu-iotests/237
index 251771d..06897f8 100755
--- a/tests/qemu-iotests/237
+++ b/tests/qemu-iotests/237
@@ -27,7 +27,8 @@ from iotests import imgfmt
iotests.verify_image_format(supported_fmts=['vmdk'])
def blockdev_create(vm, options):
- result = vm.qmp_log('blockdev-create', job_id='job0', options=options)
+ result = vm.qmp_log('blockdev-create', job_id='job0', options=options,
+ filters=[iotests.filter_qmp_testfiles])
if 'return' in result:
assert result['return'] == {}
@@ -54,7 +55,7 @@ with iotests.FilePath('t.vmdk') as disk_path, \
'size': 0 })
vm.qmp_log('blockdev-add', driver='file', filename=disk_path,
- node_name='imgfile')
+ node_name='imgfile', filters=[iotests.filter_qmp_testfiles])
blockdev_create(vm, { 'driver': imgfmt,
'file': 'imgfile',
@@ -223,7 +224,7 @@ with iotests.FilePath('t.vmdk') as disk_path, \
iotests.log("= %s %d =" % (subfmt, size))
iotests.log("")
- num_extents = math.ceil(size / 2.0**31)
+ num_extents = int(math.ceil(size / 2.0**31))
extents = [ "ext%d" % (i) for i in range(1, num_extents + 1) ]
vm.launch()
diff --git a/tests/qemu-iotests/237.out b/tests/qemu-iotests/237.out
index 241c864..2aaa68f 100644
--- a/tests/qemu-iotests/237.out
+++ b/tests/qemu-iotests/237.out
@@ -1,13 +1,13 @@
=== Successful image creation (defaults) ===
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.vmdk", "size": 0}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.vmdk", "size": 0}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
-{"execute": "blockdev-add", "arguments": {"driver": "file", "filename": "TEST_DIR/PID-t.vmdk", "node_name": "imgfile"}}
+{"execute": "blockdev-add", "arguments": {"driver": "file", "filename": "TEST_DIR/PID-t.vmdk", "node-name": "imgfile"}}
{"return": {}}
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vmdk", "file": "imgfile", "size": 5368709120}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vmdk", "file": "imgfile", "size": 5368709120}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
@@ -29,12 +29,12 @@ Format specific information:
=== Successful image creation (inline blockdev-add, explicit defaults) ===
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.vmdk", "size": 0}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.vmdk", "size": 0}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"adapter-type": "ide", "driver": "vmdk", "extents": [], "file": {"driver": "file", "filename": "TEST_DIR/PID-t.vmdk"}, "hwversion": "4", "size": 67108864, "subformat": "monolithicSparse", "zeroed-grain": false}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"adapter-type": "ide", "driver": "vmdk", "extents": [], "file": {"driver": "file", "filename": "TEST_DIR/PID-t.vmdk"}, "hwversion": "4", "size": 67108864, "subformat": "monolithicSparse", "zeroed-grain": false}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
@@ -56,12 +56,12 @@ Format specific information:
=== Successful image creation (with non-default options) ===
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.vmdk", "size": 0}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.vmdk", "size": 0}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"adapter-type": "buslogic", "driver": "vmdk", "extents": [], "file": {"driver": "file", "filename": "TEST_DIR/PID-t.vmdk"}, "size": 33554432, "subformat": "monolithicSparse", "zeroed-grain": true}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"adapter-type": "buslogic", "driver": "vmdk", "extents": [], "file": {"driver": "file", "filename": "TEST_DIR/PID-t.vmdk"}, "size": 33554432, "subformat": "monolithicSparse", "zeroed-grain": true}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
@@ -83,7 +83,7 @@ Format specific information:
=== Invalid BlockdevRef ===
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vmdk", "file": "this doesn't exist", "size": 33554432}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vmdk", "file": "this doesn't exist", "size": 33554432}}}
{"return": {}}
Job failed: Cannot find device=this doesn't exist nor node_name=this doesn't exist
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
@@ -93,38 +93,38 @@ Job failed: Cannot find device=this doesn't exist nor node_name=this doesn't exi
== Valid adapter types ==
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"adapter-type": "ide", "driver": "vmdk", "file": "node0", "size": 33554432}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"adapter-type": "ide", "driver": "vmdk", "file": "node0", "size": 33554432}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"adapter-type": "buslogic", "driver": "vmdk", "file": "node0", "size": 33554432}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"adapter-type": "buslogic", "driver": "vmdk", "file": "node0", "size": 33554432}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"adapter-type": "lsilogic", "driver": "vmdk", "file": "node0", "size": 33554432}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"adapter-type": "lsilogic", "driver": "vmdk", "file": "node0", "size": 33554432}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"adapter-type": "legacyESX", "driver": "vmdk", "file": "node0", "size": 33554432}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"adapter-type": "legacyESX", "driver": "vmdk", "file": "node0", "size": 33554432}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
== Invalid adapter types ==
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"adapter-type": "foo", "driver": "vmdk", "file": "node0", "size": 33554432}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"adapter-type": "foo", "driver": "vmdk", "file": "node0", "size": 33554432}}}
{"error": {"class": "GenericError", "desc": "Invalid parameter 'foo'"}}
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"adapter-type": "IDE", "driver": "vmdk", "file": "node0", "size": 33554432}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"adapter-type": "IDE", "driver": "vmdk", "file": "node0", "size": 33554432}}}
{"error": {"class": "GenericError", "desc": "Invalid parameter 'IDE'"}}
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"adapter-type": "legacyesx", "driver": "vmdk", "file": "node0", "size": 33554432}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"adapter-type": "legacyesx", "driver": "vmdk", "file": "node0", "size": 33554432}}}
{"error": {"class": "GenericError", "desc": "Invalid parameter 'legacyesx'"}}
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"adapter-type": 1, "driver": "vmdk", "file": "node0", "size": 33554432}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"adapter-type": 1, "driver": "vmdk", "file": "node0", "size": 33554432}}}
{"error": {"class": "GenericError", "desc": "Invalid parameter type for 'options.adapter-type', expected: string"}}
=== Other subformats ===
@@ -137,7 +137,7 @@ Formatting 'TEST_DIR/PID-t.vmdk.3', fmt=vmdk size=0 compat6=off hwversion=undefi
== Missing extent ==
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vmdk", "file": "node0", "size": 33554432, "subformat": "monolithicFlat"}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vmdk", "file": "node0", "size": 33554432, "subformat": "monolithicFlat"}}}
{"return": {}}
Job failed: Extent [0] not specified
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
@@ -145,14 +145,14 @@ Job failed: Extent [0] not specified
== Correct extent ==
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vmdk", "extents": ["ext1"], "file": "node0", "size": 33554432, "subformat": "monolithicFlat"}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vmdk", "extents": ["ext1"], "file": "node0", "size": 33554432, "subformat": "monolithicFlat"}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
== Extra extent ==
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vmdk", "extents": ["ext1", "ext2", "ext3"], "file": "node0", "size": 512, "subformat": "monolithicFlat"}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vmdk", "extents": ["ext1", "ext2", "ext3"], "file": "node0", "size": 512, "subformat": "monolithicFlat"}}}
{"return": {}}
Job failed: List of extents contains unused extents
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
@@ -162,7 +162,7 @@ Job failed: List of extents contains unused extents
= twoGbMaxExtentFlat 512 =
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vmdk", "extents": ["ext1"], "file": "node0", "size": 512, "subformat": "twoGbMaxExtentFlat"}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vmdk", "extents": ["ext1"], "file": "node0", "size": 512, "subformat": "twoGbMaxExtentFlat"}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
@@ -182,7 +182,7 @@ Format specific information:
= twoGbMaxExtentSparse 512 =
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vmdk", "extents": ["ext1"], "file": "node0", "size": 512, "subformat": "twoGbMaxExtentSparse"}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vmdk", "extents": ["ext1"], "file": "node0", "size": 512, "subformat": "twoGbMaxExtentSparse"}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
@@ -204,7 +204,7 @@ Format specific information:
= twoGbMaxExtentFlat 1073741824 =
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vmdk", "extents": ["ext1"], "file": "node0", "size": 1073741824, "subformat": "twoGbMaxExtentFlat"}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vmdk", "extents": ["ext1"], "file": "node0", "size": 1073741824, "subformat": "twoGbMaxExtentFlat"}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
@@ -224,7 +224,7 @@ Format specific information:
= twoGbMaxExtentSparse 1073741824 =
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vmdk", "extents": ["ext1"], "file": "node0", "size": 1073741824, "subformat": "twoGbMaxExtentSparse"}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vmdk", "extents": ["ext1"], "file": "node0", "size": 1073741824, "subformat": "twoGbMaxExtentSparse"}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
@@ -246,7 +246,7 @@ Format specific information:
= twoGbMaxExtentFlat 2147483648 =
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vmdk", "extents": ["ext1"], "file": "node0", "size": 2147483648, "subformat": "twoGbMaxExtentFlat"}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vmdk", "extents": ["ext1"], "file": "node0", "size": 2147483648, "subformat": "twoGbMaxExtentFlat"}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
@@ -266,7 +266,7 @@ Format specific information:
= twoGbMaxExtentSparse 2147483648 =
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vmdk", "extents": ["ext1"], "file": "node0", "size": 2147483648, "subformat": "twoGbMaxExtentSparse"}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vmdk", "extents": ["ext1"], "file": "node0", "size": 2147483648, "subformat": "twoGbMaxExtentSparse"}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
@@ -288,7 +288,7 @@ Format specific information:
= twoGbMaxExtentFlat 5368709120 =
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vmdk", "extents": ["ext1", "ext2", "ext3"], "file": "node0", "size": 5368709120, "subformat": "twoGbMaxExtentFlat"}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vmdk", "extents": ["ext1", "ext2", "ext3"], "file": "node0", "size": 5368709120, "subformat": "twoGbMaxExtentFlat"}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
@@ -316,7 +316,7 @@ Format specific information:
= twoGbMaxExtentSparse 5368709120 =
-{"execute": "blockdev-create", "arguments": {"job_id": "job0", "options": {"driver": "vmdk", "extents": ["ext1", "ext2", "ext3"], "file": "node0", "size": 5368709120, "subformat": "twoGbMaxExtentSparse"}}}
+{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vmdk", "extents": ["ext1", "ext2", "ext3"], "file": "node0", "size": 5368709120, "subformat": "twoGbMaxExtentSparse"}}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc
index e15e7a7..09a27f0 100644
--- a/tests/qemu-iotests/common.rc
+++ b/tests/qemu-iotests/common.rc
@@ -145,6 +145,7 @@ else
TEST_IMG="nbd:127.0.0.1:10810"
elif [ "$IMGPROTO" = "ssh" ]; then
TEST_IMG_FILE=$TEST_DIR/t.$IMGFMT
+ REMOTE_TEST_DIR="ssh://127.0.0.1$TEST_DIR"
TEST_IMG="ssh://127.0.0.1$TEST_IMG_FILE"
elif [ "$IMGPROTO" = "nfs" ]; then
TEST_IMG_FILE=$TEST_DIR/t.$IMGFMT
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
index fc4c416..b5ca63c 100644
--- a/tests/qemu-iotests/group
+++ b/tests/qemu-iotests/group
@@ -224,9 +224,11 @@
221 rw auto quick
222 rw auto quick
223 rw auto quick
+224 rw auto quick
225 rw auto quick
226 auto quick
227 auto quick
+228 rw auto quick
229 auto quick
231 auto quick
232 auto quick
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
index b461f53..4910fb2 100644
--- a/tests/qemu-iotests/iotests.py
+++ b/tests/qemu-iotests/iotests.py
@@ -76,14 +76,16 @@ def qemu_img(*args):
sys.stderr.write('qemu-img received signal %i: %s\n' % (-exitcode, ' '.join(qemu_img_args + list(args))))
return exitcode
-def ordered_qmp(qmsg):
+def ordered_qmp(qmsg, conv_keys=True):
# Dictionaries are not ordered prior to 3.6, therefore:
if isinstance(qmsg, list):
return [ordered_qmp(atom) for atom in qmsg]
if isinstance(qmsg, dict):
od = OrderedDict()
for k, v in sorted(qmsg.items()):
- od[k] = ordered_qmp(v)
+ if conv_keys:
+ k = k.replace('_', '-')
+ od[k] = ordered_qmp(v, conv_keys=False)
return od
return qmsg
@@ -236,6 +238,12 @@ def image_size(img):
r = qemu_img_pipe('info', '--output=json', '-f', imgfmt, img)
return json.loads(r)['virtual-size']
+def is_str(val):
+ if sys.version_info.major >= 3:
+ return isinstance(val, str)
+ else:
+ return isinstance(val, str) or isinstance(val, unicode)
+
test_dir_re = re.compile(r"%s" % test_dir)
def filter_test_dir(msg):
return test_dir_re.sub("TEST_DIR", msg)
@@ -283,7 +291,7 @@ def filter_testfiles(msg):
def filter_qmp_testfiles(qmsg):
def _filter(key, value):
- if key == 'filename' or key == 'backing-file':
+ if is_str(value):
return filter_testfiles(value)
return value
return filter_qmp(qmsg, _filter)
@@ -304,6 +312,16 @@ def filter_img_info(output, filename):
lines.append(line)
return '\n'.join(lines)
+def filter_imgfmt(msg):
+ return msg.replace(imgfmt, 'IMGFMT')
+
+def filter_qmp_imgfmt(qmsg):
+ def _filter(key, value):
+ if is_str(value):
+ return filter_imgfmt(value)
+ return value
+ return filter_qmp(qmsg, _filter)
+
def log(msg, filters=[], indent=None):
'''Logs either a string message or a JSON serializable message (like QMP).
If indent is provided, JSON serializable messages are pretty-printed.'''
@@ -514,7 +532,9 @@ class VM(qtest.QEMUQtestMachine):
log(result, filters, indent=indent)
return result
+ # Returns None on success, and an error string on failure
def run_job(self, job, auto_finalize=True, auto_dismiss=False):
+ error = None
while True:
for ev in self.get_qmp_events_filtered(wait=True):
if ev['event'] == 'JOB_STATUS_CHANGE':
@@ -523,16 +543,24 @@ class VM(qtest.QEMUQtestMachine):
result = self.qmp('query-jobs')
for j in result['return']:
if j['id'] == job:
+ error = j['error']
log('Job failed: %s' % (j['error']))
elif status == 'pending' and not auto_finalize:
self.qmp_log('job-finalize', id=job)
elif status == 'concluded' and not auto_dismiss:
self.qmp_log('job-dismiss', id=job)
elif status == 'null':
- return
+ return error
else:
iotests.log(ev)
+ def node_info(self, node_name):
+ nodes = self.qmp('query-named-block-nodes')
+ for x in nodes['return']:
+ if x['node-name'] == node_name:
+ return x
+ return None
+
index_re = re.compile(r'([^\[]+)\[([^\]]+)\]')
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
index 821be40..eda9075 100644
--- a/tests/test-bdrv-drain.c
+++ b/tests/test-bdrv-drain.c
@@ -1501,6 +1501,36 @@ static void test_append_to_drained(void)
blk_unref(blk);
}
+static void test_set_aio_context(void)
+{
+ BlockDriverState *bs;
+ IOThread *a = iothread_new();
+ IOThread *b = iothread_new();
+ AioContext *ctx_a = iothread_get_aio_context(a);
+ AioContext *ctx_b = iothread_get_aio_context(b);
+
+ bs = bdrv_new_open_driver(&bdrv_test, "test-node", BDRV_O_RDWR,
+ &error_abort);
+
+ bdrv_drained_begin(bs);
+ bdrv_set_aio_context(bs, ctx_a);
+
+ aio_context_acquire(ctx_a);
+ bdrv_drained_end(bs);
+
+ bdrv_drained_begin(bs);
+ bdrv_set_aio_context(bs, ctx_b);
+ aio_context_release(ctx_a);
+ aio_context_acquire(ctx_b);
+ bdrv_set_aio_context(bs, qemu_get_aio_context());
+ aio_context_release(ctx_b);
+ bdrv_drained_end(bs);
+
+ bdrv_unref(bs);
+ iothread_join(a);
+ iothread_join(b);
+}
+
int main(int argc, char **argv)
{
int ret;
@@ -1582,6 +1612,8 @@ int main(int argc, char **argv)
g_test_add_func("/bdrv-drain/attach/drain", test_append_to_drained);
+ g_test_add_func("/bdrv-drain/set_aio_context", test_set_aio_context);
+
ret = g_test_run();
qemu_event_destroy(&done_event);
return ret;
diff --git a/tests/test-bdrv-graph-mod.c b/tests/test-bdrv-graph-mod.c
new file mode 100644
index 0000000..458dfa6
--- /dev/null
+++ b/tests/test-bdrv-graph-mod.c
@@ -0,0 +1,198 @@
+/*
+ * Block node graph modifications tests
+ *
+ * Copyright (c) 2019 Virtuozzo International GmbH. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "block/block_int.h"
+#include "sysemu/block-backend.h"
+
+static BlockDriver bdrv_pass_through = {
+ .format_name = "pass-through",
+ .bdrv_child_perm = bdrv_filter_default_perms,
+};
+
+static void no_perm_default_perms(BlockDriverState *bs, BdrvChild *c,
+ const BdrvChildRole *role,
+ BlockReopenQueue *reopen_queue,
+ uint64_t perm, uint64_t shared,
+ uint64_t *nperm, uint64_t *nshared)
+{
+ *nperm = 0;
+ *nshared = BLK_PERM_ALL;
+}
+
+static BlockDriver bdrv_no_perm = {
+ .format_name = "no-perm",
+ .bdrv_child_perm = no_perm_default_perms,
+};
+
+static BlockDriverState *no_perm_node(const char *name)
+{
+ return bdrv_new_open_driver(&bdrv_no_perm, name, BDRV_O_RDWR, &error_abort);
+}
+
+static BlockDriverState *pass_through_node(const char *name)
+{
+ return bdrv_new_open_driver(&bdrv_pass_through, name,
+ BDRV_O_RDWR, &error_abort);
+}
+
+/*
+ * test_update_perm_tree
+ *
+ * When checking node for a possibility to update permissions, it's subtree
+ * should be correctly checked too. New permissions for each node should be
+ * calculated and checked in context of permissions of other nodes. If we
+ * check new permissions of the node only in context of old permissions of
+ * its neighbors, we can finish up with wrong permission graph.
+ *
+ * This test firstly create the following graph:
+ * +--------+
+ * | root |
+ * +--------+
+ * |
+ * | perm: write, read
+ * | shared: except write
+ * v
+ * +-------------------+ +----------------+
+ * | passtrough filter |---------->| null-co node |
+ * +-------------------+ +----------------+
+ *
+ *
+ * and then, tries to append filter under node. Expected behavior: fail.
+ * Otherwise we'll get the following picture, with two BdrvChild'ren, having
+ * write permission to one node, without actually sharing it.
+ *
+ * +--------+
+ * | root |
+ * +--------+
+ * |
+ * | perm: write, read
+ * | shared: except write
+ * v
+ * +-------------------+
+ * | passtrough filter |
+ * +-------------------+
+ * | |
+ * perm: write, read | | perm: write, read
+ * shared: except write | | shared: except write
+ * v v
+ * +----------------+
+ * | null co node |
+ * +----------------+
+ */
+static void test_update_perm_tree(void)
+{
+ Error *local_err = NULL;
+
+ BlockBackend *root = blk_new(BLK_PERM_WRITE | BLK_PERM_CONSISTENT_READ,
+ BLK_PERM_ALL & ~BLK_PERM_WRITE);
+ BlockDriverState *bs = no_perm_node("node");
+ BlockDriverState *filter = pass_through_node("filter");
+
+ blk_insert_bs(root, bs, &error_abort);
+
+ bdrv_attach_child(filter, bs, "child", &child_file, &error_abort);
+
+ bdrv_append(filter, bs, &local_err);
+
+ g_assert_nonnull(local_err);
+
+ bdrv_unref(bs);
+ blk_unref(root);
+}
+
+/*
+ * test_should_update_child
+ *
+ * Test that bdrv_replace_node, and concretely should_update_child
+ * do the right thing, i.e. not creating loops on the graph.
+ *
+ * The test does the following:
+ * 1. initial graph:
+ *
+ * +------+ +--------+
+ * | root | | filter |
+ * +------+ +--------+
+ * | |
+ * root| target|
+ * v v
+ * +------+ +--------+
+ * | node |<---------| target |
+ * +------+ backing +--------+
+ *
+ * 2. Append @filter above @node. If should_update_child works correctly,
+ * it understands, that backing child of @target should not be updated,
+ * as it will create a loop on node graph. Resulting picture should
+ * be the left one, not the right:
+ *
+ * +------+ +------+
+ * | root | | root |
+ * +------+ +------+
+ * | |
+ * root| root|
+ * v v
+ * +--------+ target +--------+ target
+ * | filter |--------------+ | filter |--------------+
+ * +--------+ | +--------+ |
+ * | | | ^ v
+ * backing| | backing| | +--------+
+ * v v | +-----------| target |
+ * +------+ +--------+ v backing +--------+
+ * | node |<---------| target | +------+
+ * +------+ backing +--------+ | node |
+ * +------+
+ *
+ * (good picture) (bad picture)
+ *
+ */
+static void test_should_update_child(void)
+{
+ BlockBackend *root = blk_new(0, BLK_PERM_ALL);
+ BlockDriverState *bs = no_perm_node("node");
+ BlockDriverState *filter = no_perm_node("filter");
+ BlockDriverState *target = no_perm_node("target");
+
+ blk_insert_bs(root, bs, &error_abort);
+
+ bdrv_set_backing_hd(target, bs, &error_abort);
+
+ g_assert(target->backing->bs == bs);
+ bdrv_attach_child(filter, target, "target", &child_file, &error_abort);
+ bdrv_append(filter, bs, &error_abort);
+ g_assert(target->backing->bs == bs);
+
+ bdrv_unref(bs);
+ blk_unref(root);
+}
+
+int main(int argc, char *argv[])
+{
+ bdrv_init();
+ qemu_init_main_loop(&error_abort);
+
+ g_test_init(&argc, &argv, NULL);
+
+ g_test_add_func("/bdrv-graph-mod/update-perm-tree", test_update_perm_tree);
+ g_test_add_func("/bdrv-graph-mod/should-update-child",
+ test_should_update_child);
+
+ return g_test_run();
+}
diff --git a/util/aio-posix.c b/util/aio-posix.c
index 8640dfd..6fbfa79 100644
--- a/util/aio-posix.c
+++ b/util/aio-posix.c
@@ -613,6 +613,8 @@ bool aio_poll(AioContext *ctx, bool blocking)
int64_t timeout;
int64_t start = 0;
+ assert(in_aio_context_home_thread(ctx));
+
/* aio_notify can avoid the expensive event_notifier_set if
* everything (file descriptors, bottom halves, timers) will
* be re-evaluated before the next blocking poll(). This is
@@ -621,7 +623,6 @@ bool aio_poll(AioContext *ctx, bool blocking)
* so disable the optimization now.
*/
if (blocking) {
- assert(in_aio_context_home_thread(ctx));
atomic_add(&ctx->notify_me, 2);
}