1 | The following changes since commit ba29883206d92a29ad5a466e679ccfc2ee6132ef: | 1 | The following changes since commit dfaecc04c46d298e9ee81bd0ca96d8754f1c27ed: |
---|---|---|---|
2 | 2 | ||
3 | Merge remote-tracking branch 'remotes/borntraeger/tags/s390x-20200310' into staging (2020-03-10 16:50:28 +0000) | 3 | Merge tag 'pull-riscv-to-apply-20250407-1' of https://github.com/alistair23/qemu into staging (2025-04-07 09:18:33 -0400) |
4 | 4 | ||
5 | are available in the Git repository at: | 5 | are available in the Git repository at: |
6 | 6 | ||
7 | git://repo.or.cz/qemu/kevin.git tags/for-upstream | 7 | https://repo.or.cz/qemu/kevin.git tags/for-upstream |
8 | 8 | ||
9 | for you to fetch changes up to 8bb3b023f2055054ee119cb45b42d2b14be7fc8a: | 9 | for you to fetch changes up to f8222bfba3409a3ce09c191941127a8cf2c7e623: |
10 | 10 | ||
11 | qemu-iotests: adding LUKS cleanup for non-UTF8 secret error (2020-03-11 15:54:38 +0100) | 11 | test-bdrv-drain: Fix data races (2025-04-08 15:00:01 +0200) |
12 | 12 | ||
13 | ---------------------------------------------------------------- | 13 | ---------------------------------------------------------------- |
14 | Block layer patches: | 14 | Block layer patches |
15 | 15 | ||
16 | - Relax restrictions for blockdev-snapshot (allows libvirt to do live | 16 | - scsi-disk: Apply error policy for host_status errors again |
17 | storage migration with blockdev-mirror) | 17 | - qcow2: Fix qemu-img info crash with missing crypto header |
18 | - luks: Delete created files when block_crypto_co_create_opts_luks fails | 18 | - qemu-img bench: Fix division by zero for zero-sized images |
19 | - Fix memleaks in qmp_object_add | 19 | - test-bdrv-drain: Fix data races |
20 | 20 | ||
21 | ---------------------------------------------------------------- | 21 | ---------------------------------------------------------------- |
22 | Daniel Henrique Barboza (4): | 22 | Denis Rastyogin (1): |
23 | block: introducing 'bdrv_co_delete_file' interface | 23 | qemu-img: fix division by zero in bench_cb() for zero-sized images |
24 | block.c: adding bdrv_co_delete_file | ||
25 | crypto.c: cleanup created file when block_crypto_co_create_opts_luks fails | ||
26 | qemu-iotests: adding LUKS cleanup for non-UTF8 secret error | ||
27 | 24 | ||
28 | Kevin Wolf (6): | 25 | Kevin Wolf (2): |
29 | block: Make bdrv_get_cumulative_perm() public | 26 | qcow2: Don't crash qemu-img info with missing crypto header |
30 | block: Relax restrictions for blockdev-snapshot | 27 | scsi-disk: Apply error policy for host_status errors again |
31 | iotests: Fix run_job() with use_log=False | ||
32 | iotests: Test mirror with temporarily disabled target backing file | ||
33 | block: Fix cross-AioContext blockdev-snapshot | ||
34 | iotests: Add iothread cases to 155 | ||
35 | 28 | ||
36 | Pan Nengyuan (1): | 29 | Vitalii Mordan (1): |
37 | qom-qmp-cmds: fix two memleaks in qmp_object_add | 30 | test-bdrv-drain: Fix data races |
38 | 31 | ||
39 | Peter Krempa (1): | 32 | include/qemu/job.h | 3 ++ |
40 | qapi: Add '@allow-write-only-overlay' feature for 'blockdev-snapshot' | 33 | block/qcow2.c | 4 +- |
41 | 34 | hw/scsi/scsi-disk.c | 39 +++++++++----- | |
42 | Philippe Mathieu-Daudé (1): | 35 | job.c | 6 +++ |
43 | tests/qemu-iotests: Fix socket_scm_helper build path | 36 | qemu-img.c | 6 ++- |
44 | 37 | tests/unit/test-bdrv-drain.c | 32 +++++++----- | |
45 | qapi/block-core.json | 9 ++++- | 38 | tests/qemu-iotests/tests/qcow2-encryption | 75 +++++++++++++++++++++++++++ |
46 | include/block/block.h | 1 + | 39 | tests/qemu-iotests/tests/qcow2-encryption.out | 32 ++++++++++++ |
47 | include/block/block_int.h | 7 ++++ | 40 | 8 files changed, 167 insertions(+), 30 deletions(-) |
48 | block.c | 33 ++++++++++++++-- | 41 | create mode 100755 tests/qemu-iotests/tests/qcow2-encryption |
49 | block/crypto.c | 18 +++++++++ | 42 | create mode 100644 tests/qemu-iotests/tests/qcow2-encryption.out |
50 | block/file-posix.c | 23 +++++++++++ | ||
51 | blockdev.c | 30 ++++----------- | ||
52 | qom/qom-qmp-cmds.c | 16 +++----- | ||
53 | tests/qemu-iotests/iotests.py | 5 ++- | ||
54 | tests/Makefile.include | 1 + | ||
55 | tests/qemu-iotests/085.out | 4 +- | ||
56 | tests/qemu-iotests/155 | 88 ++++++++++++++++++++++++++++++++++++------- | ||
57 | tests/qemu-iotests/155.out | 4 +- | ||
58 | tests/qemu-iotests/282 | 67 ++++++++++++++++++++++++++++++++ | ||
59 | tests/qemu-iotests/282.out | 11 ++++++ | ||
60 | tests/qemu-iotests/group | 1 + | ||
61 | tests/qtest/Makefile.include | 1 - | ||
62 | 17 files changed, 262 insertions(+), 57 deletions(-) | ||
63 | create mode 100755 tests/qemu-iotests/282 | ||
64 | create mode 100644 tests/qemu-iotests/282.out | ||
65 | |||
66 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | From: Pan Nengyuan <pannengyuan@huawei.com> | ||
2 | 1 | ||
3 | 'type/id' forgot to free in qmp_object_add, this patch fix that. | ||
4 | |||
5 | The leak stack: | ||
6 | Direct leak of 84 byte(s) in 6 object(s) allocated from: | ||
7 | #0 0x7fe2a5ebf768 in __interceptor_malloc (/lib64/libasan.so.5+0xef768) | ||
8 | #1 0x7fe2a5044445 in g_malloc (/lib64/libglib-2.0.so.0+0x52445) | ||
9 | #2 0x7fe2a505dd92 in g_strdup (/lib64/libglib-2.0.so.0+0x6bd92) | ||
10 | #3 0x56344954e692 in qmp_object_add /mnt/sdb/qemu-new/qemu_test/qemu/qom/qom-qmp-cmds.c:258 | ||
11 | #4 0x563449960f5a in do_qmp_dispatch /mnt/sdb/qemu-new/qemu_test/qemu/qapi/qmp-dispatch.c:132 | ||
12 | #5 0x563449960f5a in qmp_dispatch /mnt/sdb/qemu-new/qemu_test/qemu/qapi/qmp-dispatch.c:175 | ||
13 | #6 0x563449498a30 in monitor_qmp_dispatch /mnt/sdb/qemu-new/qemu_test/qemu/monitor/qmp.c:145 | ||
14 | #7 0x56344949a64f in monitor_qmp_bh_dispatcher /mnt/sdb/qemu-new/qemu_test/qemu/monitor/qmp.c:234 | ||
15 | #8 0x563449a92a3a in aio_bh_call /mnt/sdb/qemu-new/qemu_test/qemu/util/async.c:136 | ||
16 | |||
17 | Direct leak of 54 byte(s) in 6 object(s) allocated from: | ||
18 | #0 0x7fe2a5ebf768 in __interceptor_malloc (/lib64/libasan.so.5+0xef768) | ||
19 | #1 0x7fe2a5044445 in g_malloc (/lib64/libglib-2.0.so.0+0x52445) | ||
20 | #2 0x7fe2a505dd92 in g_strdup (/lib64/libglib-2.0.so.0+0x6bd92) | ||
21 | #3 0x56344954e6c4 in qmp_object_add /mnt/sdb/qemu-new/qemu_test/qemu/qom/qom-qmp-cmds.c:267 | ||
22 | #4 0x563449960f5a in do_qmp_dispatch /mnt/sdb/qemu-new/qemu_test/qemu/qapi/qmp-dispatch.c:132 | ||
23 | #5 0x563449960f5a in qmp_dispatch /mnt/sdb/qemu-new/qemu_test/qemu/qapi/qmp-dispatch.c:175 | ||
24 | #6 0x563449498a30 in monitor_qmp_dispatch /mnt/sdb/qemu-new/qemu_test/qemu/monitor/qmp.c:145 | ||
25 | #7 0x56344949a64f in monitor_qmp_bh_dispatcher /mnt/sdb/qemu-new/qemu_test/qemu/monitor/qmp.c:234 | ||
26 | #8 0x563449a92a3a in aio_bh_call /mnt/sdb/qemu-new/qemu_test/qemu/util/async.c:136 | ||
27 | |||
28 | Fixes: 5f07c4d60d091320186e7b0edaf9ed2cc16b2d1e | ||
29 | Reported-by: Euler Robot <euler.robot@huawei.com> | ||
30 | Signed-off-by: Pan Nengyuan <pannengyuan@huawei.com> | ||
31 | Message-Id: <20200310064640.5059-1-pannengyuan@huawei.com> | ||
32 | Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> | ||
33 | Acked-by: Igor Mammedov <imammedo@redhat.com> | ||
34 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
35 | --- | ||
36 | qom/qom-qmp-cmds.c | 16 ++++++---------- | ||
37 | 1 file changed, 6 insertions(+), 10 deletions(-) | ||
38 | |||
39 | diff --git a/qom/qom-qmp-cmds.c b/qom/qom-qmp-cmds.c | ||
40 | index XXXXXXX..XXXXXXX 100644 | ||
41 | --- a/qom/qom-qmp-cmds.c | ||
42 | +++ b/qom/qom-qmp-cmds.c | ||
43 | @@ -XXX,XX +XXX,XX @@ void qmp_object_add(QDict *qdict, QObject **ret_data, Error **errp) | ||
44 | QDict *pdict; | ||
45 | Visitor *v; | ||
46 | Object *obj; | ||
47 | - const char *type; | ||
48 | - const char *id; | ||
49 | + g_autofree char *type = NULL; | ||
50 | + g_autofree char *id = NULL; | ||
51 | |||
52 | - type = qdict_get_try_str(qdict, "qom-type"); | ||
53 | + type = g_strdup(qdict_get_try_str(qdict, "qom-type")); | ||
54 | if (!type) { | ||
55 | error_setg(errp, QERR_MISSING_PARAMETER, "qom-type"); | ||
56 | return; | ||
57 | - } else { | ||
58 | - type = g_strdup(type); | ||
59 | - qdict_del(qdict, "qom-type"); | ||
60 | } | ||
61 | + qdict_del(qdict, "qom-type"); | ||
62 | |||
63 | - id = qdict_get_try_str(qdict, "id"); | ||
64 | + id = g_strdup(qdict_get_try_str(qdict, "id")); | ||
65 | if (!id) { | ||
66 | error_setg(errp, QERR_MISSING_PARAMETER, "id"); | ||
67 | return; | ||
68 | - } else { | ||
69 | - id = g_strdup(id); | ||
70 | - qdict_del(qdict, "id"); | ||
71 | } | ||
72 | + qdict_del(qdict, "id"); | ||
73 | |||
74 | props = qdict_get(qdict, "props"); | ||
75 | if (props) { | ||
76 | -- | ||
77 | 2.20.1 | ||
78 | |||
79 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
2 | Message-Id: <20200310113831.27293-2-kwolf@redhat.com> | ||
3 | Reviewed-by: Peter Krempa <pkrempa@redhat.com> | ||
4 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
5 | --- | ||
6 | include/block/block_int.h | 3 +++ | ||
7 | block.c | 6 ++---- | ||
8 | 2 files changed, 5 insertions(+), 4 deletions(-) | ||
9 | 1 | ||
10 | diff --git a/include/block/block_int.h b/include/block/block_int.h | ||
11 | index XXXXXXX..XXXXXXX 100644 | ||
12 | --- a/include/block/block_int.h | ||
13 | +++ b/include/block/block_int.h | ||
14 | @@ -XXX,XX +XXX,XX @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs, | ||
15 | void *opaque, Error **errp); | ||
16 | void bdrv_root_unref_child(BdrvChild *child); | ||
17 | |||
18 | +void bdrv_get_cumulative_perm(BlockDriverState *bs, uint64_t *perm, | ||
19 | + uint64_t *shared_perm); | ||
20 | + | ||
21 | /** | ||
22 | * Sets a BdrvChild's permissions. Avoid if the parent is a BDS; use | ||
23 | * bdrv_child_refresh_perms() instead and make the parent's | ||
24 | diff --git a/block.c b/block.c | ||
25 | index XXXXXXX..XXXXXXX 100644 | ||
26 | --- a/block.c | ||
27 | +++ b/block.c | ||
28 | @@ -XXX,XX +XXX,XX @@ static int bdrv_child_check_perm(BdrvChild *c, BlockReopenQueue *q, | ||
29 | bool *tighten_restrictions, Error **errp); | ||
30 | static void bdrv_child_abort_perm_update(BdrvChild *c); | ||
31 | static void bdrv_child_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared); | ||
32 | -static void bdrv_get_cumulative_perm(BlockDriverState *bs, uint64_t *perm, | ||
33 | - uint64_t *shared_perm); | ||
34 | |||
35 | typedef struct BlockReopenQueueEntry { | ||
36 | bool prepared; | ||
37 | @@ -XXX,XX +XXX,XX @@ static void bdrv_set_perm(BlockDriverState *bs, uint64_t cumulative_perms, | ||
38 | } | ||
39 | } | ||
40 | |||
41 | -static void bdrv_get_cumulative_perm(BlockDriverState *bs, uint64_t *perm, | ||
42 | - uint64_t *shared_perm) | ||
43 | +void bdrv_get_cumulative_perm(BlockDriverState *bs, uint64_t *perm, | ||
44 | + uint64_t *shared_perm) | ||
45 | { | ||
46 | BdrvChild *c; | ||
47 | uint64_t cumulative_perms = 0; | ||
48 | -- | ||
49 | 2.20.1 | ||
50 | |||
51 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | blockdev-snapshot returned an error if the overlay was already in use, | ||
2 | which it defined as having any BlockBackend parent. This is in fact both | ||
3 | too strict (some parents can tolerate the change of visible data caused | ||
4 | by attaching a backing file) and too loose (some non-BlockBackend | ||
5 | parents may not be happy with it). | ||
6 | 1 | ||
7 | One important use case that is prevented by the too strict check is live | ||
8 | storage migration with blockdev-mirror. Here, the target node is | ||
9 | usually opened without a backing file so that the active layer is | ||
10 | mirrored while its backing chain can be copied in the background. | ||
11 | |||
12 | The backing chain should be attached to the mirror target node when | ||
13 | finalising the job, just before switching the users of the source node | ||
14 | to the new copy (at which point the mirror job still has a reference to | ||
15 | the node). drive-mirror did this automatically, but with blockdev-mirror | ||
16 | this is the job of the QMP client, so it needs a way to do this. | ||
17 | |||
18 | blockdev-snapshot is the obvious way, so this patch makes it work in | ||
19 | this scenario. The new condition is that no parent uses CONSISTENT_READ | ||
20 | permissions. This will ensure that the operation will still be blocked | ||
21 | when the node is attached to the guest device, so blockdev-snapshot | ||
22 | remains safe. | ||
23 | |||
24 | (For the sake of completeness, x-blockdev-reopen can be used to achieve | ||
25 | the same, however it is a big hammer, performs the graph change | ||
26 | completely unchecked and is still experimental. So even with the option | ||
27 | of using x-blockdev-reopen, there are reasons why blockdev-snapshot | ||
28 | should be able to perform this operation.) | ||
29 | |||
30 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
31 | Message-Id: <20200310113831.27293-3-kwolf@redhat.com> | ||
32 | Reviewed-by: Peter Krempa <pkrempa@redhat.com> | ||
33 | Tested-by: Peter Krempa <pkrempa@redhat.com> | ||
34 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
35 | --- | ||
36 | blockdev.c | 14 ++++++++------ | ||
37 | tests/qemu-iotests/085.out | 4 ++-- | ||
38 | 2 files changed, 10 insertions(+), 8 deletions(-) | ||
39 | |||
40 | diff --git a/blockdev.c b/blockdev.c | ||
41 | index XXXXXXX..XXXXXXX 100644 | ||
42 | --- a/blockdev.c | ||
43 | +++ b/blockdev.c | ||
44 | @@ -XXX,XX +XXX,XX @@ static void external_snapshot_prepare(BlkActionState *common, | ||
45 | TransactionAction *action = common->action; | ||
46 | AioContext *aio_context; | ||
47 | AioContext *old_context; | ||
48 | + uint64_t perm, shared; | ||
49 | int ret; | ||
50 | |||
51 | /* 'blockdev-snapshot' and 'blockdev-snapshot-sync' have similar | ||
52 | @@ -XXX,XX +XXX,XX @@ static void external_snapshot_prepare(BlkActionState *common, | ||
53 | goto out; | ||
54 | } | ||
55 | |||
56 | - if (bdrv_has_blk(state->new_bs)) { | ||
57 | + /* | ||
58 | + * Allow attaching a backing file to an overlay that's already in use only | ||
59 | + * if the parents don't assume that they are already seeing a valid image. | ||
60 | + * (Specifically, allow it as a mirror target, which is write-only access.) | ||
61 | + */ | ||
62 | + bdrv_get_cumulative_perm(state->new_bs, &perm, &shared); | ||
63 | + if (perm & BLK_PERM_CONSISTENT_READ) { | ||
64 | error_setg(errp, "The overlay is already in use"); | ||
65 | goto out; | ||
66 | } | ||
67 | |||
68 | - if (bdrv_op_is_blocked(state->new_bs, BLOCK_OP_TYPE_EXTERNAL_SNAPSHOT, | ||
69 | - errp)) { | ||
70 | - goto out; | ||
71 | - } | ||
72 | - | ||
73 | if (state->new_bs->backing != NULL) { | ||
74 | error_setg(errp, "The overlay already has a backing image"); | ||
75 | goto out; | ||
76 | diff --git a/tests/qemu-iotests/085.out b/tests/qemu-iotests/085.out | ||
77 | index XXXXXXX..XXXXXXX 100644 | ||
78 | --- a/tests/qemu-iotests/085.out | ||
79 | +++ b/tests/qemu-iotests/085.out | ||
80 | @@ -XXX,XX +XXX,XX @@ Formatting 'TEST_DIR/12-snapshot-v0.IMGFMT', fmt=IMGFMT size=134217728 backing_f | ||
81 | === Invalid command - cannot create a snapshot using a file BDS === | ||
82 | |||
83 | { 'execute': 'blockdev-snapshot', 'arguments': { 'node':'virtio0', 'overlay':'file_12' } } | ||
84 | -{"error": {"class": "GenericError", "desc": "The overlay does not support backing images"}} | ||
85 | +{"error": {"class": "GenericError", "desc": "The overlay is already in use"}} | ||
86 | |||
87 | === Invalid command - snapshot node used as active layer === | ||
88 | |||
89 | @@ -XXX,XX +XXX,XX @@ Formatting 'TEST_DIR/12-snapshot-v0.IMGFMT', fmt=IMGFMT size=134217728 backing_f | ||
90 | === Invalid command - snapshot node used as backing hd === | ||
91 | |||
92 | { 'execute': 'blockdev-snapshot', 'arguments': { 'node': 'virtio0', 'overlay':'snap_11' } } | ||
93 | -{"error": {"class": "GenericError", "desc": "Node 'snap_11' is busy: node is used as backing hd of 'snap_12'"}} | ||
94 | +{"error": {"class": "GenericError", "desc": "The overlay is already in use"}} | ||
95 | |||
96 | === Invalid command - snapshot node has a backing image === | ||
97 | |||
98 | -- | ||
99 | 2.20.1 | ||
100 | |||
101 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | The 'job-complete' QMP command should be run with qmp() rather than | ||
2 | qmp_log() if use_log=False is passed. | ||
3 | 1 | ||
4 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
5 | Message-Id: <20200310113831.27293-4-kwolf@redhat.com> | ||
6 | Reviewed-by: Peter Krempa <pkrempa@redhat.com> | ||
7 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
8 | --- | ||
9 | tests/qemu-iotests/iotests.py | 5 ++++- | ||
10 | 1 file changed, 4 insertions(+), 1 deletion(-) | ||
11 | |||
12 | diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py | ||
13 | index XXXXXXX..XXXXXXX 100644 | ||
14 | --- a/tests/qemu-iotests/iotests.py | ||
15 | +++ b/tests/qemu-iotests/iotests.py | ||
16 | @@ -XXX,XX +XXX,XX @@ class VM(qtest.QEMUQtestMachine): | ||
17 | if use_log: | ||
18 | log('Job failed: %s' % (j['error'])) | ||
19 | elif status == 'ready': | ||
20 | - self.qmp_log('job-complete', id=job) | ||
21 | + if use_log: | ||
22 | + self.qmp_log('job-complete', id=job) | ||
23 | + else: | ||
24 | + self.qmp('job-complete', id=job) | ||
25 | elif status == 'pending' and not auto_finalize: | ||
26 | if pre_finalize: | ||
27 | pre_finalize() | ||
28 | -- | ||
29 | 2.20.1 | ||
30 | |||
31 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | The newly tested scenario is a common live storage migration scenario: | ||
2 | The target node is opened without a backing file so that the active | ||
3 | layer is mirrored while its backing chain can be copied in the | ||
4 | background. | ||
5 | 1 | ||
6 | The backing chain should be attached to the mirror target node when | ||
7 | finalising the job, just before switching the users of the source node | ||
8 | to the new copy (at which point the mirror job still has a reference to | ||
9 | the node). drive-mirror did this automatically, but with blockdev-mirror | ||
10 | this is the job of the QMP client. | ||
11 | |||
12 | This patch adds test cases for two ways to achieve the desired result, | ||
13 | using either x-blockdev-reopen or blockdev-snapshot. | ||
14 | |||
15 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
16 | Message-Id: <20200310113831.27293-5-kwolf@redhat.com> | ||
17 | Reviewed-by: Peter Krempa <pkrempa@redhat.com> | ||
18 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
19 | --- | ||
20 | tests/qemu-iotests/155 | 56 ++++++++++++++++++++++++++++++++++---- | ||
21 | tests/qemu-iotests/155.out | 4 +-- | ||
22 | 2 files changed, 53 insertions(+), 7 deletions(-) | ||
23 | |||
24 | diff --git a/tests/qemu-iotests/155 b/tests/qemu-iotests/155 | ||
25 | index XXXXXXX..XXXXXXX 100755 | ||
26 | --- a/tests/qemu-iotests/155 | ||
27 | +++ b/tests/qemu-iotests/155 | ||
28 | @@ -XXX,XX +XXX,XX @@ target_img = os.path.join(iotests.test_dir, 'target.' + iotests.imgfmt) | ||
29 | # image during runtime, only makes sense if | ||
30 | # target_blockdev_backing is not None | ||
31 | # (None: same as target_backing) | ||
32 | +# target_open_with_backing: If True, the target image is added with its backing | ||
33 | +# chain opened right away. If False, blockdev-add | ||
34 | +# opens it without a backing file and job completion | ||
35 | +# is supposed to open the backing chain. | ||
36 | |||
37 | class BaseClass(iotests.QMPTestCase): | ||
38 | target_blockdev_backing = None | ||
39 | target_real_backing = None | ||
40 | + target_open_with_backing = True | ||
41 | |||
42 | def setUp(self): | ||
43 | qemu_img('create', '-f', iotests.imgfmt, back0_img, '1440K') | ||
44 | @@ -XXX,XX +XXX,XX @@ class BaseClass(iotests.QMPTestCase): | ||
45 | options = { 'node-name': 'target', | ||
46 | 'driver': iotests.imgfmt, | ||
47 | 'file': { 'driver': 'file', | ||
48 | + 'node-name': 'target-file', | ||
49 | 'filename': target_img } } | ||
50 | - if self.target_blockdev_backing: | ||
51 | - options['backing'] = self.target_blockdev_backing | ||
52 | + | ||
53 | + if not self.target_open_with_backing: | ||
54 | + options['backing'] = None | ||
55 | + elif self.target_blockdev_backing: | ||
56 | + options['backing'] = self.target_blockdev_backing | ||
57 | |||
58 | result = self.vm.qmp('blockdev-add', **options) | ||
59 | self.assert_qmp(result, 'return', {}) | ||
60 | @@ -XXX,XX +XXX,XX @@ class BaseClass(iotests.QMPTestCase): | ||
61 | # cmd: Mirroring command to execute, either drive-mirror or blockdev-mirror | ||
62 | |||
63 | class MirrorBaseClass(BaseClass): | ||
64 | + def openBacking(self): | ||
65 | + pass | ||
66 | + | ||
67 | def runMirror(self, sync): | ||
68 | if self.cmd == 'blockdev-mirror': | ||
69 | result = self.vm.qmp(self.cmd, job_id='mirror-job', device='source', | ||
70 | - sync=sync, target='target') | ||
71 | + sync=sync, target='target', | ||
72 | + auto_finalize=False) | ||
73 | else: | ||
74 | if self.existing: | ||
75 | mode = 'existing' | ||
76 | @@ -XXX,XX +XXX,XX @@ class MirrorBaseClass(BaseClass): | ||
77 | result = self.vm.qmp(self.cmd, job_id='mirror-job', device='source', | ||
78 | sync=sync, target=target_img, | ||
79 | format=iotests.imgfmt, mode=mode, | ||
80 | - node_name='target') | ||
81 | + node_name='target', auto_finalize=False) | ||
82 | |||
83 | self.assert_qmp(result, 'return', {}) | ||
84 | |||
85 | - self.complete_and_wait('mirror-job') | ||
86 | + self.vm.run_job('mirror-job', use_log=False, auto_finalize=False, | ||
87 | + pre_finalize=self.openBacking, auto_dismiss=True) | ||
88 | |||
89 | def testFull(self): | ||
90 | self.runMirror('full') | ||
91 | @@ -XXX,XX +XXX,XX @@ class TestBlockdevMirrorForcedBacking(MirrorBaseClass): | ||
92 | target_blockdev_backing = { 'driver': 'null-co' } | ||
93 | target_real_backing = 'null-co://' | ||
94 | |||
95 | +# Attach the backing chain only during completion, with blockdev-reopen | ||
96 | +class TestBlockdevMirrorReopen(MirrorBaseClass): | ||
97 | + cmd = 'blockdev-mirror' | ||
98 | + existing = True | ||
99 | + target_backing = 'null-co://' | ||
100 | + target_open_with_backing = False | ||
101 | + | ||
102 | + def openBacking(self): | ||
103 | + if not self.target_open_with_backing: | ||
104 | + result = self.vm.qmp('blockdev-add', node_name="backing", | ||
105 | + driver="null-co") | ||
106 | + self.assert_qmp(result, 'return', {}) | ||
107 | + result = self.vm.qmp('x-blockdev-reopen', node_name="target", | ||
108 | + driver=iotests.imgfmt, file="target-file", | ||
109 | + backing="backing") | ||
110 | + self.assert_qmp(result, 'return', {}) | ||
111 | + | ||
112 | +# Attach the backing chain only during completion, with blockdev-snapshot | ||
113 | +class TestBlockdevMirrorSnapshot(MirrorBaseClass): | ||
114 | + cmd = 'blockdev-mirror' | ||
115 | + existing = True | ||
116 | + target_backing = 'null-co://' | ||
117 | + target_open_with_backing = False | ||
118 | + | ||
119 | + def openBacking(self): | ||
120 | + if not self.target_open_with_backing: | ||
121 | + result = self.vm.qmp('blockdev-add', node_name="backing", | ||
122 | + driver="null-co") | ||
123 | + self.assert_qmp(result, 'return', {}) | ||
124 | + result = self.vm.qmp('blockdev-snapshot', node="backing", | ||
125 | + overlay="target") | ||
126 | + self.assert_qmp(result, 'return', {}) | ||
127 | |||
128 | class TestCommit(BaseClass): | ||
129 | existing = False | ||
130 | diff --git a/tests/qemu-iotests/155.out b/tests/qemu-iotests/155.out | ||
131 | index XXXXXXX..XXXXXXX 100644 | ||
132 | --- a/tests/qemu-iotests/155.out | ||
133 | +++ b/tests/qemu-iotests/155.out | ||
134 | @@ -XXX,XX +XXX,XX @@ | ||
135 | -................... | ||
136 | +......................... | ||
137 | ---------------------------------------------------------------------- | ||
138 | -Ran 19 tests | ||
139 | +Ran 25 tests | ||
140 | |||
141 | OK | ||
142 | -- | ||
143 | 2.20.1 | ||
144 | |||
145 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | external_snapshot_prepare() tries to move the overlay to the AioContext | ||
2 | of the backing file (the snapshotted node). However, it's possible that | ||
3 | this doesn't work, but the backing file can instead be moved to the | ||
4 | overlay's AioContext (e.g. opening the backing chain for a mirror | ||
5 | target). | ||
6 | 1 | ||
7 | bdrv_append() already indirectly uses bdrv_attach_node(), which takes | ||
8 | care to move nodes to make sure they use the same AioContext and which | ||
9 | tries both directions. | ||
10 | |||
11 | So the problem has a simple fix: Just delete the unnecessary extra | ||
12 | bdrv_try_set_aio_context() call in external_snapshot_prepare() and | ||
13 | instead assert in bdrv_append() that both nodes were indeed moved to the | ||
14 | same AioContext. | ||
15 | |||
16 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
17 | Message-Id: <20200310113831.27293-6-kwolf@redhat.com> | ||
18 | Tested-by: Peter Krempa <pkrempa@redhat.com> | ||
19 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
20 | --- | ||
21 | block.c | 1 + | ||
22 | blockdev.c | 16 ---------------- | ||
23 | 2 files changed, 1 insertion(+), 16 deletions(-) | ||
24 | |||
25 | diff --git a/block.c b/block.c | ||
26 | index XXXXXXX..XXXXXXX 100644 | ||
27 | --- a/block.c | ||
28 | +++ b/block.c | ||
29 | @@ -XXX,XX +XXX,XX @@ void bdrv_replace_node(BlockDriverState *from, BlockDriverState *to, | ||
30 | bdrv_ref(from); | ||
31 | |||
32 | assert(qemu_get_current_aio_context() == qemu_get_aio_context()); | ||
33 | + assert(bdrv_get_aio_context(from) == bdrv_get_aio_context(to)); | ||
34 | bdrv_drained_begin(from); | ||
35 | |||
36 | /* Put all parents into @list and calculate their cumulative permissions */ | ||
37 | diff --git a/blockdev.c b/blockdev.c | ||
38 | index XXXXXXX..XXXXXXX 100644 | ||
39 | --- a/blockdev.c | ||
40 | +++ b/blockdev.c | ||
41 | @@ -XXX,XX +XXX,XX @@ static void external_snapshot_prepare(BlkActionState *common, | ||
42 | DO_UPCAST(ExternalSnapshotState, common, common); | ||
43 | TransactionAction *action = common->action; | ||
44 | AioContext *aio_context; | ||
45 | - AioContext *old_context; | ||
46 | uint64_t perm, shared; | ||
47 | - int ret; | ||
48 | |||
49 | /* 'blockdev-snapshot' and 'blockdev-snapshot-sync' have similar | ||
50 | * purpose but a different set of parameters */ | ||
51 | @@ -XXX,XX +XXX,XX @@ static void external_snapshot_prepare(BlkActionState *common, | ||
52 | goto out; | ||
53 | } | ||
54 | |||
55 | - /* Honor bdrv_try_set_aio_context() context acquisition requirements. */ | ||
56 | - old_context = bdrv_get_aio_context(state->new_bs); | ||
57 | - aio_context_release(aio_context); | ||
58 | - aio_context_acquire(old_context); | ||
59 | - | ||
60 | - ret = bdrv_try_set_aio_context(state->new_bs, aio_context, errp); | ||
61 | - | ||
62 | - aio_context_release(old_context); | ||
63 | - aio_context_acquire(aio_context); | ||
64 | - | ||
65 | - if (ret < 0) { | ||
66 | - goto out; | ||
67 | - } | ||
68 | - | ||
69 | /* This removes our old bs and adds the new bs. This is an operation that | ||
70 | * can fail, so we need to do it in .prepare; undoing it for abort is | ||
71 | * always possible. */ | ||
72 | -- | ||
73 | 2.20.1 | ||
74 | |||
75 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | This patch adds test cases for attaching the backing chain to a mirror | ||
2 | job target right before finalising the job, where the image is in a | ||
3 | non-mainloop AioContext (i.e. the backing chain needs to be moved to the | ||
4 | AioContext of the mirror target). | ||
5 | 1 | ||
6 | This requires switching the test case from virtio-blk to virtio-scsi | ||
7 | because virtio-blk only actually starts using the iothreads when the | ||
8 | guest driver initialises the device (which never happens in a test case | ||
9 | without a guest OS). virtio-scsi always keeps its block nodes in the | ||
10 | AioContext of the the requested iothread without guest interaction. | ||
11 | |||
12 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
13 | Message-Id: <20200310113831.27293-7-kwolf@redhat.com> | ||
14 | Reviewed-by: Peter Krempa <pkrempa@redhat.com> | ||
15 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
16 | --- | ||
17 | tests/qemu-iotests/155 | 32 +++++++++++++++++++++++--------- | ||
18 | tests/qemu-iotests/155.out | 4 ++-- | ||
19 | 2 files changed, 25 insertions(+), 11 deletions(-) | ||
20 | |||
21 | diff --git a/tests/qemu-iotests/155 b/tests/qemu-iotests/155 | ||
22 | index XXXXXXX..XXXXXXX 100755 | ||
23 | --- a/tests/qemu-iotests/155 | ||
24 | +++ b/tests/qemu-iotests/155 | ||
25 | @@ -XXX,XX +XXX,XX @@ target_img = os.path.join(iotests.test_dir, 'target.' + iotests.imgfmt) | ||
26 | # chain opened right away. If False, blockdev-add | ||
27 | # opens it without a backing file and job completion | ||
28 | # is supposed to open the backing chain. | ||
29 | +# use_iothread: If True, an iothread is configured for the virtio-blk device | ||
30 | +# that uses the image being mirrored | ||
31 | |||
32 | class BaseClass(iotests.QMPTestCase): | ||
33 | target_blockdev_backing = None | ||
34 | target_real_backing = None | ||
35 | target_open_with_backing = True | ||
36 | + use_iothread = False | ||
37 | |||
38 | def setUp(self): | ||
39 | qemu_img('create', '-f', iotests.imgfmt, back0_img, '1440K') | ||
40 | @@ -XXX,XX +XXX,XX @@ class BaseClass(iotests.QMPTestCase): | ||
41 | 'file': {'driver': 'file', | ||
42 | 'filename': source_img}} | ||
43 | self.vm.add_blockdev(self.vm.qmp_to_opts(blockdev)) | ||
44 | - self.vm.add_device('virtio-blk,id=qdev0,drive=source') | ||
45 | + | ||
46 | + if self.use_iothread: | ||
47 | + self.vm.add_object('iothread,id=iothread0') | ||
48 | + iothread = ",iothread=iothread0" | ||
49 | + else: | ||
50 | + iothread = "" | ||
51 | + | ||
52 | + self.vm.add_device('virtio-scsi%s' % iothread) | ||
53 | + self.vm.add_device('scsi-hd,id=qdev0,drive=source') | ||
54 | + | ||
55 | self.vm.launch() | ||
56 | |||
57 | self.assertIntactSourceBackingChain() | ||
58 | @@ -XXX,XX +XXX,XX @@ class MirrorBaseClass(BaseClass): | ||
59 | def testFull(self): | ||
60 | self.runMirror('full') | ||
61 | |||
62 | - node = self.findBlockNode('target', | ||
63 | - '/machine/peripheral/qdev0/virtio-backend') | ||
64 | + node = self.findBlockNode('target', 'qdev0') | ||
65 | self.assertCorrectBackingImage(node, None) | ||
66 | self.assertIntactSourceBackingChain() | ||
67 | |||
68 | def testTop(self): | ||
69 | self.runMirror('top') | ||
70 | |||
71 | - node = self.findBlockNode('target', | ||
72 | - '/machine/peripheral/qdev0/virtio-backend') | ||
73 | + node = self.findBlockNode('target', 'qdev0') | ||
74 | self.assertCorrectBackingImage(node, back2_img) | ||
75 | self.assertIntactSourceBackingChain() | ||
76 | |||
77 | def testNone(self): | ||
78 | self.runMirror('none') | ||
79 | |||
80 | - node = self.findBlockNode('target', | ||
81 | - '/machine/peripheral/qdev0/virtio-backend') | ||
82 | + node = self.findBlockNode('target', 'qdev0') | ||
83 | self.assertCorrectBackingImage(node, source_img) | ||
84 | self.assertIntactSourceBackingChain() | ||
85 | |||
86 | @@ -XXX,XX +XXX,XX @@ class TestBlockdevMirrorReopen(MirrorBaseClass): | ||
87 | backing="backing") | ||
88 | self.assert_qmp(result, 'return', {}) | ||
89 | |||
90 | +class TestBlockdevMirrorReopenIothread(TestBlockdevMirrorReopen): | ||
91 | + use_iothread = True | ||
92 | + | ||
93 | # Attach the backing chain only during completion, with blockdev-snapshot | ||
94 | class TestBlockdevMirrorSnapshot(MirrorBaseClass): | ||
95 | cmd = 'blockdev-mirror' | ||
96 | @@ -XXX,XX +XXX,XX @@ class TestBlockdevMirrorSnapshot(MirrorBaseClass): | ||
97 | overlay="target") | ||
98 | self.assert_qmp(result, 'return', {}) | ||
99 | |||
100 | +class TestBlockdevMirrorSnapshotIothread(TestBlockdevMirrorSnapshot): | ||
101 | + use_iothread = True | ||
102 | + | ||
103 | class TestCommit(BaseClass): | ||
104 | existing = False | ||
105 | |||
106 | @@ -XXX,XX +XXX,XX @@ class TestCommit(BaseClass): | ||
107 | |||
108 | self.vm.event_wait('BLOCK_JOB_COMPLETED') | ||
109 | |||
110 | - node = self.findBlockNode(None, | ||
111 | - '/machine/peripheral/qdev0/virtio-backend') | ||
112 | + node = self.findBlockNode(None, 'qdev0') | ||
113 | self.assert_qmp(node, 'image' + '/backing-image' * 0 + '/filename', | ||
114 | back1_img) | ||
115 | self.assert_qmp(node, 'image' + '/backing-image' * 1 + '/filename', | ||
116 | diff --git a/tests/qemu-iotests/155.out b/tests/qemu-iotests/155.out | ||
117 | index XXXXXXX..XXXXXXX 100644 | ||
118 | --- a/tests/qemu-iotests/155.out | ||
119 | +++ b/tests/qemu-iotests/155.out | ||
120 | @@ -XXX,XX +XXX,XX @@ | ||
121 | -......................... | ||
122 | +............................... | ||
123 | ---------------------------------------------------------------------- | ||
124 | -Ran 25 tests | ||
125 | +Ran 31 tests | ||
126 | |||
127 | OK | ||
128 | -- | ||
129 | 2.20.1 | ||
130 | |||
131 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | From: Peter Krempa <pkrempa@redhat.com> | ||
2 | 1 | ||
3 | Anounce that 'blockdev-snapshot' command's permissions allow changing | ||
4 | of the backing file if the 'consistent_read' permission is not required. | ||
5 | |||
6 | This is useful for libvirt to allow late opening of the backing chain | ||
7 | during a blockdev-mirror. | ||
8 | |||
9 | Signed-off-by: Peter Krempa <pkrempa@redhat.com> | ||
10 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
11 | Message-Id: <20200310113831.27293-8-kwolf@redhat.com> | ||
12 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
13 | --- | ||
14 | qapi/block-core.json | 9 ++++++++- | ||
15 | 1 file changed, 8 insertions(+), 1 deletion(-) | ||
16 | |||
17 | diff --git a/qapi/block-core.json b/qapi/block-core.json | ||
18 | index XXXXXXX..XXXXXXX 100644 | ||
19 | --- a/qapi/block-core.json | ||
20 | +++ b/qapi/block-core.json | ||
21 | @@ -XXX,XX +XXX,XX @@ | ||
22 | # | ||
23 | # For the arguments, see the documentation of BlockdevSnapshot. | ||
24 | # | ||
25 | +# Features: | ||
26 | +# @allow-write-only-overlay: If present, the check whether this operation is safe | ||
27 | +# was relaxed so that it can be used to change | ||
28 | +# backing file of a destination of a blockdev-mirror. | ||
29 | +# (since 5.0) | ||
30 | +# | ||
31 | # Since: 2.5 | ||
32 | # | ||
33 | # Example: | ||
34 | @@ -XXX,XX +XXX,XX @@ | ||
35 | # | ||
36 | ## | ||
37 | { 'command': 'blockdev-snapshot', | ||
38 | - 'data': 'BlockdevSnapshot' } | ||
39 | + 'data': 'BlockdevSnapshot', | ||
40 | + 'features': [ 'allow-write-only-overlay' ] } | ||
41 | |||
42 | ## | ||
43 | # @change-backing-file: | ||
44 | -- | ||
45 | 2.20.1 | ||
46 | |||
47 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | From: Philippe Mathieu-Daudé <philmd@redhat.com> | ||
2 | 1 | ||
3 | The socket_scm_helper path got corrupted during the mechanical | ||
4 | refactor moving the qtests files into their own sub-directory. | ||
5 | |||
6 | Fixes: 1e8a1fae7 ("test: Move qtests to a separate directory") | ||
7 | Signed-off-by: Philippe Mathieu-Daudé <philmd@redhat.com> | ||
8 | Message-Id: <20200306165751.18986-1-philmd@redhat.com> | ||
9 | Reviewed-by: Laurent Vivier <laurent@vivier.eu> | ||
10 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
11 | --- | ||
12 | tests/Makefile.include | 1 + | ||
13 | tests/qtest/Makefile.include | 1 - | ||
14 | 2 files changed, 1 insertion(+), 1 deletion(-) | ||
15 | |||
16 | diff --git a/tests/Makefile.include b/tests/Makefile.include | ||
17 | index XXXXXXX..XXXXXXX 100644 | ||
18 | --- a/tests/Makefile.include | ||
19 | +++ b/tests/Makefile.include | ||
20 | @@ -XXX,XX +XXX,XX @@ include $(SRC_PATH)/tests/qtest/Makefile.include | ||
21 | tests/test-qga$(EXESUF): qemu-ga$(EXESUF) | ||
22 | tests/test-qga$(EXESUF): tests/test-qga.o $(qtest-obj-y) | ||
23 | tests/vhost-user-bridge$(EXESUF): tests/vhost-user-bridge.o $(test-util-obj-y) libvhost-user.a | ||
24 | +tests/qemu-iotests/socket_scm_helper$(EXESUF): tests/qemu-iotests/socket_scm_helper.o | ||
25 | |||
26 | SPEED = quick | ||
27 | |||
28 | diff --git a/tests/qtest/Makefile.include b/tests/qtest/Makefile.include | ||
29 | index XXXXXXX..XXXXXXX 100644 | ||
30 | --- a/tests/qtest/Makefile.include | ||
31 | +++ b/tests/qtest/Makefile.include | ||
32 | @@ -XXX,XX +XXX,XX @@ tests/qtest/usb-hcd-ehci-test$(EXESUF): tests/qtest/usb-hcd-ehci-test.o $(libqos | ||
33 | tests/qtest/usb-hcd-xhci-test$(EXESUF): tests/qtest/usb-hcd-xhci-test.o $(libqos-usb-obj-y) | ||
34 | tests/qtest/cpu-plug-test$(EXESUF): tests/qtest/cpu-plug-test.o | ||
35 | tests/qtest/migration-test$(EXESUF): tests/qtest/migration-test.o tests/qtest/migration-helpers.o | ||
36 | -tests/qtest/qemu-iotests/qtest/socket_scm_helper$(EXESUF): tests/qtest/qemu-iotests/qtest/socket_scm_helper.o | ||
37 | tests/qtest/test-netfilter$(EXESUF): tests/qtest/test-netfilter.o $(qtest-obj-y) | ||
38 | tests/qtest/test-filter-mirror$(EXESUF): tests/qtest/test-filter-mirror.o $(qtest-obj-y) | ||
39 | tests/qtest/test-filter-redirector$(EXESUF): tests/qtest/test-filter-redirector.o $(qtest-obj-y) | ||
40 | -- | ||
41 | 2.20.1 | ||
42 | |||
43 | diff view generated by jsdifflib |
1 | From: Daniel Henrique Barboza <danielhb413@gmail.com> | 1 | From: Denis Rastyogin <gerben@altlinux.org> |
---|---|---|---|
2 | 2 | ||
3 | When using a non-UTF8 secret to create a volume using qemu-img, the | 3 | This error was discovered by fuzzing qemu-img. |
4 | following error happens: | ||
5 | 4 | ||
6 | $ qemu-img create -f luks --object secret,id=vol_1_encrypt0,file=vol_resize_pool.vol_1.secret.qzVQrI -o key-secret=vol_1_encrypt0 /var/tmp/pool_target/vol_1 10240K | 5 | This commit fixes a division by zero error in the bench_cb() function |
6 | that occurs when using the bench command with a zero-sized image. | ||
7 | 7 | ||
8 | Formatting '/var/tmp/pool_target/vol_1', fmt=luks size=10485760 key-secret=vol_1_encrypt0 | 8 | The issue arises because b->image_size can be zero, leading to a |
9 | qemu-img: /var/tmp/pool_target/vol_1: Data from secret vol_1_encrypt0 is not valid UTF-8 | 9 | division by zero in the modulo operation (b->offset %= b->image_size). |
10 | This patch adds a check for b->image_size == 0 and resets b->offset | ||
11 | to 0 in such cases, preventing the error. | ||
10 | 12 | ||
11 | However, the created file '/var/tmp/pool_target/vol_1' is left behind in the | 13 | Signed-off-by: Denis Rastyogin <gerben@altlinux.org> |
12 | file system after the failure. This behavior can be observed when creating | 14 | Message-ID: <20250318101933.255617-1-gerben@altlinux.org> |
13 | the volume using Libvirt, via 'virsh vol-create', and then getting "volume | 15 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> |
14 | target path already exist" errors when trying to re-create the volume. | ||
15 | |||
16 | The volume file is created inside block_crypto_co_create_opts_luks(), in | ||
17 | block/crypto.c. If the bdrv_create_file() call is successful but any | ||
18 | succeeding step fails*, the existing 'fail' label does not take into | ||
19 | account the created file, leaving it behind. | ||
20 | |||
21 | This patch changes block_crypto_co_create_opts_luks() to delete | ||
22 | 'filename' in case of failure. A failure in this point means that | ||
23 | the volume is now truncated/corrupted, so even if 'filename' was an | ||
24 | existing volume before calling qemu-img, it is now unusable. Deleting | ||
25 | the file it is not much worse than leaving it in the filesystem in | ||
26 | this scenario, and we don't have to deal with checking the file | ||
27 | pre-existence in the code. | ||
28 | |||
29 | * in our case, block_crypto_co_create_generic calls qcrypto_block_create, | ||
30 | which calls qcrypto_block_luks_create, and this function fails when | ||
31 | calling qcrypto_secret_lookup_as_utf8. | ||
32 | |||
33 | Reported-by: Srikanth Aithal <bssrikanth@in.ibm.com> | ||
34 | Suggested-by: Kevin Wolf <kwolf@redhat.com> | ||
35 | Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com> | ||
36 | Message-Id: <20200130213907.2830642-4-danielhb413@gmail.com> | ||
37 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 16 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
38 | --- | 17 | --- |
39 | block/crypto.c | 18 ++++++++++++++++++ | 18 | qemu-img.c | 6 +++++- |
40 | 1 file changed, 18 insertions(+) | 19 | 1 file changed, 5 insertions(+), 1 deletion(-) |
41 | 20 | ||
42 | diff --git a/block/crypto.c b/block/crypto.c | 21 | diff --git a/qemu-img.c b/qemu-img.c |
43 | index XXXXXXX..XXXXXXX 100644 | 22 | index XXXXXXX..XXXXXXX 100644 |
44 | --- a/block/crypto.c | 23 | --- a/qemu-img.c |
45 | +++ b/block/crypto.c | 24 | +++ b/qemu-img.c |
46 | @@ -XXX,XX +XXX,XX @@ | 25 | @@ -XXX,XX +XXX,XX @@ static void bench_cb(void *opaque, int ret) |
47 | #include "qapi/error.h" | 26 | */ |
48 | #include "qemu/module.h" | 27 | b->in_flight++; |
49 | #include "qemu/option.h" | 28 | b->offset += b->step; |
50 | +#include "qemu/cutils.h" | 29 | - b->offset %= b->image_size; |
51 | #include "crypto.h" | 30 | + if (b->image_size == 0) { |
52 | 31 | + b->offset = 0; | |
53 | typedef struct BlockCrypto BlockCrypto; | 32 | + } else { |
54 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn block_crypto_co_create_opts_luks(const char *filename, | 33 | + b->offset %= b->image_size; |
55 | |||
56 | ret = 0; | ||
57 | fail: | ||
58 | + /* | ||
59 | + * If an error occurred, delete 'filename'. Even if the file existed | ||
60 | + * beforehand, it has been truncated and corrupted in the process. | ||
61 | + */ | ||
62 | + if (ret && bs) { | ||
63 | + Error *local_delete_err = NULL; | ||
64 | + int r_del = bdrv_co_delete_file(bs, &local_delete_err); | ||
65 | + /* | ||
66 | + * ENOTSUP will happen if the block driver doesn't support | ||
67 | + * the 'bdrv_co_delete_file' interface. This is a predictable | ||
68 | + * scenario and shouldn't be reported back to the user. | ||
69 | + */ | ||
70 | + if ((r_del < 0) && (r_del != -ENOTSUP)) { | ||
71 | + error_report_err(local_delete_err); | ||
72 | + } | 34 | + } |
73 | + } | 35 | if (b->write) { |
74 | + | 36 | acb = blk_aio_pwritev(b->blk, offset, b->qiov, 0, bench_cb, b); |
75 | bdrv_unref(bs); | 37 | } else { |
76 | qapi_free_QCryptoBlockCreateOptions(create_opts); | ||
77 | qobject_unref(cryptoopts); | ||
78 | -- | 38 | -- |
79 | 2.20.1 | 39 | 2.49.0 |
80 | |||
81 | diff view generated by jsdifflib |
1 | From: Daniel Henrique Barboza <danielhb413@gmail.com> | 1 | qcow2_refresh_limits() assumes that s->crypto is non-NULL whenever |
---|---|---|---|
2 | bs->encrypted is true. This is actually not the case: qcow2_do_open() | ||
3 | allows to open an image with a missing crypto header for BDRV_O_NO_IO, | ||
4 | and then bs->encrypted is true, but s->crypto is still NULL. | ||
2 | 5 | ||
3 | This patch adds a new test file to exercise the case where | 6 | It doesn't make sense to open an invalid image, so remove the exception |
4 | qemu-img fails to complete for the LUKS format when a non-UTF8 | 7 | for BDRV_O_NO_IO. This catches the problem early and any code that makes |
5 | secret is used. | 8 | the same assumption is safe now. |
6 | 9 | ||
7 | Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com> | 10 | At the same time, in the name of defensive programming, we shouldn't |
8 | Message-Id: <20200130213907.2830642-5-danielhb413@gmail.com> | 11 | make the assumption in the first place. Let qcow2_refresh_limits() check |
12 | s->crypto rather than bs->encrypted. If s->crypto is NULL, it also can't | ||
13 | make any requirement on request alignment. | ||
14 | |||
15 | Finally, start a qcow2-encryption test case that only serves as a | ||
16 | regression test for this crash for now. | ||
17 | |||
18 | Reported-by: Leonid Reviakin <L.reviakin@fobos-nt.ru> | ||
19 | Reported-by: Denis Rastyogin <gerben@altlinux.org> | ||
20 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
21 | Message-ID: <20250318201143.70657-1-kwolf@redhat.com> | ||
22 | Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> | ||
9 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 23 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
10 | --- | 24 | --- |
11 | tests/qemu-iotests/282 | 67 ++++++++++++++++++++++++++++++++++++++ | 25 | block/qcow2.c | 4 +- |
12 | tests/qemu-iotests/282.out | 11 +++++++ | 26 | tests/qemu-iotests/tests/qcow2-encryption | 75 +++++++++++++++++++ |
13 | tests/qemu-iotests/group | 1 + | 27 | tests/qemu-iotests/tests/qcow2-encryption.out | 32 ++++++++ |
14 | 3 files changed, 79 insertions(+) | 28 | 3 files changed, 109 insertions(+), 2 deletions(-) |
15 | create mode 100755 tests/qemu-iotests/282 | 29 | create mode 100755 tests/qemu-iotests/tests/qcow2-encryption |
16 | create mode 100644 tests/qemu-iotests/282.out | 30 | create mode 100644 tests/qemu-iotests/tests/qcow2-encryption.out |
17 | 31 | ||
18 | diff --git a/tests/qemu-iotests/282 b/tests/qemu-iotests/282 | 32 | diff --git a/block/qcow2.c b/block/qcow2.c |
33 | index XXXXXXX..XXXXXXX 100644 | ||
34 | --- a/block/qcow2.c | ||
35 | +++ b/block/qcow2.c | ||
36 | @@ -XXX,XX +XXX,XX @@ qcow2_do_open(BlockDriverState *bs, QDict *options, int flags, | ||
37 | ret = -EINVAL; | ||
38 | goto fail; | ||
39 | } | ||
40 | - } else if (!(flags & BDRV_O_NO_IO)) { | ||
41 | + } else { | ||
42 | error_setg(errp, "Missing CRYPTO header for crypt method %d", | ||
43 | s->crypt_method_header); | ||
44 | ret = -EINVAL; | ||
45 | @@ -XXX,XX +XXX,XX @@ static void qcow2_refresh_limits(BlockDriverState *bs, Error **errp) | ||
46 | { | ||
47 | BDRVQcow2State *s = bs->opaque; | ||
48 | |||
49 | - if (bs->encrypted) { | ||
50 | + if (s->crypto) { | ||
51 | /* Encryption works on a sector granularity */ | ||
52 | bs->bl.request_alignment = qcrypto_block_get_sector_size(s->crypto); | ||
53 | } | ||
54 | diff --git a/tests/qemu-iotests/tests/qcow2-encryption b/tests/qemu-iotests/tests/qcow2-encryption | ||
19 | new file mode 100755 | 55 | new file mode 100755 |
20 | index XXXXXXX..XXXXXXX | 56 | index XXXXXXX..XXXXXXX |
21 | --- /dev/null | 57 | --- /dev/null |
22 | +++ b/tests/qemu-iotests/282 | 58 | +++ b/tests/qemu-iotests/tests/qcow2-encryption |
23 | @@ -XXX,XX +XXX,XX @@ | 59 | @@ -XXX,XX +XXX,XX @@ |
24 | +#!/usr/bin/env bash | 60 | +#!/usr/bin/env bash |
61 | +# group: rw quick | ||
25 | +# | 62 | +# |
26 | +# Test qemu-img file cleanup for LUKS when using a non-UTF8 secret | 63 | +# Test case for encryption support in qcow2 |
27 | +# | 64 | +# |
28 | +# Copyright (C) 2020, IBM Corporation. | 65 | +# Copyright (C) 2025 Red Hat, Inc. |
29 | +# | 66 | +# |
30 | +# This program is free software; you can redistribute it and/or modify | 67 | +# This program is free software; you can redistribute it and/or modify |
31 | +# it under the terms of the GNU General Public License as published by | 68 | +# it under the terms of the GNU General Public License as published by |
32 | +# the Free Software Foundation; either version 2 of the License, or | 69 | +# the Free Software Foundation; either version 2 of the License, or |
33 | +# (at your option) any later version. | 70 | +# (at your option) any later version. |
... | ... | ||
39 | +# | 76 | +# |
40 | +# You should have received a copy of the GNU General Public License | 77 | +# You should have received a copy of the GNU General Public License |
41 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. | 78 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
42 | +# | 79 | +# |
43 | + | 80 | + |
44 | +seq=`basename $0` | 81 | +# creator |
82 | +owner=kwolf@redhat.com | ||
83 | + | ||
84 | +seq="$(basename $0)" | ||
45 | +echo "QA output created by $seq" | 85 | +echo "QA output created by $seq" |
46 | + | 86 | + |
47 | +status=1 # failure is the default! | 87 | +status=1 # failure is the default! |
48 | +TEST_IMAGE_FILE='vol.img' | ||
49 | + | 88 | + |
50 | +_cleanup() | 89 | +_cleanup() |
51 | +{ | 90 | +{ |
52 | + _cleanup_test_img | 91 | + _cleanup_test_img |
53 | + rm non_utf8_secret | ||
54 | + rm -f $TEST_IMAGE_FILE | ||
55 | +} | 92 | +} |
56 | +trap "_cleanup; exit \$status" 0 1 2 3 15 | 93 | +trap "_cleanup; exit \$status" 0 1 2 3 15 |
57 | + | 94 | + |
58 | +# get standard environment, filters and checks | 95 | +# get standard environment, filters and checks |
59 | +. ./common.rc | 96 | +. ../common.rc |
60 | +. ./common.filter | 97 | +. ../common.filter |
61 | + | 98 | + |
62 | +_supported_fmt luks | 99 | +# This tests qcow2-specific low-level functionality |
63 | +_supported_proto generic | 100 | +_supported_fmt qcow2 |
64 | +_unsupported_proto vxhs | 101 | +_supported_proto file |
102 | +_require_working_luks | ||
65 | + | 103 | + |
66 | +echo "== Create non-UTF8 secret ==" | 104 | +IMG_SIZE=64M |
67 | +echo -n -e '\x3a\x3c\x3b\xff' > non_utf8_secret | ||
68 | +SECRET="secret,id=sec0,file=non_utf8_secret" | ||
69 | + | 105 | + |
70 | +echo "== Throws an error because of invalid UTF-8 secret ==" | 106 | +echo |
71 | +$QEMU_IMG create -f $IMGFMT --object $SECRET -o "key-secret=sec0" $TEST_IMAGE_FILE 4M | 107 | +echo "=== Create an encrypted image ===" |
108 | +echo | ||
72 | + | 109 | + |
73 | +echo "== Image file should not exist after the error ==" | 110 | +_make_test_img --object secret,id=sec0,data=123456 -o encrypt.format=luks,encrypt.key-secret=sec0 $IMG_SIZE |
74 | +if test -f "$TEST_IMAGE_FILE"; then | 111 | +$PYTHON ../qcow2.py "$TEST_IMG" dump-header-exts |
75 | + exit 1 | 112 | +_img_info |
76 | +fi | 113 | +$QEMU_IMG check \ |
114 | + --object secret,id=sec0,data=123456 \ | ||
115 | + --image-opts file.filename="$TEST_IMG",encrypt.key-secret=sec0 \ | ||
116 | + | _filter_qemu_img_check | ||
77 | + | 117 | + |
78 | +echo "== Create a stub image file and run qemu-img again ==" | 118 | +echo |
79 | +touch $TEST_IMAGE_FILE | 119 | +echo "=== Remove the header extension ===" |
80 | +$QEMU_IMG create -f $IMGFMT --object $SECRET -o "key-secret=sec0" $TEST_IMAGE_FILE 4M | 120 | +echo |
81 | + | 121 | + |
82 | +echo "== Pre-existing image file should also be deleted after the error ==" | 122 | +$PYTHON ../qcow2.py "$TEST_IMG" del-header-ext 0x0537be77 |
83 | +if test -f "$TEST_IMAGE_FILE"; then | 123 | +$PYTHON ../qcow2.py "$TEST_IMG" dump-header-exts |
84 | + exit 1 | 124 | +_img_info |
85 | +fi | 125 | +$QEMU_IMG check \ |
126 | + --object secret,id=sec0,data=123456 \ | ||
127 | + --image-opts file.filename="$TEST_IMG",encrypt.key-secret=sec0 2>&1 \ | ||
128 | + | _filter_qemu_img_check \ | ||
129 | + | _filter_testdir | ||
86 | + | 130 | + |
87 | +# success, all done | 131 | +# success, all done |
88 | +echo "*** done" | 132 | +echo "*** done" |
89 | +rm -f $seq.full | 133 | +rm -f $seq.full |
90 | +status=0 | 134 | +status=0 |
91 | diff --git a/tests/qemu-iotests/282.out b/tests/qemu-iotests/282.out | 135 | diff --git a/tests/qemu-iotests/tests/qcow2-encryption.out b/tests/qemu-iotests/tests/qcow2-encryption.out |
92 | new file mode 100644 | 136 | new file mode 100644 |
93 | index XXXXXXX..XXXXXXX | 137 | index XXXXXXX..XXXXXXX |
94 | --- /dev/null | 138 | --- /dev/null |
95 | +++ b/tests/qemu-iotests/282.out | 139 | +++ b/tests/qemu-iotests/tests/qcow2-encryption.out |
96 | @@ -XXX,XX +XXX,XX @@ | 140 | @@ -XXX,XX +XXX,XX @@ |
97 | +QA output created by 282 | 141 | +QA output created by qcow2-encryption |
98 | +== Create non-UTF8 secret == | 142 | + |
99 | +== Throws an error because of invalid UTF-8 secret == | 143 | +=== Create an encrypted image === |
100 | +qemu-img: vol.img: Data from secret sec0 is not valid UTF-8 | 144 | + |
101 | +Formatting 'vol.img', fmt=luks size=4194304 key-secret=sec0 | 145 | +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 |
102 | +== Image file should not exist after the error == | 146 | +Header extension: |
103 | +== Create a stub image file and run qemu-img again == | 147 | +magic 0x537be77 (Crypto header) |
104 | +qemu-img: vol.img: Data from secret sec0 is not valid UTF-8 | 148 | +length 16 |
105 | +Formatting 'vol.img', fmt=luks size=4194304 key-secret=sec0 | 149 | +data <binary> |
106 | +== Pre-existing image file should also be deleted after the error == | 150 | + |
107 | + *** done | 151 | +Header extension: |
108 | diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group | 152 | +magic 0x6803f857 (Feature table) |
109 | index XXXXXXX..XXXXXXX 100644 | 153 | +length 384 |
110 | --- a/tests/qemu-iotests/group | 154 | +data <binary> |
111 | +++ b/tests/qemu-iotests/group | 155 | + |
112 | @@ -XXX,XX +XXX,XX @@ | 156 | +image: TEST_DIR/t.IMGFMT |
113 | 279 rw backing quick | 157 | +file format: IMGFMT |
114 | 280 rw migration quick | 158 | +virtual size: 64 MiB (67108864 bytes) |
115 | 281 rw quick | 159 | +encrypted: yes |
116 | +282 rw img quick | 160 | +cluster_size: 65536 |
117 | 283 auto quick | 161 | +No errors were found on the image. |
118 | 284 rw | 162 | + |
119 | 286 rw quick | 163 | +=== Remove the header extension === |
164 | + | ||
165 | +Header extension: | ||
166 | +magic 0x6803f857 (Feature table) | ||
167 | +length 384 | ||
168 | +data <binary> | ||
169 | + | ||
170 | +qemu-img: Could not open 'TEST_DIR/t.IMGFMT': Missing CRYPTO header for crypt method 2 | ||
171 | +qemu-img: Could not open 'file.filename=TEST_DIR/t.qcow2,encrypt.key-secret=sec0': Missing CRYPTO header for crypt method 2 | ||
172 | +*** done | ||
120 | -- | 173 | -- |
121 | 2.20.1 | 174 | 2.49.0 |
122 | 175 | ||
123 | 176 | diff view generated by jsdifflib |
1 | From: Daniel Henrique Barboza <danielhb413@gmail.com> | 1 | Originally, all failed SG_IO requests called scsi_handle_rw_error() to |
---|---|---|---|
2 | apply the configured error policy. However, commit f3126d65, which was | ||
3 | supposed to be a mere refactoring for scsi-disk.c, broke this and | ||
4 | accidentally completed the SCSI request without considering the error | ||
5 | policy any more if the error was signalled in the host_status field. | ||
2 | 6 | ||
3 | Using the new 'bdrv_co_delete_file' interface, a pure co_routine function | 7 | Apart from the commit message not describing the change as intended, |
4 | 'bdrv_co_delete_file' inside block.c can can be used in a way similar of | 8 | errors indicated in host_status are also obviously backend errors and |
5 | the existing bdrv_create_file to to clean up a created file. | 9 | not something the guest must deal with independently of the error |
10 | policy. | ||
6 | 11 | ||
7 | We're creating a pure co_routine because the only caller of | 12 | This behaviour means that some recoverable errors (such as a path error |
8 | 'bdrv_co_delete_file' will be already in co_routine context, thus there | 13 | in multipath configurations) were reported to the guest anyway, which |
9 | is no need to add all the machinery to check for qemu_in_coroutine() and | 14 | might not expect it and might consider its disk broken. |
10 | create a separated co_routine to do the job. | ||
11 | 15 | ||
12 | Suggested-by: Daniel P. Berrangé <berrange@redhat.com> | 16 | Make sure that we apply the error policy again for host_status errors, |
13 | Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com> | 17 | too. This addresses an existing FIXME comment and allows us to remove |
14 | Message-Id: <20200130213907.2830642-3-danielhb413@gmail.com> | 18 | some comments warning that callbacks weren't always called. With this |
19 | fix, they are called in all cases again. | ||
20 | |||
21 | The return value passed to the request callback doesn't have more free | ||
22 | values that could be used to indicate host_status errors as well as SAM | ||
23 | status codes and negative errno. Store the value in the host_status | ||
24 | field of the SCSIRequest instead and use -ENODEV as the return value (if | ||
25 | a path hasn't been reachable for a while, blk_aio_ioctl() will return | ||
26 | -ENODEV instead of just setting host_status, so just reuse it here - | ||
27 | it's not necessarily entirely accurate, but it's as good as any errno). | ||
28 | |||
29 | Cc: qemu-stable@nongnu.org | ||
30 | Fixes: f3126d65b393 ('scsi: move host_status handling into SCSI drivers') | ||
31 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | ||
32 | Message-ID: <20250407155949.44736-1-kwolf@redhat.com> | ||
33 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
34 | Reviewed-by: Hanna Czenczek <hreitz@redhat.com> | ||
15 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 35 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
16 | --- | 36 | --- |
17 | include/block/block.h | 1 + | 37 | hw/scsi/scsi-disk.c | 39 +++++++++++++++++++++++++-------------- |
18 | block.c | 26 ++++++++++++++++++++++++++ | 38 | 1 file changed, 25 insertions(+), 14 deletions(-) |
19 | 2 files changed, 27 insertions(+) | ||
20 | 39 | ||
21 | diff --git a/include/block/block.h b/include/block/block.h | 40 | diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c |
22 | index XXXXXXX..XXXXXXX 100644 | 41 | index XXXXXXX..XXXXXXX 100644 |
23 | --- a/include/block/block.h | 42 | --- a/hw/scsi/scsi-disk.c |
24 | +++ b/include/block/block.h | 43 | +++ b/hw/scsi/scsi-disk.c |
25 | @@ -XXX,XX +XXX,XX @@ bool bdrv_is_backing_chain_frozen(BlockDriverState *bs, BlockDriverState *base, | 44 | @@ -XXX,XX +XXX,XX @@ struct SCSIDiskClass { |
26 | int bdrv_freeze_backing_chain(BlockDriverState *bs, BlockDriverState *base, | 45 | SCSIDeviceClass parent_class; |
27 | Error **errp); | 46 | /* |
28 | void bdrv_unfreeze_backing_chain(BlockDriverState *bs, BlockDriverState *base); | 47 | * Callbacks receive ret == 0 for success. Errors are represented either as |
29 | +int coroutine_fn bdrv_co_delete_file(BlockDriverState *bs, Error **errp); | 48 | - * negative errno values, or as positive SAM status codes. |
30 | 49 | - * | |
31 | 50 | - * Beware: For errors returned in host_status, the function may directly | |
32 | typedef struct BdrvCheckResult { | 51 | - * complete the request and never call the callback. |
33 | diff --git a/block.c b/block.c | 52 | + * negative errno values, or as positive SAM status codes. For host_status |
34 | index XXXXXXX..XXXXXXX 100644 | 53 | + * errors, the function passes ret == -ENODEV and sets the host_status field |
35 | --- a/block.c | 54 | + * of the SCSIRequest. |
36 | +++ b/block.c | 55 | */ |
37 | @@ -XXX,XX +XXX,XX @@ int bdrv_create_file(const char *filename, QemuOpts *opts, Error **errp) | 56 | DMAIOFunc *dma_readv; |
38 | } | 57 | DMAIOFunc *dma_writev; |
39 | } | 58 | @@ -XXX,XX +XXX,XX @@ static bool scsi_handle_rw_error(SCSIDiskReq *r, int ret, bool acct_failed) |
40 | 59 | SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); | |
41 | +int coroutine_fn bdrv_co_delete_file(BlockDriverState *bs, Error **errp) | 60 | SCSIDiskClass *sdc = (SCSIDiskClass *) object_get_class(OBJECT(s)); |
42 | +{ | 61 | SCSISense sense = SENSE_CODE(NO_SENSE); |
43 | + Error *local_err = NULL; | 62 | + int16_t host_status; |
44 | + int ret; | 63 | int error; |
45 | + | 64 | bool req_has_sense = false; |
46 | + assert(bs != NULL); | 65 | BlockErrorAction action; |
47 | + | 66 | int status; |
48 | + if (!bs->drv) { | 67 | |
49 | + error_setg(errp, "Block node '%s' is not opened", bs->filename); | 68 | + /* |
50 | + return -ENOMEDIUM; | 69 | + * host_status should only be set for SG_IO requests that came back with a |
70 | + * host_status error in scsi_block_sgio_complete(). This error path passes | ||
71 | + * -ENODEV as the return value. | ||
72 | + * | ||
73 | + * Reset host_status in the request because we may still want to complete | ||
74 | + * the request successfully with the 'stop' or 'ignore' error policy. | ||
75 | + */ | ||
76 | + host_status = r->req.host_status; | ||
77 | + if (host_status != -1) { | ||
78 | + assert(ret == -ENODEV); | ||
79 | + r->req.host_status = -1; | ||
51 | + } | 80 | + } |
52 | + | 81 | + |
53 | + if (!bs->drv->bdrv_co_delete_file) { | 82 | if (ret < 0) { |
54 | + error_setg(errp, "Driver '%s' does not support image deletion", | 83 | status = scsi_sense_from_errno(-ret, &sense); |
55 | + bs->drv->format_name); | 84 | error = -ret; |
56 | + return -ENOTSUP; | 85 | @@ -XXX,XX +XXX,XX @@ static bool scsi_handle_rw_error(SCSIDiskReq *r, int ret, bool acct_failed) |
57 | + } | 86 | if (acct_failed) { |
58 | + | 87 | block_acct_failed(blk_get_stats(s->qdev.conf.blk), &r->acct); |
59 | + ret = bs->drv->bdrv_co_delete_file(bs, &local_err); | 88 | } |
60 | + if (ret < 0) { | 89 | + if (host_status != -1) { |
61 | + error_propagate(errp, local_err); | 90 | + scsi_req_complete_failed(&r->req, host_status); |
62 | + } | 91 | + return true; |
63 | + | 92 | + } |
64 | + return ret; | 93 | if (req_has_sense) { |
65 | +} | 94 | sdc->update_sense(&r->req); |
66 | + | 95 | } else if (status == CHECK_CONDITION) { |
67 | /** | 96 | @@ -XXX,XX +XXX,XX @@ done: |
68 | * Try to get @bs's logical and physical block size. | 97 | scsi_req_unref(&r->req); |
69 | * On success, store them in @bsz struct and return 0. | 98 | } |
99 | |||
100 | -/* May not be called in all error cases, don't rely on cleanup here */ | ||
101 | static void scsi_dma_complete(void *opaque, int ret) | ||
102 | { | ||
103 | SCSIDiskReq *r = (SCSIDiskReq *)opaque; | ||
104 | @@ -XXX,XX +XXX,XX @@ done: | ||
105 | scsi_req_unref(&r->req); | ||
106 | } | ||
107 | |||
108 | -/* May not be called in all error cases, don't rely on cleanup here */ | ||
109 | static void scsi_read_complete(void *opaque, int ret) | ||
110 | { | ||
111 | SCSIDiskReq *r = (SCSIDiskReq *)opaque; | ||
112 | @@ -XXX,XX +XXX,XX @@ done: | ||
113 | scsi_req_unref(&r->req); | ||
114 | } | ||
115 | |||
116 | -/* May not be called in all error cases, don't rely on cleanup here */ | ||
117 | static void scsi_write_complete(void * opaque, int ret) | ||
118 | { | ||
119 | SCSIDiskReq *r = (SCSIDiskReq *)opaque; | ||
120 | @@ -XXX,XX +XXX,XX @@ static void scsi_block_sgio_complete(void *opaque, int ret) | ||
121 | sg_io_hdr_t *io_hdr = &req->io_header; | ||
122 | |||
123 | if (ret == 0) { | ||
124 | - /* FIXME This skips calling req->cb() and any cleanup in it */ | ||
125 | if (io_hdr->host_status != SCSI_HOST_OK) { | ||
126 | - scsi_req_complete_failed(&r->req, io_hdr->host_status); | ||
127 | - scsi_req_unref(&r->req); | ||
128 | - return; | ||
129 | - } | ||
130 | - | ||
131 | - if (io_hdr->driver_status & SG_ERR_DRIVER_TIMEOUT) { | ||
132 | + r->req.host_status = io_hdr->host_status; | ||
133 | + ret = -ENODEV; | ||
134 | + } else if (io_hdr->driver_status & SG_ERR_DRIVER_TIMEOUT) { | ||
135 | ret = BUSY; | ||
136 | } else { | ||
137 | ret = io_hdr->status; | ||
70 | -- | 138 | -- |
71 | 2.20.1 | 139 | 2.49.0 |
72 | |||
73 | diff view generated by jsdifflib |
1 | From: Daniel Henrique Barboza <danielhb413@gmail.com> | 1 | From: Vitalii Mordan <mordan@ispras.ru> |
---|---|---|---|
2 | 2 | ||
3 | Adding to Block Drivers the capability of being able to clean up | 3 | This patch addresses potential data races involving access to Job fields |
4 | its created files can be useful in certain situations. For the | 4 | in the test-bdrv-drain test. |
5 | LUKS driver, for instance, a failure in one of its authentication | ||
6 | steps can leave files in the host that weren't there before. | ||
7 | 5 | ||
8 | This patch adds the 'bdrv_co_delete_file' interface to block | 6 | Fixes: 7253220de4 ("test-bdrv-drain: Test drain vs. block jobs") |
9 | drivers and add it to the 'file' driver in file-posix.c. The | 7 | Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2900 |
10 | implementation is given by 'raw_co_delete_file'. | 8 | Signed-off-by: Vitalii Mordan <mordan@ispras.ru> |
11 | 9 | Message-ID: <20250402102119.3345626-1-mordan@ispras.ru> | |
12 | Suggested-by: Daniel P. Berrangé <berrange@redhat.com> | 10 | [kwolf: Fixed up coding style and one missing atomic access] |
13 | Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com> | 11 | Reviewed-by: Kevin Wolf <kwolf@redhat.com> |
14 | Message-Id: <20200130213907.2830642-2-danielhb413@gmail.com> | ||
15 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> | 12 | Signed-off-by: Kevin Wolf <kwolf@redhat.com> |
16 | --- | 13 | --- |
17 | include/block/block_int.h | 4 ++++ | 14 | include/qemu/job.h | 3 +++ |
18 | block/file-posix.c | 23 +++++++++++++++++++++++ | 15 | job.c | 6 ++++++ |
19 | 2 files changed, 27 insertions(+) | 16 | tests/unit/test-bdrv-drain.c | 32 +++++++++++++++++++------------- |
17 | 3 files changed, 28 insertions(+), 13 deletions(-) | ||
20 | 18 | ||
21 | diff --git a/include/block/block_int.h b/include/block/block_int.h | 19 | diff --git a/include/qemu/job.h b/include/qemu/job.h |
22 | index XXXXXXX..XXXXXXX 100644 | 20 | index XXXXXXX..XXXXXXX 100644 |
23 | --- a/include/block/block_int.h | 21 | --- a/include/qemu/job.h |
24 | +++ b/include/block/block_int.h | 22 | +++ b/include/qemu/job.h |
25 | @@ -XXX,XX +XXX,XX @@ struct BlockDriver { | 23 | @@ -XXX,XX +XXX,XX @@ bool job_is_ready(Job *job); |
26 | */ | 24 | /* Same as job_is_ready(), but called with job lock held. */ |
27 | int coroutine_fn (*bdrv_co_flush)(BlockDriverState *bs); | 25 | bool job_is_ready_locked(Job *job); |
28 | 26 | ||
29 | + /* Delete a created file. */ | 27 | +/** Returns whether the job is paused. Called with job_mutex *not* held. */ |
30 | + int coroutine_fn (*bdrv_co_delete_file)(BlockDriverState *bs, | 28 | +bool job_is_paused(Job *job); |
31 | + Error **errp); | ||
32 | + | 29 | + |
33 | /* | 30 | /** |
34 | * Flushes all data that was already written to the OS all the way down to | 31 | * Request @job to pause at the next pause point. Must be paired with |
35 | * the disk (for example file-posix.c calls fsync()). | 32 | * job_resume(). If the job is supposed to be resumed by user action, call |
36 | diff --git a/block/file-posix.c b/block/file-posix.c | 33 | diff --git a/job.c b/job.c |
37 | index XXXXXXX..XXXXXXX 100644 | 34 | index XXXXXXX..XXXXXXX 100644 |
38 | --- a/block/file-posix.c | 35 | --- a/job.c |
39 | +++ b/block/file-posix.c | 36 | +++ b/job.c |
40 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn raw_co_create_opts(const char *filename, QemuOpts *opts, | 37 | @@ -XXX,XX +XXX,XX @@ bool job_is_cancelled_locked(Job *job) |
41 | return raw_co_create(&options, errp); | 38 | return job->force_cancel; |
42 | } | 39 | } |
43 | 40 | ||
44 | +static int coroutine_fn raw_co_delete_file(BlockDriverState *bs, | 41 | +bool job_is_paused(Job *job) |
45 | + Error **errp) | ||
46 | +{ | 42 | +{ |
47 | + struct stat st; | 43 | + JOB_LOCK_GUARD(); |
48 | + int ret; | 44 | + return job->paused; |
49 | + | ||
50 | + if (!(stat(bs->filename, &st) == 0) || !S_ISREG(st.st_mode)) { | ||
51 | + error_setg_errno(errp, ENOENT, "%s is not a regular file", | ||
52 | + bs->filename); | ||
53 | + return -ENOENT; | ||
54 | + } | ||
55 | + | ||
56 | + ret = unlink(bs->filename); | ||
57 | + if (ret < 0) { | ||
58 | + ret = -errno; | ||
59 | + error_setg_errno(errp, -ret, "Error when deleting file %s", | ||
60 | + bs->filename); | ||
61 | + } | ||
62 | + | ||
63 | + return ret; | ||
64 | +} | 45 | +} |
65 | + | 46 | + |
66 | /* | 47 | bool job_is_cancelled(Job *job) |
67 | * Find allocation range in @bs around offset @start. | 48 | { |
68 | * May change underlying file descriptor's file offset. | 49 | JOB_LOCK_GUARD(); |
69 | @@ -XXX,XX +XXX,XX @@ BlockDriver bdrv_file = { | 50 | diff --git a/tests/unit/test-bdrv-drain.c b/tests/unit/test-bdrv-drain.c |
70 | .bdrv_co_block_status = raw_co_block_status, | 51 | index XXXXXXX..XXXXXXX 100644 |
71 | .bdrv_co_invalidate_cache = raw_co_invalidate_cache, | 52 | --- a/tests/unit/test-bdrv-drain.c |
72 | .bdrv_co_pwrite_zeroes = raw_co_pwrite_zeroes, | 53 | +++ b/tests/unit/test-bdrv-drain.c |
73 | + .bdrv_co_delete_file = raw_co_delete_file, | 54 | @@ -XXX,XX +XXX,XX @@ typedef struct TestBlockJob { |
74 | 55 | BlockDriverState *bs; | |
75 | .bdrv_co_preadv = raw_co_preadv, | 56 | int run_ret; |
76 | .bdrv_co_pwritev = raw_co_pwritev, | 57 | int prepare_ret; |
58 | + | ||
59 | + /* Accessed with atomics */ | ||
60 | bool running; | ||
61 | bool should_complete; | ||
62 | } TestBlockJob; | ||
63 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn test_job_run(Job *job, Error **errp) | ||
64 | |||
65 | /* We are running the actual job code past the pause point in | ||
66 | * job_co_entry(). */ | ||
67 | - s->running = true; | ||
68 | + qatomic_set(&s->running, true); | ||
69 | |||
70 | job_transition_to_ready(&s->common.job); | ||
71 | - while (!s->should_complete) { | ||
72 | + while (!qatomic_read(&s->should_complete)) { | ||
73 | /* Avoid job_sleep_ns() because it marks the job as !busy. We want to | ||
74 | * emulate some actual activity (probably some I/O) here so that drain | ||
75 | * has to wait for this activity to stop. */ | ||
76 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn test_job_run(Job *job, Error **errp) | ||
77 | static void test_job_complete(Job *job, Error **errp) | ||
78 | { | ||
79 | TestBlockJob *s = container_of(job, TestBlockJob, common.job); | ||
80 | - s->should_complete = true; | ||
81 | + qatomic_set(&s->should_complete, true); | ||
82 | } | ||
83 | |||
84 | BlockJobDriver test_job_driver = { | ||
85 | @@ -XXX,XX +XXX,XX @@ static void test_blockjob_common_drain_node(enum drain_type drain_type, | ||
86 | /* job_co_entry() is run in the I/O thread, wait for the actual job | ||
87 | * code to start (we don't want to catch the job in the pause point in | ||
88 | * job_co_entry(). */ | ||
89 | - while (!tjob->running) { | ||
90 | + while (!qatomic_read(&tjob->running)) { | ||
91 | aio_poll(qemu_get_aio_context(), false); | ||
92 | } | ||
93 | } | ||
94 | @@ -XXX,XX +XXX,XX @@ static void test_blockjob_common_drain_node(enum drain_type drain_type, | ||
95 | WITH_JOB_LOCK_GUARD() { | ||
96 | g_assert_cmpint(job->job.pause_count, ==, 0); | ||
97 | g_assert_false(job->job.paused); | ||
98 | - g_assert_true(tjob->running); | ||
99 | + g_assert_true(qatomic_read(&tjob->running)); | ||
100 | g_assert_true(job->job.busy); /* We're in qemu_co_sleep_ns() */ | ||
101 | } | ||
102 | |||
103 | @@ -XXX,XX +XXX,XX @@ static void test_blockjob_common_drain_node(enum drain_type drain_type, | ||
104 | * | ||
105 | * paused is reset in the I/O thread, wait for it | ||
106 | */ | ||
107 | - while (job->job.paused) { | ||
108 | + while (job_is_paused(&job->job)) { | ||
109 | aio_poll(qemu_get_aio_context(), false); | ||
110 | } | ||
111 | } | ||
112 | @@ -XXX,XX +XXX,XX @@ static void test_blockjob_common_drain_node(enum drain_type drain_type, | ||
113 | * | ||
114 | * paused is reset in the I/O thread, wait for it | ||
115 | */ | ||
116 | - while (job->job.paused) { | ||
117 | + while (job_is_paused(&job->job)) { | ||
118 | aio_poll(qemu_get_aio_context(), false); | ||
119 | } | ||
120 | } | ||
121 | @@ -XXX,XX +XXX,XX @@ static void test_set_aio_context(void) | ||
122 | |||
123 | typedef struct TestDropBackingBlockJob { | ||
124 | BlockJob common; | ||
125 | - bool should_complete; | ||
126 | bool *did_complete; | ||
127 | BlockDriverState *detach_also; | ||
128 | BlockDriverState *bs; | ||
129 | + | ||
130 | + /* Accessed with atomics */ | ||
131 | + bool should_complete; | ||
132 | } TestDropBackingBlockJob; | ||
133 | |||
134 | static int coroutine_fn test_drop_backing_job_run(Job *job, Error **errp) | ||
135 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn test_drop_backing_job_run(Job *job, Error **errp) | ||
136 | TestDropBackingBlockJob *s = | ||
137 | container_of(job, TestDropBackingBlockJob, common.job); | ||
138 | |||
139 | - while (!s->should_complete) { | ||
140 | + while (!qatomic_read(&s->should_complete)) { | ||
141 | job_sleep_ns(job, 0); | ||
142 | } | ||
143 | |||
144 | @@ -XXX,XX +XXX,XX @@ static void test_blockjob_commit_by_drained_end(void) | ||
145 | |||
146 | job_start(&job->common.job); | ||
147 | |||
148 | - job->should_complete = true; | ||
149 | + qatomic_set(&job->should_complete, true); | ||
150 | bdrv_drained_begin(bs_child); | ||
151 | g_assert(!job_has_completed); | ||
152 | bdrv_drained_end(bs_child); | ||
153 | @@ -XXX,XX +XXX,XX @@ static void test_blockjob_commit_by_drained_end(void) | ||
154 | |||
155 | typedef struct TestSimpleBlockJob { | ||
156 | BlockJob common; | ||
157 | - bool should_complete; | ||
158 | bool *did_complete; | ||
159 | + | ||
160 | + /* Accessed with atomics */ | ||
161 | + bool should_complete; | ||
162 | } TestSimpleBlockJob; | ||
163 | |||
164 | static int coroutine_fn test_simple_job_run(Job *job, Error **errp) | ||
165 | { | ||
166 | TestSimpleBlockJob *s = container_of(job, TestSimpleBlockJob, common.job); | ||
167 | |||
168 | - while (!s->should_complete) { | ||
169 | + while (!qatomic_read(&s->should_complete)) { | ||
170 | job_sleep_ns(job, 0); | ||
171 | } | ||
172 | |||
173 | @@ -XXX,XX +XXX,XX @@ static void test_drop_intermediate_poll(void) | ||
174 | job->did_complete = &job_has_completed; | ||
175 | |||
176 | job_start(&job->common.job); | ||
177 | - job->should_complete = true; | ||
178 | + qatomic_set(&job->should_complete, true); | ||
179 | |||
180 | g_assert(!job_has_completed); | ||
181 | ret = bdrv_drop_intermediate(chain[1], chain[0], NULL, false); | ||
77 | -- | 182 | -- |
78 | 2.20.1 | 183 | 2.49.0 |
79 | |||
80 | diff view generated by jsdifflib |