1
The following changes since commit 5ec2eca83dc478ddf24077e02a8b34dd26cd3ff9:
1
The following changes since commit 474f3938d79ab36b9231c9ad3b5a9314c2aeacde:
2
2
3
Merge remote-tracking branch 'remotes/awilliam/tags/vfio-updates-20190613.0' into staging (2019-06-14 09:33:55 +0100)
3
Merge remote-tracking branch 'remotes/amarkovic/tags/mips-queue-jun-21-2019' into staging (2019-06-21 15:40:50 +0100)
4
4
5
are available in the Git repository at:
5
are available in the Git repository at:
6
6
7
https://github.com/XanClic/qemu.git tags/pull-block-2019-06-14
7
https://github.com/XanClic/qemu.git tags/pull-block-2019-06-24
8
8
9
for you to fetch changes up to 21c1ce592a144188dfe59b9e156a97da412a59a2:
9
for you to fetch changes up to ab5d4a30f7f3803ca5106b370969c1b7b54136f8:
10
10
11
iotests: Test qemu-img convert -C --salvage (2019-06-14 15:09:42 +0200)
11
iotests: Fix 205 for concurrent runs (2019-06-24 16:01:40 +0200)
12
12
13
----------------------------------------------------------------
13
----------------------------------------------------------------
14
Block patches:
14
Block patches:
15
- Allow blockdev-backup from nodes that are not in qemu's main AIO
15
- The SSH block driver now uses libssh instead of libssh2
16
context to newly added nodes
16
- The VMDK block driver gets read-only support for the seSparse
17
- Add salvaging mode to qemu-img convert
17
subformat
18
- Minor fixes to tests, documentation, and for less Valgrind annoyance
18
- Various fixes
19
20
---
21
22
v2:
23
- Squashed Pino's fix for pre-0.8 libssh into the libssh patch
19
24
20
----------------------------------------------------------------
25
----------------------------------------------------------------
21
Andrey Shinkevich (1):
26
Anton Nefedov (1):
22
hw/block/fdc: floppy command FIFO memory initialization
27
iotest 134: test cluster-misaligned encrypted write
23
28
24
John Snow (6):
29
Klaus Birkelund Jensen (1):
25
blockdev-backup: don't check aio_context too early
30
nvme: do not advertise support for unsupported arbitration mechanism
26
iotests.py: do not use infinite waits
27
QEMUMachine: add events_wait method
28
iotests.py: rewrite run_job to be pickier
29
iotests: add iotest 256 for testing blockdev-backup across iothread
30
contexts
31
event_match: always match on None value
32
31
33
Max Reitz (12):
32
Max Reitz (1):
34
iotests: Filter 175's allocation information
33
iotests: Fix 205 for concurrent runs
35
iotests: Fix intermittent failure in 219
34
36
qemu-img: Fix options leakage in img_rebase()
35
Pino Toscano (1):
37
qapi/block-core: Overlays are not snapshots
36
ssh: switch from libssh2 to libssh
38
blockdev: Overlays are not snapshots
37
39
qemu-img: Move quiet into ImgConvertState
38
Sam Eiderman (3):
40
qemu-img: Add salvaging mode to convert
39
vmdk: Fix comment regarding max l1_size coverage
41
blkdebug: Add @iotype error option
40
vmdk: Reduce the max bound for L1 table size
42
blkdebug: Add "none" event
41
vmdk: Add read-only support for seSparse snapshots
43
blkdebug: Inject errors on .bdrv_co_block_status()
44
iotests: Test qemu-img convert --salvage
45
iotests: Test qemu-img convert -C --salvage
46
42
47
Vladimir Sementsov-Ogievskiy (1):
43
Vladimir Sementsov-Ogievskiy (1):
48
iotests: restrict 254 to support only qcow2
44
blockdev: enable non-root nodes for transaction drive-backup source
49
45
50
qapi/block-core.json | 53 ++++++++---
46
configure | 65 +-
51
block/blkdebug.c | 60 ++++++++++--
47
block/Makefile.objs | 6 +-
52
blockdev.c | 14 +--
48
block/ssh.c | 652 ++++++++++--------
53
hw/block/fdc.c | 1 +
49
block/vmdk.c | 372 +++++++++-
54
qemu-img.c | 106 +++++++++++++++------
50
blockdev.c | 2 +-
55
python/qemu/__init__.py | 67 ++++++++++----
51
hw/block/nvme.c | 1 -
56
qemu-img-cmds.hx | 4 +-
52
.travis.yml | 4 +-
57
qemu-img.texi | 4 +
53
block/trace-events | 14 +-
58
tests/qemu-iotests/082 | 1 +
54
docs/qemu-block-drivers.texi | 2 +-
59
tests/qemu-iotests/082.out | 3 +
55
.../dockerfiles/debian-win32-cross.docker | 1 -
60
tests/qemu-iotests/085.out | 10 +-
56
.../dockerfiles/debian-win64-cross.docker | 1 -
61
tests/qemu-iotests/175 | 26 +++++-
57
tests/docker/dockerfiles/fedora.docker | 4 +-
62
tests/qemu-iotests/175.out | 8 +-
58
tests/docker/dockerfiles/ubuntu.docker | 2 +-
63
tests/qemu-iotests/219 | 13 ++-
59
tests/docker/dockerfiles/ubuntu1804.docker | 2 +-
64
tests/qemu-iotests/251 | 170 ++++++++++++++++++++++++++++++++++
60
tests/qemu-iotests/059.out | 2 +-
65
tests/qemu-iotests/251.out | 43 +++++++++
61
tests/qemu-iotests/134 | 9 +
66
tests/qemu-iotests/254 | 2 +
62
tests/qemu-iotests/134.out | 10 +
67
tests/qemu-iotests/256 | 122 ++++++++++++++++++++++++
63
tests/qemu-iotests/205 | 2 +-
68
tests/qemu-iotests/256.out | 119 ++++++++++++++++++++++++
64
tests/qemu-iotests/207 | 54 +-
69
tests/qemu-iotests/group | 2 +
65
tests/qemu-iotests/207.out | 2 +-
70
tests/qemu-iotests/iotests.py | 60 +++++++-----
66
20 files changed, 823 insertions(+), 384 deletions(-)
71
21 files changed, 772 insertions(+), 116 deletions(-)
72
create mode 100755 tests/qemu-iotests/251
73
create mode 100644 tests/qemu-iotests/251.out
74
create mode 100755 tests/qemu-iotests/256
75
create mode 100644 tests/qemu-iotests/256.out
76
67
77
--
68
--
78
2.21.0
69
2.21.0
79
70
80
71
diff view generated by jsdifflib
1
We do not support this combination (yet), so this should yield an error
1
From: Klaus Birkelund Jensen <klaus@birkelund.eu>
2
message.
3
2
4
Signed-off-by: Max Reitz <mreitz@redhat.com>
3
The device mistakenly reports that the Weighted Round Robin with Urgent
5
Tested-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
4
Priority Class arbitration mechanism is supported.
6
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
5
7
Message-id: 20190507203508.18026-8-mreitz@redhat.com
6
It is not.
7
8
Signed-off-by: Klaus Birkelund Jensen <klaus.jensen@cnexlabs.com>
9
Message-id: 20190606092530.14206-1-klaus@birkelund.eu
10
Acked-by: Maxim Levitsky <mlevitsk@redhat.com>
8
Signed-off-by: Max Reitz <mreitz@redhat.com>
11
Signed-off-by: Max Reitz <mreitz@redhat.com>
9
---
12
---
10
tests/qemu-iotests/082 | 1 +
13
hw/block/nvme.c | 1 -
11
tests/qemu-iotests/082.out | 3 +++
14
1 file changed, 1 deletion(-)
12
2 files changed, 4 insertions(+)
13
15
14
diff --git a/tests/qemu-iotests/082 b/tests/qemu-iotests/082
16
diff --git a/hw/block/nvme.c b/hw/block/nvme.c
15
index XXXXXXX..XXXXXXX 100755
16
--- a/tests/qemu-iotests/082
17
+++ b/tests/qemu-iotests/082
18
@@ -XXX,XX +XXX,XX @@ echo === convert: -C and other options ===
19
run_qemu_img convert -C -S 4k -O $IMGFMT "$TEST_IMG" "$TEST_IMG".target
20
run_qemu_img convert -C -S 8k -O $IMGFMT "$TEST_IMG" "$TEST_IMG".target
21
run_qemu_img convert -C -c -O $IMGFMT "$TEST_IMG" "$TEST_IMG".target
22
+run_qemu_img convert -C --salvage -O $IMGFMT "$TEST_IMG" "$TEST_IMG".target
23
24
echo
25
echo === amend: Options specified more than once ===
26
diff --git a/tests/qemu-iotests/082.out b/tests/qemu-iotests/082.out
27
index XXXXXXX..XXXXXXX 100644
17
index XXXXXXX..XXXXXXX 100644
28
--- a/tests/qemu-iotests/082.out
18
--- a/hw/block/nvme.c
29
+++ b/tests/qemu-iotests/082.out
19
+++ b/hw/block/nvme.c
30
@@ -XXX,XX +XXX,XX @@ qemu-img: Cannot enable copy offloading when -S is used
20
@@ -XXX,XX +XXX,XX @@ static void nvme_realize(PCIDevice *pci_dev, Error **errp)
31
Testing: convert -C -c -O qcow2 TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.target
21
n->bar.cap = 0;
32
qemu-img: Cannot enable copy offloading when -c is used
22
NVME_CAP_SET_MQES(n->bar.cap, 0x7ff);
33
23
NVME_CAP_SET_CQR(n->bar.cap, 1);
34
+Testing: convert -C --salvage -O qcow2 TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.target
24
- NVME_CAP_SET_AMS(n->bar.cap, 1);
35
+qemu-img: Cannot use copy offloading in salvaging mode
25
NVME_CAP_SET_TO(n->bar.cap, 0xf);
36
+
26
NVME_CAP_SET_CSS(n->bar.cap, 1);
37
=== amend: Options specified more than once ===
27
NVME_CAP_SET_MPSMAX(n->bar.cap, 4);
38
39
Testing: amend -f foo -f qcow2 -o lazy_refcounts=on TEST_DIR/t.qcow2
40
--
28
--
41
2.21.0
29
2.21.0
42
30
43
31
diff view generated by jsdifflib
1
From: John Snow <jsnow@redhat.com>
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
2
3
in blockdev_backup_prepare, we check to make sure that the target is
3
We forget to enable it for transaction .prepare, while it is already
4
associated with a compatible aio context. However, do_blockdev_backup is
4
enabled in do_drive_backup since commit a2d665c1bc362
5
called later and has some logic to move the target to a compatible
5
"blockdev: loosen restrictions on drive-backup source node"
6
aio_context. The transaction version will fail certain commands
7
needlessly early as a result.
8
6
9
Allow blockdev_backup_prepare to simply call do_blockdev_backup, which
7
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
10
will ultimately decide if the contexts are compatible or not.
8
Message-id: 20190618140804.59214-1-vsementsov@virtuozzo.com
11
9
Reviewed-by: John Snow <jsnow@redhat.com>
12
Note: the transaction version has always disallowed this operation since
13
its initial commit bd8baecd (2014), whereas the version of
14
qmp_blockdev_backup at the time, from commit c29c1dd312f, tried to
15
enforce the aio_context switch instead. It's not clear, and I can't see
16
from the mailing list archives at the time, why the two functions take a
17
different approach. It wasn't until later in efd7556708b (2016) that the
18
standalone version tried to determine if it could set the context or
19
not.
20
21
Reported-by: aihua liang <aliang@redhat.com>
22
Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1683498
23
Signed-off-by: John Snow <jsnow@redhat.com>
24
Message-id: 20190523170643.20794-2-jsnow@redhat.com
25
Reviewed-by: Max Reitz <mreitz@redhat.com>
26
Signed-off-by: Max Reitz <mreitz@redhat.com>
10
Signed-off-by: Max Reitz <mreitz@redhat.com>
27
---
11
---
28
blockdev.c | 4 ----
12
blockdev.c | 2 +-
29
1 file changed, 4 deletions(-)
13
1 file changed, 1 insertion(+), 1 deletion(-)
30
14
31
diff --git a/blockdev.c b/blockdev.c
15
diff --git a/blockdev.c b/blockdev.c
32
index XXXXXXX..XXXXXXX 100644
16
index XXXXXXX..XXXXXXX 100644
33
--- a/blockdev.c
17
--- a/blockdev.c
34
+++ b/blockdev.c
18
+++ b/blockdev.c
35
@@ -XXX,XX +XXX,XX @@ static void blockdev_backup_prepare(BlkActionState *common, Error **errp)
19
@@ -XXX,XX +XXX,XX @@ static void drive_backup_prepare(BlkActionState *common, Error **errp)
20
assert(common->action->type == TRANSACTION_ACTION_KIND_DRIVE_BACKUP);
21
backup = common->action->u.drive_backup.data;
22
23
- bs = qmp_get_root_bs(backup->device, errp);
24
+ bs = bdrv_lookup_bs(backup->device, backup->device, errp);
25
if (!bs) {
26
return;
36
}
27
}
37
38
aio_context = bdrv_get_aio_context(bs);
39
- if (aio_context != bdrv_get_aio_context(target)) {
40
- error_setg(errp, "Backup between two IO threads is not implemented");
41
- return;
42
- }
43
aio_context_acquire(aio_context);
44
state->bs = bs;
45
46
--
28
--
47
2.21.0
29
2.21.0
48
30
49
31
diff view generated by jsdifflib
1
This test converts a simple image to another, but blkdebug injects
1
From: Anton Nefedov <anton.nefedov@virtuozzo.com>
2
block_status and read faults at some offsets. The resulting image
3
should be the same as the input image, except that sectors that could
4
not be read have to be 0.
5
2
6
Signed-off-by: Max Reitz <mreitz@redhat.com>
3
COW (even empty/zero) areas require encryption too
7
Message-id: 20190507203508.18026-7-mreitz@redhat.com
4
8
Tested-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
5
Signed-off-by: Anton Nefedov <anton.nefedov@virtuozzo.com>
9
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
6
Reviewed-by: Eric Blake <eblake@redhat.com>
10
[mreitz: Dropped superfluous printf from _filter_offsets, as suggested
7
Reviewed-by: Max Reitz <mreitz@redhat.com>
11
by Vladimir; disable test for VDI and IMGOPTSSYNTAX]
8
Reviewed-by: Alberto Garcia <berto@igalia.com>
9
Message-id: 20190516143028.81155-1-anton.nefedov@virtuozzo.com
12
Signed-off-by: Max Reitz <mreitz@redhat.com>
10
Signed-off-by: Max Reitz <mreitz@redhat.com>
13
---
11
---
14
tests/qemu-iotests/251 | 170 +++++++++++++++++++++++++++++++++++++
12
tests/qemu-iotests/134 | 9 +++++++++
15
tests/qemu-iotests/251.out | 43 ++++++++++
13
tests/qemu-iotests/134.out | 10 ++++++++++
16
tests/qemu-iotests/group | 1 +
14
2 files changed, 19 insertions(+)
17
3 files changed, 214 insertions(+)
18
create mode 100755 tests/qemu-iotests/251
19
create mode 100644 tests/qemu-iotests/251.out
20
15
21
diff --git a/tests/qemu-iotests/251 b/tests/qemu-iotests/251
16
diff --git a/tests/qemu-iotests/134 b/tests/qemu-iotests/134
22
new file mode 100755
17
index XXXXXXX..XXXXXXX 100755
23
index XXXXXXX..XXXXXXX
18
--- a/tests/qemu-iotests/134
24
--- /dev/null
19
+++ b/tests/qemu-iotests/134
25
+++ b/tests/qemu-iotests/251
20
@@ -XXX,XX +XXX,XX @@ echo
26
@@ -XXX,XX +XXX,XX @@
21
echo "== reading whole image =="
27
+#!/usr/bin/env bash
22
$QEMU_IO --object $SECRET -c "read 0 $size" --image-opts $IMGSPEC | _filter_qemu_io | _filter_testdir
28
+#
23
29
+# Test qemu-img convert --salvage
24
+echo
30
+#
25
+echo "== rewriting cluster part =="
31
+# Copyright (C) 2019 Red Hat, Inc.
26
+$QEMU_IO --object $SECRET -c "write -P 0xb 512 512" --image-opts $IMGSPEC | _filter_qemu_io | _filter_testdir
32
+#
33
+# This program is free software; you can redistribute it and/or modify
34
+# it under the terms of the GNU General Public License as published by
35
+# the Free Software Foundation; either version 2 of the License, or
36
+# (at your option) any later version.
37
+#
38
+# This program is distributed in the hope that it will be useful,
39
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
40
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
41
+# GNU General Public License for more details.
42
+#
43
+# You should have received a copy of the GNU General Public License
44
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
45
+#
46
+
47
+# creator
48
+owner=mreitz@redhat.com
49
+
50
+seq=$(basename $0)
51
+echo "QA output created by $seq"
52
+
53
+status=1    # failure is the default!
54
+
55
+_cleanup()
56
+{
57
+ _cleanup_test_img
58
+}
59
+trap "_cleanup; exit \$status" 0 1 2 3 15
60
+
61
+# get standard environment, filters and checks
62
+. ./common.rc
63
+. ./common.filter
64
+. ./common.qemu
65
+
66
+_supported_fmt generic
67
+_supported_proto file
68
+_supported_os Linux
69
+
70
+if [ "$IMGOPTSSYNTAX" = "true" ]; then
71
+ # We use json:{} filenames here, so we cannot work with additional options.
72
+ _unsupported_fmt $IMGFMT
73
+else
74
+ # With VDI, the output is ordered differently. Just disable it.
75
+ _unsupported_fmt vdi
76
+fi
77
+
78
+
79
+TEST_IMG="$TEST_IMG.orig" _make_test_img 64M
80
+
81
+$QEMU_IO -c 'write -P 42 0 64M' "$TEST_IMG.orig" | _filter_qemu_io
82
+
83
+
84
+sector_size=512
85
+
86
+# Offsets on which to fail block-status. Keep in ascending order so
87
+# the indexing done by _filter_offsets will appear in ascending order
88
+# in the output as well.
89
+status_fail_offsets="$((16 * 1024 * 1024 + 8192))
90
+ $((33 * 1024 * 1024 + 512))"
91
+
92
+# Offsets on which to fail reads. Keep in ascending order for the
93
+# same reason.
94
+# The second element is shared with $status_fail_offsets on purpose.
95
+# Starting with the third element, we test what happens when a
96
+# continuous range of sectors is inaccessible.
97
+read_fail_offsets="$((32 * 1024 * 1024 - 65536))
98
+ $((33 * 1024 * 1024 + 512))
99
+ $(seq $((34 * 1024 * 1024)) $sector_size \
100
+ $((34 * 1024 * 1024 + 4096 - $sector_size)))"
101
+
102
+
103
+# blkdebug must be above the format layer so it can intercept all
104
+# block-status events
105
+source_img="json:{'driver': 'blkdebug',
106
+ 'image': {
107
+ 'driver': '$IMGFMT',
108
+ 'file': {
109
+ 'driver': 'file',
110
+ 'filename': '$TEST_IMG.orig'
111
+ }
112
+ },
113
+ 'inject-error': ["
114
+
115
+for ofs in $status_fail_offsets
116
+do
117
+ source_img+="{ 'event': 'none',
118
+ 'iotype': 'block-status',
119
+ 'errno': 5,
120
+ 'sector': $((ofs / sector_size)) },"
121
+done
122
+
123
+for ofs in $read_fail_offsets
124
+do
125
+ source_img+="{ 'event': 'none',
126
+ 'iotype': 'read',
127
+ 'errno': 5,
128
+ 'sector': $((ofs / sector_size)) },"
129
+done
130
+
131
+# Remove the trailing comma and terminate @inject-error and json:{}
132
+source_img="${source_img%,} ] }"
133
+
134
+
27
+
135
+echo
28
+echo
29
+echo "== verify pattern =="
30
+$QEMU_IO --object $SECRET -c "read -P 0 0 512" --image-opts $IMGSPEC | _filter_qemu_io | _filter_testdir
31
+$QEMU_IO --object $SECRET -c "read -P 0xb 512 512" --image-opts $IMGSPEC | _filter_qemu_io | _filter_testdir
136
+
32
+
137
+
33
echo
138
+_filter_offsets() {
34
echo "== rewriting whole image =="
139
+ filters=
35
$QEMU_IO --object $SECRET -c "write -P 0xa 0 $size" --image-opts $IMGSPEC | _filter_qemu_io | _filter_testdir
140
+
36
diff --git a/tests/qemu-iotests/134.out b/tests/qemu-iotests/134.out
141
+ index=0
37
index XXXXXXX..XXXXXXX 100644
142
+ for ofs in $1
38
--- a/tests/qemu-iotests/134.out
143
+ do
39
+++ b/tests/qemu-iotests/134.out
144
+ filters+=" -e s/$ofs/status_fail_offset_$index/"
40
@@ -XXX,XX +XXX,XX @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 encryption=on encrypt.
145
+ index=$((index + 1))
41
read 134217728/134217728 bytes at offset 0
146
+ done
42
128 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
147
+
43
148
+ index=0
44
+== rewriting cluster part ==
149
+ for ofs in $2
45
+wrote 512/512 bytes at offset 512
150
+ do
151
+ filters+=" -e s/$ofs/read_fail_offset_$index/"
152
+ index=$((index + 1))
153
+ done
154
+
155
+ sed $filters
156
+}
157
+
158
+# While determining the number of allocated sectors in the input
159
+# image, we should see one block status warning per element of
160
+# $status_fail_offsets.
161
+#
162
+# Then, the image is read. Since the block status is queried in
163
+# basically the same way, the same warnings as in the previous step
164
+# should reappear. Interleaved with those we should see a read
165
+# warning per element of $read_fail_offsets.
166
+# Note that $read_fail_offsets and $status_fail_offsets share an
167
+# element (read_fail_offset_1 == status_fail_offset_1), so
168
+# "status_fail_offset_1" in the output is the same as
169
+# "read_fail_offset_1".
170
+$QEMU_IMG convert --salvage "$source_img" "$TEST_IMG" 2>&1 \
171
+ | _filter_offsets "$status_fail_offsets" "$read_fail_offsets"
172
+
173
+echo
174
+
175
+# The offsets where the block status could not be determined should
176
+# have been treated as containing data and thus should be correct in
177
+# the output image.
178
+# The offsets where reading failed altogether should be 0. Make them
179
+# 0 in the input image, too, so we can compare both images.
180
+for ofs in $read_fail_offsets
181
+do
182
+ $QEMU_IO -c "write -z $ofs $sector_size" "$TEST_IMG.orig" \
183
+ | _filter_qemu_io \
184
+ | _filter_offsets '' "$read_fail_offsets"
185
+done
186
+
187
+echo
188
+
189
+# These should be equal now.
190
+$QEMU_IMG compare "$TEST_IMG.orig" "$TEST_IMG"
191
+
192
+
193
+# success, all done
194
+echo "*** done"
195
+rm -f $seq.full
196
+status=0
197
diff --git a/tests/qemu-iotests/251.out b/tests/qemu-iotests/251.out
198
new file mode 100644
199
index XXXXXXX..XXXXXXX
200
--- /dev/null
201
+++ b/tests/qemu-iotests/251.out
202
@@ -XXX,XX +XXX,XX @@
203
+QA output created by 251
204
+Formatting 'TEST_DIR/t.IMGFMT.orig', fmt=IMGFMT size=67108864
205
+wrote 67108864/67108864 bytes at offset 0
206
+64 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
207
+
208
+qemu-img: warning: error while reading block status at offset status_fail_offset_0: Input/output error
209
+qemu-img: warning: error while reading block status at offset status_fail_offset_1: Input/output error
210
+qemu-img: warning: error while reading block status at offset status_fail_offset_0: Input/output error
211
+qemu-img: warning: error while reading offset read_fail_offset_0: Input/output error
212
+qemu-img: warning: error while reading block status at offset status_fail_offset_1: Input/output error
213
+qemu-img: warning: error while reading offset status_fail_offset_1: Input/output error
214
+qemu-img: warning: error while reading offset read_fail_offset_2: Input/output error
215
+qemu-img: warning: error while reading offset read_fail_offset_3: Input/output error
216
+qemu-img: warning: error while reading offset read_fail_offset_4: Input/output error
217
+qemu-img: warning: error while reading offset read_fail_offset_5: Input/output error
218
+qemu-img: warning: error while reading offset read_fail_offset_6: Input/output error
219
+qemu-img: warning: error while reading offset read_fail_offset_7: Input/output error
220
+qemu-img: warning: error while reading offset read_fail_offset_8: Input/output error
221
+qemu-img: warning: error while reading offset read_fail_offset_9: Input/output error
222
+
223
+wrote 512/512 bytes at offset read_fail_offset_0
224
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
225
+wrote 512/512 bytes at offset read_fail_offset_1
226
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
227
+wrote 512/512 bytes at offset read_fail_offset_2
228
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
229
+wrote 512/512 bytes at offset read_fail_offset_3
230
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
231
+wrote 512/512 bytes at offset read_fail_offset_4
232
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
233
+wrote 512/512 bytes at offset read_fail_offset_5
234
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
235
+wrote 512/512 bytes at offset read_fail_offset_6
236
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
237
+wrote 512/512 bytes at offset read_fail_offset_7
238
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
239
+wrote 512/512 bytes at offset read_fail_offset_8
240
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
241
+wrote 512/512 bytes at offset read_fail_offset_9
242
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
46
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
243
+
47
+
244
+Images are identical.
48
+== verify pattern ==
245
+*** done
49
+read 512/512 bytes at offset 0
246
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
50
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
247
index XXXXXXX..XXXXXXX 100644
51
+read 512/512 bytes at offset 512
248
--- a/tests/qemu-iotests/group
52
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
249
+++ b/tests/qemu-iotests/group
53
+
250
@@ -XXX,XX +XXX,XX @@
54
== rewriting whole image ==
251
248 rw quick
55
wrote 134217728/134217728 bytes at offset 0
252
249 rw auto quick
56
128 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
253
250 rw auto quick
254
+251 rw auto quick
255
252 rw auto backing quick
256
253 rw auto quick
257
254 rw auto backing quick
258
--
57
--
259
2.21.0
58
2.21.0
260
59
261
60
diff view generated by jsdifflib
1
From: John Snow <jsnow@redhat.com>
1
From: Sam Eiderman <shmuel.eiderman@oracle.com>
2
2
3
Instead of event_wait which looks for a single event, add an events_wait
3
Commit b0651b8c246d ("vmdk: Move l1_size check into vmdk_add_extent")
4
which can look for any number of events simultaneously. However, it
4
extended the l1_size check from VMDK4 to VMDK3 but did not update the
5
will still only return one at a time, whichever happens first.
5
default coverage in the moved comment.
6
6
7
Signed-off-by: John Snow <jsnow@redhat.com>
7
The previous vmdk4 calculation:
8
Message-id: 20190523170643.20794-4-jsnow@redhat.com
8
9
(512 * 1024 * 1024) * 512(l2 entries) * 65536(grain) = 16PB
10
11
The added vmdk3 calculation:
12
13
(512 * 1024 * 1024) * 4096(l2 entries) * 512(grain) = 1PB
14
15
Adding the calculation of vmdk3 to the comment.
16
17
In any case, VMware does not offer virtual disks more than 2TB for
18
vmdk4/vmdk3 or 64TB for the new undocumented seSparse format which is
19
not implemented yet in qemu.
20
21
Reviewed-by: Karl Heubaum <karl.heubaum@oracle.com>
22
Reviewed-by: Eyal Moscovici <eyal.moscovici@oracle.com>
23
Reviewed-by: Liran Alon <liran.alon@oracle.com>
24
Reviewed-by: Arbel Moshe <arbel.moshe@oracle.com>
25
Signed-off-by: Sam Eiderman <shmuel.eiderman@oracle.com>
26
Message-id: 20190620091057.47441-2-shmuel.eiderman@oracle.com
27
Reviewed-by: yuchenlin <yuchenlin@synology.com>
9
Reviewed-by: Max Reitz <mreitz@redhat.com>
28
Reviewed-by: Max Reitz <mreitz@redhat.com>
10
Signed-off-by: Max Reitz <mreitz@redhat.com>
29
Signed-off-by: Max Reitz <mreitz@redhat.com>
11
---
30
---
12
python/qemu/__init__.py | 69 +++++++++++++++++++++++++++++------------
31
block/vmdk.c | 11 ++++++++---
13
1 file changed, 49 insertions(+), 20 deletions(-)
32
1 file changed, 8 insertions(+), 3 deletions(-)
14
33
15
diff --git a/python/qemu/__init__.py b/python/qemu/__init__.py
34
diff --git a/block/vmdk.c b/block/vmdk.c
16
index XXXXXXX..XXXXXXX 100644
35
index XXXXXXX..XXXXXXX 100644
17
--- a/python/qemu/__init__.py
36
--- a/block/vmdk.c
18
+++ b/python/qemu/__init__.py
37
+++ b/block/vmdk.c
19
@@ -XXX,XX +XXX,XX @@ class QEMUMachine(object):
38
@@ -XXX,XX +XXX,XX @@ static int vmdk_add_extent(BlockDriverState *bs,
20
self._qmp.clear_events()
39
return -EFBIG;
21
return events
40
}
22
41
if (l1_size > 512 * 1024 * 1024) {
23
- def event_wait(self, name, timeout=60.0, match=None):
42
- /* Although with big capacity and small l1_entry_sectors, we can get a
24
+ @staticmethod
43
+ /*
25
+ def event_match(event, match=None):
44
+ * Although with big capacity and small l1_entry_sectors, we can get a
26
"""
45
* big l1_size, we don't want unbounded value to allocate the table.
27
- Wait for specified timeout on named event in QMP; optionally filter
46
- * Limit it to 512M, which is 16PB for default cluster and L2 table
28
- results by match.
47
- * size */
29
+ Check if an event matches optional match criteria.
48
+ * Limit it to 512M, which is:
30
49
+ * 16PB - for default "Hosted Sparse Extent" (VMDK4)
31
- The 'match' is checked to be a recursive subset of the 'event'; skips
50
+ * cluster size: 64KB, L2 table size: 512 entries
32
- branch processing on match's value None
51
+ * 1PB - for default "ESXi Host Sparse Extent" (VMDK3/vmfsSparse)
33
- {"foo": {"bar": 1}} matches {"foo": None}
52
+ * cluster size: 512B, L2 table size: 4096 entries
34
- {"foo": {"bar": 1}} does not matches {"foo": {"baz": None}}
53
+ */
35
+ The match criteria takes the form of a matching subdict. The event is
54
error_setg(errp, "L1 size too big");
36
+ checked to be a superset of the subdict, recursively, with matching
55
return -EFBIG;
37
+ values whenever those values are not None.
56
}
38
+
39
+ Examples, with the subdict queries on the left:
40
+ - None matches any object.
41
+ - {"foo": None} matches {"foo": {"bar": 1}}
42
+ - {"foo": {"baz": None}} does not match {"foo": {"bar": 1}}
43
+ - {"foo": {"baz": 2}} matches {"foo": {"bar": 1, "baz": 2}}
44
"""
45
- def event_match(event, match=None):
46
- if match is None:
47
- return True
48
+ if match is None:
49
+ return True
50
51
- for key in match:
52
- if key in event:
53
- if isinstance(event[key], dict):
54
- if not event_match(event[key], match[key]):
55
- return False
56
- elif event[key] != match[key]:
57
+ for key in match:
58
+ if key in event:
59
+ if isinstance(event[key], dict):
60
+ if not QEMUMachine.event_match(event[key], match[key]):
61
return False
62
- else:
63
+ elif event[key] != match[key]:
64
return False
65
+ else:
66
+ return False
67
+ return True
68
69
- return True
70
+ def event_wait(self, name, timeout=60.0, match=None):
71
+ """
72
+ event_wait waits for and returns a named event from QMP with a timeout.
73
+
74
+ name: The event to wait for.
75
+ timeout: QEMUMonitorProtocol.pull_event timeout parameter.
76
+ match: Optional match criteria. See event_match for details.
77
+ """
78
+ return self.events_wait([(name, match)], timeout)
79
+
80
+ def events_wait(self, events, timeout=60.0):
81
+ """
82
+ events_wait waits for and returns a named event from QMP with a timeout.
83
+
84
+ events: a sequence of (name, match_criteria) tuples.
85
+ The match criteria are optional and may be None.
86
+ See event_match for details.
87
+ timeout: QEMUMonitorProtocol.pull_event timeout parameter.
88
+ """
89
+ def _match(event):
90
+ for name, match in events:
91
+ if (event['event'] == name and
92
+ self.event_match(event, match)):
93
+ return True
94
+ return False
95
96
# Search cached events
97
for event in self._events:
98
- if (event['event'] == name) and event_match(event, match):
99
+ if _match(event):
100
self._events.remove(event)
101
return event
102
103
# Poll for new events
104
while True:
105
event = self._qmp.pull_event(wait=timeout)
106
- if (event['event'] == name) and event_match(event, match):
107
+ if _match(event):
108
return event
109
self._events.append(event)
110
111
--
57
--
112
2.21.0
58
2.21.0
113
59
114
60
diff view generated by jsdifflib
1
From: John Snow <jsnow@redhat.com>
1
From: Sam Eiderman <shmuel.eiderman@oracle.com>
2
2
3
Cap waits to 60 seconds so that iotests can fail gracefully if something
3
512M of L1 entries is a very loose bound, only 32M are required to store
4
goes wrong.
4
the maximal supported VMDK file size of 2TB.
5
5
6
Signed-off-by: John Snow <jsnow@redhat.com>
6
Fixed qemu-iotest 59# - now failure occures before on impossible L1
7
Message-id: 20190523170643.20794-3-jsnow@redhat.com
7
table size.
8
9
Reviewed-by: Karl Heubaum <karl.heubaum@oracle.com>
10
Reviewed-by: Eyal Moscovici <eyal.moscovici@oracle.com>
11
Reviewed-by: Liran Alon <liran.alon@oracle.com>
12
Reviewed-by: Arbel Moshe <arbel.moshe@oracle.com>
13
Signed-off-by: Sam Eiderman <shmuel.eiderman@oracle.com>
14
Message-id: 20190620091057.47441-3-shmuel.eiderman@oracle.com
8
Reviewed-by: Max Reitz <mreitz@redhat.com>
15
Reviewed-by: Max Reitz <mreitz@redhat.com>
9
Signed-off-by: Max Reitz <mreitz@redhat.com>
16
Signed-off-by: Max Reitz <mreitz@redhat.com>
10
---
17
---
11
tests/qemu-iotests/iotests.py | 14 +++++++-------
18
block/vmdk.c | 13 +++++++------
12
1 file changed, 7 insertions(+), 7 deletions(-)
19
tests/qemu-iotests/059.out | 2 +-
20
2 files changed, 8 insertions(+), 7 deletions(-)
13
21
14
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
22
diff --git a/block/vmdk.c b/block/vmdk.c
15
index XXXXXXX..XXXXXXX 100644
23
index XXXXXXX..XXXXXXX 100644
16
--- a/tests/qemu-iotests/iotests.py
24
--- a/block/vmdk.c
17
+++ b/tests/qemu-iotests/iotests.py
25
+++ b/block/vmdk.c
18
@@ -XXX,XX +XXX,XX @@ class VM(qtest.QEMUQtestMachine):
26
@@ -XXX,XX +XXX,XX @@ static int vmdk_add_extent(BlockDriverState *bs,
19
output_list += [key + '=' + obj[key]]
27
error_setg(errp, "Invalid granularity, image may be corrupt");
20
return ','.join(output_list)
28
return -EFBIG;
21
29
}
22
- def get_qmp_events_filtered(self, wait=True):
30
- if (l1_size > 512 * 1024 * 1024) {
23
+ def get_qmp_events_filtered(self, wait=60.0):
31
+ if (l1_size > 32 * 1024 * 1024) {
24
result = []
32
/*
25
for ev in self.get_qmp_events(wait=wait):
33
* Although with big capacity and small l1_entry_sectors, we can get a
26
result.append(filter_qmp_event(ev))
34
* big l1_size, we don't want unbounded value to allocate the table.
27
@@ -XXX,XX +XXX,XX @@ class VM(qtest.QEMUQtestMachine):
35
- * Limit it to 512M, which is:
28
36
- * 16PB - for default "Hosted Sparse Extent" (VMDK4)
29
# Returns None on success, and an error string on failure
37
- * cluster size: 64KB, L2 table size: 512 entries
30
def run_job(self, job, auto_finalize=True, auto_dismiss=False,
38
- * 1PB - for default "ESXi Host Sparse Extent" (VMDK3/vmfsSparse)
31
- pre_finalize=None):
39
- * cluster size: 512B, L2 table size: 4096 entries
32
+ pre_finalize=None, wait=60.0):
40
+ * Limit it to 32M, which is enough to store:
33
error = None
41
+ * 8TB - for both VMDK3 & VMDK4 with
34
while True:
42
+ * minimal cluster size: 512B
35
- for ev in self.get_qmp_events_filtered(wait=True):
43
+ * minimal L2 table size: 512 entries
36
+ for ev in self.get_qmp_events_filtered(wait=wait):
44
+ * 8 TB is still more than the maximal value supported for
37
if ev['event'] == 'JOB_STATUS_CHANGE':
45
+ * VMDK3 & VMDK4 which is 2TB.
38
status = ev['data']['status']
46
*/
39
if status == 'aborting':
47
error_setg(errp, "L1 size too big");
40
@@ -XXX,XX +XXX,XX @@ class QMPTestCase(unittest.TestCase):
48
return -EFBIG;
41
self.assertEqual(self.vm.flatten_qmp_object(json.loads(json_filename[5:])),
49
diff --git a/tests/qemu-iotests/059.out b/tests/qemu-iotests/059.out
42
self.vm.flatten_qmp_object(reference))
50
index XXXXXXX..XXXXXXX 100644
43
51
--- a/tests/qemu-iotests/059.out
44
- def cancel_and_wait(self, drive='drive0', force=False, resume=False):
52
+++ b/tests/qemu-iotests/059.out
45
+ def cancel_and_wait(self, drive='drive0', force=False, resume=False, wait=60.0):
53
@@ -XXX,XX +XXX,XX @@ Offset Length Mapped to File
46
'''Cancel a block job and wait for it to finish, returning the event'''
54
0x140000000 0x10000 0x50000 TEST_DIR/t-s003.vmdk
47
result = self.vm.qmp('block-job-cancel', device=drive, force=force)
55
48
self.assert_qmp(result, 'return', {})
56
=== Testing afl image with a very large capacity ===
49
@@ -XXX,XX +XXX,XX @@ class QMPTestCase(unittest.TestCase):
57
-qemu-img: Can't get image size 'TEST_DIR/afl9.IMGFMT': File too large
50
cancelled = False
58
+qemu-img: Could not open 'TEST_DIR/afl9.IMGFMT': L1 size too big
51
result = None
59
*** done
52
while not cancelled:
53
- for event in self.vm.get_qmp_events(wait=True):
54
+ for event in self.vm.get_qmp_events(wait=wait):
55
if event['event'] == 'BLOCK_JOB_COMPLETED' or \
56
event['event'] == 'BLOCK_JOB_CANCELLED':
57
self.assert_qmp(event, 'data/device', drive)
58
@@ -XXX,XX +XXX,XX @@ class QMPTestCase(unittest.TestCase):
59
self.assert_no_active_block_jobs()
60
return result
61
62
- def wait_until_completed(self, drive='drive0', check_offset=True):
63
+ def wait_until_completed(self, drive='drive0', check_offset=True, wait=60.0):
64
'''Wait for a block job to finish, returning the event'''
65
while True:
66
- for event in self.vm.get_qmp_events(wait=True):
67
+ for event in self.vm.get_qmp_events(wait=wait):
68
if event['event'] == 'BLOCK_JOB_COMPLETED':
69
self.assert_qmp(event, 'data/device', drive)
70
self.assert_qmp_absent(event, 'data/error')
71
--
60
--
72
2.21.0
61
2.21.0
73
62
74
63
diff view generated by jsdifflib
Deleted patch
1
From: John Snow <jsnow@redhat.com>
2
1
3
Don't pull events out of the queue that don't belong to us;
4
be choosier so that we can use this method to drive jobs that
5
were launched by transactions that may have more jobs.
6
7
Signed-off-by: John Snow <jsnow@redhat.com>
8
Message-id: 20190523170643.20794-5-jsnow@redhat.com
9
Reviewed-by: Max Reitz <mreitz@redhat.com>
10
Signed-off-by: Max Reitz <mreitz@redhat.com>
11
---
12
tests/qemu-iotests/iotests.py | 48 +++++++++++++++++++++--------------
13
1 file changed, 29 insertions(+), 19 deletions(-)
14
15
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
16
index XXXXXXX..XXXXXXX 100644
17
--- a/tests/qemu-iotests/iotests.py
18
+++ b/tests/qemu-iotests/iotests.py
19
@@ -XXX,XX +XXX,XX @@ class VM(qtest.QEMUQtestMachine):
20
# Returns None on success, and an error string on failure
21
def run_job(self, job, auto_finalize=True, auto_dismiss=False,
22
pre_finalize=None, wait=60.0):
23
+ match_device = {'data': {'device': job}}
24
+ match_id = {'data': {'id': job}}
25
+ events = [
26
+ ('BLOCK_JOB_COMPLETED', match_device),
27
+ ('BLOCK_JOB_CANCELLED', match_device),
28
+ ('BLOCK_JOB_ERROR', match_device),
29
+ ('BLOCK_JOB_READY', match_device),
30
+ ('BLOCK_JOB_PENDING', match_id),
31
+ ('JOB_STATUS_CHANGE', match_id)
32
+ ]
33
error = None
34
while True:
35
- for ev in self.get_qmp_events_filtered(wait=wait):
36
- if ev['event'] == 'JOB_STATUS_CHANGE':
37
- status = ev['data']['status']
38
- if status == 'aborting':
39
- result = self.qmp('query-jobs')
40
- for j in result['return']:
41
- if j['id'] == job:
42
- error = j['error']
43
- log('Job failed: %s' % (j['error']))
44
- elif status == 'pending' and not auto_finalize:
45
- if pre_finalize:
46
- pre_finalize()
47
- self.qmp_log('job-finalize', id=job)
48
- elif status == 'concluded' and not auto_dismiss:
49
- self.qmp_log('job-dismiss', id=job)
50
- elif status == 'null':
51
- return error
52
- else:
53
- log(ev)
54
+ ev = filter_qmp_event(self.events_wait(events))
55
+ if ev['event'] != 'JOB_STATUS_CHANGE':
56
+ log(ev)
57
+ continue
58
+ status = ev['data']['status']
59
+ if status == 'aborting':
60
+ result = self.qmp('query-jobs')
61
+ for j in result['return']:
62
+ if j['id'] == job:
63
+ error = j['error']
64
+ log('Job failed: %s' % (j['error']))
65
+ elif status == 'pending' and not auto_finalize:
66
+ if pre_finalize:
67
+ pre_finalize()
68
+ self.qmp_log('job-finalize', id=job)
69
+ elif status == 'concluded' and not auto_dismiss:
70
+ self.qmp_log('job-dismiss', id=job)
71
+ elif status == 'null':
72
+ return error
73
74
def node_info(self, node_name):
75
nodes = self.qmp('query-named-block-nodes')
76
--
77
2.21.0
78
79
diff view generated by jsdifflib
Deleted patch
1
From: John Snow <jsnow@redhat.com>
2
1
3
Signed-off-by: John Snow <jsnow@redhat.com>
4
Message-id: 20190523170643.20794-6-jsnow@redhat.com
5
Reviewed-by: Max Reitz <mreitz@redhat.com>
6
[mreitz: Moved from 250 to 256]
7
Signed-off-by: Max Reitz <mreitz@redhat.com>
8
---
9
tests/qemu-iotests/256 | 122 +++++++++++++++++++++++++++++++++++++
10
tests/qemu-iotests/256.out | 119 ++++++++++++++++++++++++++++++++++++
11
tests/qemu-iotests/group | 1 +
12
3 files changed, 242 insertions(+)
13
create mode 100755 tests/qemu-iotests/256
14
create mode 100644 tests/qemu-iotests/256.out
15
16
diff --git a/tests/qemu-iotests/256 b/tests/qemu-iotests/256
17
new file mode 100755
18
index XXXXXXX..XXXXXXX
19
--- /dev/null
20
+++ b/tests/qemu-iotests/256
21
@@ -XXX,XX +XXX,XX @@
22
+#!/usr/bin/env python
23
+#
24
+# Test incremental/backup across iothread contexts
25
+#
26
+# Copyright (c) 2019 John Snow for Red Hat, Inc.
27
+#
28
+# This program is free software; you can redistribute it and/or modify
29
+# it under the terms of the GNU General Public License as published by
30
+# the Free Software Foundation; either version 2 of the License, or
31
+# (at your option) any later version.
32
+#
33
+# This program is distributed in the hope that it will be useful,
34
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
35
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
36
+# GNU General Public License for more details.
37
+#
38
+# You should have received a copy of the GNU General Public License
39
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
40
+#
41
+# owner=jsnow@redhat.com
42
+
43
+import os
44
+import iotests
45
+from iotests import log
46
+
47
+iotests.verify_image_format(supported_fmts=['qcow2'])
48
+size = 64 * 1024 * 1024
49
+
50
+with iotests.FilePath('img0') as img0_path, \
51
+ iotests.FilePath('img1') as img1_path, \
52
+ iotests.FilePath('img0-full') as img0_full_path, \
53
+ iotests.FilePath('img1-full') as img1_full_path, \
54
+ iotests.FilePath('img0-incr') as img0_incr_path, \
55
+ iotests.FilePath('img1-incr') as img1_incr_path, \
56
+ iotests.VM() as vm:
57
+
58
+ def create_target(filepath, name, size):
59
+ basename = os.path.basename(filepath)
60
+ nodename = "file_{}".format(basename)
61
+ log(vm.command('blockdev-create', job_id='job1',
62
+ options={
63
+ 'driver': 'file',
64
+ 'filename': filepath,
65
+ 'size': 0,
66
+ }))
67
+ vm.run_job('job1')
68
+ log(vm.command('blockdev-add', driver='file',
69
+ node_name=nodename, filename=filepath))
70
+ log(vm.command('blockdev-create', job_id='job2',
71
+ options={
72
+ 'driver': iotests.imgfmt,
73
+ 'file': nodename,
74
+ 'size': size,
75
+ }))
76
+ vm.run_job('job2')
77
+ log(vm.command('blockdev-add', driver=iotests.imgfmt,
78
+ node_name=name,
79
+ file=nodename))
80
+
81
+ log('--- Preparing images & VM ---\n')
82
+ vm.add_object('iothread,id=iothread0')
83
+ vm.add_object('iothread,id=iothread1')
84
+ vm.add_device('virtio-scsi-pci,id=scsi0,iothread=iothread0')
85
+ vm.add_device('virtio-scsi-pci,id=scsi1,iothread=iothread1')
86
+ iotests.qemu_img_create('-f', iotests.imgfmt, img0_path, str(size))
87
+ iotests.qemu_img_create('-f', iotests.imgfmt, img1_path, str(size))
88
+ vm.add_drive(img0_path, interface='none')
89
+ vm.add_device('scsi-hd,id=device0,drive=drive0,bus=scsi0.0')
90
+ vm.add_drive(img1_path, interface='none')
91
+ vm.add_device('scsi-hd,id=device1,drive=drive1,bus=scsi1.0')
92
+
93
+ log('--- Starting VM ---\n')
94
+ vm.launch()
95
+
96
+ log('--- Create Targets & Full Backups ---\n')
97
+ create_target(img0_full_path, 'img0-full', size)
98
+ create_target(img1_full_path, 'img1-full', size)
99
+ ret = vm.qmp_log('transaction', indent=2, actions=[
100
+ { 'type': 'block-dirty-bitmap-add',
101
+ 'data': { 'node': 'drive0', 'name': 'bitmap0' }},
102
+ { 'type': 'block-dirty-bitmap-add',
103
+ 'data': { 'node': 'drive1', 'name': 'bitmap1' }},
104
+ { 'type': 'blockdev-backup',
105
+ 'data': { 'device': 'drive0',
106
+ 'target': 'img0-full',
107
+ 'sync': 'full',
108
+ 'job-id': 'j0' }},
109
+ { 'type': 'blockdev-backup',
110
+ 'data': { 'device': 'drive1',
111
+ 'target': 'img1-full',
112
+ 'sync': 'full',
113
+ 'job-id': 'j1' }}
114
+ ])
115
+ if "error" in ret:
116
+ raise Exception(ret['error']['desc'])
117
+ vm.run_job('j0', auto_dismiss=True)
118
+ vm.run_job('j1', auto_dismiss=True)
119
+
120
+ log('\n--- Create Targets & Incremental Backups ---\n')
121
+ create_target(img0_incr_path, 'img0-incr', size)
122
+ create_target(img1_incr_path, 'img1-incr', size)
123
+ ret = vm.qmp_log('transaction', indent=2, actions=[
124
+ { 'type': 'blockdev-backup',
125
+ 'data': { 'device': 'drive0',
126
+ 'target': 'img0-incr',
127
+ 'sync': 'incremental',
128
+ 'bitmap': 'bitmap0',
129
+ 'job-id': 'j2' }},
130
+ { 'type': 'blockdev-backup',
131
+ 'data': { 'device': 'drive1',
132
+ 'target': 'img1-incr',
133
+ 'sync': 'incremental',
134
+ 'bitmap': 'bitmap1',
135
+ 'job-id': 'j3' }}
136
+ ])
137
+ if "error" in ret:
138
+ raise Exception(ret['error']['desc'])
139
+ vm.run_job('j2', auto_dismiss=True)
140
+ vm.run_job('j3', auto_dismiss=True)
141
+
142
+ log('\n--- Done ---')
143
+ vm.shutdown()
144
diff --git a/tests/qemu-iotests/256.out b/tests/qemu-iotests/256.out
145
new file mode 100644
146
index XXXXXXX..XXXXXXX
147
--- /dev/null
148
+++ b/tests/qemu-iotests/256.out
149
@@ -XXX,XX +XXX,XX @@
150
+--- Preparing images & VM ---
151
+
152
+--- Starting VM ---
153
+
154
+--- Create Targets & Full Backups ---
155
+
156
+{}
157
+{"execute": "job-dismiss", "arguments": {"id": "job1"}}
158
+{"return": {}}
159
+{}
160
+{}
161
+{"execute": "job-dismiss", "arguments": {"id": "job2"}}
162
+{"return": {}}
163
+{}
164
+{}
165
+{"execute": "job-dismiss", "arguments": {"id": "job1"}}
166
+{"return": {}}
167
+{}
168
+{}
169
+{"execute": "job-dismiss", "arguments": {"id": "job2"}}
170
+{"return": {}}
171
+{}
172
+{
173
+ "execute": "transaction",
174
+ "arguments": {
175
+ "actions": [
176
+ {
177
+ "data": {
178
+ "name": "bitmap0",
179
+ "node": "drive0"
180
+ },
181
+ "type": "block-dirty-bitmap-add"
182
+ },
183
+ {
184
+ "data": {
185
+ "name": "bitmap1",
186
+ "node": "drive1"
187
+ },
188
+ "type": "block-dirty-bitmap-add"
189
+ },
190
+ {
191
+ "data": {
192
+ "device": "drive0",
193
+ "job-id": "j0",
194
+ "sync": "full",
195
+ "target": "img0-full"
196
+ },
197
+ "type": "blockdev-backup"
198
+ },
199
+ {
200
+ "data": {
201
+ "device": "drive1",
202
+ "job-id": "j1",
203
+ "sync": "full",
204
+ "target": "img1-full"
205
+ },
206
+ "type": "blockdev-backup"
207
+ }
208
+ ]
209
+ }
210
+}
211
+{
212
+ "return": {}
213
+}
214
+{"data": {"device": "j0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
215
+{"data": {"device": "j1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
216
+
217
+--- Create Targets & Incremental Backups ---
218
+
219
+{}
220
+{"execute": "job-dismiss", "arguments": {"id": "job1"}}
221
+{"return": {}}
222
+{}
223
+{}
224
+{"execute": "job-dismiss", "arguments": {"id": "job2"}}
225
+{"return": {}}
226
+{}
227
+{}
228
+{"execute": "job-dismiss", "arguments": {"id": "job1"}}
229
+{"return": {}}
230
+{}
231
+{}
232
+{"execute": "job-dismiss", "arguments": {"id": "job2"}}
233
+{"return": {}}
234
+{}
235
+{
236
+ "execute": "transaction",
237
+ "arguments": {
238
+ "actions": [
239
+ {
240
+ "data": {
241
+ "bitmap": "bitmap0",
242
+ "device": "drive0",
243
+ "job-id": "j2",
244
+ "sync": "incremental",
245
+ "target": "img0-incr"
246
+ },
247
+ "type": "blockdev-backup"
248
+ },
249
+ {
250
+ "data": {
251
+ "bitmap": "bitmap1",
252
+ "device": "drive1",
253
+ "job-id": "j3",
254
+ "sync": "incremental",
255
+ "target": "img1-incr"
256
+ },
257
+ "type": "blockdev-backup"
258
+ }
259
+ ]
260
+ }
261
+}
262
+{
263
+ "return": {}
264
+}
265
+{"data": {"device": "j2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
266
+{"data": {"device": "j3", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
267
+
268
+--- Done ---
269
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
270
index XXXXXXX..XXXXXXX 100644
271
--- a/tests/qemu-iotests/group
272
+++ b/tests/qemu-iotests/group
273
@@ -XXX,XX +XXX,XX @@
274
253 rw auto quick
275
254 rw auto backing quick
276
255 rw auto quick
277
+256 rw auto quick
278
--
279
2.21.0
280
281
diff view generated by jsdifflib
Deleted patch
1
From: John Snow <jsnow@redhat.com>
2
1
3
Before, event_match didn't always recurse if the event value was not a
4
dictionary, and would instead check for equality immediately.
5
6
By delaying equality checking to post-recursion, we can allow leaf
7
values like "5" to match "None" and take advantage of the generic
8
None-returns-True clause.
9
10
This makes the matching a little more obviously consistent at the
11
expense of being able to check for explicit None values, which is
12
probably not that important given what this function is used for.
13
14
Signed-off-by: John Snow <jsnow@redhat.com>
15
Message-id: 20190528183857.26167-1-jsnow@redhat.com
16
Signed-off-by: Max Reitz <mreitz@redhat.com>
17
---
18
python/qemu/__init__.py | 24 ++++++++++++++----------
19
1 file changed, 14 insertions(+), 10 deletions(-)
20
21
diff --git a/python/qemu/__init__.py b/python/qemu/__init__.py
22
index XXXXXXX..XXXXXXX 100644
23
--- a/python/qemu/__init__.py
24
+++ b/python/qemu/__init__.py
25
@@ -XXX,XX +XXX,XX @@ class QEMUMachine(object):
26
27
The match criteria takes the form of a matching subdict. The event is
28
checked to be a superset of the subdict, recursively, with matching
29
- values whenever those values are not None.
30
+ values whenever the subdict values are not None.
31
+
32
+ This has a limitation that you cannot explicitly check for None values.
33
34
Examples, with the subdict queries on the left:
35
- None matches any object.
36
- {"foo": None} matches {"foo": {"bar": 1}}
37
- - {"foo": {"baz": None}} does not match {"foo": {"bar": 1}}
38
- - {"foo": {"baz": 2}} matches {"foo": {"bar": 1, "baz": 2}}
39
+ - {"foo": None} matches {"foo": 5}
40
+ - {"foo": {"abc": None}} does not match {"foo": {"bar": 1}}
41
+ - {"foo": {"rab": 2}} matches {"foo": {"bar": 1, "rab": 2}}
42
"""
43
if match is None:
44
return True
45
46
- for key in match:
47
- if key in event:
48
- if isinstance(event[key], dict):
49
+ try:
50
+ for key in match:
51
+ if key in event:
52
if not QEMUMachine.event_match(event[key], match[key]):
53
return False
54
- elif event[key] != match[key]:
55
+ else:
56
return False
57
- else:
58
- return False
59
- return True
60
+ return True
61
+ except TypeError:
62
+ # either match or event wasn't iterable (not a dict)
63
+ return match == event
64
65
def event_wait(self, name, timeout=60.0, match=None):
66
"""
67
--
68
2.21.0
69
70
diff view generated by jsdifflib
Deleted patch
1
It is possible for an empty file to take up blocks on a filesystem, for
2
example:
3
1
4
$ qemu-img create -f raw test.img 1G
5
Formatting 'test.img', fmt=raw size=1073741824
6
$ mkfs.ext4 -I 128 -q test.img
7
$ mkdir test-mount
8
$ sudo mount -o loop test.img test-mount
9
$ sudo touch test-mount/test-file
10
$ stat -c 'blocks=%b' test-mount/test-file
11
blocks=8
12
13
These extra blocks (one cluster) are apparently used for metadata,
14
because they are always there, on top of blocks used for data:
15
16
$ sudo dd if=/dev/zero of=test-mount/test-file bs=1M count=1
17
1+0 records in
18
1+0 records out
19
1048576 bytes (1.0 MB, 1.0 MiB) copied, 0.00135339 s, 775 MB/s
20
$ stat -c 'blocks=%b' test-mount/test-file
21
blocks=2056
22
23
Make iotest 175 take this into account.
24
25
Reported-by: Thomas Huth <thuth@redhat.com>
26
Signed-off-by: Max Reitz <mreitz@redhat.com>
27
Reviewed-by: Eric Blake <eblake@redhat.com>
28
Reviewed-by: Nir Soffer <nsoffer@redhat.com>
29
Message-id: 20190516144319.12570-1-mreitz@redhat.com
30
Signed-off-by: Max Reitz <mreitz@redhat.com>
31
---
32
tests/qemu-iotests/175 | 26 ++++++++++++++++++++++----
33
tests/qemu-iotests/175.out | 8 ++++----
34
2 files changed, 26 insertions(+), 8 deletions(-)
35
36
diff --git a/tests/qemu-iotests/175 b/tests/qemu-iotests/175
37
index XXXXXXX..XXXXXXX 100755
38
--- a/tests/qemu-iotests/175
39
+++ b/tests/qemu-iotests/175
40
@@ -XXX,XX +XXX,XX @@ status=1    # failure is the default!
41
42
_cleanup()
43
{
44
-    _cleanup_test_img
45
+ _cleanup_test_img
46
+ rm -f "$TEST_DIR/empty"
47
}
48
trap "_cleanup; exit \$status" 0 1 2 3 15
49
50
+# Some file systems sometimes allocate extra blocks independently of
51
+# the file size. This function hides the resulting difference in the
52
+# stat -c '%b' output.
53
+# Parameter 1: Number of blocks an empty file occupies
54
+# Parameter 2: Image size in bytes
55
+_filter_blocks()
56
+{
57
+ extra_blocks=$1
58
+ img_size=$2
59
+
60
+ sed -e "s/blocks=$extra_blocks\\(\$\\|[^0-9]\\)/nothing allocated/" \
61
+ -e "s/blocks=$((extra_blocks + img_size / 512))\\(\$\\|[^0-9]\\)/everything allocated/"
62
+}
63
+
64
# get standard environment, filters and checks
65
. ./common.rc
66
. ./common.filter
67
@@ -XXX,XX +XXX,XX @@ _supported_fmt raw
68
_supported_proto file
69
_supported_os Linux
70
71
-size=1m
72
+size=$((1 * 1024 * 1024))
73
+
74
+touch "$TEST_DIR/empty"
75
+extra_blocks=$(stat -c '%b' "$TEST_DIR/empty")
76
77
echo
78
echo "== creating image with default preallocation =="
79
_make_test_img $size | _filter_imgfmt
80
-stat -c "size=%s, blocks=%b" $TEST_IMG
81
+stat -c "size=%s, blocks=%b" $TEST_IMG | _filter_blocks $extra_blocks $size
82
83
for mode in off full falloc; do
84
echo
85
echo "== creating image with preallocation $mode =="
86
IMGOPTS=preallocation=$mode _make_test_img $size | _filter_imgfmt
87
- stat -c "size=%s, blocks=%b" $TEST_IMG
88
+ stat -c "size=%s, blocks=%b" $TEST_IMG | _filter_blocks $extra_blocks $size
89
done
90
91
# success, all done
92
diff --git a/tests/qemu-iotests/175.out b/tests/qemu-iotests/175.out
93
index XXXXXXX..XXXXXXX 100644
94
--- a/tests/qemu-iotests/175.out
95
+++ b/tests/qemu-iotests/175.out
96
@@ -XXX,XX +XXX,XX @@ QA output created by 175
97
98
== creating image with default preallocation ==
99
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576
100
-size=1048576, blocks=0
101
+size=1048576, nothing allocated
102
103
== creating image with preallocation off ==
104
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 preallocation=off
105
-size=1048576, blocks=0
106
+size=1048576, nothing allocated
107
108
== creating image with preallocation full ==
109
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 preallocation=full
110
-size=1048576, blocks=2048
111
+size=1048576, everything allocated
112
113
== creating image with preallocation falloc ==
114
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 preallocation=falloc
115
-size=1048576, blocks=2048
116
+size=1048576, everything allocated
117
*** done
118
--
119
2.21.0
120
121
diff view generated by jsdifflib
Deleted patch
1
In 219, we wait for the job to make progress before we emit its status.
2
This makes the output reliable. We do not wait for any more progress if
3
the job's current-progress already matches its total-progress.
4
1
5
Unfortunately, there is a bug: Right after the job has been started,
6
it's possible that total-progress is still 0. In that case, we may skip
7
the first progress-making step and keep ending up 64 kB short.
8
9
To fix that bug, we can simply wait for total-progress to reach 4 MB
10
(the image size) after starting the job.
11
12
Reported-by: Karen Mezick <kmezick@redhat.com>
13
Buglink: https://bugzilla.redhat.com/show_bug.cgi?id=1686651
14
Signed-off-by: Max Reitz <mreitz@redhat.com>
15
Message-id: 20190516161114.27596-1-mreitz@redhat.com
16
Reviewed-by: John Snow <jsnow@redhat.com>
17
[mreitz: Adjusted commit message as per John's proposal]
18
Signed-off-by: Max Reitz <mreitz@redhat.com>
19
---
20
tests/qemu-iotests/219 | 13 ++++++++++---
21
1 file changed, 10 insertions(+), 3 deletions(-)
22
23
diff --git a/tests/qemu-iotests/219 b/tests/qemu-iotests/219
24
index XXXXXXX..XXXXXXX 100755
25
--- a/tests/qemu-iotests/219
26
+++ b/tests/qemu-iotests/219
27
@@ -XXX,XX +XXX,XX @@ import iotests
28
29
iotests.verify_image_format(supported_fmts=['qcow2'])
30
31
+img_size = 4 * 1024 * 1024
32
+
33
def pause_wait(vm, job_id):
34
with iotests.Timeout(3, "Timeout waiting for job to pause"):
35
while True:
36
@@ -XXX,XX +XXX,XX @@ def test_pause_resume(vm):
37
iotests.log(vm.qmp('query-jobs'))
38
39
def test_job_lifecycle(vm, job, job_args, has_ready=False):
40
+ global img_size
41
+
42
iotests.log('')
43
iotests.log('')
44
iotests.log('Starting block job: %s (auto-finalize: %s; auto-dismiss: %s)' %
45
@@ -XXX,XX +XXX,XX @@ def test_job_lifecycle(vm, job, job_args, has_ready=False):
46
iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
47
iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
48
49
+ # Wait for total-progress to stabilize
50
+ while vm.qmp('query-jobs')['return'][0]['total-progress'] < img_size:
51
+ pass
52
+
53
# RUNNING state:
54
# pause/resume should work, complete/finalize/dismiss should error out
55
iotests.log('')
56
@@ -XXX,XX +XXX,XX @@ with iotests.FilePath('disk.img') as disk_path, \
57
iotests.FilePath('copy.img') as copy_path, \
58
iotests.VM() as vm:
59
60
- img_size = '4M'
61
- iotests.qemu_img_create('-f', iotests.imgfmt, disk_path, img_size)
62
- iotests.qemu_io('-c', 'write 0 %s' % (img_size),
63
+ iotests.qemu_img_create('-f', iotests.imgfmt, disk_path, str(img_size))
64
+ iotests.qemu_io('-c', 'write 0 %i' % (img_size),
65
'-f', iotests.imgfmt, disk_path)
66
67
iotests.log('Launching VM...')
68
--
69
2.21.0
70
71
diff view generated by jsdifflib
Deleted patch
1
From: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com>
2
1
3
The uninitialized memory allocated for the command FIFO of the
4
floppy controller during the VM hardware initialization incurs
5
many unwanted reports by Valgrind when VM state is being saved.
6
That verbosity hardens a search for the real memory issues when
7
the iotests run. Particularly, the patch eliminates 20 unnecessary
8
reports of the Valgrind tool in the iotest #169.
9
10
Signed-off-by: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com>
11
Message-id: 1559154027-282547-1-git-send-email-andrey.shinkevich@virtuozzo.com
12
Reviewed-by: John Snow <jsnow@redhat.com>
13
Signed-off-by: Max Reitz <mreitz@redhat.com>
14
---
15
hw/block/fdc.c | 1 +
16
1 file changed, 1 insertion(+)
17
18
diff --git a/hw/block/fdc.c b/hw/block/fdc.c
19
index XXXXXXX..XXXXXXX 100644
20
--- a/hw/block/fdc.c
21
+++ b/hw/block/fdc.c
22
@@ -XXX,XX +XXX,XX @@ static void fdctrl_realize_common(DeviceState *dev, FDCtrl *fdctrl,
23
24
FLOPPY_DPRINTF("init controller\n");
25
fdctrl->fifo = qemu_memalign(512, FD_SECTOR_LEN);
26
+ memset(fdctrl->fifo, 0, FD_SECTOR_LEN);
27
fdctrl->fifo_size = 512;
28
fdctrl->result_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
29
fdctrl_result_timer, fdctrl);
30
--
31
2.21.0
32
33
diff view generated by jsdifflib
Deleted patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
1
3
Test fails at least for qcow, because of different cluster sizes in
4
base and top (and therefore different granularities of bitmaps we are
5
trying to merge).
6
7
The test aim is to check block-dirty-bitmap-merge between different
8
nodes functionality, no needs to check all formats. So, let's just drop
9
support for anything except qcow2.
10
11
Reported-by: Max Reitz <mreitz@redhat.com>
12
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
13
Message-id: 20190605155405.104384-1-vsementsov@virtuozzo.com
14
Signed-off-by: Max Reitz <mreitz@redhat.com>
15
---
16
tests/qemu-iotests/254 | 2 ++
17
1 file changed, 2 insertions(+)
18
19
diff --git a/tests/qemu-iotests/254 b/tests/qemu-iotests/254
20
index XXXXXXX..XXXXXXX 100755
21
--- a/tests/qemu-iotests/254
22
+++ b/tests/qemu-iotests/254
23
@@ -XXX,XX +XXX,XX @@
24
import iotests
25
from iotests import qemu_img_create, file_path, log
26
27
+iotests.verify_image_format(supported_fmts=['qcow2'])
28
+
29
disk, top = file_path('disk', 'top')
30
size = 1024 * 1024
31
32
--
33
2.21.0
34
35
diff view generated by jsdifflib
Deleted patch
1
img_rebase() can leak a QDict in two occasions. Fix it.
2
1
3
Coverity: CID 1401416
4
Fixes: d16699b64671466b42079c45b89127aeea1ca565
5
Fixes: 330c72957196e0ae382abcaa97ebf4eb9bc8574f
6
Signed-off-by: Max Reitz <mreitz@redhat.com>
7
Message-id: 20190528195338.12376-1-mreitz@redhat.com
8
Reviewed-by: John Snow <jsnow@redhat.com>
9
Signed-off-by: Max Reitz <mreitz@redhat.com>
10
---
11
qemu-img.c | 3 +++
12
1 file changed, 3 insertions(+)
13
14
diff --git a/qemu-img.c b/qemu-img.c
15
index XXXXXXX..XXXXXXX 100644
16
--- a/qemu-img.c
17
+++ b/qemu-img.c
18
@@ -XXX,XX +XXX,XX @@ static int img_rebase(int argc, char **argv)
19
out_baseimg,
20
&local_err);
21
if (local_err) {
22
+ qobject_unref(options);
23
error_reportf_err(local_err,
24
"Could not resolve backing filename: ");
25
ret = -1;
26
@@ -XXX,XX +XXX,XX @@ static int img_rebase(int argc, char **argv)
27
*/
28
prefix_chain_bs = bdrv_find_backing_image(bs, out_real_path);
29
if (prefix_chain_bs) {
30
+ qobject_unref(options);
31
g_free(out_real_path);
32
+
33
blk_new_backing = blk_new(qemu_get_aio_context(),
34
BLK_PERM_CONSISTENT_READ,
35
BLK_PERM_ALL);
36
--
37
2.21.0
38
39
diff view generated by jsdifflib
Deleted patch
1
A snapshot is something that reflects the state of something at a
2
certain point in time. It does not change.
3
1
4
The file our snapshot commands create (or the node they install) is not
5
a snapshot, as it does change over time. It is an overlay. We cannot
6
do anything about the parameter names, but we can at least adjust the
7
descriptions to reflect that fact.
8
9
Signed-off-by: Max Reitz <mreitz@redhat.com>
10
Reviewed-by: Eric Blake <eblake@redhat.com>
11
Message-id: 20190603202236.1342-2-mreitz@redhat.com
12
Reviewed-by: John Snow <jsnow@redhat.com>
13
Reviewed-by: Alberto Garcia <berto@igalia.com>
14
Signed-off-by: Max Reitz <mreitz@redhat.com>
15
---
16
qapi/block-core.json | 20 ++++++++++----------
17
1 file changed, 10 insertions(+), 10 deletions(-)
18
19
diff --git a/qapi/block-core.json b/qapi/block-core.json
20
index XXXXXXX..XXXXXXX 100644
21
--- a/qapi/block-core.json
22
+++ b/qapi/block-core.json
23
@@ -XXX,XX +XXX,XX @@
24
#
25
# Either @device or @node-name must be set but not both.
26
#
27
-# @device: the name of the device to generate the snapshot from.
28
+# @device: the name of the device to take a snapshot of.
29
#
30
# @node-name: graph node name to generate the snapshot from (Since 2.0)
31
#
32
-# @snapshot-file: the target of the new image. If the file exists, or
33
-# if it is a device, the snapshot will be created in the existing
34
-# file/device. Otherwise, a new file will be created.
35
+# @snapshot-file: the target of the new overlay image. If the file
36
+# exists, or if it is a device, the overlay will be created in the
37
+# existing file/device. Otherwise, a new file will be created.
38
#
39
# @snapshot-node-name: the graph node name of the new image (Since 2.0)
40
#
41
-# @format: the format of the snapshot image, default is 'qcow2'.
42
+# @format: the format of the overlay image, default is 'qcow2'.
43
#
44
# @mode: whether and how QEMU should create a new image, default is
45
# 'absolute-paths'.
46
@@ -XXX,XX +XXX,XX @@
47
##
48
# @BlockdevSnapshot:
49
#
50
-# @node: device or node name that will have a snapshot created.
51
+# @node: device or node name that will have a snapshot taken.
52
#
53
# @overlay: reference to the existing block device that will become
54
-# the overlay of @node, as part of creating the snapshot.
55
+# the overlay of @node, as part of taking the snapshot.
56
# It must not have a current backing file (this can be
57
# achieved by passing "backing": null to blockdev-add).
58
#
59
@@ -XXX,XX +XXX,XX @@
60
##
61
# @blockdev-snapshot-sync:
62
#
63
-# Generates a synchronous snapshot of a block device.
64
+# Takes a synchronous snapshot of a block device.
65
#
66
# For the arguments, see the documentation of BlockdevSnapshotSync.
67
#
68
@@ -XXX,XX +XXX,XX @@
69
##
70
# @blockdev-snapshot:
71
#
72
-# Generates a snapshot of a block device.
73
+# Takes a snapshot of a block device.
74
#
75
-# Create a snapshot, by installing 'node' as the backing image of
76
+# Take a snapshot, by installing 'node' as the backing image of
77
# 'overlay'. Additionally, if 'node' is associated with a block
78
# device, the block device changes to using 'overlay' as its new active
79
# image.
80
--
81
2.21.0
82
83
diff view generated by jsdifflib
1
This new error option allows users of blkdebug to inject errors only on
1
From: Sam Eiderman <shmuel.eiderman@oracle.com>
2
certain kinds of I/O operations. Users usually want to make a very
2
3
specific operation fail, not just any; but right now they simply hope
3
Until ESXi 6.5 VMware used the vmfsSparse format for snapshots (VMDK3 in
4
that the event that triggers the error injection is followed up with
4
QEMU).
5
that very operation. That may not be true, however, because the block
5
6
layer is changing (including blkdebug, which may increase the number of
6
This format was lacking in the following:
7
types of I/O operations on which to inject errors).
7
8
8
* Grain directory (L1) and grain table (L2) entries were 32-bit,
9
The new option's default has been chosen to keep backwards
9
allowing access to only 2TB (slightly less) of data.
10
compatibility.
10
* The grain size (default) was 512 bytes - leading to data
11
11
fragmentation and many grain tables.
12
Note that similar to the internal representation, we could choose to
12
* For space reclamation purposes, it was necessary to find all the
13
expose this option as a list of I/O types. But there is no practical
13
grains which are not pointed to by any grain table - so a reverse
14
use for this, because as described above, users usually know exactly
14
mapping of "offset of grain in vmdk" to "grain table" must be
15
which kind of operation they want to make fail, so there is no need to
15
constructed - which takes large amounts of CPU/RAM.
16
specify multiple I/O types at once. In addition, exposing this option
16
17
as a list would require non-trivial changes to qemu_opts_absorb_qdict().
17
The format specification can be found in VMware's documentation:
18
18
https://www.vmware.com/support/developer/vddk/vmdk_50_technote.pdf
19
Signed-off-by: Max Reitz <mreitz@redhat.com>
19
20
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
20
In ESXi 6.5, to support snapshot files larger than 2TB, a new format was
21
Message-id: 20190507203508.18026-4-mreitz@redhat.com
21
introduced: SESparse (Space Efficient).
22
23
This format fixes the above issues:
24
25
* All entries are now 64-bit.
26
* The grain size (default) is 4KB.
27
* Grain directory and grain tables are now located at the beginning
28
of the file.
29
+ seSparse format reserves space for all grain tables.
30
+ Grain tables can be addressed using an index.
31
+ Grains are located in the end of the file and can also be
32
addressed with an index.
33
- seSparse vmdks of large disks (64TB) have huge preallocated
34
headers - mainly due to L2 tables, even for empty snapshots.
35
* The header contains a reverse mapping ("backmap") of "offset of
36
grain in vmdk" to "grain table" and a bitmap ("free bitmap") which
37
specifies for each grain - whether it is allocated or not.
38
Using these data structures we can implement space reclamation
39
efficiently.
40
* Due to the fact that the header now maintains two mappings:
41
* The regular one (grain directory & grain tables)
42
* A reverse one (backmap and free bitmap)
43
These data structures can lose consistency upon crash and result
44
in a corrupted VMDK.
45
Therefore, a journal is also added to the VMDK and is replayed
46
when the VMware reopens the file after a crash.
47
48
Since ESXi 6.7 - SESparse is the only snapshot format available.
49
50
Unfortunately, VMware does not provide documentation regarding the new
51
seSparse format.
52
53
This commit is based on black-box research of the seSparse format.
54
Various in-guest block operations and their effect on the snapshot file
55
were tested.
56
57
The only VMware provided source of information (regarding the underlying
58
implementation) was a log file on the ESXi:
59
60
/var/log/hostd.log
61
62
Whenever an seSparse snapshot is created - the log is being populated
63
with seSparse records.
64
65
Relevant log records are of the form:
66
67
[...] Const Header:
68
[...] constMagic = 0xcafebabe
69
[...] version = 2.1
70
[...] capacity = 204800
71
[...] grainSize = 8
72
[...] grainTableSize = 64
73
[...] flags = 0
74
[...] Extents:
75
[...] Header : <1 : 1>
76
[...] JournalHdr : <2 : 2>
77
[...] Journal : <2048 : 2048>
78
[...] GrainDirectory : <4096 : 2048>
79
[...] GrainTables : <6144 : 2048>
80
[...] FreeBitmap : <8192 : 2048>
81
[...] BackMap : <10240 : 2048>
82
[...] Grain : <12288 : 204800>
83
[...] Volatile Header:
84
[...] volatileMagic = 0xcafecafe
85
[...] FreeGTNumber = 0
86
[...] nextTxnSeqNumber = 0
87
[...] replayJournal = 0
88
89
The sizes that are seen in the log file are in sectors.
90
Extents are of the following format: <offset : size>
91
92
This commit is a strict implementation which enforces:
93
* magics
94
* version number 2.1
95
* grain size of 8 sectors (4KB)
96
* grain table size of 64 sectors
97
* zero flags
98
* extent locations
99
100
Additionally, this commit proivdes only a subset of the functionality
101
offered by seSparse's format:
102
* Read-only
103
* No journal replay
104
* No space reclamation
105
* No unmap support
106
107
Hence, journal header, journal, free bitmap and backmap extents are
108
unused, only the "classic" (L1 -> L2 -> data) grain access is
109
implemented.
110
111
However there are several differences in the grain access itself.
112
Grain directory (L1):
113
* Grain directory entries are indexes (not offsets) to grain
114
tables.
115
* Valid grain directory entries have their highest nibble set to
116
0x1.
117
* Since grain tables are always located in the beginning of the
118
file - the index can fit into 32 bits - so we can use its low
119
part if it's valid.
120
Grain table (L2):
121
* Grain table entries are indexes (not offsets) to grains.
122
* If the highest nibble of the entry is:
123
0x0:
124
The grain in not allocated.
125
The rest of the bytes are 0.
126
0x1:
127
The grain is unmapped - guest sees a zero grain.
128
The rest of the bits point to the previously mapped grain,
129
see 0x3 case.
130
0x2:
131
The grain is zero.
132
0x3:
133
The grain is allocated - to get the index calculate:
134
((entry & 0x0fff000000000000) >> 48) |
135
((entry & 0x0000ffffffffffff) << 12)
136
* The difference between 0x1 and 0x2 is that 0x1 is an unallocated
137
grain which results from the guest using sg_unmap to unmap the
138
grain - but the grain itself still exists in the grain extent - a
139
space reclamation procedure should delete it.
140
Unmapping a zero grain has no effect (0x2 will not change to 0x1)
141
but unmapping an unallocated grain will (0x0 to 0x1) - naturally.
142
143
In order to implement seSparse some fields had to be changed to support
144
both 32-bit and 64-bit entry sizes.
145
146
Reviewed-by: Karl Heubaum <karl.heubaum@oracle.com>
147
Reviewed-by: Eyal Moscovici <eyal.moscovici@oracle.com>
148
Reviewed-by: Arbel Moshe <arbel.moshe@oracle.com>
149
Signed-off-by: Sam Eiderman <shmuel.eiderman@oracle.com>
150
Message-id: 20190620091057.47441-4-shmuel.eiderman@oracle.com
22
Signed-off-by: Max Reitz <mreitz@redhat.com>
151
Signed-off-by: Max Reitz <mreitz@redhat.com>
23
---
152
---
24
qapi/block-core.json | 26 +++++++++++++++++++++++
153
block/vmdk.c | 358 ++++++++++++++++++++++++++++++++++++++++++++++++---
25
block/blkdebug.c | 50 ++++++++++++++++++++++++++++++++++++--------
154
1 file changed, 342 insertions(+), 16 deletions(-)
26
2 files changed, 67 insertions(+), 9 deletions(-)
155
27
156
diff --git a/block/vmdk.c b/block/vmdk.c
28
diff --git a/qapi/block-core.json b/qapi/block-core.json
29
index XXXXXXX..XXXXXXX 100644
157
index XXXXXXX..XXXXXXX 100644
30
--- a/qapi/block-core.json
158
--- a/block/vmdk.c
31
+++ b/qapi/block-core.json
159
+++ b/block/vmdk.c
32
@@ -XXX,XX +XXX,XX @@
160
@@ -XXX,XX +XXX,XX @@ typedef struct {
33
'l1_shrink_write_table', 'l1_shrink_free_l2_clusters',
161
uint16_t compressAlgorithm;
34
'cor_write', 'cluster_alloc_space'] }
162
} QEMU_PACKED VMDK4Header;
35
163
36
+##
164
+typedef struct VMDKSESparseConstHeader {
37
+# @BlkdebugIOType:
165
+ uint64_t magic;
38
+#
166
+ uint64_t version;
39
+# Kinds of I/O that blkdebug can inject errors in.
167
+ uint64_t capacity;
40
+#
168
+ uint64_t grain_size;
41
+# @read: .bdrv_co_preadv()
169
+ uint64_t grain_table_size;
42
+#
170
+ uint64_t flags;
43
+# @write: .bdrv_co_pwritev()
171
+ uint64_t reserved1;
44
+#
172
+ uint64_t reserved2;
45
+# @write-zeroes: .bdrv_co_pwrite_zeroes()
173
+ uint64_t reserved3;
46
+#
174
+ uint64_t reserved4;
47
+# @discard: .bdrv_co_pdiscard()
175
+ uint64_t volatile_header_offset;
48
+#
176
+ uint64_t volatile_header_size;
49
+# @flush: .bdrv_co_flush_to_disk()
177
+ uint64_t journal_header_offset;
50
+#
178
+ uint64_t journal_header_size;
51
+# Since: 4.1
179
+ uint64_t journal_offset;
52
+##
180
+ uint64_t journal_size;
53
+{ 'enum': 'BlkdebugIOType', 'prefix': 'BLKDEBUG_IO_TYPE',
181
+ uint64_t grain_dir_offset;
54
+ 'data': [ 'read', 'write', 'write-zeroes', 'discard', 'flush' ] }
182
+ uint64_t grain_dir_size;
55
+
183
+ uint64_t grain_tables_offset;
56
##
184
+ uint64_t grain_tables_size;
57
# @BlkdebugInjectErrorOptions:
185
+ uint64_t free_bitmap_offset;
58
#
186
+ uint64_t free_bitmap_size;
59
@@ -XXX,XX +XXX,XX @@
187
+ uint64_t backmap_offset;
60
# @state: the state identifier blkdebug needs to be in to
188
+ uint64_t backmap_size;
61
# actually trigger the event; defaults to "any"
189
+ uint64_t grains_offset;
62
#
190
+ uint64_t grains_size;
63
+# @iotype: the type of I/O operations on which this error should
191
+ uint8_t pad[304];
64
+# be injected; defaults to "all read, write,
192
+} QEMU_PACKED VMDKSESparseConstHeader;
65
+# write-zeroes, discard, and flush operations"
193
+
66
+# (since: 4.1)
194
+typedef struct VMDKSESparseVolatileHeader {
67
+#
195
+ uint64_t magic;
68
# @errno: error identifier (errno) to be returned; defaults to
196
+ uint64_t free_gt_number;
69
# EIO
197
+ uint64_t next_txn_seq_number;
70
#
198
+ uint64_t replay_journal;
71
@@ -XXX,XX +XXX,XX @@
199
+ uint8_t pad[480];
72
{ 'struct': 'BlkdebugInjectErrorOptions',
200
+} QEMU_PACKED VMDKSESparseVolatileHeader;
73
'data': { 'event': 'BlkdebugEvent',
201
+
74
'*state': 'int',
202
#define L2_CACHE_SIZE 16
75
+ '*iotype': 'BlkdebugIOType',
203
76
'*errno': 'int',
204
typedef struct VmdkExtent {
77
'*sector': 'int',
205
@@ -XXX,XX +XXX,XX @@ typedef struct VmdkExtent {
78
'*once': 'bool',
206
bool compressed;
79
diff --git a/block/blkdebug.c b/block/blkdebug.c
207
bool has_marker;
80
index XXXXXXX..XXXXXXX 100644
208
bool has_zero_grain;
81
--- a/block/blkdebug.c
209
+ bool sesparse;
82
+++ b/block/blkdebug.c
210
+ uint64_t sesparse_l2_tables_offset;
83
@@ -XXX,XX +XXX,XX @@ typedef struct BlkdebugRule {
211
+ uint64_t sesparse_clusters_offset;
84
int state;
212
+ int32_t entry_size;
85
union {
213
int version;
86
struct {
214
int64_t sectors;
87
+ uint64_t iotype_mask;
215
int64_t end_sector;
88
int error;
216
int64_t flat_start_offset;
89
int immediately;
217
int64_t l1_table_offset;
90
int once;
218
int64_t l1_backup_table_offset;
91
@@ -XXX,XX +XXX,XX @@ typedef struct BlkdebugRule {
219
- uint32_t *l1_table;
92
QSIMPLEQ_ENTRY(BlkdebugRule) active_next;
220
+ void *l1_table;
93
} BlkdebugRule;
221
uint32_t *l1_backup_table;
94
222
unsigned int l1_size;
95
+QEMU_BUILD_BUG_MSG(BLKDEBUG_IO_TYPE__MAX > 64,
223
uint32_t l1_entry_sectors;
96
+ "BlkdebugIOType mask does not fit into an uint64_t");
224
97
+
225
unsigned int l2_size;
98
static QemuOptsList inject_error_opts = {
226
- uint32_t *l2_cache;
99
.name = "inject-error",
227
+ void *l2_cache;
100
.head = QTAILQ_HEAD_INITIALIZER(inject_error_opts.head),
228
uint32_t l2_cache_offsets[L2_CACHE_SIZE];
101
@@ -XXX,XX +XXX,XX @@ static QemuOptsList inject_error_opts = {
229
uint32_t l2_cache_counts[L2_CACHE_SIZE];
102
.name = "state",
230
103
.type = QEMU_OPT_NUMBER,
231
@@ -XXX,XX +XXX,XX @@ static int vmdk_add_extent(BlockDriverState *bs,
104
},
232
* minimal L2 table size: 512 entries
105
+ {
233
* 8 TB is still more than the maximal value supported for
106
+ .name = "iotype",
234
* VMDK3 & VMDK4 which is 2TB.
107
+ .type = QEMU_OPT_STRING,
235
+ * 64TB - for "ESXi seSparse Extent"
108
+ },
236
+ * minimal cluster size: 512B (default is 4KB)
109
{
237
+ * L2 table size: 4096 entries (const).
110
.name = "errno",
238
+ * 64TB is more than the maximal value supported for
111
.type = QEMU_OPT_NUMBER,
239
+ * seSparse VMDKs (which is slightly less than 64TB)
112
@@ -XXX,XX +XXX,XX @@ static int add_rule(void *opaque, QemuOpts *opts, Error **errp)
240
*/
113
int event;
241
error_setg(errp, "L1 size too big");
114
struct BlkdebugRule *rule;
242
return -EFBIG;
115
int64_t sector;
243
@@ -XXX,XX +XXX,XX @@ static int vmdk_add_extent(BlockDriverState *bs,
116
+ BlkdebugIOType iotype;
244
extent->l2_size = l2_size;
117
+ Error *local_error = NULL;
245
extent->cluster_sectors = flat ? sectors : cluster_sectors;
118
246
extent->next_cluster_sector = ROUND_UP(nb_sectors, cluster_sectors);
119
/* Find the right event for the rule */
247
+ extent->entry_size = sizeof(uint32_t);
120
event_name = qemu_opt_get(opts, "event");
248
121
@@ -XXX,XX +XXX,XX @@ static int add_rule(void *opaque, QemuOpts *opts, Error **errp)
249
if (s->num_extents > 1) {
122
sector = qemu_opt_get_number(opts, "sector", -1);
250
extent->end_sector = (*(extent - 1)).end_sector + extent->sectors;
123
rule->options.inject.offset =
251
@@ -XXX,XX +XXX,XX @@ static int vmdk_init_tables(BlockDriverState *bs, VmdkExtent *extent,
124
sector == -1 ? -1 : sector * BDRV_SECTOR_SIZE;
252
int i;
125
+
253
126
+ iotype = qapi_enum_parse(&BlkdebugIOType_lookup,
254
/* read the L1 table */
127
+ qemu_opt_get(opts, "iotype"),
255
- l1_size = extent->l1_size * sizeof(uint32_t);
128
+ BLKDEBUG_IO_TYPE__MAX, &local_error);
256
+ l1_size = extent->l1_size * extent->entry_size;
129
+ if (local_error) {
257
extent->l1_table = g_try_malloc(l1_size);
130
+ error_propagate(errp, local_error);
258
if (l1_size && extent->l1_table == NULL) {
131
+ return -1;
259
return -ENOMEM;
260
@@ -XXX,XX +XXX,XX @@ static int vmdk_init_tables(BlockDriverState *bs, VmdkExtent *extent,
261
goto fail_l1;
262
}
263
for (i = 0; i < extent->l1_size; i++) {
264
- le32_to_cpus(&extent->l1_table[i]);
265
+ if (extent->entry_size == sizeof(uint64_t)) {
266
+ le64_to_cpus((uint64_t *)extent->l1_table + i);
267
+ } else {
268
+ assert(extent->entry_size == sizeof(uint32_t));
269
+ le32_to_cpus((uint32_t *)extent->l1_table + i);
132
+ }
270
+ }
133
+ if (iotype != BLKDEBUG_IO_TYPE__MAX) {
271
}
134
+ rule->options.inject.iotype_mask = (1ull << iotype);
272
135
+ } else {
273
if (extent->l1_backup_table_offset) {
136
+ /* Apply the default */
274
+ assert(!extent->sesparse);
137
+ rule->options.inject.iotype_mask =
275
extent->l1_backup_table = g_try_malloc(l1_size);
138
+ (1ull << BLKDEBUG_IO_TYPE_READ)
276
if (l1_size && extent->l1_backup_table == NULL) {
139
+ | (1ull << BLKDEBUG_IO_TYPE_WRITE)
277
ret = -ENOMEM;
140
+ | (1ull << BLKDEBUG_IO_TYPE_WRITE_ZEROES)
278
@@ -XXX,XX +XXX,XX @@ static int vmdk_init_tables(BlockDriverState *bs, VmdkExtent *extent,
141
+ | (1ull << BLKDEBUG_IO_TYPE_DISCARD)
279
}
142
+ | (1ull << BLKDEBUG_IO_TYPE_FLUSH);
280
143
+ }
281
extent->l2_cache =
144
+
282
- g_new(uint32_t, extent->l2_size * L2_CACHE_SIZE);
145
break;
283
+ g_malloc(extent->entry_size * extent->l2_size * L2_CACHE_SIZE);
146
284
return 0;
147
case ACTION_SET_STATE:
285
fail_l1b:
148
@@ -XXX,XX +XXX,XX @@ out:
286
g_free(extent->l1_backup_table);
287
@@ -XXX,XX +XXX,XX @@ static int vmdk_open_vmfs_sparse(BlockDriverState *bs,
149
return ret;
288
return ret;
150
}
289
}
151
290
152
-static int rule_check(BlockDriverState *bs, uint64_t offset, uint64_t bytes)
291
+#define SESPARSE_CONST_HEADER_MAGIC UINT64_C(0x00000000cafebabe)
153
+static int rule_check(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
292
+#define SESPARSE_VOLATILE_HEADER_MAGIC UINT64_C(0x00000000cafecafe)
154
+ BlkdebugIOType iotype)
293
+
294
+/* Strict checks - format not officially documented */
295
+static int check_se_sparse_const_header(VMDKSESparseConstHeader *header,
296
+ Error **errp)
297
+{
298
+ header->magic = le64_to_cpu(header->magic);
299
+ header->version = le64_to_cpu(header->version);
300
+ header->grain_size = le64_to_cpu(header->grain_size);
301
+ header->grain_table_size = le64_to_cpu(header->grain_table_size);
302
+ header->flags = le64_to_cpu(header->flags);
303
+ header->reserved1 = le64_to_cpu(header->reserved1);
304
+ header->reserved2 = le64_to_cpu(header->reserved2);
305
+ header->reserved3 = le64_to_cpu(header->reserved3);
306
+ header->reserved4 = le64_to_cpu(header->reserved4);
307
+
308
+ header->volatile_header_offset =
309
+ le64_to_cpu(header->volatile_header_offset);
310
+ header->volatile_header_size = le64_to_cpu(header->volatile_header_size);
311
+
312
+ header->journal_header_offset = le64_to_cpu(header->journal_header_offset);
313
+ header->journal_header_size = le64_to_cpu(header->journal_header_size);
314
+
315
+ header->journal_offset = le64_to_cpu(header->journal_offset);
316
+ header->journal_size = le64_to_cpu(header->journal_size);
317
+
318
+ header->grain_dir_offset = le64_to_cpu(header->grain_dir_offset);
319
+ header->grain_dir_size = le64_to_cpu(header->grain_dir_size);
320
+
321
+ header->grain_tables_offset = le64_to_cpu(header->grain_tables_offset);
322
+ header->grain_tables_size = le64_to_cpu(header->grain_tables_size);
323
+
324
+ header->free_bitmap_offset = le64_to_cpu(header->free_bitmap_offset);
325
+ header->free_bitmap_size = le64_to_cpu(header->free_bitmap_size);
326
+
327
+ header->backmap_offset = le64_to_cpu(header->backmap_offset);
328
+ header->backmap_size = le64_to_cpu(header->backmap_size);
329
+
330
+ header->grains_offset = le64_to_cpu(header->grains_offset);
331
+ header->grains_size = le64_to_cpu(header->grains_size);
332
+
333
+ if (header->magic != SESPARSE_CONST_HEADER_MAGIC) {
334
+ error_setg(errp, "Bad const header magic: 0x%016" PRIx64,
335
+ header->magic);
336
+ return -EINVAL;
337
+ }
338
+
339
+ if (header->version != 0x0000000200000001) {
340
+ error_setg(errp, "Unsupported version: 0x%016" PRIx64,
341
+ header->version);
342
+ return -ENOTSUP;
343
+ }
344
+
345
+ if (header->grain_size != 8) {
346
+ error_setg(errp, "Unsupported grain size: %" PRIu64,
347
+ header->grain_size);
348
+ return -ENOTSUP;
349
+ }
350
+
351
+ if (header->grain_table_size != 64) {
352
+ error_setg(errp, "Unsupported grain table size: %" PRIu64,
353
+ header->grain_table_size);
354
+ return -ENOTSUP;
355
+ }
356
+
357
+ if (header->flags != 0) {
358
+ error_setg(errp, "Unsupported flags: 0x%016" PRIx64,
359
+ header->flags);
360
+ return -ENOTSUP;
361
+ }
362
+
363
+ if (header->reserved1 != 0 || header->reserved2 != 0 ||
364
+ header->reserved3 != 0 || header->reserved4 != 0) {
365
+ error_setg(errp, "Unsupported reserved bits:"
366
+ " 0x%016" PRIx64 " 0x%016" PRIx64
367
+ " 0x%016" PRIx64 " 0x%016" PRIx64,
368
+ header->reserved1, header->reserved2,
369
+ header->reserved3, header->reserved4);
370
+ return -ENOTSUP;
371
+ }
372
+
373
+ /* check that padding is 0 */
374
+ if (!buffer_is_zero(header->pad, sizeof(header->pad))) {
375
+ error_setg(errp, "Unsupported non-zero const header padding");
376
+ return -ENOTSUP;
377
+ }
378
+
379
+ return 0;
380
+}
381
+
382
+static int check_se_sparse_volatile_header(VMDKSESparseVolatileHeader *header,
383
+ Error **errp)
384
+{
385
+ header->magic = le64_to_cpu(header->magic);
386
+ header->free_gt_number = le64_to_cpu(header->free_gt_number);
387
+ header->next_txn_seq_number = le64_to_cpu(header->next_txn_seq_number);
388
+ header->replay_journal = le64_to_cpu(header->replay_journal);
389
+
390
+ if (header->magic != SESPARSE_VOLATILE_HEADER_MAGIC) {
391
+ error_setg(errp, "Bad volatile header magic: 0x%016" PRIx64,
392
+ header->magic);
393
+ return -EINVAL;
394
+ }
395
+
396
+ if (header->replay_journal) {
397
+ error_setg(errp, "Image is dirty, Replaying journal not supported");
398
+ return -ENOTSUP;
399
+ }
400
+
401
+ /* check that padding is 0 */
402
+ if (!buffer_is_zero(header->pad, sizeof(header->pad))) {
403
+ error_setg(errp, "Unsupported non-zero volatile header padding");
404
+ return -ENOTSUP;
405
+ }
406
+
407
+ return 0;
408
+}
409
+
410
+static int vmdk_open_se_sparse(BlockDriverState *bs,
411
+ BdrvChild *file,
412
+ int flags, Error **errp)
413
+{
414
+ int ret;
415
+ VMDKSESparseConstHeader const_header;
416
+ VMDKSESparseVolatileHeader volatile_header;
417
+ VmdkExtent *extent;
418
+
419
+ ret = bdrv_apply_auto_read_only(bs,
420
+ "No write support for seSparse images available", errp);
421
+ if (ret < 0) {
422
+ return ret;
423
+ }
424
+
425
+ assert(sizeof(const_header) == SECTOR_SIZE);
426
+
427
+ ret = bdrv_pread(file, 0, &const_header, sizeof(const_header));
428
+ if (ret < 0) {
429
+ bdrv_refresh_filename(file->bs);
430
+ error_setg_errno(errp, -ret,
431
+ "Could not read const header from file '%s'",
432
+ file->bs->filename);
433
+ return ret;
434
+ }
435
+
436
+ /* check const header */
437
+ ret = check_se_sparse_const_header(&const_header, errp);
438
+ if (ret < 0) {
439
+ return ret;
440
+ }
441
+
442
+ assert(sizeof(volatile_header) == SECTOR_SIZE);
443
+
444
+ ret = bdrv_pread(file,
445
+ const_header.volatile_header_offset * SECTOR_SIZE,
446
+ &volatile_header, sizeof(volatile_header));
447
+ if (ret < 0) {
448
+ bdrv_refresh_filename(file->bs);
449
+ error_setg_errno(errp, -ret,
450
+ "Could not read volatile header from file '%s'",
451
+ file->bs->filename);
452
+ return ret;
453
+ }
454
+
455
+ /* check volatile header */
456
+ ret = check_se_sparse_volatile_header(&volatile_header, errp);
457
+ if (ret < 0) {
458
+ return ret;
459
+ }
460
+
461
+ ret = vmdk_add_extent(bs, file, false,
462
+ const_header.capacity,
463
+ const_header.grain_dir_offset * SECTOR_SIZE,
464
+ 0,
465
+ const_header.grain_dir_size *
466
+ SECTOR_SIZE / sizeof(uint64_t),
467
+ const_header.grain_table_size *
468
+ SECTOR_SIZE / sizeof(uint64_t),
469
+ const_header.grain_size,
470
+ &extent,
471
+ errp);
472
+ if (ret < 0) {
473
+ return ret;
474
+ }
475
+
476
+ extent->sesparse = true;
477
+ extent->sesparse_l2_tables_offset = const_header.grain_tables_offset;
478
+ extent->sesparse_clusters_offset = const_header.grains_offset;
479
+ extent->entry_size = sizeof(uint64_t);
480
+
481
+ ret = vmdk_init_tables(bs, extent, errp);
482
+ if (ret) {
483
+ /* free extent allocated by vmdk_add_extent */
484
+ vmdk_free_last_extent(bs);
485
+ }
486
+
487
+ return ret;
488
+}
489
+
490
static int vmdk_open_desc_file(BlockDriverState *bs, int flags, char *buf,
491
QDict *options, Error **errp);
492
493
@@ -XXX,XX +XXX,XX @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
494
* RW [size in sectors] SPARSE "file-name.vmdk"
495
* RW [size in sectors] VMFS "file-name.vmdk"
496
* RW [size in sectors] VMFSSPARSE "file-name.vmdk"
497
+ * RW [size in sectors] SESPARSE "file-name.vmdk"
498
*/
499
flat_offset = -1;
500
matches = sscanf(p, "%10s %" SCNd64 " %10s \"%511[^\n\r\"]\" %" SCNd64,
501
@@ -XXX,XX +XXX,XX @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
502
503
if (sectors <= 0 ||
504
(strcmp(type, "FLAT") && strcmp(type, "SPARSE") &&
505
- strcmp(type, "VMFS") && strcmp(type, "VMFSSPARSE")) ||
506
+ strcmp(type, "VMFS") && strcmp(type, "VMFSSPARSE") &&
507
+ strcmp(type, "SESPARSE")) ||
508
(strcmp(access, "RW"))) {
509
continue;
510
}
511
@@ -XXX,XX +XXX,XX @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
512
return ret;
513
}
514
extent = &s->extents[s->num_extents - 1];
515
+ } else if (!strcmp(type, "SESPARSE")) {
516
+ ret = vmdk_open_se_sparse(bs, extent_file, bs->open_flags, errp);
517
+ if (ret) {
518
+ bdrv_unref_child(bs, extent_file);
519
+ return ret;
520
+ }
521
+ extent = &s->extents[s->num_extents - 1];
522
} else {
523
error_setg(errp, "Unsupported extent type '%s'", type);
524
bdrv_unref_child(bs, extent_file);
525
@@ -XXX,XX +XXX,XX @@ static int vmdk_open_desc_file(BlockDriverState *bs, int flags, char *buf,
526
if (strcmp(ct, "monolithicFlat") &&
527
strcmp(ct, "vmfs") &&
528
strcmp(ct, "vmfsSparse") &&
529
+ strcmp(ct, "seSparse") &&
530
strcmp(ct, "twoGbMaxExtentSparse") &&
531
strcmp(ct, "twoGbMaxExtentFlat")) {
532
error_setg(errp, "Unsupported image type '%s'", ct);
533
@@ -XXX,XX +XXX,XX @@ static int get_cluster_offset(BlockDriverState *bs,
155
{
534
{
156
BDRVBlkdebugState *s = bs->opaque;
535
unsigned int l1_index, l2_offset, l2_index;
157
BlkdebugRule *rule = NULL;
536
int min_index, i, j;
158
@@ -XXX,XX +XXX,XX @@ static int rule_check(BlockDriverState *bs, uint64_t offset, uint64_t bytes)
537
- uint32_t min_count, *l2_table;
159
QSIMPLEQ_FOREACH(rule, &s->active_rules, active_next) {
538
+ uint32_t min_count;
160
uint64_t inject_offset = rule->options.inject.offset;
539
+ void *l2_table;
161
540
bool zeroed = false;
162
- if (inject_offset == -1 ||
541
int64_t ret;
163
- (bytes && inject_offset >= offset &&
542
int64_t cluster_sector;
164
- inject_offset < offset + bytes))
543
+ unsigned int l2_size_bytes = extent->l2_size * extent->entry_size;
165
+ if ((inject_offset == -1 ||
544
166
+ (bytes && inject_offset >= offset &&
545
if (m_data) {
167
+ inject_offset < offset + bytes)) &&
546
m_data->valid = 0;
168
+ (rule->options.inject.iotype_mask & (1ull << iotype)))
547
@@ -XXX,XX +XXX,XX @@ static int get_cluster_offset(BlockDriverState *bs,
169
{
548
if (l1_index >= extent->l1_size) {
170
break;
549
return VMDK_ERROR;
550
}
551
- l2_offset = extent->l1_table[l1_index];
552
+ if (extent->sesparse) {
553
+ uint64_t l2_offset_u64;
554
+
555
+ assert(extent->entry_size == sizeof(uint64_t));
556
+
557
+ l2_offset_u64 = ((uint64_t *)extent->l1_table)[l1_index];
558
+ if (l2_offset_u64 == 0) {
559
+ l2_offset = 0;
560
+ } else if ((l2_offset_u64 & 0xffffffff00000000) != 0x1000000000000000) {
561
+ /*
562
+ * Top most nibble is 0x1 if grain table is allocated.
563
+ * strict check - top most 4 bytes must be 0x10000000 since max
564
+ * supported size is 64TB for disk - so no more than 64TB / 16MB
565
+ * grain directories which is smaller than uint32,
566
+ * where 16MB is the only supported default grain table coverage.
567
+ */
568
+ return VMDK_ERROR;
569
+ } else {
570
+ l2_offset_u64 = l2_offset_u64 & 0x00000000ffffffff;
571
+ l2_offset_u64 = extent->sesparse_l2_tables_offset +
572
+ l2_offset_u64 * l2_size_bytes / SECTOR_SIZE;
573
+ if (l2_offset_u64 > 0x00000000ffffffff) {
574
+ return VMDK_ERROR;
575
+ }
576
+ l2_offset = (unsigned int)(l2_offset_u64);
577
+ }
578
+ } else {
579
+ assert(extent->entry_size == sizeof(uint32_t));
580
+ l2_offset = ((uint32_t *)extent->l1_table)[l1_index];
581
+ }
582
if (!l2_offset) {
583
return VMDK_UNALLOC;
584
}
585
@@ -XXX,XX +XXX,XX @@ static int get_cluster_offset(BlockDriverState *bs,
586
extent->l2_cache_counts[j] >>= 1;
587
}
588
}
589
- l2_table = extent->l2_cache + (i * extent->l2_size);
590
+ l2_table = (char *)extent->l2_cache + (i * l2_size_bytes);
591
goto found;
171
}
592
}
172
@@ -XXX,XX +XXX,XX @@ blkdebug_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
593
}
173
assert(bytes <= bs->bl.max_transfer);
594
@@ -XXX,XX +XXX,XX @@ static int get_cluster_offset(BlockDriverState *bs,
174
}
595
min_index = i;
175
596
}
176
- err = rule_check(bs, offset, bytes);
597
}
177
+ err = rule_check(bs, offset, bytes, BLKDEBUG_IO_TYPE_READ);
598
- l2_table = extent->l2_cache + (min_index * extent->l2_size);
178
if (err) {
599
+ l2_table = (char *)extent->l2_cache + (min_index * l2_size_bytes);
179
return err;
600
BLKDBG_EVENT(extent->file, BLKDBG_L2_LOAD);
180
}
601
if (bdrv_pread(extent->file,
181
@@ -XXX,XX +XXX,XX @@ blkdebug_co_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
602
(int64_t)l2_offset * 512,
182
assert(bytes <= bs->bl.max_transfer);
603
l2_table,
183
}
604
- extent->l2_size * sizeof(uint32_t)
184
605
- ) != extent->l2_size * sizeof(uint32_t)) {
185
- err = rule_check(bs, offset, bytes);
606
+ l2_size_bytes
186
+ err = rule_check(bs, offset, bytes, BLKDEBUG_IO_TYPE_WRITE);
607
+ ) != l2_size_bytes) {
187
if (err) {
608
return VMDK_ERROR;
188
return err;
609
}
189
}
610
190
@@ -XXX,XX +XXX,XX @@ blkdebug_co_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
611
@@ -XXX,XX +XXX,XX @@ static int get_cluster_offset(BlockDriverState *bs,
191
612
extent->l2_cache_counts[min_index] = 1;
192
static int blkdebug_co_flush(BlockDriverState *bs)
613
found:
193
{
614
l2_index = ((offset >> 9) / extent->cluster_sectors) % extent->l2_size;
194
- int err = rule_check(bs, 0, 0);
615
- cluster_sector = le32_to_cpu(l2_table[l2_index]);
195
+ int err = rule_check(bs, 0, 0, BLKDEBUG_IO_TYPE_FLUSH);
616
196
617
- if (extent->has_zero_grain && cluster_sector == VMDK_GTE_ZEROED) {
197
if (err) {
618
- zeroed = true;
198
return err;
619
+ if (extent->sesparse) {
199
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn blkdebug_co_pwrite_zeroes(BlockDriverState *bs,
620
+ cluster_sector = le64_to_cpu(((uint64_t *)l2_table)[l2_index]);
200
assert(bytes <= bs->bl.max_pwrite_zeroes);
621
+ switch (cluster_sector & 0xf000000000000000) {
201
}
622
+ case 0x0000000000000000:
202
623
+ /* unallocated grain */
203
- err = rule_check(bs, offset, bytes);
624
+ if (cluster_sector != 0) {
204
+ err = rule_check(bs, offset, bytes, BLKDEBUG_IO_TYPE_WRITE_ZEROES);
625
+ return VMDK_ERROR;
205
if (err) {
626
+ }
206
return err;
627
+ break;
207
}
628
+ case 0x1000000000000000:
208
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn blkdebug_co_pdiscard(BlockDriverState *bs,
629
+ /* scsi-unmapped grain - fallthrough */
209
assert(bytes <= bs->bl.max_pdiscard);
630
+ case 0x2000000000000000:
210
}
631
+ /* zero grain */
211
632
+ zeroed = true;
212
- err = rule_check(bs, offset, bytes);
633
+ break;
213
+ err = rule_check(bs, offset, bytes, BLKDEBUG_IO_TYPE_DISCARD);
634
+ case 0x3000000000000000:
214
if (err) {
635
+ /* allocated grain */
215
return err;
636
+ cluster_sector = (((cluster_sector & 0x0fff000000000000) >> 48) |
216
}
637
+ ((cluster_sector & 0x0000ffffffffffff) << 12));
638
+ cluster_sector = extent->sesparse_clusters_offset +
639
+ cluster_sector * extent->cluster_sectors;
640
+ break;
641
+ default:
642
+ return VMDK_ERROR;
643
+ }
644
+ } else {
645
+ cluster_sector = le32_to_cpu(((uint32_t *)l2_table)[l2_index]);
646
+
647
+ if (extent->has_zero_grain && cluster_sector == VMDK_GTE_ZEROED) {
648
+ zeroed = true;
649
+ }
650
}
651
652
if (!cluster_sector || zeroed) {
653
if (!allocate) {
654
return zeroed ? VMDK_ZEROED : VMDK_UNALLOC;
655
}
656
+ assert(!extent->sesparse);
657
658
if (extent->next_cluster_sector >= VMDK_EXTENT_MAX_SECTORS) {
659
return VMDK_ERROR;
660
@@ -XXX,XX +XXX,XX @@ static int get_cluster_offset(BlockDriverState *bs,
661
m_data->l1_index = l1_index;
662
m_data->l2_index = l2_index;
663
m_data->l2_offset = l2_offset;
664
- m_data->l2_cache_entry = &l2_table[l2_index];
665
+ m_data->l2_cache_entry = ((uint32_t *)l2_table) + l2_index;
666
}
667
}
668
*cluster_offset = cluster_sector << BDRV_SECTOR_BITS;
669
@@ -XXX,XX +XXX,XX @@ static int vmdk_pwritev(BlockDriverState *bs, uint64_t offset,
670
if (!extent) {
671
return -EIO;
672
}
673
+ if (extent->sesparse) {
674
+ return -ENOTSUP;
675
+ }
676
offset_in_cluster = vmdk_find_offset_in_cluster(extent, offset);
677
n_bytes = MIN(bytes, extent->cluster_sectors * BDRV_SECTOR_SIZE
678
- offset_in_cluster);
217
--
679
--
218
2.21.0
680
2.21.0
219
681
220
682
diff view generated by jsdifflib
1
There are error messages which refer to an overlay node as the snapshot.
1
From: Pino Toscano <ptoscano@redhat.com>
2
That is wrong, those are two different things.
3
2
4
Signed-off-by: Max Reitz <mreitz@redhat.com>
3
Rewrite the implementation of the ssh block driver to use libssh instead
5
Reviewed-by: Eric Blake <eblake@redhat.com>
4
of libssh2. The libssh library has various advantages over libssh2:
6
Message-id: 20190603202236.1342-3-mreitz@redhat.com
5
- easier API for authentication (for example for using ssh-agent)
7
Reviewed-by: John Snow <jsnow@redhat.com>
6
- easier API for known_hosts handling
8
Reviewed-by: Alberto Garcia <berto@igalia.com>
7
- supports newer types of keys in known_hosts
8
9
Use APIs/features available in libssh 0.8 conditionally, to support
10
older versions (which are not recommended though).
11
12
Adjust the iotest 207 according to the different error message, and to
13
find the default key type for localhost (to properly compare the
14
fingerprint with).
15
Contributed-by: Max Reitz <mreitz@redhat.com>
16
17
Adjust the various Docker/Travis scripts to use libssh when available
18
instead of libssh2. The mingw/mxe testing is dropped for now, as there
19
are no packages for it.
20
21
Signed-off-by: Pino Toscano <ptoscano@redhat.com>
22
Tested-by: Philippe Mathieu-Daudé <philmd@redhat.com>
23
Acked-by: Alex Bennée <alex.bennee@linaro.org>
24
Message-id: 20190620200840.17655-1-ptoscano@redhat.com
25
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
26
Message-id: 5873173.t2JhDm7DL7@lindworm.usersys.redhat.com
9
Signed-off-by: Max Reitz <mreitz@redhat.com>
27
Signed-off-by: Max Reitz <mreitz@redhat.com>
10
---
28
---
11
blockdev.c | 10 +++++-----
29
configure | 65 +-
12
tests/qemu-iotests/085.out | 10 +++++-----
30
block/Makefile.objs | 6 +-
13
2 files changed, 10 insertions(+), 10 deletions(-)
31
block/ssh.c | 652 ++++++++++--------
32
.travis.yml | 4 +-
33
block/trace-events | 14 +-
34
docs/qemu-block-drivers.texi | 2 +-
35
.../dockerfiles/debian-win32-cross.docker | 1 -
36
.../dockerfiles/debian-win64-cross.docker | 1 -
37
tests/docker/dockerfiles/fedora.docker | 4 +-
38
tests/docker/dockerfiles/ubuntu.docker | 2 +-
39
tests/docker/dockerfiles/ubuntu1804.docker | 2 +-
40
tests/qemu-iotests/207 | 54 +-
41
tests/qemu-iotests/207.out | 2 +-
42
13 files changed, 449 insertions(+), 360 deletions(-)
14
43
15
diff --git a/blockdev.c b/blockdev.c
44
diff --git a/configure b/configure
45
index XXXXXXX..XXXXXXX 100755
46
--- a/configure
47
+++ b/configure
48
@@ -XXX,XX +XXX,XX @@ auth_pam=""
49
vte=""
50
virglrenderer=""
51
tpm=""
52
-libssh2=""
53
+libssh=""
54
live_block_migration="yes"
55
numa=""
56
tcmalloc="no"
57
@@ -XXX,XX +XXX,XX @@ for opt do
58
;;
59
--enable-tpm) tpm="yes"
60
;;
61
- --disable-libssh2) libssh2="no"
62
+ --disable-libssh) libssh="no"
63
;;
64
- --enable-libssh2) libssh2="yes"
65
+ --enable-libssh) libssh="yes"
66
;;
67
--disable-live-block-migration) live_block_migration="no"
68
;;
69
@@ -XXX,XX +XXX,XX @@ disabled with --disable-FEATURE, default is enabled if available:
70
coroutine-pool coroutine freelist (better performance)
71
glusterfs GlusterFS backend
72
tpm TPM support
73
- libssh2 ssh block device support
74
+ libssh ssh block device support
75
numa libnuma support
76
libxml2 for Parallels image format
77
tcmalloc tcmalloc support
78
@@ -XXX,XX +XXX,XX @@ EOF
79
fi
80
81
##########################################
82
-# libssh2 probe
83
-min_libssh2_version=1.2.8
84
-if test "$libssh2" != "no" ; then
85
- if $pkg_config --atleast-version=$min_libssh2_version libssh2; then
86
- libssh2_cflags=$($pkg_config libssh2 --cflags)
87
- libssh2_libs=$($pkg_config libssh2 --libs)
88
- libssh2=yes
89
+# libssh probe
90
+if test "$libssh" != "no" ; then
91
+ if $pkg_config --exists libssh; then
92
+ libssh_cflags=$($pkg_config libssh --cflags)
93
+ libssh_libs=$($pkg_config libssh --libs)
94
+ libssh=yes
95
else
96
- if test "$libssh2" = "yes" ; then
97
- error_exit "libssh2 >= $min_libssh2_version required for --enable-libssh2"
98
+ if test "$libssh" = "yes" ; then
99
+ error_exit "libssh required for --enable-libssh"
100
fi
101
- libssh2=no
102
+ libssh=no
103
fi
104
fi
105
106
##########################################
107
-# libssh2_sftp_fsync probe
108
+# Check for libssh 0.8
109
+# This is done like this instead of using the LIBSSH_VERSION_* and
110
+# SSH_VERSION_* macros because some distributions in the past shipped
111
+# snapshots of the future 0.8 from Git, and those snapshots did not
112
+# have updated version numbers (still referring to 0.7.0).
113
114
-if test "$libssh2" = "yes"; then
115
+if test "$libssh" = "yes"; then
116
cat > $TMPC <<EOF
117
-#include <stdio.h>
118
-#include <libssh2.h>
119
-#include <libssh2_sftp.h>
120
-int main(void) {
121
- LIBSSH2_SESSION *session;
122
- LIBSSH2_SFTP *sftp;
123
- LIBSSH2_SFTP_HANDLE *sftp_handle;
124
- session = libssh2_session_init ();
125
- sftp = libssh2_sftp_init (session);
126
- sftp_handle = libssh2_sftp_open (sftp, "/", 0, 0);
127
- libssh2_sftp_fsync (sftp_handle);
128
- return 0;
129
-}
130
+#include <libssh/libssh.h>
131
+int main(void) { return ssh_get_server_publickey(NULL, NULL); }
132
EOF
133
- # libssh2_cflags/libssh2_libs defined in previous test.
134
- if compile_prog "$libssh2_cflags" "$libssh2_libs" ; then
135
- QEMU_CFLAGS="-DHAS_LIBSSH2_SFTP_FSYNC $QEMU_CFLAGS"
136
+ if compile_prog "$libssh_cflags" "$libssh_libs"; then
137
+ libssh_cflags="-DHAVE_LIBSSH_0_8 $libssh_cflags"
138
fi
139
fi
140
141
@@ -XXX,XX +XXX,XX @@ echo "GlusterFS support $glusterfs"
142
echo "gcov $gcov_tool"
143
echo "gcov enabled $gcov"
144
echo "TPM support $tpm"
145
-echo "libssh2 support $libssh2"
146
+echo "libssh support $libssh"
147
echo "QOM debugging $qom_cast_debug"
148
echo "Live block migration $live_block_migration"
149
echo "lzo support $lzo"
150
@@ -XXX,XX +XXX,XX @@ if test "$glusterfs_iocb_has_stat" = "yes" ; then
151
echo "CONFIG_GLUSTERFS_IOCB_HAS_STAT=y" >> $config_host_mak
152
fi
153
154
-if test "$libssh2" = "yes" ; then
155
- echo "CONFIG_LIBSSH2=m" >> $config_host_mak
156
- echo "LIBSSH2_CFLAGS=$libssh2_cflags" >> $config_host_mak
157
- echo "LIBSSH2_LIBS=$libssh2_libs" >> $config_host_mak
158
+if test "$libssh" = "yes" ; then
159
+ echo "CONFIG_LIBSSH=m" >> $config_host_mak
160
+ echo "LIBSSH_CFLAGS=$libssh_cflags" >> $config_host_mak
161
+ echo "LIBSSH_LIBS=$libssh_libs" >> $config_host_mak
162
fi
163
164
if test "$live_block_migration" = "yes" ; then
165
diff --git a/block/Makefile.objs b/block/Makefile.objs
16
index XXXXXXX..XXXXXXX 100644
166
index XXXXXXX..XXXXXXX 100644
17
--- a/blockdev.c
167
--- a/block/Makefile.objs
18
+++ b/blockdev.c
168
+++ b/block/Makefile.objs
19
@@ -XXX,XX +XXX,XX @@ static void external_snapshot_prepare(BlkActionState *common,
169
@@ -XXX,XX +XXX,XX @@ block-obj-$(CONFIG_CURL) += curl.o
20
s->has_snapshot_node_name ? s->snapshot_node_name : NULL;
170
block-obj-$(CONFIG_RBD) += rbd.o
21
171
block-obj-$(CONFIG_GLUSTERFS) += gluster.o
22
if (node_name && !snapshot_node_name) {
172
block-obj-$(CONFIG_VXHS) += vxhs.o
23
- error_setg(errp, "New snapshot node name missing");
173
-block-obj-$(CONFIG_LIBSSH2) += ssh.o
24
+ error_setg(errp, "New overlay node name missing");
174
+block-obj-$(CONFIG_LIBSSH) += ssh.o
175
block-obj-y += accounting.o dirty-bitmap.o
176
block-obj-y += write-threshold.o
177
block-obj-y += backup.o
178
@@ -XXX,XX +XXX,XX @@ rbd.o-libs := $(RBD_LIBS)
179
gluster.o-cflags := $(GLUSTERFS_CFLAGS)
180
gluster.o-libs := $(GLUSTERFS_LIBS)
181
vxhs.o-libs := $(VXHS_LIBS)
182
-ssh.o-cflags := $(LIBSSH2_CFLAGS)
183
-ssh.o-libs := $(LIBSSH2_LIBS)
184
+ssh.o-cflags := $(LIBSSH_CFLAGS)
185
+ssh.o-libs := $(LIBSSH_LIBS)
186
block-obj-dmg-bz2-$(CONFIG_BZIP2) += dmg-bz2.o
187
block-obj-$(if $(CONFIG_DMG),m,n) += $(block-obj-dmg-bz2-y)
188
dmg-bz2.o-libs := $(BZIP2_LIBS)
189
diff --git a/block/ssh.c b/block/ssh.c
190
index XXXXXXX..XXXXXXX 100644
191
--- a/block/ssh.c
192
+++ b/block/ssh.c
193
@@ -XXX,XX +XXX,XX @@
194
195
#include "qemu/osdep.h"
196
197
-#include <libssh2.h>
198
-#include <libssh2_sftp.h>
199
+#include <libssh/libssh.h>
200
+#include <libssh/sftp.h>
201
202
#include "block/block_int.h"
203
#include "block/qdict.h"
204
@@ -XXX,XX +XXX,XX @@
205
#include "trace.h"
206
207
/*
208
- * TRACE_LIBSSH2=<bitmask> enables tracing in libssh2 itself. Note
209
- * that this requires that libssh2 was specially compiled with the
210
- * `./configure --enable-debug' option, so most likely you will have
211
- * to compile it yourself. The meaning of <bitmask> is described
212
- * here: http://www.libssh2.org/libssh2_trace.html
213
+ * TRACE_LIBSSH=<level> enables tracing in libssh itself.
214
+ * The meaning of <level> is described here:
215
+ * http://api.libssh.org/master/group__libssh__log.html
216
*/
217
-#define TRACE_LIBSSH2 0 /* or try: LIBSSH2_TRACE_SFTP */
218
+#define TRACE_LIBSSH 0 /* see: SSH_LOG_* */
219
220
typedef struct BDRVSSHState {
221
/* Coroutine. */
222
@@ -XXX,XX +XXX,XX @@ typedef struct BDRVSSHState {
223
224
/* SSH connection. */
225
int sock; /* socket */
226
- LIBSSH2_SESSION *session; /* ssh session */
227
- LIBSSH2_SFTP *sftp; /* sftp session */
228
- LIBSSH2_SFTP_HANDLE *sftp_handle; /* sftp remote file handle */
229
+ ssh_session session; /* ssh session */
230
+ sftp_session sftp; /* sftp session */
231
+ sftp_file sftp_handle; /* sftp remote file handle */
232
233
- /* See ssh_seek() function below. */
234
- int64_t offset;
235
- bool offset_op_read;
236
-
237
- /* File attributes at open. We try to keep the .filesize field
238
+ /*
239
+ * File attributes at open. We try to keep the .size field
240
* updated if it changes (eg by writing at the end of the file).
241
*/
242
- LIBSSH2_SFTP_ATTRIBUTES attrs;
243
+ sftp_attributes attrs;
244
245
InetSocketAddress *inet;
246
247
@@ -XXX,XX +XXX,XX @@ static void ssh_state_init(BDRVSSHState *s)
248
{
249
memset(s, 0, sizeof *s);
250
s->sock = -1;
251
- s->offset = -1;
252
qemu_co_mutex_init(&s->lock);
253
}
254
255
@@ -XXX,XX +XXX,XX @@ static void ssh_state_free(BDRVSSHState *s)
256
{
257
g_free(s->user);
258
259
+ if (s->attrs) {
260
+ sftp_attributes_free(s->attrs);
261
+ }
262
if (s->sftp_handle) {
263
- libssh2_sftp_close(s->sftp_handle);
264
+ sftp_close(s->sftp_handle);
265
}
266
if (s->sftp) {
267
- libssh2_sftp_shutdown(s->sftp);
268
+ sftp_free(s->sftp);
269
}
270
if (s->session) {
271
- libssh2_session_disconnect(s->session,
272
- "from qemu ssh client: "
273
- "user closed the connection");
274
- libssh2_session_free(s->session);
275
- }
276
- if (s->sock >= 0) {
277
- close(s->sock);
278
+ ssh_disconnect(s->session);
279
+ ssh_free(s->session); /* This frees s->sock */
280
}
281
}
282
283
@@ -XXX,XX +XXX,XX @@ session_error_setg(Error **errp, BDRVSSHState *s, const char *fs, ...)
284
va_end(args);
285
286
if (s->session) {
287
- char *ssh_err;
288
+ const char *ssh_err;
289
int ssh_err_code;
290
291
- /* This is not an errno. See <libssh2.h>. */
292
- ssh_err_code = libssh2_session_last_error(s->session,
293
- &ssh_err, NULL, 0);
294
- error_setg(errp, "%s: %s (libssh2 error code: %d)",
295
+ /* This is not an errno. See <libssh/libssh.h>. */
296
+ ssh_err = ssh_get_error(s->session);
297
+ ssh_err_code = ssh_get_error_code(s->session);
298
+ error_setg(errp, "%s: %s (libssh error code: %d)",
299
msg, ssh_err, ssh_err_code);
300
} else {
301
error_setg(errp, "%s", msg);
302
@@ -XXX,XX +XXX,XX @@ sftp_error_setg(Error **errp, BDRVSSHState *s, const char *fs, ...)
303
va_end(args);
304
305
if (s->sftp) {
306
- char *ssh_err;
307
+ const char *ssh_err;
308
int ssh_err_code;
309
- unsigned long sftp_err_code;
310
+ int sftp_err_code;
311
312
- /* This is not an errno. See <libssh2.h>. */
313
- ssh_err_code = libssh2_session_last_error(s->session,
314
- &ssh_err, NULL, 0);
315
- /* See <libssh2_sftp.h>. */
316
- sftp_err_code = libssh2_sftp_last_error((s)->sftp);
317
+ /* This is not an errno. See <libssh/libssh.h>. */
318
+ ssh_err = ssh_get_error(s->session);
319
+ ssh_err_code = ssh_get_error_code(s->session);
320
+ /* See <libssh/sftp.h>. */
321
+ sftp_err_code = sftp_get_error(s->sftp);
322
323
error_setg(errp,
324
- "%s: %s (libssh2 error code: %d, sftp error code: %lu)",
325
+ "%s: %s (libssh error code: %d, sftp error code: %d)",
326
msg, ssh_err, ssh_err_code, sftp_err_code);
327
} else {
328
error_setg(errp, "%s", msg);
329
@@ -XXX,XX +XXX,XX @@ sftp_error_setg(Error **errp, BDRVSSHState *s, const char *fs, ...)
330
331
static void sftp_error_trace(BDRVSSHState *s, const char *op)
332
{
333
- char *ssh_err;
334
+ const char *ssh_err;
335
int ssh_err_code;
336
- unsigned long sftp_err_code;
337
+ int sftp_err_code;
338
339
- /* This is not an errno. See <libssh2.h>. */
340
- ssh_err_code = libssh2_session_last_error(s->session,
341
- &ssh_err, NULL, 0);
342
- /* See <libssh2_sftp.h>. */
343
- sftp_err_code = libssh2_sftp_last_error((s)->sftp);
344
+ /* This is not an errno. See <libssh/libssh.h>. */
345
+ ssh_err = ssh_get_error(s->session);
346
+ ssh_err_code = ssh_get_error_code(s->session);
347
+ /* See <libssh/sftp.h>. */
348
+ sftp_err_code = sftp_get_error(s->sftp);
349
350
trace_sftp_error(op, ssh_err, ssh_err_code, sftp_err_code);
351
}
352
@@ -XXX,XX +XXX,XX @@ static void ssh_parse_filename(const char *filename, QDict *options,
353
parse_uri(filename, options, errp);
354
}
355
356
-static int check_host_key_knownhosts(BDRVSSHState *s,
357
- const char *host, int port, Error **errp)
358
+static int check_host_key_knownhosts(BDRVSSHState *s, Error **errp)
359
{
360
- const char *home;
361
- char *knh_file = NULL;
362
- LIBSSH2_KNOWNHOSTS *knh = NULL;
363
- struct libssh2_knownhost *found;
364
- int ret, r;
365
- const char *hostkey;
366
- size_t len;
367
- int type;
368
-
369
- hostkey = libssh2_session_hostkey(s->session, &len, &type);
370
- if (!hostkey) {
371
+ int ret;
372
+#ifdef HAVE_LIBSSH_0_8
373
+ enum ssh_known_hosts_e state;
374
+ int r;
375
+ ssh_key pubkey;
376
+ enum ssh_keytypes_e pubkey_type;
377
+ unsigned char *server_hash = NULL;
378
+ size_t server_hash_len;
379
+ char *fingerprint = NULL;
380
+
381
+ state = ssh_session_is_known_server(s->session);
382
+ trace_ssh_server_status(state);
383
+
384
+ switch (state) {
385
+ case SSH_KNOWN_HOSTS_OK:
386
+ /* OK */
387
+ trace_ssh_check_host_key_knownhosts();
388
+ break;
389
+ case SSH_KNOWN_HOSTS_CHANGED:
390
ret = -EINVAL;
391
- session_error_setg(errp, s, "failed to read remote host key");
392
+ r = ssh_get_server_publickey(s->session, &pubkey);
393
+ if (r == 0) {
394
+ r = ssh_get_publickey_hash(pubkey, SSH_PUBLICKEY_HASH_SHA256,
395
+ &server_hash, &server_hash_len);
396
+ pubkey_type = ssh_key_type(pubkey);
397
+ ssh_key_free(pubkey);
398
+ }
399
+ if (r == 0) {
400
+ fingerprint = ssh_get_fingerprint_hash(SSH_PUBLICKEY_HASH_SHA256,
401
+ server_hash,
402
+ server_hash_len);
403
+ ssh_clean_pubkey_hash(&server_hash);
404
+ }
405
+ if (fingerprint) {
406
+ error_setg(errp,
407
+ "host key (%s key with fingerprint %s) does not match "
408
+ "the one in known_hosts; this may be a possible attack",
409
+ ssh_key_type_to_char(pubkey_type), fingerprint);
410
+ ssh_string_free_char(fingerprint);
411
+ } else {
412
+ error_setg(errp,
413
+ "host key does not match the one in known_hosts; this "
414
+ "may be a possible attack");
415
+ }
416
goto out;
417
- }
418
-
419
- knh = libssh2_knownhost_init(s->session);
420
- if (!knh) {
421
+ case SSH_KNOWN_HOSTS_OTHER:
422
ret = -EINVAL;
423
- session_error_setg(errp, s,
424
- "failed to initialize known hosts support");
425
+ error_setg(errp,
426
+ "host key for this server not found, another type exists");
427
+ goto out;
428
+ case SSH_KNOWN_HOSTS_UNKNOWN:
429
+ ret = -EINVAL;
430
+ error_setg(errp, "no host key was found in known_hosts");
431
+ goto out;
432
+ case SSH_KNOWN_HOSTS_NOT_FOUND:
433
+ ret = -ENOENT;
434
+ error_setg(errp, "known_hosts file not found");
435
+ goto out;
436
+ case SSH_KNOWN_HOSTS_ERROR:
437
+ ret = -EINVAL;
438
+ error_setg(errp, "error while checking the host");
439
+ goto out;
440
+ default:
441
+ ret = -EINVAL;
442
+ error_setg(errp, "error while checking for known server (%d)", state);
443
goto out;
444
}
445
+#else /* !HAVE_LIBSSH_0_8 */
446
+ int state;
447
448
- home = getenv("HOME");
449
- if (home) {
450
- knh_file = g_strdup_printf("%s/.ssh/known_hosts", home);
451
- } else {
452
- knh_file = g_strdup_printf("/root/.ssh/known_hosts");
453
- }
454
-
455
- /* Read all known hosts from OpenSSH-style known_hosts file. */
456
- libssh2_knownhost_readfile(knh, knh_file, LIBSSH2_KNOWNHOST_FILE_OPENSSH);
457
+ state = ssh_is_server_known(s->session);
458
+ trace_ssh_server_status(state);
459
460
- r = libssh2_knownhost_checkp(knh, host, port, hostkey, len,
461
- LIBSSH2_KNOWNHOST_TYPE_PLAIN|
462
- LIBSSH2_KNOWNHOST_KEYENC_RAW,
463
- &found);
464
- switch (r) {
465
- case LIBSSH2_KNOWNHOST_CHECK_MATCH:
466
+ switch (state) {
467
+ case SSH_SERVER_KNOWN_OK:
468
/* OK */
469
- trace_ssh_check_host_key_knownhosts(found->key);
470
+ trace_ssh_check_host_key_knownhosts();
471
break;
472
- case LIBSSH2_KNOWNHOST_CHECK_MISMATCH:
473
+ case SSH_SERVER_KNOWN_CHANGED:
474
ret = -EINVAL;
475
- session_error_setg(errp, s,
476
- "host key does not match the one in known_hosts"
477
- " (found key %s)", found->key);
478
+ error_setg(errp,
479
+ "host key does not match the one in known_hosts; this "
480
+ "may be a possible attack");
481
goto out;
482
- case LIBSSH2_KNOWNHOST_CHECK_NOTFOUND:
483
+ case SSH_SERVER_FOUND_OTHER:
484
ret = -EINVAL;
485
- session_error_setg(errp, s, "no host key was found in known_hosts");
486
+ error_setg(errp,
487
+ "host key for this server not found, another type exists");
488
+ goto out;
489
+ case SSH_SERVER_FILE_NOT_FOUND:
490
+ ret = -ENOENT;
491
+ error_setg(errp, "known_hosts file not found");
492
goto out;
493
- case LIBSSH2_KNOWNHOST_CHECK_FAILURE:
494
+ case SSH_SERVER_NOT_KNOWN:
495
ret = -EINVAL;
496
- session_error_setg(errp, s,
497
- "failure matching the host key with known_hosts");
498
+ error_setg(errp, "no host key was found in known_hosts");
499
+ goto out;
500
+ case SSH_SERVER_ERROR:
501
+ ret = -EINVAL;
502
+ error_setg(errp, "server error");
503
goto out;
504
default:
505
ret = -EINVAL;
506
- session_error_setg(errp, s, "unknown error matching the host key"
507
- " with known_hosts (%d)", r);
508
+ error_setg(errp, "error while checking for known server (%d)", state);
509
goto out;
510
}
511
+#endif /* !HAVE_LIBSSH_0_8 */
512
513
/* known_hosts checking successful. */
514
ret = 0;
515
516
out:
517
- if (knh != NULL) {
518
- libssh2_knownhost_free(knh);
519
- }
520
- g_free(knh_file);
521
return ret;
522
}
523
524
@@ -XXX,XX +XXX,XX @@ static int compare_fingerprint(const unsigned char *fingerprint, size_t len,
525
526
static int
527
check_host_key_hash(BDRVSSHState *s, const char *hash,
528
- int hash_type, size_t fingerprint_len, Error **errp)
529
+ enum ssh_publickey_hash_type type, Error **errp)
530
{
531
- const char *fingerprint;
532
-
533
- fingerprint = libssh2_hostkey_hash(s->session, hash_type);
534
- if (!fingerprint) {
535
+ int r;
536
+ ssh_key pubkey;
537
+ unsigned char *server_hash;
538
+ size_t server_hash_len;
539
+
540
+#ifdef HAVE_LIBSSH_0_8
541
+ r = ssh_get_server_publickey(s->session, &pubkey);
542
+#else
543
+ r = ssh_get_publickey(s->session, &pubkey);
544
+#endif
545
+ if (r != SSH_OK) {
546
session_error_setg(errp, s, "failed to read remote host key");
547
return -EINVAL;
548
}
549
550
- if(compare_fingerprint((unsigned char *) fingerprint, fingerprint_len,
551
- hash) != 0) {
552
+ r = ssh_get_publickey_hash(pubkey, type, &server_hash, &server_hash_len);
553
+ ssh_key_free(pubkey);
554
+ if (r != 0) {
555
+ session_error_setg(errp, s,
556
+ "failed reading the hash of the server SSH key");
557
+ return -EINVAL;
558
+ }
559
+
560
+ r = compare_fingerprint(server_hash, server_hash_len, hash);
561
+ ssh_clean_pubkey_hash(&server_hash);
562
+ if (r != 0) {
563
error_setg(errp, "remote host key does not match host_key_check '%s'",
564
hash);
565
return -EPERM;
566
@@ -XXX,XX +XXX,XX @@ check_host_key_hash(BDRVSSHState *s, const char *hash,
567
return 0;
568
}
569
570
-static int check_host_key(BDRVSSHState *s, const char *host, int port,
571
- SshHostKeyCheck *hkc, Error **errp)
572
+static int check_host_key(BDRVSSHState *s, SshHostKeyCheck *hkc, Error **errp)
573
{
574
SshHostKeyCheckMode mode;
575
576
@@ -XXX,XX +XXX,XX @@ static int check_host_key(BDRVSSHState *s, const char *host, int port,
577
case SSH_HOST_KEY_CHECK_MODE_HASH:
578
if (hkc->u.hash.type == SSH_HOST_KEY_CHECK_HASH_TYPE_MD5) {
579
return check_host_key_hash(s, hkc->u.hash.hash,
580
- LIBSSH2_HOSTKEY_HASH_MD5, 16, errp);
581
+ SSH_PUBLICKEY_HASH_MD5, errp);
582
} else if (hkc->u.hash.type == SSH_HOST_KEY_CHECK_HASH_TYPE_SHA1) {
583
return check_host_key_hash(s, hkc->u.hash.hash,
584
- LIBSSH2_HOSTKEY_HASH_SHA1, 20, errp);
585
+ SSH_PUBLICKEY_HASH_SHA1, errp);
586
}
587
g_assert_not_reached();
588
break;
589
case SSH_HOST_KEY_CHECK_MODE_KNOWN_HOSTS:
590
- return check_host_key_knownhosts(s, host, port, errp);
591
+ return check_host_key_knownhosts(s, errp);
592
default:
593
g_assert_not_reached();
594
}
595
@@ -XXX,XX +XXX,XX @@ static int check_host_key(BDRVSSHState *s, const char *host, int port,
596
return -EINVAL;
597
}
598
599
-static int authenticate(BDRVSSHState *s, const char *user, Error **errp)
600
+static int authenticate(BDRVSSHState *s, Error **errp)
601
{
602
int r, ret;
603
- const char *userauthlist;
604
- LIBSSH2_AGENT *agent = NULL;
605
- struct libssh2_agent_publickey *identity;
606
- struct libssh2_agent_publickey *prev_identity = NULL;
607
+ int method;
608
609
- userauthlist = libssh2_userauth_list(s->session, user, strlen(user));
610
- if (strstr(userauthlist, "publickey") == NULL) {
611
+ /* Try to authenticate with the "none" method. */
612
+ r = ssh_userauth_none(s->session, NULL);
613
+ if (r == SSH_AUTH_ERROR) {
614
ret = -EPERM;
615
- error_setg(errp,
616
- "remote server does not support \"publickey\" authentication");
617
+ session_error_setg(errp, s, "failed to authenticate using none "
618
+ "authentication");
619
goto out;
620
- }
621
-
622
- /* Connect to ssh-agent and try each identity in turn. */
623
- agent = libssh2_agent_init(s->session);
624
- if (!agent) {
625
- ret = -EINVAL;
626
- session_error_setg(errp, s, "failed to initialize ssh-agent support");
627
- goto out;
628
- }
629
- if (libssh2_agent_connect(agent)) {
630
- ret = -ECONNREFUSED;
631
- session_error_setg(errp, s, "failed to connect to ssh-agent");
632
- goto out;
633
- }
634
- if (libssh2_agent_list_identities(agent)) {
635
- ret = -EINVAL;
636
- session_error_setg(errp, s,
637
- "failed requesting identities from ssh-agent");
638
+ } else if (r == SSH_AUTH_SUCCESS) {
639
+ /* Authenticated! */
640
+ ret = 0;
641
goto out;
642
}
643
644
- for(;;) {
645
- r = libssh2_agent_get_identity(agent, &identity, prev_identity);
646
- if (r == 1) { /* end of list */
647
- break;
648
- }
649
- if (r < 0) {
650
+ method = ssh_userauth_list(s->session, NULL);
651
+ trace_ssh_auth_methods(method);
652
+
653
+ /*
654
+ * Try to authenticate with publickey, using the ssh-agent
655
+ * if available.
656
+ */
657
+ if (method & SSH_AUTH_METHOD_PUBLICKEY) {
658
+ r = ssh_userauth_publickey_auto(s->session, NULL, NULL);
659
+ if (r == SSH_AUTH_ERROR) {
660
ret = -EINVAL;
661
- session_error_setg(errp, s,
662
- "failed to obtain identity from ssh-agent");
663
+ session_error_setg(errp, s, "failed to authenticate using "
664
+ "publickey authentication");
665
goto out;
666
- }
667
- r = libssh2_agent_userauth(agent, user, identity);
668
- if (r == 0) {
669
+ } else if (r == SSH_AUTH_SUCCESS) {
670
/* Authenticated! */
671
ret = 0;
25
goto out;
672
goto out;
26
}
673
}
27
674
- /* Failed to authenticate with this identity, try the next one. */
28
if (snapshot_node_name &&
675
- prev_identity = identity;
29
bdrv_lookup_bs(snapshot_node_name, snapshot_node_name, NULL)) {
676
}
30
- error_setg(errp, "New snapshot node name already in use");
677
31
+ error_setg(errp, "New overlay node name already in use");
678
ret = -EPERM;
32
goto out;
679
@@ -XXX,XX +XXX,XX @@ static int authenticate(BDRVSSHState *s, const char *user, Error **errp)
680
"and the identities held by your ssh-agent");
681
682
out:
683
- if (agent != NULL) {
684
- /* Note: libssh2 implementation implicitly calls
685
- * libssh2_agent_disconnect if necessary.
686
- */
687
- libssh2_agent_free(agent);
688
- }
689
-
690
return ret;
691
}
692
693
@@ -XXX,XX +XXX,XX @@ static int connect_to_ssh(BDRVSSHState *s, BlockdevOptionsSsh *opts,
694
int ssh_flags, int creat_mode, Error **errp)
695
{
696
int r, ret;
697
- long port = 0;
698
+ unsigned int port = 0;
699
+ int new_sock = -1;
700
701
if (opts->has_user) {
702
s->user = g_strdup(opts->user);
703
@@ -XXX,XX +XXX,XX @@ static int connect_to_ssh(BDRVSSHState *s, BlockdevOptionsSsh *opts,
704
s->inet = opts->server;
705
opts->server = NULL;
706
707
- if (qemu_strtol(s->inet->port, NULL, 10, &port) < 0) {
708
+ if (qemu_strtoui(s->inet->port, NULL, 10, &port) < 0) {
709
error_setg(errp, "Use only numeric port value");
710
ret = -EINVAL;
711
goto err;
712
}
713
714
/* Open the socket and connect. */
715
- s->sock = inet_connect_saddr(s->inet, errp);
716
- if (s->sock < 0) {
717
+ new_sock = inet_connect_saddr(s->inet, errp);
718
+ if (new_sock < 0) {
719
ret = -EIO;
720
goto err;
721
}
722
723
+ /*
724
+ * Try to disable the Nagle algorithm on TCP sockets to reduce latency,
725
+ * but do not fail if it cannot be disabled.
726
+ */
727
+ r = socket_set_nodelay(new_sock);
728
+ if (r < 0) {
729
+ warn_report("can't set TCP_NODELAY for the ssh server %s: %s",
730
+ s->inet->host, strerror(errno));
731
+ }
732
+
733
/* Create SSH session. */
734
- s->session = libssh2_session_init();
735
+ s->session = ssh_new();
736
if (!s->session) {
737
ret = -EINVAL;
738
- session_error_setg(errp, s, "failed to initialize libssh2 session");
739
+ session_error_setg(errp, s, "failed to initialize libssh session");
740
goto err;
741
}
742
743
-#if TRACE_LIBSSH2 != 0
744
- libssh2_trace(s->session, TRACE_LIBSSH2);
745
-#endif
746
+ /*
747
+ * Make sure we are in blocking mode during the connection and
748
+ * authentication phases.
749
+ */
750
+ ssh_set_blocking(s->session, 1);
751
752
- r = libssh2_session_handshake(s->session, s->sock);
753
- if (r != 0) {
754
+ r = ssh_options_set(s->session, SSH_OPTIONS_USER, s->user);
755
+ if (r < 0) {
756
+ ret = -EINVAL;
757
+ session_error_setg(errp, s,
758
+ "failed to set the user in the libssh session");
759
+ goto err;
760
+ }
761
+
762
+ r = ssh_options_set(s->session, SSH_OPTIONS_HOST, s->inet->host);
763
+ if (r < 0) {
764
+ ret = -EINVAL;
765
+ session_error_setg(errp, s,
766
+ "failed to set the host in the libssh session");
767
+ goto err;
768
+ }
769
+
770
+ if (port > 0) {
771
+ r = ssh_options_set(s->session, SSH_OPTIONS_PORT, &port);
772
+ if (r < 0) {
773
+ ret = -EINVAL;
774
+ session_error_setg(errp, s,
775
+ "failed to set the port in the libssh session");
776
+ goto err;
777
+ }
778
+ }
779
+
780
+ r = ssh_options_set(s->session, SSH_OPTIONS_COMPRESSION, "none");
781
+ if (r < 0) {
782
+ ret = -EINVAL;
783
+ session_error_setg(errp, s,
784
+ "failed to disable the compression in the libssh "
785
+ "session");
786
+ goto err;
787
+ }
788
+
789
+ /* Read ~/.ssh/config. */
790
+ r = ssh_options_parse_config(s->session, NULL);
791
+ if (r < 0) {
792
+ ret = -EINVAL;
793
+ session_error_setg(errp, s, "failed to parse ~/.ssh/config");
794
+ goto err;
795
+ }
796
+
797
+ r = ssh_options_set(s->session, SSH_OPTIONS_FD, &new_sock);
798
+ if (r < 0) {
799
+ ret = -EINVAL;
800
+ session_error_setg(errp, s,
801
+ "failed to set the socket in the libssh session");
802
+ goto err;
803
+ }
804
+ /* libssh took ownership of the socket. */
805
+ s->sock = new_sock;
806
+ new_sock = -1;
807
+
808
+ /* Connect. */
809
+ r = ssh_connect(s->session);
810
+ if (r != SSH_OK) {
811
ret = -EINVAL;
812
session_error_setg(errp, s, "failed to establish SSH session");
813
goto err;
814
}
815
816
/* Check the remote host's key against known_hosts. */
817
- ret = check_host_key(s, s->inet->host, port, opts->host_key_check, errp);
818
+ ret = check_host_key(s, opts->host_key_check, errp);
819
if (ret < 0) {
820
goto err;
821
}
822
823
/* Authenticate. */
824
- ret = authenticate(s, s->user, errp);
825
+ ret = authenticate(s, errp);
826
if (ret < 0) {
827
goto err;
828
}
829
830
/* Start SFTP. */
831
- s->sftp = libssh2_sftp_init(s->session);
832
+ s->sftp = sftp_new(s->session);
833
if (!s->sftp) {
834
- session_error_setg(errp, s, "failed to initialize sftp handle");
835
+ session_error_setg(errp, s, "failed to create sftp handle");
836
+ ret = -EINVAL;
837
+ goto err;
838
+ }
839
+
840
+ r = sftp_init(s->sftp);
841
+ if (r < 0) {
842
+ sftp_error_setg(errp, s, "failed to initialize sftp handle");
843
ret = -EINVAL;
844
goto err;
845
}
846
847
/* Open the remote file. */
848
trace_ssh_connect_to_ssh(opts->path, ssh_flags, creat_mode);
849
- s->sftp_handle = libssh2_sftp_open(s->sftp, opts->path, ssh_flags,
850
- creat_mode);
851
+ s->sftp_handle = sftp_open(s->sftp, opts->path, ssh_flags, creat_mode);
852
if (!s->sftp_handle) {
853
- session_error_setg(errp, s, "failed to open remote file '%s'",
854
- opts->path);
855
+ sftp_error_setg(errp, s, "failed to open remote file '%s'",
856
+ opts->path);
857
ret = -EINVAL;
858
goto err;
859
}
860
861
- r = libssh2_sftp_fstat(s->sftp_handle, &s->attrs);
862
- if (r < 0) {
863
+ /* Make sure the SFTP file is handled in blocking mode. */
864
+ sftp_file_set_blocking(s->sftp_handle);
865
+
866
+ s->attrs = sftp_fstat(s->sftp_handle);
867
+ if (!s->attrs) {
868
sftp_error_setg(errp, s, "failed to read file attributes");
869
return -EINVAL;
870
}
871
@@ -XXX,XX +XXX,XX @@ static int connect_to_ssh(BDRVSSHState *s, BlockdevOptionsSsh *opts,
872
return 0;
873
874
err:
875
+ if (s->attrs) {
876
+ sftp_attributes_free(s->attrs);
877
+ }
878
+ s->attrs = NULL;
879
if (s->sftp_handle) {
880
- libssh2_sftp_close(s->sftp_handle);
881
+ sftp_close(s->sftp_handle);
882
}
883
s->sftp_handle = NULL;
884
if (s->sftp) {
885
- libssh2_sftp_shutdown(s->sftp);
886
+ sftp_free(s->sftp);
887
}
888
s->sftp = NULL;
889
if (s->session) {
890
- libssh2_session_disconnect(s->session,
891
- "from qemu ssh client: "
892
- "error opening connection");
893
- libssh2_session_free(s->session);
894
+ ssh_disconnect(s->session);
895
+ ssh_free(s->session);
896
}
897
s->session = NULL;
898
+ s->sock = -1;
899
+ if (new_sock >= 0) {
900
+ close(new_sock);
901
+ }
902
903
return ret;
904
}
905
@@ -XXX,XX +XXX,XX @@ static int ssh_file_open(BlockDriverState *bs, QDict *options, int bdrv_flags,
906
907
ssh_state_init(s);
908
909
- ssh_flags = LIBSSH2_FXF_READ;
910
+ ssh_flags = 0;
911
if (bdrv_flags & BDRV_O_RDWR) {
912
- ssh_flags |= LIBSSH2_FXF_WRITE;
913
+ ssh_flags |= O_RDWR;
914
+ } else {
915
+ ssh_flags |= O_RDONLY;
916
}
917
918
opts = ssh_parse_options(options, errp);
919
@@ -XXX,XX +XXX,XX @@ static int ssh_file_open(BlockDriverState *bs, QDict *options, int bdrv_flags,
920
}
921
922
/* Go non-blocking. */
923
- libssh2_session_set_blocking(s->session, 0);
924
+ ssh_set_blocking(s->session, 0);
925
926
qapi_free_BlockdevOptionsSsh(opts);
927
928
return 0;
929
930
err:
931
- if (s->sock >= 0) {
932
- close(s->sock);
933
- }
934
- s->sock = -1;
935
-
936
qapi_free_BlockdevOptionsSsh(opts);
937
938
return ret;
939
@@ -XXX,XX +XXX,XX @@ static int ssh_grow_file(BDRVSSHState *s, int64_t offset, Error **errp)
940
{
941
ssize_t ret;
942
char c[1] = { '\0' };
943
- int was_blocking = libssh2_session_get_blocking(s->session);
944
+ int was_blocking = ssh_is_blocking(s->session);
945
946
/* offset must be strictly greater than the current size so we do
947
* not overwrite anything */
948
- assert(offset > 0 && offset > s->attrs.filesize);
949
+ assert(offset > 0 && offset > s->attrs->size);
950
951
- libssh2_session_set_blocking(s->session, 1);
952
+ ssh_set_blocking(s->session, 1);
953
954
- libssh2_sftp_seek64(s->sftp_handle, offset - 1);
955
- ret = libssh2_sftp_write(s->sftp_handle, c, 1);
956
+ sftp_seek64(s->sftp_handle, offset - 1);
957
+ ret = sftp_write(s->sftp_handle, c, 1);
958
959
- libssh2_session_set_blocking(s->session, was_blocking);
960
+ ssh_set_blocking(s->session, was_blocking);
961
962
if (ret < 0) {
963
sftp_error_setg(errp, s, "Failed to grow file");
964
return -EIO;
965
}
966
967
- s->attrs.filesize = offset;
968
+ s->attrs->size = offset;
969
return 0;
970
}
971
972
@@ -XXX,XX +XXX,XX @@ static int ssh_co_create(BlockdevCreateOptions *options, Error **errp)
973
ssh_state_init(&s);
974
975
ret = connect_to_ssh(&s, opts->location,
976
- LIBSSH2_FXF_READ|LIBSSH2_FXF_WRITE|
977
- LIBSSH2_FXF_CREAT|LIBSSH2_FXF_TRUNC,
978
+ O_RDWR | O_CREAT | O_TRUNC,
979
0644, errp);
980
if (ret < 0) {
981
goto fail;
982
@@ -XXX,XX +XXX,XX @@ static int ssh_has_zero_init(BlockDriverState *bs)
983
/* Assume false, unless we can positively prove it's true. */
984
int has_zero_init = 0;
985
986
- if (s->attrs.flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) {
987
- if (s->attrs.permissions & LIBSSH2_SFTP_S_IFREG) {
988
- has_zero_init = 1;
989
- }
990
+ if (s->attrs->type == SSH_FILEXFER_TYPE_REGULAR) {
991
+ has_zero_init = 1;
992
}
993
994
return has_zero_init;
995
@@ -XXX,XX +XXX,XX @@ static coroutine_fn void co_yield(BDRVSSHState *s, BlockDriverState *bs)
996
.co = qemu_coroutine_self()
997
};
998
999
- r = libssh2_session_block_directions(s->session);
1000
+ r = ssh_get_poll_flags(s->session);
1001
1002
- if (r & LIBSSH2_SESSION_BLOCK_INBOUND) {
1003
+ if (r & SSH_READ_PENDING) {
1004
rd_handler = restart_coroutine;
1005
}
1006
- if (r & LIBSSH2_SESSION_BLOCK_OUTBOUND) {
1007
+ if (r & SSH_WRITE_PENDING) {
1008
wr_handler = restart_coroutine;
1009
}
1010
1011
@@ -XXX,XX +XXX,XX @@ static coroutine_fn void co_yield(BDRVSSHState *s, BlockDriverState *bs)
1012
trace_ssh_co_yield_back(s->sock);
1013
}
1014
1015
-/* SFTP has a function `libssh2_sftp_seek64' which seeks to a position
1016
- * in the remote file. Notice that it just updates a field in the
1017
- * sftp_handle structure, so there is no network traffic and it cannot
1018
- * fail.
1019
- *
1020
- * However, `libssh2_sftp_seek64' does have a catastrophic effect on
1021
- * performance since it causes the handle to throw away all in-flight
1022
- * reads and buffered readahead data. Therefore this function tries
1023
- * to be intelligent about when to call the underlying libssh2 function.
1024
- */
1025
-#define SSH_SEEK_WRITE 0
1026
-#define SSH_SEEK_READ 1
1027
-#define SSH_SEEK_FORCE 2
1028
-
1029
-static void ssh_seek(BDRVSSHState *s, int64_t offset, int flags)
1030
-{
1031
- bool op_read = (flags & SSH_SEEK_READ) != 0;
1032
- bool force = (flags & SSH_SEEK_FORCE) != 0;
1033
-
1034
- if (force || op_read != s->offset_op_read || offset != s->offset) {
1035
- trace_ssh_seek(offset);
1036
- libssh2_sftp_seek64(s->sftp_handle, offset);
1037
- s->offset = offset;
1038
- s->offset_op_read = op_read;
1039
- }
1040
-}
1041
-
1042
static coroutine_fn int ssh_read(BDRVSSHState *s, BlockDriverState *bs,
1043
int64_t offset, size_t size,
1044
QEMUIOVector *qiov)
1045
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int ssh_read(BDRVSSHState *s, BlockDriverState *bs,
1046
1047
trace_ssh_read(offset, size);
1048
1049
- ssh_seek(s, offset, SSH_SEEK_READ);
1050
+ trace_ssh_seek(offset);
1051
+ sftp_seek64(s->sftp_handle, offset);
1052
1053
/* This keeps track of the current iovec element ('i'), where we
1054
* will write to next ('buf'), and the end of the current iovec
1055
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int ssh_read(BDRVSSHState *s, BlockDriverState *bs,
1056
buf = i->iov_base;
1057
end_of_vec = i->iov_base + i->iov_len;
1058
1059
- /* libssh2 has a hard-coded limit of 2000 bytes per request,
1060
- * although it will also do readahead behind our backs. Therefore
1061
- * we may have to do repeated reads here until we have read 'size'
1062
- * bytes.
1063
- */
1064
for (got = 0; got < size; ) {
1065
+ size_t request_read_size;
1066
again:
1067
- trace_ssh_read_buf(buf, end_of_vec - buf);
1068
- r = libssh2_sftp_read(s->sftp_handle, buf, end_of_vec - buf);
1069
- trace_ssh_read_return(r);
1070
+ /*
1071
+ * The size of SFTP packets is limited to 32K bytes, so limit
1072
+ * the amount of data requested to 16K, as libssh currently
1073
+ * does not handle multiple requests on its own.
1074
+ */
1075
+ request_read_size = MIN(end_of_vec - buf, 16384);
1076
+ trace_ssh_read_buf(buf, end_of_vec - buf, request_read_size);
1077
+ r = sftp_read(s->sftp_handle, buf, request_read_size);
1078
+ trace_ssh_read_return(r, sftp_get_error(s->sftp));
1079
1080
- if (r == LIBSSH2_ERROR_EAGAIN || r == LIBSSH2_ERROR_TIMEOUT) {
1081
+ if (r == SSH_AGAIN) {
1082
co_yield(s, bs);
1083
goto again;
33
}
1084
}
34
1085
- if (r < 0) {
35
@@ -XXX,XX +XXX,XX @@ static void external_snapshot_prepare(BlkActionState *common,
1086
- sftp_error_trace(s, "read");
36
}
1087
- s->offset = -1;
37
1088
- return -EIO;
38
if (bdrv_has_blk(state->new_bs)) {
1089
- }
39
- error_setg(errp, "The snapshot is already in use");
1090
- if (r == 0) {
40
+ error_setg(errp, "The overlay is already in use");
1091
+ if (r == SSH_EOF || (r == 0 && sftp_get_error(s->sftp) == SSH_FX_EOF)) {
41
goto out;
1092
/* EOF: Short read so pad the buffer with zeroes and return it. */
42
}
1093
qemu_iovec_memset(qiov, got, 0, size - got);
43
1094
return 0;
44
@@ -XXX,XX +XXX,XX @@ static void external_snapshot_prepare(BlkActionState *common,
1095
}
45
}
1096
+ if (r <= 0) {
46
1097
+ sftp_error_trace(s, "read");
47
if (state->new_bs->backing != NULL) {
1098
+ return -EIO;
48
- error_setg(errp, "The snapshot already has a backing image");
1099
+ }
49
+ error_setg(errp, "The overlay already has a backing image");
1100
50
goto out;
1101
got += r;
51
}
1102
buf += r;
52
1103
- s->offset += r;
53
if (!state->new_bs->drv->supports_backing) {
1104
if (buf >= end_of_vec && got < size) {
54
- error_setg(errp, "The snapshot does not support backing images");
1105
i++;
55
+ error_setg(errp, "The overlay does not support backing images");
1106
buf = i->iov_base;
56
goto out;
1107
@@ -XXX,XX +XXX,XX @@ static int ssh_write(BDRVSSHState *s, BlockDriverState *bs,
57
}
1108
58
1109
trace_ssh_write(offset, size);
59
diff --git a/tests/qemu-iotests/085.out b/tests/qemu-iotests/085.out
1110
1111
- ssh_seek(s, offset, SSH_SEEK_WRITE);
1112
+ trace_ssh_seek(offset);
1113
+ sftp_seek64(s->sftp_handle, offset);
1114
1115
/* This keeps track of the current iovec element ('i'), where we
1116
* will read from next ('buf'), and the end of the current iovec
1117
@@ -XXX,XX +XXX,XX @@ static int ssh_write(BDRVSSHState *s, BlockDriverState *bs,
1118
end_of_vec = i->iov_base + i->iov_len;
1119
1120
for (written = 0; written < size; ) {
1121
+ size_t request_write_size;
1122
again:
1123
- trace_ssh_write_buf(buf, end_of_vec - buf);
1124
- r = libssh2_sftp_write(s->sftp_handle, buf, end_of_vec - buf);
1125
- trace_ssh_write_return(r);
1126
+ /*
1127
+ * Avoid too large data packets, as libssh currently does not
1128
+ * handle multiple requests on its own.
1129
+ */
1130
+ request_write_size = MIN(end_of_vec - buf, 131072);
1131
+ trace_ssh_write_buf(buf, end_of_vec - buf, request_write_size);
1132
+ r = sftp_write(s->sftp_handle, buf, request_write_size);
1133
+ trace_ssh_write_return(r, sftp_get_error(s->sftp));
1134
1135
- if (r == LIBSSH2_ERROR_EAGAIN || r == LIBSSH2_ERROR_TIMEOUT) {
1136
+ if (r == SSH_AGAIN) {
1137
co_yield(s, bs);
1138
goto again;
1139
}
1140
if (r < 0) {
1141
sftp_error_trace(s, "write");
1142
- s->offset = -1;
1143
return -EIO;
1144
}
1145
- /* The libssh2 API is very unclear about this. A comment in
1146
- * the code says "nothing was acked, and no EAGAIN was
1147
- * received!" which apparently means that no data got sent
1148
- * out, and the underlying channel didn't return any EAGAIN
1149
- * indication. I think this is a bug in either libssh2 or
1150
- * OpenSSH (server-side). In any case, forcing a seek (to
1151
- * discard libssh2 internal buffers), and then trying again
1152
- * works for me.
1153
- */
1154
- if (r == 0) {
1155
- ssh_seek(s, offset + written, SSH_SEEK_WRITE|SSH_SEEK_FORCE);
1156
- co_yield(s, bs);
1157
- goto again;
1158
- }
1159
1160
written += r;
1161
buf += r;
1162
- s->offset += r;
1163
if (buf >= end_of_vec && written < size) {
1164
i++;
1165
buf = i->iov_base;
1166
end_of_vec = i->iov_base + i->iov_len;
1167
}
1168
1169
- if (offset + written > s->attrs.filesize)
1170
- s->attrs.filesize = offset + written;
1171
+ if (offset + written > s->attrs->size) {
1172
+ s->attrs->size = offset + written;
1173
+ }
1174
}
1175
1176
return 0;
1177
@@ -XXX,XX +XXX,XX @@ static void unsafe_flush_warning(BDRVSSHState *s, const char *what)
1178
}
1179
}
1180
1181
-#ifdef HAS_LIBSSH2_SFTP_FSYNC
1182
+#ifdef HAVE_LIBSSH_0_8
1183
1184
static coroutine_fn int ssh_flush(BDRVSSHState *s, BlockDriverState *bs)
1185
{
1186
int r;
1187
1188
trace_ssh_flush();
1189
+
1190
+ if (!sftp_extension_supported(s->sftp, "fsync@openssh.com", "1")) {
1191
+ unsafe_flush_warning(s, "OpenSSH >= 6.3");
1192
+ return 0;
1193
+ }
1194
again:
1195
- r = libssh2_sftp_fsync(s->sftp_handle);
1196
- if (r == LIBSSH2_ERROR_EAGAIN || r == LIBSSH2_ERROR_TIMEOUT) {
1197
+ r = sftp_fsync(s->sftp_handle);
1198
+ if (r == SSH_AGAIN) {
1199
co_yield(s, bs);
1200
goto again;
1201
}
1202
- if (r == LIBSSH2_ERROR_SFTP_PROTOCOL &&
1203
- libssh2_sftp_last_error(s->sftp) == LIBSSH2_FX_OP_UNSUPPORTED) {
1204
- unsafe_flush_warning(s, "OpenSSH >= 6.3");
1205
- return 0;
1206
- }
1207
if (r < 0) {
1208
sftp_error_trace(s, "fsync");
1209
return -EIO;
1210
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int ssh_co_flush(BlockDriverState *bs)
1211
return ret;
1212
}
1213
1214
-#else /* !HAS_LIBSSH2_SFTP_FSYNC */
1215
+#else /* !HAVE_LIBSSH_0_8 */
1216
1217
static coroutine_fn int ssh_co_flush(BlockDriverState *bs)
1218
{
1219
BDRVSSHState *s = bs->opaque;
1220
1221
- unsafe_flush_warning(s, "libssh2 >= 1.4.4");
1222
+ unsafe_flush_warning(s, "libssh >= 0.8.0");
1223
return 0;
1224
}
1225
1226
-#endif /* !HAS_LIBSSH2_SFTP_FSYNC */
1227
+#endif /* !HAVE_LIBSSH_0_8 */
1228
1229
static int64_t ssh_getlength(BlockDriverState *bs)
1230
{
1231
BDRVSSHState *s = bs->opaque;
1232
int64_t length;
1233
1234
- /* Note we cannot make a libssh2 call here. */
1235
- length = (int64_t) s->attrs.filesize;
1236
+ /* Note we cannot make a libssh call here. */
1237
+ length = (int64_t) s->attrs->size;
1238
trace_ssh_getlength(length);
1239
1240
return length;
1241
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn ssh_co_truncate(BlockDriverState *bs, int64_t offset,
1242
return -ENOTSUP;
1243
}
1244
1245
- if (offset < s->attrs.filesize) {
1246
+ if (offset < s->attrs->size) {
1247
error_setg(errp, "ssh driver does not support shrinking files");
1248
return -ENOTSUP;
1249
}
1250
1251
- if (offset == s->attrs.filesize) {
1252
+ if (offset == s->attrs->size) {
1253
return 0;
1254
}
1255
1256
@@ -XXX,XX +XXX,XX @@ static void bdrv_ssh_init(void)
1257
{
1258
int r;
1259
1260
- r = libssh2_init(0);
1261
+ r = ssh_init();
1262
if (r != 0) {
1263
- fprintf(stderr, "libssh2 initialization failed, %d\n", r);
1264
+ fprintf(stderr, "libssh initialization failed, %d\n", r);
1265
exit(EXIT_FAILURE);
1266
}
1267
1268
+#if TRACE_LIBSSH != 0
1269
+ ssh_set_log_level(TRACE_LIBSSH);
1270
+#endif
1271
+
1272
bdrv_register(&bdrv_ssh);
1273
}
1274
1275
diff --git a/.travis.yml b/.travis.yml
60
index XXXXXXX..XXXXXXX 100644
1276
index XXXXXXX..XXXXXXX 100644
61
--- a/tests/qemu-iotests/085.out
1277
--- a/.travis.yml
62
+++ b/tests/qemu-iotests/085.out
1278
+++ b/.travis.yml
63
@@ -XXX,XX +XXX,XX @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/
1279
@@ -XXX,XX +XXX,XX @@ addons:
64
1280
- libseccomp-dev
65
=== Invalid command - cannot create a snapshot using a file BDS ===
1281
- libspice-protocol-dev
66
1282
- libspice-server-dev
67
-{"error": {"class": "GenericError", "desc": "The snapshot does not support backing images"}}
1283
- - libssh2-1-dev
68
+{"error": {"class": "GenericError", "desc": "The overlay does not support backing images"}}
1284
+ - libssh-dev
69
1285
- liburcu-dev
70
=== Invalid command - snapshot node used as active layer ===
1286
- libusb-1.0-0-dev
71
1287
- libvte-2.91-dev
72
-{"error": {"class": "GenericError", "desc": "The snapshot is already in use"}}
1288
@@ -XXX,XX +XXX,XX @@ matrix:
73
-{"error": {"class": "GenericError", "desc": "The snapshot is already in use"}}
1289
- libseccomp-dev
74
-{"error": {"class": "GenericError", "desc": "The snapshot is already in use"}}
1290
- libspice-protocol-dev
75
+{"error": {"class": "GenericError", "desc": "The overlay is already in use"}}
1291
- libspice-server-dev
76
+{"error": {"class": "GenericError", "desc": "The overlay is already in use"}}
1292
- - libssh2-1-dev
77
+{"error": {"class": "GenericError", "desc": "The overlay is already in use"}}
1293
+ - libssh-dev
78
1294
- liburcu-dev
79
=== Invalid command - snapshot node used as backing hd ===
1295
- libusb-1.0-0-dev
80
1296
- libvte-2.91-dev
81
@@ -XXX,XX +XXX,XX @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/
1297
diff --git a/block/trace-events b/block/trace-events
82
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134217728
1298
index XXXXXXX..XXXXXXX 100644
83
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base
1299
--- a/block/trace-events
1300
+++ b/block/trace-events
1301
@@ -XXX,XX +XXX,XX @@ nbd_client_connect_success(const char *export_name) "export '%s'"
1302
# ssh.c
1303
ssh_restart_coroutine(void *co) "co=%p"
1304
ssh_flush(void) "fsync"
1305
-ssh_check_host_key_knownhosts(const char *key) "host key OK: %s"
1306
+ssh_check_host_key_knownhosts(void) "host key OK"
1307
ssh_connect_to_ssh(char *path, int flags, int mode) "opening file %s flags=0x%x creat_mode=0%o"
1308
ssh_co_yield(int sock, void *rd_handler, void *wr_handler) "s->sock=%d rd_handler=%p wr_handler=%p"
1309
ssh_co_yield_back(int sock) "s->sock=%d - back"
1310
ssh_getlength(int64_t length) "length=%" PRIi64
1311
ssh_co_create_opts(uint64_t size) "total_size=%" PRIu64
1312
ssh_read(int64_t offset, size_t size) "offset=%" PRIi64 " size=%zu"
1313
-ssh_read_buf(void *buf, size_t size) "sftp_read buf=%p size=%zu"
1314
-ssh_read_return(ssize_t ret) "sftp_read returned %zd"
1315
+ssh_read_buf(void *buf, size_t size, size_t actual_size) "sftp_read buf=%p size=%zu (actual size=%zu)"
1316
+ssh_read_return(ssize_t ret, int sftp_err) "sftp_read returned %zd (sftp error=%d)"
1317
ssh_write(int64_t offset, size_t size) "offset=%" PRIi64 " size=%zu"
1318
-ssh_write_buf(void *buf, size_t size) "sftp_write buf=%p size=%zu"
1319
-ssh_write_return(ssize_t ret) "sftp_write returned %zd"
1320
+ssh_write_buf(void *buf, size_t size, size_t actual_size) "sftp_write buf=%p size=%zu (actual size=%zu)"
1321
+ssh_write_return(ssize_t ret, int sftp_err) "sftp_write returned %zd (sftp error=%d)"
1322
ssh_seek(int64_t offset) "seeking to offset=%" PRIi64
1323
+ssh_auth_methods(int methods) "auth methods=0x%x"
1324
+ssh_server_status(int status) "server status=%d"
1325
1326
# curl.c
1327
curl_timer_cb(long timeout_ms) "timer callback timeout_ms %ld"
1328
@@ -XXX,XX +XXX,XX @@ sheepdog_snapshot_create(const char *sn_name, const char *id) "%s %s"
1329
sheepdog_snapshot_create_inode(const char *name, uint32_t snap, uint32_t vdi) "s->inode: name %s snap_id 0x%" PRIx32 " vdi 0x%" PRIx32
1330
1331
# ssh.c
1332
-sftp_error(const char *op, const char *ssh_err, int ssh_err_code, unsigned long sftp_err_code) "%s failed: %s (libssh2 error code: %d, sftp error code: %lu)"
1333
+sftp_error(const char *op, const char *ssh_err, int ssh_err_code, int sftp_err_code) "%s failed: %s (libssh error code: %d, sftp error code: %d)"
1334
diff --git a/docs/qemu-block-drivers.texi b/docs/qemu-block-drivers.texi
1335
index XXXXXXX..XXXXXXX 100644
1336
--- a/docs/qemu-block-drivers.texi
1337
+++ b/docs/qemu-block-drivers.texi
1338
@@ -XXX,XX +XXX,XX @@ print a warning when @code{fsync} is not supported:
1339
1340
warning: ssh server @code{ssh.example.com:22} does not support fsync
1341
1342
-With sufficiently new versions of libssh2 and OpenSSH, @code{fsync} is
1343
+With sufficiently new versions of libssh and OpenSSH, @code{fsync} is
1344
supported.
1345
1346
@node disk_images_nvme
1347
diff --git a/tests/docker/dockerfiles/debian-win32-cross.docker b/tests/docker/dockerfiles/debian-win32-cross.docker
1348
index XXXXXXX..XXXXXXX 100644
1349
--- a/tests/docker/dockerfiles/debian-win32-cross.docker
1350
+++ b/tests/docker/dockerfiles/debian-win32-cross.docker
1351
@@ -XXX,XX +XXX,XX @@ RUN DEBIAN_FRONTEND=noninteractive eatmydata \
1352
mxe-$TARGET-w64-mingw32.shared-curl \
1353
mxe-$TARGET-w64-mingw32.shared-glib \
1354
mxe-$TARGET-w64-mingw32.shared-libgcrypt \
1355
- mxe-$TARGET-w64-mingw32.shared-libssh2 \
1356
mxe-$TARGET-w64-mingw32.shared-libusb1 \
1357
mxe-$TARGET-w64-mingw32.shared-lzo \
1358
mxe-$TARGET-w64-mingw32.shared-nettle \
1359
diff --git a/tests/docker/dockerfiles/debian-win64-cross.docker b/tests/docker/dockerfiles/debian-win64-cross.docker
1360
index XXXXXXX..XXXXXXX 100644
1361
--- a/tests/docker/dockerfiles/debian-win64-cross.docker
1362
+++ b/tests/docker/dockerfiles/debian-win64-cross.docker
1363
@@ -XXX,XX +XXX,XX @@ RUN DEBIAN_FRONTEND=noninteractive eatmydata \
1364
mxe-$TARGET-w64-mingw32.shared-curl \
1365
mxe-$TARGET-w64-mingw32.shared-glib \
1366
mxe-$TARGET-w64-mingw32.shared-libgcrypt \
1367
- mxe-$TARGET-w64-mingw32.shared-libssh2 \
1368
mxe-$TARGET-w64-mingw32.shared-libusb1 \
1369
mxe-$TARGET-w64-mingw32.shared-lzo \
1370
mxe-$TARGET-w64-mingw32.shared-nettle \
1371
diff --git a/tests/docker/dockerfiles/fedora.docker b/tests/docker/dockerfiles/fedora.docker
1372
index XXXXXXX..XXXXXXX 100644
1373
--- a/tests/docker/dockerfiles/fedora.docker
1374
+++ b/tests/docker/dockerfiles/fedora.docker
1375
@@ -XXX,XX +XXX,XX @@ ENV PACKAGES \
1376
libpng-devel \
1377
librbd-devel \
1378
libseccomp-devel \
1379
- libssh2-devel \
1380
+ libssh-devel \
1381
libubsan \
1382
libusbx-devel \
1383
libxml2-devel \
1384
@@ -XXX,XX +XXX,XX @@ ENV PACKAGES \
1385
mingw32-gtk3 \
1386
mingw32-libjpeg-turbo \
1387
mingw32-libpng \
1388
- mingw32-libssh2 \
1389
mingw32-libtasn1 \
1390
mingw32-nettle \
1391
mingw32-pixman \
1392
@@ -XXX,XX +XXX,XX @@ ENV PACKAGES \
1393
mingw64-gtk3 \
1394
mingw64-libjpeg-turbo \
1395
mingw64-libpng \
1396
- mingw64-libssh2 \
1397
mingw64-libtasn1 \
1398
mingw64-nettle \
1399
mingw64-pixman \
1400
diff --git a/tests/docker/dockerfiles/ubuntu.docker b/tests/docker/dockerfiles/ubuntu.docker
1401
index XXXXXXX..XXXXXXX 100644
1402
--- a/tests/docker/dockerfiles/ubuntu.docker
1403
+++ b/tests/docker/dockerfiles/ubuntu.docker
1404
@@ -XXX,XX +XXX,XX @@ ENV PACKAGES flex bison \
1405
libsnappy-dev \
1406
libspice-protocol-dev \
1407
libspice-server-dev \
1408
- libssh2-1-dev \
1409
+ libssh-dev \
1410
libusb-1.0-0-dev \
1411
libusbredirhost-dev \
1412
libvdeplug-dev \
1413
diff --git a/tests/docker/dockerfiles/ubuntu1804.docker b/tests/docker/dockerfiles/ubuntu1804.docker
1414
index XXXXXXX..XXXXXXX 100644
1415
--- a/tests/docker/dockerfiles/ubuntu1804.docker
1416
+++ b/tests/docker/dockerfiles/ubuntu1804.docker
1417
@@ -XXX,XX +XXX,XX @@ ENV PACKAGES flex bison \
1418
libsnappy-dev \
1419
libspice-protocol-dev \
1420
libspice-server-dev \
1421
- libssh2-1-dev \
1422
+ libssh-dev \
1423
libusb-1.0-0-dev \
1424
libusbredirhost-dev \
1425
libvdeplug-dev \
1426
diff --git a/tests/qemu-iotests/207 b/tests/qemu-iotests/207
1427
index XXXXXXX..XXXXXXX 100755
1428
--- a/tests/qemu-iotests/207
1429
+++ b/tests/qemu-iotests/207
1430
@@ -XXX,XX +XXX,XX @@ with iotests.FilePath('t.img') as disk_path, \
1431
1432
iotests.img_info_log(remote_path)
1433
1434
- md5_key = subprocess.check_output(
1435
- 'ssh-keyscan -t rsa 127.0.0.1 2>/dev/null | grep -v "\\^#" | ' +
1436
- 'cut -d" " -f3 | base64 -d | md5sum -b | cut -d" " -f1',
1437
- shell=True).rstrip().decode('ascii')
1438
+ keys = subprocess.check_output(
1439
+ 'ssh-keyscan 127.0.0.1 2>/dev/null | grep -v "\\^#" | ' +
1440
+ 'cut -d" " -f3',
1441
+ shell=True).rstrip().decode('ascii').split('\n')
1442
+
1443
+ # Mappings of base64 representations to digests
1444
+ md5_keys = {}
1445
+ sha1_keys = {}
1446
+
1447
+ for key in keys:
1448
+ md5_keys[key] = subprocess.check_output(
1449
+ 'echo %s | base64 -d | md5sum -b | cut -d" " -f1' % key,
1450
+ shell=True).rstrip().decode('ascii')
1451
+
1452
+ sha1_keys[key] = subprocess.check_output(
1453
+ 'echo %s | base64 -d | sha1sum -b | cut -d" " -f1' % key,
1454
+ shell=True).rstrip().decode('ascii')
1455
1456
vm.launch()
1457
+
1458
+ # Find correct key first
1459
+ matching_key = None
1460
+ for key in keys:
1461
+ result = vm.qmp('blockdev-add',
1462
+ driver='ssh', node_name='node0', path=disk_path,
1463
+ server={
1464
+ 'host': '127.0.0.1',
1465
+ 'port': '22',
1466
+ }, host_key_check={
1467
+ 'mode': 'hash',
1468
+ 'type': 'md5',
1469
+ 'hash': md5_keys[key],
1470
+ })
1471
+
1472
+ if 'error' not in result:
1473
+ vm.qmp('blockdev-del', node_name='node0')
1474
+ matching_key = key
1475
+ break
1476
+
1477
+ if matching_key is None:
1478
+ vm.shutdown()
1479
+ iotests.notrun('Did not find a key that fits 127.0.0.1')
1480
+
1481
blockdev_create(vm, { 'driver': 'ssh',
1482
'location': {
1483
'path': disk_path,
1484
@@ -XXX,XX +XXX,XX @@ with iotests.FilePath('t.img') as disk_path, \
1485
'host-key-check': {
1486
'mode': 'hash',
1487
'type': 'md5',
1488
- 'hash': md5_key,
1489
+ 'hash': md5_keys[matching_key],
1490
}
1491
},
1492
'size': 8388608 })
1493
@@ -XXX,XX +XXX,XX @@ with iotests.FilePath('t.img') as disk_path, \
1494
1495
iotests.img_info_log(remote_path)
1496
1497
- sha1_key = subprocess.check_output(
1498
- 'ssh-keyscan -t rsa 127.0.0.1 2>/dev/null | grep -v "\\^#" | ' +
1499
- 'cut -d" " -f3 | base64 -d | sha1sum -b | cut -d" " -f1',
1500
- shell=True).rstrip().decode('ascii')
1501
-
1502
vm.launch()
1503
blockdev_create(vm, { 'driver': 'ssh',
1504
'location': {
1505
@@ -XXX,XX +XXX,XX @@ with iotests.FilePath('t.img') as disk_path, \
1506
'host-key-check': {
1507
'mode': 'hash',
1508
'type': 'sha1',
1509
- 'hash': sha1_key,
1510
+ 'hash': sha1_keys[matching_key],
1511
}
1512
},
1513
'size': 4194304 })
1514
diff --git a/tests/qemu-iotests/207.out b/tests/qemu-iotests/207.out
1515
index XXXXXXX..XXXXXXX 100644
1516
--- a/tests/qemu-iotests/207.out
1517
+++ b/tests/qemu-iotests/207.out
1518
@@ -XXX,XX +XXX,XX @@ virtual size: 4 MiB (4194304 bytes)
1519
1520
{"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}}}
84
{"return": {}}
1521
{"return": {}}
85
-{"error": {"class": "GenericError", "desc": "The snapshot already has a backing image"}}
1522
-Job failed: failed to open remote file '/this/is/not/an/existing/path': Failed opening remote file (libssh2 error code: -31)
86
+{"error": {"class": "GenericError", "desc": "The overlay already has a backing image"}}
1523
+Job failed: failed to open remote file '/this/is/not/an/existing/path': SFTP server: No such file (libssh error code: 1, sftp error code: 2)
87
1524
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
88
=== Invalid command - The node does not exist ===
1525
{"return": {}}
89
1526
90
--
1527
--
91
2.21.0
1528
2.21.0
92
1529
93
1530
diff view generated by jsdifflib
Deleted patch
1
Move img_convert()'s quiet flag into the ImgConvertState so it is
2
accessible by nested functions. -q dictates that it suppresses anything
3
but errors, so if those functions want to emit warnings, they need to
4
query this flag first. (There currently are no such warnings, but there
5
will be as of the next patch.)
6
1
7
Signed-off-by: Max Reitz <mreitz@redhat.com>
8
Reviewed-by: Eric Blake <eblake@redhat.com>
9
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
10
Message-id: 20190507203508.18026-2-mreitz@redhat.com
11
Signed-off-by: Max Reitz <mreitz@redhat.com>
12
---
13
qemu-img.c | 13 +++++++------
14
1 file changed, 7 insertions(+), 6 deletions(-)
15
16
diff --git a/qemu-img.c b/qemu-img.c
17
index XXXXXXX..XXXXXXX 100644
18
--- a/qemu-img.c
19
+++ b/qemu-img.c
20
@@ -XXX,XX +XXX,XX @@ typedef struct ImgConvertState {
21
int64_t target_backing_sectors; /* negative if unknown */
22
bool wr_in_order;
23
bool copy_range;
24
+ bool quiet;
25
int min_sparse;
26
int alignment;
27
size_t cluster_sectors;
28
@@ -XXX,XX +XXX,XX @@ static int img_convert(int argc, char **argv)
29
QDict *open_opts = NULL;
30
char *options = NULL;
31
Error *local_err = NULL;
32
- bool writethrough, src_writethrough, quiet = false, image_opts = false,
33
+ bool writethrough, src_writethrough, image_opts = false,
34
skip_create = false, progress = false, tgt_image_opts = false;
35
int64_t ret = -EINVAL;
36
bool force_share = false;
37
@@ -XXX,XX +XXX,XX @@ static int img_convert(int argc, char **argv)
38
src_cache = optarg;
39
break;
40
case 'q':
41
- quiet = true;
42
+ s.quiet = true;
43
break;
44
case 'n':
45
skip_create = true;
46
@@ -XXX,XX +XXX,XX @@ static int img_convert(int argc, char **argv)
47
}
48
49
/* Initialize before goto out */
50
- if (quiet) {
51
+ if (s.quiet) {
52
progress = false;
53
}
54
qemu_progress_init(progress, 1.0);
55
@@ -XXX,XX +XXX,XX @@ static int img_convert(int argc, char **argv)
56
57
for (bs_i = 0; bs_i < s.src_num; bs_i++) {
58
s.src[bs_i] = img_open(image_opts, argv[optind + bs_i],
59
- fmt, src_flags, src_writethrough, quiet,
60
+ fmt, src_flags, src_writethrough, s.quiet,
61
force_share);
62
if (!s.src[bs_i]) {
63
ret = -1;
64
@@ -XXX,XX +XXX,XX @@ static int img_convert(int argc, char **argv)
65
66
if (skip_create) {
67
s.target = img_open(tgt_image_opts, out_filename, out_fmt,
68
- flags, writethrough, quiet, false);
69
+ flags, writethrough, s.quiet, false);
70
} else {
71
/* TODO ultimately we should allow --target-image-opts
72
* to be used even when -n is not given.
73
@@ -XXX,XX +XXX,XX @@ static int img_convert(int argc, char **argv)
74
* to allow filenames in option syntax
75
*/
76
s.target = img_open_file(out_filename, open_opts, out_fmt,
77
- flags, writethrough, quiet, false);
78
+ flags, writethrough, s.quiet, false);
79
open_opts = NULL; /* blk_new_open will have freed it */
80
}
81
if (!s.target) {
82
--
83
2.21.0
84
85
diff view generated by jsdifflib
Deleted patch
1
This adds a salvaging mode (--salvage) to qemu-img convert which ignores
2
read errors and treats the respective areas as containing only zeroes.
3
This can be used for instance to at least partially recover the data
4
from terminally corrupted qcow2 images.
5
1
6
Signed-off-by: Max Reitz <mreitz@redhat.com>
7
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
8
Message-id: 20190507203508.18026-3-mreitz@redhat.com
9
Signed-off-by: Max Reitz <mreitz@redhat.com>
10
---
11
qemu-img.c | 90 +++++++++++++++++++++++++++++++++++++-----------
12
qemu-img-cmds.hx | 4 +--
13
qemu-img.texi | 4 +++
14
3 files changed, 75 insertions(+), 23 deletions(-)
15
16
diff --git a/qemu-img.c b/qemu-img.c
17
index XXXXXXX..XXXXXXX 100644
18
--- a/qemu-img.c
19
+++ b/qemu-img.c
20
@@ -XXX,XX +XXX,XX @@ enum {
21
OPTION_SIZE = 264,
22
OPTION_PREALLOCATION = 265,
23
OPTION_SHRINK = 266,
24
+ OPTION_SALVAGE = 267,
25
};
26
27
typedef enum OutputFormat {
28
@@ -XXX,XX +XXX,XX @@ typedef struct ImgConvertState {
29
int64_t target_backing_sectors; /* negative if unknown */
30
bool wr_in_order;
31
bool copy_range;
32
+ bool salvage;
33
bool quiet;
34
int min_sparse;
35
int alignment;
36
@@ -XXX,XX +XXX,XX @@ static int convert_iteration_sectors(ImgConvertState *s, int64_t sector_num)
37
}
38
39
if (s->sector_next_status <= sector_num) {
40
- int64_t count = n * BDRV_SECTOR_SIZE;
41
+ uint64_t offset = (sector_num - src_cur_offset) * BDRV_SECTOR_SIZE;
42
+ int64_t count;
43
44
- if (s->target_has_backing) {
45
+ do {
46
+ count = n * BDRV_SECTOR_SIZE;
47
+
48
+ if (s->target_has_backing) {
49
+ ret = bdrv_block_status(blk_bs(s->src[src_cur]), offset,
50
+ count, &count, NULL, NULL);
51
+ } else {
52
+ ret = bdrv_block_status_above(blk_bs(s->src[src_cur]), NULL,
53
+ offset, count, &count, NULL,
54
+ NULL);
55
+ }
56
+
57
+ if (ret < 0) {
58
+ if (s->salvage) {
59
+ if (n == 1) {
60
+ if (!s->quiet) {
61
+ warn_report("error while reading block status at "
62
+ "offset %" PRIu64 ": %s", offset,
63
+ strerror(-ret));
64
+ }
65
+ /* Just try to read the data, then */
66
+ ret = BDRV_BLOCK_DATA;
67
+ count = BDRV_SECTOR_SIZE;
68
+ } else {
69
+ /* Retry on a shorter range */
70
+ n = DIV_ROUND_UP(n, 4);
71
+ }
72
+ } else {
73
+ error_report("error while reading block status at offset "
74
+ "%" PRIu64 ": %s", offset, strerror(-ret));
75
+ return ret;
76
+ }
77
+ }
78
+ } while (ret < 0);
79
80
- ret = bdrv_block_status(blk_bs(s->src[src_cur]),
81
- (sector_num - src_cur_offset) *
82
- BDRV_SECTOR_SIZE,
83
- count, &count, NULL, NULL);
84
- } else {
85
- ret = bdrv_block_status_above(blk_bs(s->src[src_cur]), NULL,
86
- (sector_num - src_cur_offset) *
87
- BDRV_SECTOR_SIZE,
88
- count, &count, NULL, NULL);
89
- }
90
- if (ret < 0) {
91
- error_report("error while reading block status of sector %" PRId64
92
- ": %s", sector_num, strerror(-ret));
93
- return ret;
94
- }
95
n = DIV_ROUND_UP(count, BDRV_SECTOR_SIZE);
96
97
if (ret & BDRV_BLOCK_ZERO) {
98
@@ -XXX,XX +XXX,XX @@ static int convert_iteration_sectors(ImgConvertState *s, int64_t sector_num)
99
static int coroutine_fn convert_co_read(ImgConvertState *s, int64_t sector_num,
100
int nb_sectors, uint8_t *buf)
101
{
102
+ uint64_t single_read_until = 0;
103
int n, ret;
104
105
assert(nb_sectors <= s->buf_sectors);
106
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn convert_co_read(ImgConvertState *s, int64_t sector_num,
107
BlockBackend *blk;
108
int src_cur;
109
int64_t bs_sectors, src_cur_offset;
110
+ uint64_t offset;
111
112
/* In the case of compression with multiple source files, we can get a
113
* nb_sectors that spreads into the next part. So we must be able to
114
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn convert_co_read(ImgConvertState *s, int64_t sector_num,
115
blk = s->src[src_cur];
116
bs_sectors = s->src_sectors[src_cur];
117
118
+ offset = (sector_num - src_cur_offset) << BDRV_SECTOR_BITS;
119
+
120
n = MIN(nb_sectors, bs_sectors - (sector_num - src_cur_offset));
121
+ if (single_read_until > offset) {
122
+ n = 1;
123
+ }
124
125
- ret = blk_co_pread(
126
- blk, (sector_num - src_cur_offset) << BDRV_SECTOR_BITS,
127
- n << BDRV_SECTOR_BITS, buf, 0);
128
+ ret = blk_co_pread(blk, offset, n << BDRV_SECTOR_BITS, buf, 0);
129
if (ret < 0) {
130
- return ret;
131
+ if (s->salvage) {
132
+ if (n > 1) {
133
+ single_read_until = offset + (n << BDRV_SECTOR_BITS);
134
+ continue;
135
+ } else {
136
+ if (!s->quiet) {
137
+ warn_report("error while reading offset %" PRIu64
138
+ ": %s", offset, strerror(-ret));
139
+ }
140
+ memset(buf, 0, BDRV_SECTOR_SIZE);
141
+ }
142
+ } else {
143
+ return ret;
144
+ }
145
}
146
147
sector_num += n;
148
@@ -XXX,XX +XXX,XX @@ static int img_convert(int argc, char **argv)
149
{"image-opts", no_argument, 0, OPTION_IMAGE_OPTS},
150
{"force-share", no_argument, 0, 'U'},
151
{"target-image-opts", no_argument, 0, OPTION_TARGET_IMAGE_OPTS},
152
+ {"salvage", no_argument, 0, OPTION_SALVAGE},
153
{0, 0, 0, 0}
154
};
155
c = getopt_long(argc, argv, ":hf:O:B:Cco:l:S:pt:T:qnm:WU",
156
@@ -XXX,XX +XXX,XX @@ static int img_convert(int argc, char **argv)
157
case OPTION_IMAGE_OPTS:
158
image_opts = true;
159
break;
160
+ case OPTION_SALVAGE:
161
+ s.salvage = true;
162
+ break;
163
case OPTION_TARGET_IMAGE_OPTS:
164
tgt_image_opts = true;
165
break;
166
@@ -XXX,XX +XXX,XX @@ static int img_convert(int argc, char **argv)
167
goto fail_getopt;
168
}
169
170
+ if (s.copy_range && s.salvage) {
171
+ error_report("Cannot use copy offloading in salvaging mode");
172
+ goto fail_getopt;
173
+ }
174
+
175
if (tgt_image_opts && !skip_create) {
176
error_report("--target-image-opts requires use of -n flag");
177
goto fail_getopt;
178
diff --git a/qemu-img-cmds.hx b/qemu-img-cmds.hx
179
index XXXXXXX..XXXXXXX 100644
180
--- a/qemu-img-cmds.hx
181
+++ b/qemu-img-cmds.hx
182
@@ -XXX,XX +XXX,XX @@ STEXI
183
ETEXI
184
185
DEF("convert", img_convert,
186
- "convert [--object objectdef] [--image-opts] [--target-image-opts] [-U] [-C] [-c] [-p] [-q] [-n] [-f fmt] [-t cache] [-T src_cache] [-O output_fmt] [-B backing_file] [-o options] [-l snapshot_param] [-S sparse_size] [-m num_coroutines] [-W] filename [filename2 [...]] output_filename")
187
+ "convert [--object objectdef] [--image-opts] [--target-image-opts] [-U] [-C] [-c] [-p] [-q] [-n] [-f fmt] [-t cache] [-T src_cache] [-O output_fmt] [-B backing_file] [-o options] [-l snapshot_param] [-S sparse_size] [-m num_coroutines] [-W] [--salvage] filename [filename2 [...]] output_filename")
188
STEXI
189
-@item convert [--object @var{objectdef}] [--image-opts] [--target-image-opts] [-U] [-C] [-c] [-p] [-q] [-n] [-f @var{fmt}] [-t @var{cache}] [-T @var{src_cache}] [-O @var{output_fmt}] [-B @var{backing_file}] [-o @var{options}] [-l @var{snapshot_param}] [-S @var{sparse_size}] [-m @var{num_coroutines}] [-W] @var{filename} [@var{filename2} [...]] @var{output_filename}
190
+@item convert [--object @var{objectdef}] [--image-opts] [--target-image-opts] [-U] [-C] [-c] [-p] [-q] [-n] [-f @var{fmt}] [-t @var{cache}] [-T @var{src_cache}] [-O @var{output_fmt}] [-B @var{backing_file}] [-o @var{options}] [-l @var{snapshot_param}] [-S @var{sparse_size}] [-m @var{num_coroutines}] [-W] [--salvage] @var{filename} [@var{filename2} [...]] @var{output_filename}
191
ETEXI
192
193
DEF("create", img_create,
194
diff --git a/qemu-img.texi b/qemu-img.texi
195
index XXXXXXX..XXXXXXX 100644
196
--- a/qemu-img.texi
197
+++ b/qemu-img.texi
198
@@ -XXX,XX +XXX,XX @@ improve performance if the data is remote, such as with NFS or iSCSI backends,
199
but will not automatically sparsify zero sectors, and may result in a fully
200
allocated target image depending on the host support for getting allocation
201
information.
202
+@item --salvage
203
+Try to ignore I/O errors when reading. Unless in quiet mode (@code{-q}), errors
204
+will still be printed. Areas that cannot be read from the source will be
205
+treated as containing only zeroes.
206
@end table
207
208
Parameters to dd subcommand:
209
--
210
2.21.0
211
212
diff view generated by jsdifflib
1
Together with @iotypes and @sector, this can be used to trap e.g. the
1
Tests should place their files into the test directory. This includes
2
first read or write access to a certain sector without having to know
2
Unix sockets. 205 currently fails to do so, which prevents it from
3
what happens internally in the block layer, i.e. which "real" events
3
being run concurrently.
4
happen right before such an access.
5
4
6
Signed-off-by: Max Reitz <mreitz@redhat.com>
5
Signed-off-by: Max Reitz <mreitz@redhat.com>
7
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
6
Message-id: 20190618210238.9524-1-mreitz@redhat.com
8
Message-id: 20190507203508.18026-5-mreitz@redhat.com
7
Reviewed-by: Eric Blake <eblake@redhat.com>
9
Signed-off-by: Max Reitz <mreitz@redhat.com>
8
Signed-off-by: Max Reitz <mreitz@redhat.com>
10
---
9
---
11
qapi/block-core.json | 4 +++-
10
tests/qemu-iotests/205 | 2 +-
12
block/blkdebug.c | 2 ++
11
1 file changed, 1 insertion(+), 1 deletion(-)
13
2 files changed, 5 insertions(+), 1 deletion(-)
14
12
15
diff --git a/qapi/block-core.json b/qapi/block-core.json
13
diff --git a/tests/qemu-iotests/205 b/tests/qemu-iotests/205
16
index XXXXXXX..XXXXXXX 100644
14
index XXXXXXX..XXXXXXX 100755
17
--- a/qapi/block-core.json
15
--- a/tests/qemu-iotests/205
18
+++ b/qapi/block-core.json
16
+++ b/tests/qemu-iotests/205
19
@@ -XXX,XX +XXX,XX @@
17
@@ -XXX,XX +XXX,XX @@ import iotests
20
#
18
import time
21
# @cluster_alloc_space: an allocation of file space for a cluster (since 4.1)
19
from iotests import qemu_img_create, qemu_io, filter_qemu_io, QemuIoInteractive
22
#
20
23
+# @none: triggers once at creation of the blkdebug node (since 4.1)
21
-nbd_sock = 'nbd_sock'
24
+#
22
+nbd_sock = os.path.join(iotests.test_dir, 'nbd_sock')
25
# Since: 2.9
23
nbd_uri = 'nbd+unix:///exp?socket=' + nbd_sock
26
##
24
disk = os.path.join(iotests.test_dir, 'disk')
27
{ 'enum': 'BlkdebugEvent', 'prefix': 'BLKDBG',
25
28
@@ -XXX,XX +XXX,XX @@
29
'pwritev_rmw_tail', 'pwritev_rmw_after_tail', 'pwritev',
30
'pwritev_zero', 'pwritev_done', 'empty_image_prepare',
31
'l1_shrink_write_table', 'l1_shrink_free_l2_clusters',
32
- 'cor_write', 'cluster_alloc_space'] }
33
+ 'cor_write', 'cluster_alloc_space', 'none'] }
34
35
##
36
# @BlkdebugIOType:
37
diff --git a/block/blkdebug.c b/block/blkdebug.c
38
index XXXXXXX..XXXXXXX 100644
39
--- a/block/blkdebug.c
40
+++ b/block/blkdebug.c
41
@@ -XXX,XX +XXX,XX @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags,
42
goto out;
43
}
44
45
+ bdrv_debug_event(bs, BLKDBG_NONE);
46
+
47
ret = 0;
48
out:
49
if (ret < 0) {
50
--
26
--
51
2.21.0
27
2.21.0
52
28
53
29
diff view generated by jsdifflib
Deleted patch
1
Signed-off-by: Max Reitz <mreitz@redhat.com>
2
Reviewed-by: Eric Blake <eblake@redhat.com>
3
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
4
Message-id: 20190507203508.18026-6-mreitz@redhat.com
5
Signed-off-by: Max Reitz <mreitz@redhat.com>
6
---
7
qapi/block-core.json | 5 ++++-
8
block/blkdebug.c | 8 ++++++++
9
2 files changed, 12 insertions(+), 1 deletion(-)
10
1
11
diff --git a/qapi/block-core.json b/qapi/block-core.json
12
index XXXXXXX..XXXXXXX 100644
13
--- a/qapi/block-core.json
14
+++ b/qapi/block-core.json
15
@@ -XXX,XX +XXX,XX @@
16
#
17
# @flush: .bdrv_co_flush_to_disk()
18
#
19
+# @block-status: .bdrv_co_block_status()
20
+#
21
# Since: 4.1
22
##
23
{ 'enum': 'BlkdebugIOType', 'prefix': 'BLKDEBUG_IO_TYPE',
24
- 'data': [ 'read', 'write', 'write-zeroes', 'discard', 'flush' ] }
25
+ 'data': [ 'read', 'write', 'write-zeroes', 'discard', 'flush',
26
+ 'block-status' ] }
27
28
##
29
# @BlkdebugInjectErrorOptions:
30
diff --git a/block/blkdebug.c b/block/blkdebug.c
31
index XXXXXXX..XXXXXXX 100644
32
--- a/block/blkdebug.c
33
+++ b/block/blkdebug.c
34
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn blkdebug_co_block_status(BlockDriverState *bs,
35
int64_t *map,
36
BlockDriverState **file)
37
{
38
+ int err;
39
+
40
assert(QEMU_IS_ALIGNED(offset | bytes, bs->bl.request_alignment));
41
+
42
+ err = rule_check(bs, offset, bytes, BLKDEBUG_IO_TYPE_BLOCK_STATUS);
43
+ if (err) {
44
+ return err;
45
+ }
46
+
47
return bdrv_co_block_status_from_file(bs, want_zero, offset, bytes,
48
pnum, map, file);
49
}
50
--
51
2.21.0
52
53
diff view generated by jsdifflib