1
The following changes since commit 5375af3cd7b8adcc10c18d8083b7be63976c9645:
1
The following changes since commit 474f3938d79ab36b9231c9ad3b5a9314c2aeacde:
2
2
3
Merge remote-tracking branch 'remotes/mst/tags/for_upstream' into staging (2020-05-04 15:51:09 +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-2020-05-05
7
https://github.com/XanClic/qemu.git tags/pull-block-2019-06-24
8
8
9
for you to fetch changes up to 4ce5dd3e9b5ee0fac18625860eb3727399ee965e:
9
for you to fetch changes up to ab5d4a30f7f3803ca5106b370969c1b7b54136f8:
10
10
11
block/block-copy: use aio-task-pool API (2020-05-05 14:03:28 +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
- Asynchronous copying for block-copy (i.e., the backup job)
15
- The SSH block driver now uses libssh instead of libssh2
16
- Allow resizing of qcow2 images when they have internal snapshots
16
- The VMDK block driver gets read-only support for the seSparse
17
- iotests: Logging improvements for Python tests
17
subformat
18
- iotest 153 fix, and block comment cleanups
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
Eric Blake (4):
26
Anton Nefedov (1):
22
block: Add blk_new_with_bs() helper
27
iotest 134: test cluster-misaligned encrypted write
23
qcow2: Allow resize of images with internal snapshots
24
qcow2: Tweak comment about bitmaps vs. resize
25
block: Comment cleanups
26
28
27
John Snow (14):
29
Klaus Birkelund Jensen (1):
28
iotests: do a light delinting
30
nvme: do not advertise support for unsupported arbitration mechanism
29
iotests: don't use 'format' for drive_add
30
iotests: ignore import warnings from pylint
31
iotests: replace mutable list default args
32
iotests: add pylintrc file
33
iotests: alphabetize standard imports
34
iotests: drop pre-Python 3.4 compatibility code
35
iotests: touch up log function signature
36
iotests: limit line length to 79 chars
37
iotests: add hmp helper with logging
38
iotests: add script_initialize
39
iotest 258: use script_main
40
iotests: Mark verify functions as private
41
iotests: use python logging for iotests.log()
42
31
43
Maxim Levitsky (1):
32
Max Reitz (1):
44
Fix iotest 153
33
iotests: Fix 205 for concurrent runs
45
34
46
Vladimir Sementsov-Ogievskiy (5):
35
Pino Toscano (1):
47
block/block-copy: rename in-flight requests to tasks
36
ssh: switch from libssh2 to libssh
48
block/block-copy: alloc task on each iteration
49
block/block-copy: add state pointer to BlockCopyTask
50
block/block-copy: refactor task creation
51
block/block-copy: use aio-task-pool API
52
37
53
include/sysemu/block-backend.h | 2 +
38
Sam Eiderman (3):
54
block/block-backend.c | 23 +++
39
vmdk: Fix comment regarding max l1_size coverage
55
block/block-copy.c | 279 +++++++++++++++++--------
40
vmdk: Reduce the max bound for L1 table size
56
block/crypto.c | 9 +-
41
vmdk: Add read-only support for seSparse snapshots
57
block/io.c | 3 +-
42
58
block/parallels.c | 8 +-
43
Vladimir Sementsov-Ogievskiy (1):
59
block/qcow.c | 8 +-
44
blockdev: enable non-root nodes for transaction drive-backup source
60
block/qcow2-refcount.c | 2 +-
45
61
block/qcow2-snapshot.c | 20 +-
46
configure | 65 +-
62
block/qcow2.c | 45 ++--
47
block/Makefile.objs | 6 +-
63
block/qed.c | 8 +-
48
block/ssh.c | 652 ++++++++++--------
64
block/sheepdog.c | 10 +-
49
block/vmdk.c | 372 +++++++++-
65
block/vdi.c | 8 +-
50
blockdev.c | 2 +-
66
block/vhdx.c | 8 +-
51
hw/block/nvme.c | 1 -
67
block/vmdk.c | 9 +-
52
.travis.yml | 4 +-
68
block/vpc.c | 8 +-
53
block/trace-events | 14 +-
69
block/vvfat.c | 10 +-
54
docs/qemu-block-drivers.texi | 2 +-
70
blockdev.c | 8 +-
55
.../dockerfiles/debian-win32-cross.docker | 1 -
71
blockjob.c | 7 +-
56
.../dockerfiles/debian-win64-cross.docker | 1 -
72
tests/qemu-iotests/001 | 2 +-
57
tests/docker/dockerfiles/fedora.docker | 4 +-
73
tests/qemu-iotests/030 | 4 +-
58
tests/docker/dockerfiles/ubuntu.docker | 2 +-
74
tests/qemu-iotests/052 | 2 +-
59
tests/docker/dockerfiles/ubuntu1804.docker | 2 +-
75
tests/qemu-iotests/055 | 3 +-
60
tests/qemu-iotests/059.out | 2 +-
76
tests/qemu-iotests/061 | 35 ++++
61
tests/qemu-iotests/134 | 9 +
77
tests/qemu-iotests/061.out | 28 +++
62
tests/qemu-iotests/134.out | 10 +
78
tests/qemu-iotests/134 | 2 +-
63
tests/qemu-iotests/205 | 2 +-
79
tests/qemu-iotests/149 | 3 +-
64
tests/qemu-iotests/207 | 54 +-
80
tests/qemu-iotests/153 | 2 +-
65
tests/qemu-iotests/207.out | 2 +-
81
tests/qemu-iotests/153.out | 12 +-
66
20 files changed, 823 insertions(+), 384 deletions(-)
82
tests/qemu-iotests/155 | 2 +-
83
tests/qemu-iotests/188 | 2 +-
84
tests/qemu-iotests/194 | 4 +-
85
tests/qemu-iotests/202 | 4 +-
86
tests/qemu-iotests/203 | 4 +-
87
tests/qemu-iotests/206 | 2 +-
88
tests/qemu-iotests/207 | 6 +-
89
tests/qemu-iotests/208 | 2 +-
90
tests/qemu-iotests/209 | 2 +-
91
tests/qemu-iotests/210 | 6 +-
92
tests/qemu-iotests/211 | 6 +-
93
tests/qemu-iotests/212 | 6 +-
94
tests/qemu-iotests/213 | 6 +-
95
tests/qemu-iotests/216 | 4 +-
96
tests/qemu-iotests/218 | 2 +-
97
tests/qemu-iotests/219 | 2 +-
98
tests/qemu-iotests/222 | 7 +-
99
tests/qemu-iotests/224 | 4 +-
100
tests/qemu-iotests/228 | 6 +-
101
tests/qemu-iotests/234 | 4 +-
102
tests/qemu-iotests/235 | 4 +-
103
tests/qemu-iotests/236 | 2 +-
104
tests/qemu-iotests/237 | 2 +-
105
tests/qemu-iotests/238 | 2 +
106
tests/qemu-iotests/242 | 2 +-
107
tests/qemu-iotests/245 | 1 +
108
tests/qemu-iotests/245.out | 10 +-
109
tests/qemu-iotests/246 | 2 +-
110
tests/qemu-iotests/248 | 2 +-
111
tests/qemu-iotests/254 | 2 +-
112
tests/qemu-iotests/255 | 2 +-
113
tests/qemu-iotests/256 | 2 +-
114
tests/qemu-iotests/258 | 10 +-
115
tests/qemu-iotests/260 | 4 +-
116
tests/qemu-iotests/262 | 4 +-
117
tests/qemu-iotests/264 | 4 +-
118
tests/qemu-iotests/274 | 4 +-
119
tests/qemu-iotests/277 | 2 +
120
tests/qemu-iotests/280 | 8 +-
121
tests/qemu-iotests/283 | 4 +-
122
tests/qemu-iotests/iotests.py | 366 ++++++++++++++++++++-------------
123
tests/qemu-iotests/pylintrc | 26 +++
124
71 files changed, 728 insertions(+), 386 deletions(-)
125
create mode 100644 tests/qemu-iotests/pylintrc
126
67
127
--
68
--
128
2.26.2
69
2.21.0
129
70
130
71
diff view generated by jsdifflib
Deleted patch
1
From: John Snow <jsnow@redhat.com>
2
1
3
This doesn't fix everything in here, but it does help clean up the
4
pylint report considerably.
5
6
This should be 100% style changes only; the intent is to make pylint
7
more useful by working on establishing a baseline for iotests that we
8
can gate against in the future.
9
10
Signed-off-by: John Snow <jsnow@redhat.com>
11
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
12
Reviewed-by: Max Reitz <mreitz@redhat.com>
13
Message-Id: <20200331000014.11581-2-jsnow@redhat.com>
14
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
15
Signed-off-by: Max Reitz <mreitz@redhat.com>
16
---
17
tests/qemu-iotests/iotests.py | 83 ++++++++++++++++++-----------------
18
1 file changed, 43 insertions(+), 40 deletions(-)
19
20
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
21
index XXXXXXX..XXXXXXX 100644
22
--- a/tests/qemu-iotests/iotests.py
23
+++ b/tests/qemu-iotests/iotests.py
24
@@ -XXX,XX +XXX,XX @@
25
# along with this program. If not, see <http://www.gnu.org/licenses/>.
26
#
27
28
-import errno
29
import os
30
import re
31
import subprocess
32
-import string
33
import unittest
34
import sys
35
import struct
36
@@ -XXX,XX +XXX,XX @@ import faulthandler
37
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
38
from qemu import qtest
39
40
-assert sys.version_info >= (3,6)
41
+assert sys.version_info >= (3, 6)
42
43
faulthandler.enable()
44
45
@@ -XXX,XX +XXX,XX @@ def qemu_img_log(*args):
46
return result
47
48
def img_info_log(filename, filter_path=None, imgopts=False, extra_args=[]):
49
- args = [ 'info' ]
50
+ args = ['info']
51
if imgopts:
52
args.append('--image-opts')
53
else:
54
- args += [ '-f', imgfmt ]
55
+ args += ['-f', imgfmt]
56
args += extra_args
57
args.append(filename)
58
59
@@ -XXX,XX +XXX,XX @@ class QemuIoInteractive:
60
# quit command is in close(), '\n' is added automatically
61
assert '\n' not in cmd
62
cmd = cmd.strip()
63
- assert cmd != 'q' and cmd != 'quit'
64
+ assert cmd not in ('q', 'quit')
65
self._p.stdin.write(cmd + '\n')
66
self._p.stdin.flush()
67
return self._read_output()
68
@@ -XXX,XX +XXX,XX @@ def qemu_nbd_early_pipe(*args):
69
sys.stderr.write('qemu-nbd received signal %i: %s\n' %
70
(-exitcode,
71
' '.join(qemu_nbd_args + ['--fork'] + list(args))))
72
- if exitcode == 0:
73
- return exitcode, ''
74
- else:
75
- return exitcode, subp.communicate()[0]
76
+
77
+ return exitcode, subp.communicate()[0] if exitcode else ''
78
79
def qemu_nbd_popen(*args):
80
'''Run qemu-nbd in daemon mode and return the parent's exit code'''
81
@@ -XXX,XX +XXX,XX @@ def filter_qmp(qmsg, filter_fn):
82
items = qmsg.items()
83
84
for k, v in items:
85
- if isinstance(v, list) or isinstance(v, dict):
86
+ if isinstance(v, (dict, list)):
87
qmsg[k] = filter_qmp(v, filter_fn)
88
else:
89
qmsg[k] = filter_fn(k, v)
90
@@ -XXX,XX +XXX,XX @@ def filter_testfiles(msg):
91
return msg.replace(prefix, 'TEST_DIR/PID-')
92
93
def filter_qmp_testfiles(qmsg):
94
- def _filter(key, value):
95
+ def _filter(_key, value):
96
if is_str(value):
97
return filter_testfiles(value)
98
return value
99
@@ -XXX,XX +XXX,XX @@ def filter_imgfmt(msg):
100
return msg.replace(imgfmt, 'IMGFMT')
101
102
def filter_qmp_imgfmt(qmsg):
103
- def _filter(key, value):
104
+ def _filter(_key, value):
105
if is_str(value):
106
return filter_imgfmt(value)
107
return value
108
@@ -XXX,XX +XXX,XX @@ def log(msg, filters=[], indent=None):
109
If indent is provided, JSON serializable messages are pretty-printed.'''
110
for flt in filters:
111
msg = flt(msg)
112
- if isinstance(msg, dict) or isinstance(msg, list):
113
+ if isinstance(msg, (dict, list)):
114
# Python < 3.4 needs to know not to add whitespace when pretty-printing:
115
separators = (', ', ': ') if indent is None else (',', ': ')
116
# Don't sort if it's already sorted
117
@@ -XXX,XX +XXX,XX @@ def log(msg, filters=[], indent=None):
118
print(msg)
119
120
class Timeout:
121
- def __init__(self, seconds, errmsg = "Timeout"):
122
+ def __init__(self, seconds, errmsg="Timeout"):
123
self.seconds = seconds
124
self.errmsg = errmsg
125
def __enter__(self):
126
signal.signal(signal.SIGALRM, self.timeout)
127
signal.setitimer(signal.ITIMER_REAL, self.seconds)
128
return self
129
- def __exit__(self, type, value, traceback):
130
+ def __exit__(self, exc_type, value, traceback):
131
signal.setitimer(signal.ITIMER_REAL, 0)
132
return False
133
def timeout(self, signum, frame):
134
@@ -XXX,XX +XXX,XX @@ class Timeout:
135
def file_pattern(name):
136
return "{0}-{1}".format(os.getpid(), name)
137
138
-class FilePaths(object):
139
+class FilePaths:
140
"""
141
FilePaths is an auto-generated filename that cleans itself up.
142
143
@@ -XXX,XX +XXX,XX @@ class VM(qtest.QEMUQtestMachine):
144
self.pause_drive(drive, "write_aio")
145
return
146
self.qmp('human-monitor-command',
147
- command_line='qemu-io %s "break %s bp_%s"' % (drive, event, drive))
148
+ command_line='qemu-io %s "break %s bp_%s"' % (drive, event, drive))
149
150
def resume_drive(self, drive):
151
self.qmp('human-monitor-command',
152
- command_line='qemu-io %s "remove_break bp_%s"' % (drive, drive))
153
+ command_line='qemu-io %s "remove_break bp_%s"' % (drive, drive))
154
155
def hmp_qemu_io(self, drive, cmd):
156
'''Write to a given drive using an HMP command'''
157
@@ -XXX,XX +XXX,XX @@ class VM(qtest.QEMUQtestMachine):
158
if output is None:
159
output = dict()
160
if isinstance(obj, list):
161
- for i in range(len(obj)):
162
- self.flatten_qmp_object(obj[i], output, basestr + str(i) + '.')
163
+ for i, item in enumerate(obj):
164
+ self.flatten_qmp_object(item, output, basestr + str(i) + '.')
165
elif isinstance(obj, dict):
166
for key in obj:
167
self.flatten_qmp_object(obj[key], output, basestr + key + '.')
168
@@ -XXX,XX +XXX,XX @@ class VM(qtest.QEMUQtestMachine):
169
170
for bitmap in bitmaps[node_name]:
171
if bitmap.get('name', '') == bitmap_name:
172
- if recording is None:
173
- return bitmap
174
- elif bitmap.get('recording') == recording:
175
+ if recording is None or bitmap.get('recording') == recording:
176
return bitmap
177
return None
178
179
@@ -XXX,XX +XXX,XX @@ class VM(qtest.QEMUQtestMachine):
180
assert node is not None, 'Cannot follow path %s%s' % (root, path)
181
182
try:
183
- node_id = next(edge['child'] for edge in graph['edges'] \
184
- if edge['parent'] == node['id'] and
185
- edge['name'] == child_name)
186
+ node_id = next(edge['child'] for edge in graph['edges']
187
+ if (edge['parent'] == node['id'] and
188
+ edge['name'] == child_name))
189
+
190
+ node = next(node for node in graph['nodes']
191
+ if node['id'] == node_id)
192
193
- node = next(node for node in graph['nodes'] \
194
- if node['id'] == node_id)
195
except StopIteration:
196
node = None
197
198
@@ -XXX,XX +XXX,XX @@ index_re = re.compile(r'([^\[]+)\[([^\]]+)\]')
199
class QMPTestCase(unittest.TestCase):
200
'''Abstract base class for QMP test cases'''
201
202
+ def __init__(self, *args, **kwargs):
203
+ super().__init__(*args, **kwargs)
204
+ # Many users of this class set a VM property we rely on heavily
205
+ # in the methods below.
206
+ self.vm = None
207
+
208
def dictpath(self, d, path):
209
'''Traverse a path in a nested dict'''
210
for component in path.split('/'):
211
@@ -XXX,XX +XXX,XX @@ class QMPTestCase(unittest.TestCase):
212
else:
213
self.assertEqual(result, value,
214
'"%s" is "%s", expected "%s"'
215
- % (path, str(result), str(value)))
216
+ % (path, str(result), str(value)))
217
218
def assert_no_active_block_jobs(self):
219
result = self.vm.qmp('query-block-jobs')
220
@@ -XXX,XX +XXX,XX @@ class QMPTestCase(unittest.TestCase):
221
"""Issue a query-named-block-nodes and assert node_name and/or
222
file_name is present in the result"""
223
def check_equal_or_none(a, b):
224
- return a == None or b == None or a == b
225
+ return a is None or b is None or a == b
226
assert node_name or file_name
227
result = self.vm.qmp('query-named-block-nodes')
228
for x in result["return"]:
229
if check_equal_or_none(x.get("node-name"), node_name) and \
230
check_equal_or_none(x.get("file"), file_name):
231
return
232
- self.assertTrue(False, "Cannot find %s %s in result:\n%s" % \
233
- (node_name, file_name, result))
234
+ self.fail("Cannot find %s %s in result:\n%s" %
235
+ (node_name, file_name, result))
236
237
def assert_json_filename_equal(self, json_filename, reference):
238
'''Asserts that the given filename is a json: filename and that its
239
@@ -XXX,XX +XXX,XX @@ class QMPTestCase(unittest.TestCase):
240
self.assert_qmp(event, 'data/error', error)
241
self.assert_no_active_block_jobs()
242
return event
243
- elif event['event'] == 'JOB_STATUS_CHANGE':
244
+ if event['event'] == 'JOB_STATUS_CHANGE':
245
self.assert_qmp(event, 'data/id', drive)
246
247
def wait_ready(self, drive='drive0'):
248
- '''Wait until a block job BLOCK_JOB_READY event'''
249
- f = {'data': {'type': 'mirror', 'device': drive } }
250
- event = self.vm.event_wait(name='BLOCK_JOB_READY', match=f)
251
+ """Wait until a BLOCK_JOB_READY event, and return the event."""
252
+ f = {'data': {'type': 'mirror', 'device': drive}}
253
+ return self.vm.event_wait(name='BLOCK_JOB_READY', match=f)
254
255
def wait_ready_and_cancel(self, drive='drive0'):
256
self.wait_ready(drive=drive)
257
@@ -XXX,XX +XXX,XX @@ class QMPTestCase(unittest.TestCase):
258
for job in result['return']:
259
if job['device'] == job_id:
260
found = True
261
- if job['paused'] == True and job['busy'] == False:
262
+ if job['paused'] and not job['busy']:
263
return job
264
break
265
assert found
266
@@ -XXX,XX +XXX,XX @@ def qemu_pipe(*args):
267
universal_newlines=True)
268
exitcode = subp.wait()
269
if exitcode < 0:
270
- sys.stderr.write('qemu received signal %i: %s\n' % (-exitcode,
271
- ' '.join(args)))
272
+ sys.stderr.write('qemu received signal %i: %s\n' %
273
+ (-exitcode, ' '.join(args)))
274
return subp.communicate()[0]
275
276
def supported_formats(read_only=False):
277
@@ -XXX,XX +XXX,XX @@ def skip_if_unsupported(required_formats=[], read_only=False):
278
if usf_list:
279
test_case.case_skip('{}: formats {} are not whitelisted'.format(
280
test_case, usf_list))
281
+ return None
282
else:
283
return func(test_case, *args, **kwargs)
284
return func_wrapper
285
@@ -XXX,XX +XXX,XX @@ def skip_if_user_is_root(func):
286
def func_wrapper(*args, **kwargs):
287
if os.getuid() == 0:
288
case_notrun('{}: cannot be run as root'.format(args[0]))
289
+ return None
290
else:
291
return func(*args, **kwargs)
292
return func_wrapper
293
--
294
2.26.2
295
296
diff view generated by jsdifflib
Deleted patch
1
From: John Snow <jsnow@redhat.com>
2
1
3
It shadows (with a different type) the built-in format.
4
Use something else.
5
6
Signed-off-by: John Snow <jsnow@redhat.com>
7
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
8
Reviewed-by: Max Reitz <mreitz@redhat.com>
9
Message-Id: <20200331000014.11581-3-jsnow@redhat.com>
10
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
11
Signed-off-by: Max Reitz <mreitz@redhat.com>
12
---
13
tests/qemu-iotests/055 | 3 ++-
14
tests/qemu-iotests/iotests.py | 6 +++---
15
2 files changed, 5 insertions(+), 4 deletions(-)
16
17
diff --git a/tests/qemu-iotests/055 b/tests/qemu-iotests/055
18
index XXXXXXX..XXXXXXX 100755
19
--- a/tests/qemu-iotests/055
20
+++ b/tests/qemu-iotests/055
21
@@ -XXX,XX +XXX,XX @@ class TestDriveCompression(iotests.QMPTestCase):
22
qemu_img('create', '-f', fmt, blockdev_target_img,
23
str(TestDriveCompression.image_len), *args)
24
if attach_target:
25
- self.vm.add_drive(blockdev_target_img, format=fmt, interface="none")
26
+ self.vm.add_drive(blockdev_target_img,
27
+ img_format=fmt, interface="none")
28
29
self.vm.launch()
30
31
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
32
index XXXXXXX..XXXXXXX 100644
33
--- a/tests/qemu-iotests/iotests.py
34
+++ b/tests/qemu-iotests/iotests.py
35
@@ -XXX,XX +XXX,XX @@ class VM(qtest.QEMUQtestMachine):
36
self._args.append(opts)
37
return self
38
39
- def add_drive(self, path, opts='', interface='virtio', format=imgfmt):
40
+ def add_drive(self, path, opts='', interface='virtio', img_format=imgfmt):
41
'''Add a virtio-blk drive to the VM'''
42
options = ['if=%s' % interface,
43
'id=drive%d' % self._num_drives]
44
45
if path is not None:
46
options.append('file=%s' % path)
47
- options.append('format=%s' % format)
48
+ options.append('format=%s' % img_format)
49
options.append('cache=%s' % cachemode)
50
options.append('aio=%s' % aiomode)
51
52
if opts:
53
options.append(opts)
54
55
- if format == 'luks' and 'key-secret' not in opts:
56
+ if img_format == 'luks' and 'key-secret' not in opts:
57
# default luks support
58
if luks_default_secret_object not in self._args:
59
self.add_object(luks_default_secret_object)
60
--
61
2.26.2
62
63
diff view generated by jsdifflib
Deleted patch
1
From: John Snow <jsnow@redhat.com>
2
1
3
The right way to solve this is to come up with a virtual environment
4
infrastructure that sets all the paths correctly, and/or to create
5
installable python modules that can be imported normally.
6
7
That's hard, so just silence this error for now.
8
9
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
10
Reviewed-by: Max Reitz <mreitz@redhat.com>
11
Signed-off-by: John Snow <jsnow@redhat.com>
12
Message-Id: <20200331000014.11581-4-jsnow@redhat.com>
13
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
14
Signed-off-by: Max Reitz <mreitz@redhat.com>
15
---
16
tests/qemu-iotests/iotests.py | 1 +
17
1 file changed, 1 insertion(+)
18
19
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
20
index XXXXXXX..XXXXXXX 100644
21
--- a/tests/qemu-iotests/iotests.py
22
+++ b/tests/qemu-iotests/iotests.py
23
@@ -XXX,XX +XXX,XX @@ import io
24
from collections import OrderedDict
25
import faulthandler
26
27
+# pylint: disable=import-error, wrong-import-position
28
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
29
from qemu import qtest
30
31
--
32
2.26.2
33
34
diff view generated by jsdifflib
Deleted patch
1
From: John Snow <jsnow@redhat.com>
2
1
3
It's bad hygiene: if we modify this list, it will be modified across all
4
invocations.
5
6
(Remaining bad usages are fixed in a subsequent patch which changes the
7
function signature anyway.)
8
9
Signed-off-by: John Snow <jsnow@redhat.com>
10
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
11
Reviewed-by: Max Reitz <mreitz@redhat.com>
12
Message-Id: <20200331000014.11581-5-jsnow@redhat.com>
13
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
14
Signed-off-by: Max Reitz <mreitz@redhat.com>
15
---
16
tests/qemu-iotests/iotests.py | 24 ++++++++++++------------
17
1 file changed, 12 insertions(+), 12 deletions(-)
18
19
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
20
index XXXXXXX..XXXXXXX 100644
21
--- a/tests/qemu-iotests/iotests.py
22
+++ b/tests/qemu-iotests/iotests.py
23
@@ -XXX,XX +XXX,XX @@ def qemu_img_log(*args):
24
log(result, filters=[filter_testfiles])
25
return result
26
27
-def img_info_log(filename, filter_path=None, imgopts=False, extra_args=[]):
28
+def img_info_log(filename, filter_path=None, imgopts=False, extra_args=()):
29
args = ['info']
30
if imgopts:
31
args.append('--image-opts')
32
@@ -XXX,XX +XXX,XX @@ def filter_qmp_imgfmt(qmsg):
33
return value
34
return filter_qmp(qmsg, _filter)
35
36
-def log(msg, filters=[], indent=None):
37
+def log(msg, filters=(), indent=None):
38
'''Logs either a string message or a JSON serializable message (like QMP).
39
If indent is provided, JSON serializable messages are pretty-printed.'''
40
for flt in filters:
41
@@ -XXX,XX +XXX,XX @@ class VM(qtest.QEMUQtestMachine):
42
result.append(filter_qmp_event(ev))
43
return result
44
45
- def qmp_log(self, cmd, filters=[], indent=None, **kwargs):
46
+ def qmp_log(self, cmd, filters=(), indent=None, **kwargs):
47
full_cmd = OrderedDict((
48
("execute", cmd),
49
("arguments", ordered_qmp(kwargs))
50
@@ -XXX,XX +XXX,XX @@ def case_notrun(reason):
51
open('%s/%s.casenotrun' % (output_dir, seq), 'a').write(
52
' [case not run] ' + reason + '\n')
53
54
-def verify_image_format(supported_fmts=[], unsupported_fmts=[]):
55
+def verify_image_format(supported_fmts=(), unsupported_fmts=()):
56
assert not (supported_fmts and unsupported_fmts)
57
58
if 'generic' in supported_fmts and \
59
@@ -XXX,XX +XXX,XX @@ def verify_image_format(supported_fmts=[], unsupported_fmts=[]):
60
if not_sup or (imgfmt in unsupported_fmts):
61
notrun('not suitable for this image format: %s' % imgfmt)
62
63
-def verify_protocol(supported=[], unsupported=[]):
64
+def verify_protocol(supported=(), unsupported=()):
65
assert not (supported and unsupported)
66
67
if 'generic' in supported:
68
@@ -XXX,XX +XXX,XX @@ def verify_platform(supported=None, unsupported=None):
69
if not any((sys.platform.startswith(x) for x in supported)):
70
notrun('not suitable for this OS: %s' % sys.platform)
71
72
-def verify_cache_mode(supported_cache_modes=[]):
73
+def verify_cache_mode(supported_cache_modes=()):
74
if supported_cache_modes and (cachemode not in supported_cache_modes):
75
notrun('not suitable for this cache mode: %s' % cachemode)
76
77
-def verify_aio_mode(supported_aio_modes=[]):
78
+def verify_aio_mode(supported_aio_modes=()):
79
if supported_aio_modes and (aiomode not in supported_aio_modes):
80
notrun('not suitable for this aio mode: %s' % aiomode)
81
82
@@ -XXX,XX +XXX,XX @@ def supported_formats(read_only=False):
83
84
return supported_formats.formats[read_only]
85
86
-def skip_if_unsupported(required_formats=[], read_only=False):
87
+def skip_if_unsupported(required_formats=(), read_only=False):
88
'''Skip Test Decorator
89
Runs the test if all the required formats are whitelisted'''
90
def skip_test_decorator(func):
91
@@ -XXX,XX +XXX,XX @@ def execute_unittest(output, verbosity, debug):
92
sys.stderr.write(out)
93
94
def execute_test(test_function=None,
95
- supported_fmts=[],
96
+ supported_fmts=(),
97
supported_platforms=None,
98
- supported_cache_modes=[], supported_aio_modes={},
99
- unsupported_fmts=[], supported_protocols=[],
100
- unsupported_protocols=[]):
101
+ supported_cache_modes=(), supported_aio_modes=(),
102
+ unsupported_fmts=(), supported_protocols=(),
103
+ unsupported_protocols=()):
104
"""Run either unittest or script-style tests."""
105
106
# We are using TEST_DIR and QEMU_DEFAULT_MACHINE as proxies to
107
--
108
2.26.2
109
110
diff view generated by jsdifflib
Deleted patch
1
From: John Snow <jsnow@redhat.com>
2
1
3
This allows others to get repeatable results with pylint. If you run
4
`pylint iotests.py`, you should see a 100% pass.
5
6
Signed-off-by: John Snow <jsnow@redhat.com>
7
Reviewed-by: Max Reitz <mreitz@redhat.com>
8
Message-Id: <20200331000014.11581-6-jsnow@redhat.com>
9
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
10
Signed-off-by: Max Reitz <mreitz@redhat.com>
11
---
12
tests/qemu-iotests/pylintrc | 22 ++++++++++++++++++++++
13
1 file changed, 22 insertions(+)
14
create mode 100644 tests/qemu-iotests/pylintrc
15
16
diff --git a/tests/qemu-iotests/pylintrc b/tests/qemu-iotests/pylintrc
17
new file mode 100644
18
index XXXXXXX..XXXXXXX
19
--- /dev/null
20
+++ b/tests/qemu-iotests/pylintrc
21
@@ -XXX,XX +XXX,XX @@
22
+[MESSAGES CONTROL]
23
+
24
+# Disable the message, report, category or checker with the given id(s). You
25
+# can either give multiple identifiers separated by comma (,) or put this
26
+# option multiple times (only on the command line, not in the configuration
27
+# file where it should appear only once). You can also use "--disable=all" to
28
+# disable everything first and then reenable specific checks. For example, if
29
+# you want to run only the similarities checker, you can use "--disable=all
30
+# --enable=similarities". If you want to run only the classes checker, but have
31
+# no Warning level messages displayed, use "--disable=all --enable=classes
32
+# --disable=W".
33
+disable=invalid-name,
34
+ no-else-return,
35
+ too-few-public-methods,
36
+ too-many-arguments,
37
+ too-many-branches,
38
+ too-many-lines,
39
+ too-many-locals,
40
+ too-many-public-methods,
41
+ # These are temporary, and should be removed:
42
+ line-too-long,
43
+ missing-docstring,
44
--
45
2.26.2
46
47
diff view generated by jsdifflib
Deleted patch
1
From: John Snow <jsnow@redhat.com>
2
1
3
I had to fix a merge conflict, so do this tiny harmless thing while I'm
4
here.
5
6
Signed-off-by: John Snow <jsnow@redhat.com>
7
Reviewed-by: Max Reitz <mreitz@redhat.com>
8
Message-Id: <20200331000014.11581-7-jsnow@redhat.com>
9
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
10
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
11
Signed-off-by: Max Reitz <mreitz@redhat.com>
12
---
13
tests/qemu-iotests/iotests.py | 18 +++++++++---------
14
1 file changed, 9 insertions(+), 9 deletions(-)
15
16
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
17
index XXXXXXX..XXXXXXX 100644
18
--- a/tests/qemu-iotests/iotests.py
19
+++ b/tests/qemu-iotests/iotests.py
20
@@ -XXX,XX +XXX,XX @@
21
# along with this program. If not, see <http://www.gnu.org/licenses/>.
22
#
23
24
+import atexit
25
+from collections import OrderedDict
26
+import faulthandler
27
+import io
28
+import json
29
+import logging
30
import os
31
import re
32
+import signal
33
+import struct
34
import subprocess
35
-import unittest
36
import sys
37
-import struct
38
-import json
39
-import signal
40
-import logging
41
-import atexit
42
-import io
43
-from collections import OrderedDict
44
-import faulthandler
45
+import unittest
46
47
# pylint: disable=import-error, wrong-import-position
48
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
49
--
50
2.26.2
51
52
diff view generated by jsdifflib
Deleted patch
1
From: John Snow <jsnow@redhat.com>
2
1
3
We no longer need to accommodate <3.4, drop this code.
4
(The lines were > 79 chars and it stood out.)
5
6
Signed-off-by: John Snow <jsnow@redhat.com>
7
Reviewed-by: Max Reitz <mreitz@redhat.com>
8
Message-Id: <20200331000014.11581-8-jsnow@redhat.com>
9
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
10
Signed-off-by: Max Reitz <mreitz@redhat.com>
11
---
12
tests/qemu-iotests/iotests.py | 5 +----
13
1 file changed, 1 insertion(+), 4 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 @@ def log(msg, filters=(), indent=None):
20
for flt in filters:
21
msg = flt(msg)
22
if isinstance(msg, (dict, list)):
23
- # Python < 3.4 needs to know not to add whitespace when pretty-printing:
24
- separators = (', ', ': ') if indent is None else (',', ': ')
25
# Don't sort if it's already sorted
26
do_sort = not isinstance(msg, OrderedDict)
27
- print(json.dumps(msg, sort_keys=do_sort,
28
- indent=indent, separators=separators))
29
+ print(json.dumps(msg, sort_keys=do_sort, indent=indent))
30
else:
31
print(msg)
32
33
--
34
2.26.2
35
36
diff view generated by jsdifflib
Deleted patch
1
From: John Snow <jsnow@redhat.com>
2
1
3
Representing nested, recursive data structures in mypy is notoriously
4
difficult; the best we can reliably do right now is denote the leaf
5
types as "Any" while describing the general shape of the data.
6
7
Regardless, this fully annotates the log() function.
8
9
Typing notes:
10
11
TypeVar is a Type variable that can optionally be constrained by a
12
sequence of possible types. This variable is bound to a specific type
13
per-invocation, like a Generic.
14
15
log() behaves as log<Msg>() now, where the incoming type informs the
16
signature it expects for any filter arguments passed in. If Msg is a
17
str, then filter should take and return a str.
18
19
Signed-off-by: John Snow <jsnow@redhat.com>
20
Reviewed-by: Max Reitz <mreitz@redhat.com>
21
Message-Id: <20200331000014.11581-9-jsnow@redhat.com>
22
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
23
Signed-off-by: Max Reitz <mreitz@redhat.com>
24
---
25
tests/qemu-iotests/iotests.py | 14 +++++++++++---
26
1 file changed, 11 insertions(+), 3 deletions(-)
27
28
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
29
index XXXXXXX..XXXXXXX 100644
30
--- a/tests/qemu-iotests/iotests.py
31
+++ b/tests/qemu-iotests/iotests.py
32
@@ -XXX,XX +XXX,XX @@ import signal
33
import struct
34
import subprocess
35
import sys
36
+from typing import (Any, Callable, Dict, Iterable, List, Optional, TypeVar)
37
import unittest
38
39
# pylint: disable=import-error, wrong-import-position
40
@@ -XXX,XX +XXX,XX @@ def filter_qmp_imgfmt(qmsg):
41
return value
42
return filter_qmp(qmsg, _filter)
43
44
-def log(msg, filters=(), indent=None):
45
- '''Logs either a string message or a JSON serializable message (like QMP).
46
- If indent is provided, JSON serializable messages are pretty-printed.'''
47
+
48
+Msg = TypeVar('Msg', Dict[str, Any], List[Any], str)
49
+
50
+def log(msg: Msg,
51
+ filters: Iterable[Callable[[Msg], Msg]] = (),
52
+ indent: Optional[int] = None) -> None:
53
+ """
54
+ Logs either a string message or a JSON serializable message (like QMP).
55
+ If indent is provided, JSON serializable messages are pretty-printed.
56
+ """
57
for flt in filters:
58
msg = flt(msg)
59
if isinstance(msg, (dict, list)):
60
--
61
2.26.2
62
63
diff view generated by jsdifflib
Deleted patch
1
From: John Snow <jsnow@redhat.com>
2
1
3
79 is the PEP8 recommendation. This recommendation works well for
4
reading patch diffs in TUI email clients.
5
6
Signed-off-by: John Snow <jsnow@redhat.com>
7
Reviewed-by: Max Reitz <mreitz@redhat.com>
8
Message-Id: <20200331000014.11581-10-jsnow@redhat.com>
9
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
10
Signed-off-by: Max Reitz <mreitz@redhat.com>
11
---
12
tests/qemu-iotests/iotests.py | 64 +++++++++++++++++++++++------------
13
tests/qemu-iotests/pylintrc | 6 +++-
14
2 files changed, 47 insertions(+), 23 deletions(-)
15
16
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
17
index XXXXXXX..XXXXXXX 100644
18
--- a/tests/qemu-iotests/iotests.py
19
+++ b/tests/qemu-iotests/iotests.py
20
@@ -XXX,XX +XXX,XX @@ luks_default_key_secret_opt = 'key-secret=keysec0'
21
def qemu_img(*args):
22
'''Run qemu-img and return the exit code'''
23
devnull = open('/dev/null', 'r+')
24
- exitcode = subprocess.call(qemu_img_args + list(args), stdin=devnull, stdout=devnull)
25
+ exitcode = subprocess.call(qemu_img_args + list(args),
26
+ stdin=devnull, stdout=devnull)
27
if exitcode < 0:
28
- sys.stderr.write('qemu-img received signal %i: %s\n' % (-exitcode, ' '.join(qemu_img_args + list(args))))
29
+ sys.stderr.write('qemu-img received signal %i: %s\n'
30
+ % (-exitcode, ' '.join(qemu_img_args + list(args))))
31
return exitcode
32
33
def ordered_qmp(qmsg, conv_keys=True):
34
@@ -XXX,XX +XXX,XX @@ def qemu_img_verbose(*args):
35
'''Run qemu-img without suppressing its output and return the exit code'''
36
exitcode = subprocess.call(qemu_img_args + list(args))
37
if exitcode < 0:
38
- sys.stderr.write('qemu-img received signal %i: %s\n' % (-exitcode, ' '.join(qemu_img_args + list(args))))
39
+ sys.stderr.write('qemu-img received signal %i: %s\n'
40
+ % (-exitcode, ' '.join(qemu_img_args + list(args))))
41
return exitcode
42
43
def qemu_img_pipe(*args):
44
@@ -XXX,XX +XXX,XX @@ def qemu_img_pipe(*args):
45
universal_newlines=True)
46
exitcode = subp.wait()
47
if exitcode < 0:
48
- sys.stderr.write('qemu-img received signal %i: %s\n' % (-exitcode, ' '.join(qemu_img_args + list(args))))
49
+ sys.stderr.write('qemu-img received signal %i: %s\n'
50
+ % (-exitcode, ' '.join(qemu_img_args + list(args))))
51
return subp.communicate()[0]
52
53
def qemu_img_log(*args):
54
@@ -XXX,XX +XXX,XX @@ def qemu_io(*args):
55
universal_newlines=True)
56
exitcode = subp.wait()
57
if exitcode < 0:
58
- sys.stderr.write('qemu-io received signal %i: %s\n' % (-exitcode, ' '.join(args)))
59
+ sys.stderr.write('qemu-io received signal %i: %s\n'
60
+ % (-exitcode, ' '.join(args)))
61
return subp.communicate()[0]
62
63
def qemu_io_log(*args):
64
@@ -XXX,XX +XXX,XX @@ win32_re = re.compile(r"\r")
65
def filter_win32(msg):
66
return win32_re.sub("", msg)
67
68
-qemu_io_re = re.compile(r"[0-9]* ops; [0-9\/:. sec]* \([0-9\/.inf]* [EPTGMKiBbytes]*\/sec and [0-9\/.inf]* ops\/sec\)")
69
+qemu_io_re = re.compile(r"[0-9]* ops; [0-9\/:. sec]* "
70
+ r"\([0-9\/.inf]* [EPTGMKiBbytes]*\/sec "
71
+ r"and [0-9\/.inf]* ops\/sec\)")
72
def filter_qemu_io(msg):
73
msg = filter_win32(msg)
74
- return qemu_io_re.sub("X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)", msg)
75
+ return qemu_io_re.sub("X ops; XX:XX:XX.X "
76
+ "(XXX YYY/sec and XXX ops/sec)", msg)
77
78
chown_re = re.compile(r"chown [0-9]+:[0-9]+")
79
def filter_chown(msg):
80
@@ -XXX,XX +XXX,XX @@ def filter_img_info(output, filename):
81
line = filter_testfiles(line)
82
line = line.replace(imgfmt, 'IMGFMT')
83
line = re.sub('iters: [0-9]+', 'iters: XXX', line)
84
- line = re.sub('uuid: [-a-f0-9]+', 'uuid: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX', line)
85
+ line = re.sub('uuid: [-a-f0-9]+',
86
+ 'uuid: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX',
87
+ line)
88
line = re.sub('cid: [0-9]+', 'cid: XXXXXXXXXX', line)
89
lines.append(line)
90
return '\n'.join(lines)
91
@@ -XXX,XX +XXX,XX @@ class VM(qtest.QEMUQtestMachine):
92
self.pause_drive(drive, "write_aio")
93
return
94
self.qmp('human-monitor-command',
95
- command_line='qemu-io %s "break %s bp_%s"' % (drive, event, drive))
96
+ command_line='qemu-io %s "break %s bp_%s"'
97
+ % (drive, event, drive))
98
99
def resume_drive(self, drive):
100
self.qmp('human-monitor-command',
101
- command_line='qemu-io %s "remove_break bp_%s"' % (drive, drive))
102
+ command_line='qemu-io %s "remove_break bp_%s"'
103
+ % (drive, drive))
104
105
def hmp_qemu_io(self, drive, cmd):
106
'''Write to a given drive using an HMP command'''
107
@@ -XXX,XX +XXX,XX @@ class QMPTestCase(unittest.TestCase):
108
idx = int(idx)
109
110
if not isinstance(d, dict) or component not in d:
111
- self.fail('failed path traversal for "%s" in "%s"' % (path, str(d)))
112
+ self.fail(f'failed path traversal for "{path}" in "{d}"')
113
d = d[component]
114
115
if m:
116
if not isinstance(d, list):
117
- self.fail('path component "%s" in "%s" is not a list in "%s"' % (component, path, str(d)))
118
+ self.fail(f'path component "{component}" in "{path}" '
119
+ f'is not a list in "{d}"')
120
try:
121
d = d[idx]
122
except IndexError:
123
- self.fail('invalid index "%s" in path "%s" in "%s"' % (idx, path, str(d)))
124
+ self.fail(f'invalid index "{idx}" in path "{path}" '
125
+ f'in "{d}"')
126
return d
127
128
def assert_qmp_absent(self, d, path):
129
@@ -XXX,XX +XXX,XX @@ class QMPTestCase(unittest.TestCase):
130
'''Asserts that the given filename is a json: filename and that its
131
content is equal to the given reference object'''
132
self.assertEqual(json_filename[:5], 'json:')
133
- self.assertEqual(self.vm.flatten_qmp_object(json.loads(json_filename[5:])),
134
- self.vm.flatten_qmp_object(reference))
135
+ self.assertEqual(
136
+ self.vm.flatten_qmp_object(json.loads(json_filename[5:])),
137
+ self.vm.flatten_qmp_object(reference)
138
+ )
139
140
- def cancel_and_wait(self, drive='drive0', force=False, resume=False, wait=60.0):
141
+ def cancel_and_wait(self, drive='drive0', force=False,
142
+ resume=False, wait=60.0):
143
'''Cancel a block job and wait for it to finish, returning the event'''
144
result = self.vm.qmp('block-job-cancel', device=drive, force=force)
145
self.assert_qmp(result, 'return', {})
146
@@ -XXX,XX +XXX,XX @@ class QMPTestCase(unittest.TestCase):
147
self.assert_no_active_block_jobs()
148
return result
149
150
- def wait_until_completed(self, drive='drive0', check_offset=True, wait=60.0,
151
- error=None):
152
+ def wait_until_completed(self, drive='drive0', check_offset=True,
153
+ wait=60.0, error=None):
154
'''Wait for a block job to finish, returning the event'''
155
while True:
156
for event in self.vm.get_qmp_events(wait=wait):
157
@@ -XXX,XX +XXX,XX @@ def verify_quorum():
158
notrun('quorum support missing')
159
160
def qemu_pipe(*args):
161
- '''Run qemu with an option to print something and exit (e.g. a help option),
162
- and return its output'''
163
+ """
164
+ Run qemu with an option to print something and exit (e.g. a help option).
165
+
166
+ :return: QEMU's stdout output.
167
+ """
168
args = [qemu_prog] + qemu_opts + list(args)
169
subp = subprocess.Popen(args, stdout=subprocess.PIPE,
170
stderr=subprocess.STDOUT,
171
@@ -XXX,XX +XXX,XX @@ def skip_if_unsupported(required_formats=(), read_only=False):
172
173
usf_list = list(set(fmts) - set(supported_formats(read_only)))
174
if usf_list:
175
- test_case.case_skip('{}: formats {} are not whitelisted'.format(
176
- test_case, usf_list))
177
+ msg = f'{test_case}: formats {usf_list} are not whitelisted'
178
+ test_case.case_skip(msg)
179
return None
180
else:
181
return func(test_case, *args, **kwargs)
182
diff --git a/tests/qemu-iotests/pylintrc b/tests/qemu-iotests/pylintrc
183
index XXXXXXX..XXXXXXX 100644
184
--- a/tests/qemu-iotests/pylintrc
185
+++ b/tests/qemu-iotests/pylintrc
186
@@ -XXX,XX +XXX,XX @@ disable=invalid-name,
187
too-many-locals,
188
too-many-public-methods,
189
# These are temporary, and should be removed:
190
- line-too-long,
191
missing-docstring,
192
+
193
+[FORMAT]
194
+
195
+# Maximum number of characters on a single line.
196
+max-line-length=79
197
--
198
2.26.2
199
200
diff view generated by jsdifflib
Deleted patch
1
From: John Snow <jsnow@redhat.com>
2
1
3
Minor cleanup for HMP functions; helps with line length and consolidates
4
HMP helpers through one implementation function.
5
6
Although we are adding a universal toggle to turn QMP logging on or off,
7
many existing callers to hmp functions don't expect that output to be
8
logged, which causes quite a few changes in the test output.
9
10
For now, offer a use_log parameter.
11
12
Typing notes:
13
14
QMPResponse is just an alias for Dict[str, Any]. It holds no special
15
meanings and it is not a formal subtype of Dict[str, Any]. It is best
16
thought of as a lexical synonym.
17
18
We may well wish to add stricter subtypes in the future for certain
19
shapes of data that are not formalized as Python objects, at which point
20
we can simply retire the alias and allow mypy to more strictly check
21
usages of the name.
22
23
Signed-off-by: John Snow <jsnow@redhat.com>
24
Message-Id: <20200331000014.11581-11-jsnow@redhat.com>
25
Reviewed-by: Max Reitz <mreitz@redhat.com>
26
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
27
Signed-off-by: Max Reitz <mreitz@redhat.com>
28
---
29
tests/qemu-iotests/iotests.py | 39 +++++++++++++++++++++--------------
30
1 file changed, 24 insertions(+), 15 deletions(-)
31
32
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
33
index XXXXXXX..XXXXXXX 100644
34
--- a/tests/qemu-iotests/iotests.py
35
+++ b/tests/qemu-iotests/iotests.py
36
@@ -XXX,XX +XXX,XX @@ from qemu import qtest
37
38
assert sys.version_info >= (3, 6)
39
40
+# Type Aliases
41
+QMPResponse = Dict[str, Any]
42
+
43
+
44
faulthandler.enable()
45
46
# This will not work if arguments contain spaces but is necessary if we
47
@@ -XXX,XX +XXX,XX @@ class VM(qtest.QEMUQtestMachine):
48
self._args.append(addr)
49
return self
50
51
- def pause_drive(self, drive, event=None):
52
- '''Pause drive r/w operations'''
53
+ def hmp(self, command_line: str, use_log: bool = False) -> QMPResponse:
54
+ cmd = 'human-monitor-command'
55
+ kwargs = {'command-line': command_line}
56
+ if use_log:
57
+ return self.qmp_log(cmd, **kwargs)
58
+ else:
59
+ return self.qmp(cmd, **kwargs)
60
+
61
+ def pause_drive(self, drive: str, event: Optional[str] = None) -> None:
62
+ """Pause drive r/w operations"""
63
if not event:
64
self.pause_drive(drive, "read_aio")
65
self.pause_drive(drive, "write_aio")
66
return
67
- self.qmp('human-monitor-command',
68
- command_line='qemu-io %s "break %s bp_%s"'
69
- % (drive, event, drive))
70
-
71
- def resume_drive(self, drive):
72
- self.qmp('human-monitor-command',
73
- command_line='qemu-io %s "remove_break bp_%s"'
74
- % (drive, drive))
75
-
76
- def hmp_qemu_io(self, drive, cmd):
77
- '''Write to a given drive using an HMP command'''
78
- return self.qmp('human-monitor-command',
79
- command_line='qemu-io %s "%s"' % (drive, cmd))
80
+ self.hmp(f'qemu-io {drive} "break {event} bp_{drive}"')
81
+
82
+ def resume_drive(self, drive: str) -> None:
83
+ """Resume drive r/w operations"""
84
+ self.hmp(f'qemu-io {drive} "remove_break bp_{drive}"')
85
+
86
+ def hmp_qemu_io(self, drive: str, cmd: str,
87
+ use_log: bool = False) -> QMPResponse:
88
+ """Write to a given drive using an HMP command"""
89
+ return self.hmp(f'qemu-io {drive} "{cmd}"', use_log=use_log)
90
91
def flatten_qmp_object(self, obj, output=None, basestr=''):
92
if output is None:
93
--
94
2.26.2
95
96
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
From: Klaus Birkelund Jensen <klaus@birkelund.eu>
2
2
3
Instead of just relying on the comment "Called only on full-dirty
3
The device mistakenly reports that the Weighted Round Robin with Urgent
4
region" in block_copy_task_create() let's move initial dirty area
4
Priority Class arbitration mechanism is supported.
5
search directly to block_copy_task_create(). Let's also use effective
6
bdrv_dirty_bitmap_next_dirty_area instead of looping through all
7
non-dirty clusters.
8
5
9
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
6
It is not.
10
Reviewed-by: Max Reitz <mreitz@redhat.com>
7
11
Message-Id: <20200429130847.28124-5-vsementsov@virtuozzo.com>
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>
12
Signed-off-by: Max Reitz <mreitz@redhat.com>
11
Signed-off-by: Max Reitz <mreitz@redhat.com>
13
---
12
---
14
block/block-copy.c | 80 ++++++++++++++++++++++++++--------------------
13
hw/block/nvme.c | 1 -
15
1 file changed, 46 insertions(+), 34 deletions(-)
14
1 file changed, 1 deletion(-)
16
15
17
diff --git a/block/block-copy.c b/block/block-copy.c
16
diff --git a/hw/block/nvme.c b/hw/block/nvme.c
18
index XXXXXXX..XXXXXXX 100644
17
index XXXXXXX..XXXXXXX 100644
19
--- a/block/block-copy.c
18
--- a/hw/block/nvme.c
20
+++ b/block/block-copy.c
19
+++ b/hw/block/nvme.c
21
@@ -XXX,XX +XXX,XX @@ typedef struct BlockCopyTask {
20
@@ -XXX,XX +XXX,XX @@ static void nvme_realize(PCIDevice *pci_dev, Error **errp)
22
CoQueue wait_queue; /* coroutines blocked on this task */
21
n->bar.cap = 0;
23
} BlockCopyTask;
22
NVME_CAP_SET_MQES(n->bar.cap, 0x7ff);
24
23
NVME_CAP_SET_CQR(n->bar.cap, 1);
25
+static int64_t task_end(BlockCopyTask *task)
24
- NVME_CAP_SET_AMS(n->bar.cap, 1);
26
+{
25
NVME_CAP_SET_TO(n->bar.cap, 0xf);
27
+ return task->offset + task->bytes;
26
NVME_CAP_SET_CSS(n->bar.cap, 1);
28
+}
27
NVME_CAP_SET_MPSMAX(n->bar.cap, 4);
29
+
30
typedef struct BlockCopyState {
31
/*
32
* BdrvChild objects are not owned or managed by block-copy. They are
33
@@ -XXX,XX +XXX,XX @@ static bool coroutine_fn block_copy_wait_one(BlockCopyState *s, int64_t offset,
34
return true;
35
}
36
37
-/* Called only on full-dirty region */
38
+/*
39
+ * Search for the first dirty area in offset/bytes range and create task at
40
+ * the beginning of it.
41
+ */
42
static BlockCopyTask *block_copy_task_create(BlockCopyState *s,
43
int64_t offset, int64_t bytes)
44
{
45
- BlockCopyTask *task = g_new(BlockCopyTask, 1);
46
+ BlockCopyTask *task;
47
48
+ if (!bdrv_dirty_bitmap_next_dirty_area(s->copy_bitmap,
49
+ offset, offset + bytes,
50
+ s->copy_size, &offset, &bytes))
51
+ {
52
+ return NULL;
53
+ }
54
+
55
+ /* region is dirty, so no existent tasks possible in it */
56
assert(!find_conflicting_task(s, offset, bytes));
57
58
bdrv_reset_dirty_bitmap(s->copy_bitmap, offset, bytes);
59
s->in_flight_bytes += bytes;
60
61
+ task = g_new(BlockCopyTask, 1);
62
*task = (BlockCopyTask) {
63
.s = s,
64
.offset = offset,
65
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn block_copy_dirty_clusters(BlockCopyState *s,
66
{
67
int ret = 0;
68
bool found_dirty = false;
69
+ int64_t end = offset + bytes;
70
71
/*
72
* block_copy() user is responsible for keeping source and target in same
73
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn block_copy_dirty_clusters(BlockCopyState *s,
74
75
while (bytes) {
76
g_autofree BlockCopyTask *task = NULL;
77
- int64_t next_zero, cur_bytes, status_bytes;
78
+ int64_t status_bytes;
79
80
- if (!bdrv_dirty_bitmap_get(s->copy_bitmap, offset)) {
81
- trace_block_copy_skip(s, offset);
82
- offset += s->cluster_size;
83
- bytes -= s->cluster_size;
84
- continue; /* already copied */
85
+ task = block_copy_task_create(s, offset, bytes);
86
+ if (!task) {
87
+ /* No more dirty bits in the bitmap */
88
+ trace_block_copy_skip_range(s, offset, bytes);
89
+ break;
90
+ }
91
+ if (task->offset > offset) {
92
+ trace_block_copy_skip_range(s, offset, task->offset - offset);
93
}
94
95
found_dirty = true;
96
97
- cur_bytes = MIN(bytes, s->copy_size);
98
-
99
- next_zero = bdrv_dirty_bitmap_next_zero(s->copy_bitmap, offset,
100
- cur_bytes);
101
- if (next_zero >= 0) {
102
- assert(next_zero > offset); /* offset is dirty */
103
- assert(next_zero < offset + cur_bytes); /* no need to do MIN() */
104
- cur_bytes = next_zero - offset;
105
- }
106
- task = block_copy_task_create(s, offset, cur_bytes);
107
-
108
- ret = block_copy_block_status(s, offset, cur_bytes, &status_bytes);
109
+ ret = block_copy_block_status(s, task->offset, task->bytes,
110
+ &status_bytes);
111
assert(ret >= 0); /* never fail */
112
- cur_bytes = MIN(cur_bytes, status_bytes);
113
- block_copy_task_shrink(task, cur_bytes);
114
+ if (status_bytes < task->bytes) {
115
+ block_copy_task_shrink(task, status_bytes);
116
+ }
117
if (s->skip_unallocated && !(ret & BDRV_BLOCK_ALLOCATED)) {
118
block_copy_task_end(task, 0);
119
progress_set_remaining(s->progress,
120
bdrv_get_dirty_count(s->copy_bitmap) +
121
s->in_flight_bytes);
122
- trace_block_copy_skip_range(s, offset, status_bytes);
123
- offset += status_bytes;
124
- bytes -= status_bytes;
125
+ trace_block_copy_skip_range(s, task->offset, task->bytes);
126
+ offset = task_end(task);
127
+ bytes = end - offset;
128
continue;
129
}
130
131
- trace_block_copy_process(s, offset);
132
+ trace_block_copy_process(s, task->offset);
133
134
- co_get_from_shres(s->mem, cur_bytes);
135
- ret = block_copy_do_copy(s, offset, cur_bytes, ret & BDRV_BLOCK_ZERO,
136
- error_is_read);
137
- co_put_to_shres(s->mem, cur_bytes);
138
+ co_get_from_shres(s->mem, task->bytes);
139
+ ret = block_copy_do_copy(s, task->offset, task->bytes,
140
+ ret & BDRV_BLOCK_ZERO, error_is_read);
141
+ co_put_to_shres(s->mem, task->bytes);
142
block_copy_task_end(task, ret);
143
if (ret < 0) {
144
return ret;
145
}
146
147
- progress_work_done(s->progress, cur_bytes);
148
- s->progress_bytes_callback(cur_bytes, s->progress_opaque);
149
- offset += cur_bytes;
150
- bytes -= cur_bytes;
151
+ progress_work_done(s->progress, task->bytes);
152
+ s->progress_bytes_callback(task->bytes, s->progress_opaque);
153
+ offset = task_end(task);
154
+ bytes = end - offset;
155
}
156
157
return found_dirty;
158
--
28
--
159
2.26.2
29
2.21.0
160
30
161
31
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
2
3
We are going to use aio-task-pool API and extend in-flight request
3
We forget to enable it for transaction .prepare, while it is already
4
structure to be a successor of AioTask, so rename things appropriately.
4
enabled in do_drive_backup since commit a2d665c1bc362
5
"blockdev: loosen restrictions on drive-backup source node"
5
6
6
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
7
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
7
Reviewed-by: Max Reitz <mreitz@redhat.com>
8
Message-id: 20190618140804.59214-1-vsementsov@virtuozzo.com
8
Message-Id: <20200429130847.28124-2-vsementsov@virtuozzo.com>
9
Reviewed-by: John Snow <jsnow@redhat.com>
9
Signed-off-by: Max Reitz <mreitz@redhat.com>
10
Signed-off-by: Max Reitz <mreitz@redhat.com>
10
---
11
---
11
block/block-copy.c | 98 +++++++++++++++++++++++-----------------------
12
blockdev.c | 2 +-
12
1 file changed, 48 insertions(+), 50 deletions(-)
13
1 file changed, 1 insertion(+), 1 deletion(-)
13
14
14
diff --git a/block/block-copy.c b/block/block-copy.c
15
diff --git a/blockdev.c b/blockdev.c
15
index XXXXXXX..XXXXXXX 100644
16
index XXXXXXX..XXXXXXX 100644
16
--- a/block/block-copy.c
17
--- a/blockdev.c
17
+++ b/block/block-copy.c
18
+++ b/blockdev.c
18
@@ -XXX,XX +XXX,XX @@
19
@@ -XXX,XX +XXX,XX @@ static void drive_backup_prepare(BlkActionState *common, Error **errp)
19
#define BLOCK_COPY_MAX_BUFFER (1 * MiB)
20
assert(common->action->type == TRANSACTION_ACTION_KIND_DRIVE_BACKUP);
20
#define BLOCK_COPY_MAX_MEM (128 * MiB)
21
backup = common->action->u.drive_backup.data;
21
22
22
-typedef struct BlockCopyInFlightReq {
23
- bs = qmp_get_root_bs(backup->device, errp);
23
+typedef struct BlockCopyTask {
24
+ bs = bdrv_lookup_bs(backup->device, backup->device, errp);
24
int64_t offset;
25
if (!bs) {
25
int64_t bytes;
26
- QLIST_ENTRY(BlockCopyInFlightReq) list;
27
- CoQueue wait_queue; /* coroutines blocked on this request */
28
-} BlockCopyInFlightReq;
29
+ QLIST_ENTRY(BlockCopyTask) list;
30
+ CoQueue wait_queue; /* coroutines blocked on this task */
31
+} BlockCopyTask;
32
33
typedef struct BlockCopyState {
34
/*
35
@@ -XXX,XX +XXX,XX @@ typedef struct BlockCopyState {
36
bool use_copy_range;
37
int64_t copy_size;
38
uint64_t len;
39
- QLIST_HEAD(, BlockCopyInFlightReq) inflight_reqs;
40
+ QLIST_HEAD(, BlockCopyTask) tasks;
41
42
BdrvRequestFlags write_flags;
43
44
@@ -XXX,XX +XXX,XX @@ typedef struct BlockCopyState {
45
SharedResource *mem;
46
} BlockCopyState;
47
48
-static BlockCopyInFlightReq *find_conflicting_inflight_req(BlockCopyState *s,
49
- int64_t offset,
50
- int64_t bytes)
51
+static BlockCopyTask *find_conflicting_task(BlockCopyState *s,
52
+ int64_t offset, int64_t bytes)
53
{
54
- BlockCopyInFlightReq *req;
55
+ BlockCopyTask *t;
56
57
- QLIST_FOREACH(req, &s->inflight_reqs, list) {
58
- if (offset + bytes > req->offset && offset < req->offset + req->bytes) {
59
- return req;
60
+ QLIST_FOREACH(t, &s->tasks, list) {
61
+ if (offset + bytes > t->offset && offset < t->offset + t->bytes) {
62
+ return t;
63
}
64
}
65
66
@@ -XXX,XX +XXX,XX @@ static BlockCopyInFlightReq *find_conflicting_inflight_req(BlockCopyState *s,
67
}
68
69
/*
70
- * If there are no intersecting requests return false. Otherwise, wait for the
71
- * first found intersecting request to finish and return true.
72
+ * If there are no intersecting tasks return false. Otherwise, wait for the
73
+ * first found intersecting tasks to finish and return true.
74
*/
75
static bool coroutine_fn block_copy_wait_one(BlockCopyState *s, int64_t offset,
76
int64_t bytes)
77
{
78
- BlockCopyInFlightReq *req = find_conflicting_inflight_req(s, offset, bytes);
79
+ BlockCopyTask *task = find_conflicting_task(s, offset, bytes);
80
81
- if (!req) {
82
+ if (!task) {
83
return false;
84
}
85
86
- qemu_co_queue_wait(&req->wait_queue, NULL);
87
+ qemu_co_queue_wait(&task->wait_queue, NULL);
88
89
return true;
90
}
91
92
/* Called only on full-dirty region */
93
-static void block_copy_inflight_req_begin(BlockCopyState *s,
94
- BlockCopyInFlightReq *req,
95
- int64_t offset, int64_t bytes)
96
+static void block_copy_task_begin(BlockCopyState *s, BlockCopyTask *task,
97
+ int64_t offset, int64_t bytes)
98
{
99
- assert(!find_conflicting_inflight_req(s, offset, bytes));
100
+ assert(!find_conflicting_task(s, offset, bytes));
101
102
bdrv_reset_dirty_bitmap(s->copy_bitmap, offset, bytes);
103
s->in_flight_bytes += bytes;
104
105
- req->offset = offset;
106
- req->bytes = bytes;
107
- qemu_co_queue_init(&req->wait_queue);
108
- QLIST_INSERT_HEAD(&s->inflight_reqs, req, list);
109
+ task->offset = offset;
110
+ task->bytes = bytes;
111
+ qemu_co_queue_init(&task->wait_queue);
112
+ QLIST_INSERT_HEAD(&s->tasks, task, list);
113
}
114
115
/*
116
- * block_copy_inflight_req_shrink
117
+ * block_copy_task_shrink
118
*
119
- * Drop the tail of the request to be handled later. Set dirty bits back and
120
- * wake up all requests waiting for us (may be some of them are not intersecting
121
- * with shrunk request)
122
+ * Drop the tail of the task to be handled later. Set dirty bits back and
123
+ * wake up all tasks waiting for us (may be some of them are not intersecting
124
+ * with shrunk task)
125
*/
126
-static void coroutine_fn block_copy_inflight_req_shrink(BlockCopyState *s,
127
- BlockCopyInFlightReq *req, int64_t new_bytes)
128
+static void coroutine_fn block_copy_task_shrink(BlockCopyState *s,
129
+ BlockCopyTask *task,
130
+ int64_t new_bytes)
131
{
132
- if (new_bytes == req->bytes) {
133
+ if (new_bytes == task->bytes) {
134
return;
26
return;
135
}
27
}
136
137
- assert(new_bytes > 0 && new_bytes < req->bytes);
138
+ assert(new_bytes > 0 && new_bytes < task->bytes);
139
140
- s->in_flight_bytes -= req->bytes - new_bytes;
141
+ s->in_flight_bytes -= task->bytes - new_bytes;
142
bdrv_set_dirty_bitmap(s->copy_bitmap,
143
- req->offset + new_bytes, req->bytes - new_bytes);
144
+ task->offset + new_bytes, task->bytes - new_bytes);
145
146
- req->bytes = new_bytes;
147
- qemu_co_queue_restart_all(&req->wait_queue);
148
+ task->bytes = new_bytes;
149
+ qemu_co_queue_restart_all(&task->wait_queue);
150
}
151
152
-static void coroutine_fn block_copy_inflight_req_end(BlockCopyState *s,
153
- BlockCopyInFlightReq *req,
154
- int ret)
155
+static void coroutine_fn block_copy_task_end(BlockCopyState *s,
156
+ BlockCopyTask *task, int ret)
157
{
158
- s->in_flight_bytes -= req->bytes;
159
+ s->in_flight_bytes -= task->bytes;
160
if (ret < 0) {
161
- bdrv_set_dirty_bitmap(s->copy_bitmap, req->offset, req->bytes);
162
+ bdrv_set_dirty_bitmap(s->copy_bitmap, task->offset, task->bytes);
163
}
164
- QLIST_REMOVE(req, list);
165
- qemu_co_queue_restart_all(&req->wait_queue);
166
+ QLIST_REMOVE(task, list);
167
+ qemu_co_queue_restart_all(&task->wait_queue);
168
}
169
170
void block_copy_state_free(BlockCopyState *s)
171
@@ -XXX,XX +XXX,XX @@ BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target,
172
s->copy_size = MAX(s->cluster_size, BLOCK_COPY_MAX_BUFFER);
173
}
174
175
- QLIST_INIT(&s->inflight_reqs);
176
+ QLIST_INIT(&s->tasks);
177
178
return s;
179
}
180
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn block_copy_dirty_clusters(BlockCopyState *s,
181
assert(QEMU_IS_ALIGNED(bytes, s->cluster_size));
182
183
while (bytes) {
184
- BlockCopyInFlightReq req;
185
+ BlockCopyTask task;
186
int64_t next_zero, cur_bytes, status_bytes;
187
188
if (!bdrv_dirty_bitmap_get(s->copy_bitmap, offset)) {
189
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn block_copy_dirty_clusters(BlockCopyState *s,
190
assert(next_zero < offset + cur_bytes); /* no need to do MIN() */
191
cur_bytes = next_zero - offset;
192
}
193
- block_copy_inflight_req_begin(s, &req, offset, cur_bytes);
194
+ block_copy_task_begin(s, &task, offset, cur_bytes);
195
196
ret = block_copy_block_status(s, offset, cur_bytes, &status_bytes);
197
assert(ret >= 0); /* never fail */
198
cur_bytes = MIN(cur_bytes, status_bytes);
199
- block_copy_inflight_req_shrink(s, &req, cur_bytes);
200
+ block_copy_task_shrink(s, &task, cur_bytes);
201
if (s->skip_unallocated && !(ret & BDRV_BLOCK_ALLOCATED)) {
202
- block_copy_inflight_req_end(s, &req, 0);
203
+ block_copy_task_end(s, &task, 0);
204
progress_set_remaining(s->progress,
205
bdrv_get_dirty_count(s->copy_bitmap) +
206
s->in_flight_bytes);
207
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn block_copy_dirty_clusters(BlockCopyState *s,
208
ret = block_copy_do_copy(s, offset, cur_bytes, ret & BDRV_BLOCK_ZERO,
209
error_is_read);
210
co_put_to_shres(s->mem, cur_bytes);
211
- block_copy_inflight_req_end(s, &req, ret);
212
+ block_copy_task_end(s, &task, ret);
213
if (ret < 0) {
214
return ret;
215
}
216
--
28
--
217
2.26.2
29
2.21.0
218
30
219
31
diff view generated by jsdifflib
1
From: Eric Blake <eblake@redhat.com>
1
From: Anton Nefedov <anton.nefedov@virtuozzo.com>
2
2
3
It's been a while since we got rid of the sector-based bdrv_read and
3
COW (even empty/zero) areas require encryption too
4
bdrv_write (commit 2e11d756); let's finish the job on a few remaining
5
comments.
6
4
7
Signed-off-by: Eric Blake <eblake@redhat.com>
5
Signed-off-by: Anton Nefedov <anton.nefedov@virtuozzo.com>
8
Message-Id: <20200428213807.776655-1-eblake@redhat.com>
6
Reviewed-by: Eric Blake <eblake@redhat.com>
7
Reviewed-by: Max Reitz <mreitz@redhat.com>
9
Reviewed-by: Alberto Garcia <berto@igalia.com>
8
Reviewed-by: Alberto Garcia <berto@igalia.com>
9
Message-id: 20190516143028.81155-1-anton.nefedov@virtuozzo.com
10
Signed-off-by: Max Reitz <mreitz@redhat.com>
10
Signed-off-by: Max Reitz <mreitz@redhat.com>
11
---
11
---
12
block/io.c | 3 ++-
12
tests/qemu-iotests/134 | 9 +++++++++
13
block/qcow2-refcount.c | 2 +-
13
tests/qemu-iotests/134.out | 10 ++++++++++
14
block/vvfat.c | 10 +++++-----
14
2 files changed, 19 insertions(+)
15
tests/qemu-iotests/001 | 2 +-
16
tests/qemu-iotests/052 | 2 +-
17
tests/qemu-iotests/134 | 2 +-
18
tests/qemu-iotests/188 | 2 +-
19
7 files changed, 12 insertions(+), 11 deletions(-)
20
15
21
diff --git a/block/io.c b/block/io.c
22
index XXXXXXX..XXXXXXX 100644
23
--- a/block/io.c
24
+++ b/block/io.c
25
@@ -XXX,XX +XXX,XX @@ int bdrv_pwrite_zeroes(BdrvChild *child, int64_t offset,
26
* flags are passed through to bdrv_pwrite_zeroes (e.g. BDRV_REQ_MAY_UNMAP,
27
* BDRV_REQ_FUA).
28
*
29
- * Returns < 0 on error, 0 on success. For error codes see bdrv_write().
30
+ * Returns < 0 on error, 0 on success. For error codes see bdrv_pwrite().
31
*/
32
int bdrv_make_zero(BdrvChild *child, BdrvRequestFlags flags)
33
{
34
@@ -XXX,XX +XXX,XX @@ int bdrv_make_zero(BdrvChild *child, BdrvRequestFlags flags)
35
}
36
}
37
38
+/* return < 0 if error. See bdrv_pwrite() for the return codes */
39
int bdrv_preadv(BdrvChild *child, int64_t offset, QEMUIOVector *qiov)
40
{
41
int ret;
42
diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c
43
index XXXXXXX..XXXXXXX 100644
44
--- a/block/qcow2-refcount.c
45
+++ b/block/qcow2-refcount.c
46
@@ -XXX,XX +XXX,XX @@ fail:
47
* - 0 if writing to this offset will not affect the mentioned metadata
48
* - a positive QCow2MetadataOverlap value indicating one overlapping section
49
* - a negative value (-errno) indicating an error while performing a check,
50
- * e.g. when bdrv_read failed on QCOW2_OL_INACTIVE_L2
51
+ * e.g. when bdrv_pread failed on QCOW2_OL_INACTIVE_L2
52
*/
53
int qcow2_check_metadata_overlap(BlockDriverState *bs, int ign, int64_t offset,
54
int64_t size)
55
diff --git a/block/vvfat.c b/block/vvfat.c
56
index XXXXXXX..XXXXXXX 100644
57
--- a/block/vvfat.c
58
+++ b/block/vvfat.c
59
@@ -XXX,XX +XXX,XX @@ DLOG(checkpoint());
60
* - get modified FAT
61
* - compare the two FATs (TODO)
62
* - get buffer for marking used clusters
63
- * - recurse direntries from root (using bs->bdrv_read to make
64
+ * - recurse direntries from root (using bs->bdrv_pread to make
65
* sure to get the new data)
66
* - check that the FAT agrees with the size
67
* - count the number of clusters occupied by this directory and
68
@@ -XXX,XX +XXX,XX @@ static int handle_deletes(BDRVVVFATState* s)
69
/*
70
* synchronize mapping with new state:
71
*
72
- * - copy FAT (with bdrv_read)
73
+ * - copy FAT (with bdrv_pread)
74
* - mark all filenames corresponding to mappings as deleted
75
- * - recurse direntries from root (using bs->bdrv_read)
76
+ * - recurse direntries from root (using bs->bdrv_pread)
77
* - delete files corresponding to mappings marked as deleted
78
*/
79
static int do_commit(BDRVVVFATState* s)
80
@@ -XXX,XX +XXX,XX @@ static int do_commit(BDRVVVFATState* s)
81
return ret;
82
}
83
84
- /* copy FAT (with bdrv_read) */
85
+ /* copy FAT (with bdrv_pread) */
86
memcpy(s->fat.pointer, s->fat2, 0x200 * s->sectors_per_fat);
87
88
- /* recurse direntries from root (using bs->bdrv_read) */
89
+ /* recurse direntries from root (using bs->bdrv_pread) */
90
ret = commit_direntries(s, 0, -1);
91
if (ret) {
92
fprintf(stderr, "Fatal: error while committing (%d)\n", ret);
93
diff --git a/tests/qemu-iotests/001 b/tests/qemu-iotests/001
94
index XXXXXXX..XXXXXXX 100755
95
--- a/tests/qemu-iotests/001
96
+++ b/tests/qemu-iotests/001
97
@@ -XXX,XX +XXX,XX @@
98
#!/usr/bin/env bash
99
#
100
-# Test simple read/write using plain bdrv_read/bdrv_write
101
+# Test simple read/write using plain bdrv_pread/bdrv_pwrite
102
#
103
# Copyright (C) 2009 Red Hat, Inc.
104
#
105
diff --git a/tests/qemu-iotests/052 b/tests/qemu-iotests/052
106
index XXXXXXX..XXXXXXX 100755
107
--- a/tests/qemu-iotests/052
108
+++ b/tests/qemu-iotests/052
109
@@ -XXX,XX +XXX,XX @@
110
#!/usr/bin/env bash
111
#
112
-# Test bdrv_read/bdrv_write using BDRV_O_SNAPSHOT
113
+# Test bdrv_pread/bdrv_pwrite using BDRV_O_SNAPSHOT
114
#
115
# Copyright (C) 2013 Red Hat, Inc.
116
#
117
diff --git a/tests/qemu-iotests/134 b/tests/qemu-iotests/134
16
diff --git a/tests/qemu-iotests/134 b/tests/qemu-iotests/134
118
index XXXXXXX..XXXXXXX 100755
17
index XXXXXXX..XXXXXXX 100755
119
--- a/tests/qemu-iotests/134
18
--- a/tests/qemu-iotests/134
120
+++ b/tests/qemu-iotests/134
19
+++ b/tests/qemu-iotests/134
121
@@ -XXX,XX +XXX,XX @@
20
@@ -XXX,XX +XXX,XX @@ echo
122
#!/usr/bin/env bash
21
echo "== reading whole image =="
123
#
22
$QEMU_IO --object $SECRET -c "read 0 $size" --image-opts $IMGSPEC | _filter_qemu_io | _filter_testdir
124
-# Test encrypted read/write using plain bdrv_read/bdrv_write
23
125
+# Test encrypted read/write using plain bdrv_pread/bdrv_pwrite
24
+echo
126
#
25
+echo "== rewriting cluster part =="
127
# Copyright (C) 2015 Red Hat, Inc.
26
+$QEMU_IO --object $SECRET -c "write -P 0xb 512 512" --image-opts $IMGSPEC | _filter_qemu_io | _filter_testdir
128
#
27
+
129
diff --git a/tests/qemu-iotests/188 b/tests/qemu-iotests/188
28
+echo
130
index XXXXXXX..XXXXXXX 100755
29
+echo "== verify pattern =="
131
--- a/tests/qemu-iotests/188
30
+$QEMU_IO --object $SECRET -c "read -P 0 0 512" --image-opts $IMGSPEC | _filter_qemu_io | _filter_testdir
132
+++ b/tests/qemu-iotests/188
31
+$QEMU_IO --object $SECRET -c "read -P 0xb 512 512" --image-opts $IMGSPEC | _filter_qemu_io | _filter_testdir
133
@@ -XXX,XX +XXX,XX @@
32
+
134
#!/usr/bin/env bash
33
echo
135
#
34
echo "== rewriting whole image =="
136
-# Test encrypted read/write using plain bdrv_read/bdrv_write
35
$QEMU_IO --object $SECRET -c "write -P 0xa 0 $size" --image-opts $IMGSPEC | _filter_qemu_io | _filter_testdir
137
+# Test encrypted read/write using plain bdrv_pread/bdrv_pwrite
36
diff --git a/tests/qemu-iotests/134.out b/tests/qemu-iotests/134.out
138
#
37
index XXXXXXX..XXXXXXX 100644
139
# Copyright (C) 2017 Red Hat, Inc.
38
--- a/tests/qemu-iotests/134.out
140
#
39
+++ b/tests/qemu-iotests/134.out
40
@@ -XXX,XX +XXX,XX @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 encryption=on encrypt.
41
read 134217728/134217728 bytes at offset 0
42
128 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
43
44
+== rewriting cluster part ==
45
+wrote 512/512 bytes at offset 512
46
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
47
+
48
+== verify pattern ==
49
+read 512/512 bytes at offset 0
50
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
51
+read 512/512 bytes at offset 512
52
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
53
+
54
== rewriting whole image ==
55
wrote 134217728/134217728 bytes at offset 0
56
128 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
141
--
57
--
142
2.26.2
58
2.21.0
143
59
144
60
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
From: Sam Eiderman <shmuel.eiderman@oracle.com>
2
2
3
We are going to use aio-task-pool API, so we'll need state pointer in
3
Commit b0651b8c246d ("vmdk: Move l1_size check into vmdk_add_extent")
4
BlockCopyTask anyway. Add it now and use where possible.
4
extended the l1_size check from VMDK4 to VMDK3 but did not update the
5
default coverage in the moved comment.
5
6
6
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
7
The previous vmdk4 calculation:
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>
7
Reviewed-by: Max Reitz <mreitz@redhat.com>
28
Reviewed-by: Max Reitz <mreitz@redhat.com>
8
Message-Id: <20200429130847.28124-4-vsementsov@virtuozzo.com>
9
Signed-off-by: Max Reitz <mreitz@redhat.com>
29
Signed-off-by: Max Reitz <mreitz@redhat.com>
10
---
30
---
11
block/block-copy.c | 28 +++++++++++++++-------------
31
block/vmdk.c | 11 ++++++++---
12
1 file changed, 15 insertions(+), 13 deletions(-)
32
1 file changed, 8 insertions(+), 3 deletions(-)
13
33
14
diff --git a/block/block-copy.c b/block/block-copy.c
34
diff --git a/block/vmdk.c b/block/vmdk.c
15
index XXXXXXX..XXXXXXX 100644
35
index XXXXXXX..XXXXXXX 100644
16
--- a/block/block-copy.c
36
--- a/block/vmdk.c
17
+++ b/block/block-copy.c
37
+++ b/block/vmdk.c
18
@@ -XXX,XX +XXX,XX @@
38
@@ -XXX,XX +XXX,XX @@ static int vmdk_add_extent(BlockDriverState *bs,
19
#define BLOCK_COPY_MAX_MEM (128 * MiB)
39
return -EFBIG;
20
21
typedef struct BlockCopyTask {
22
+ BlockCopyState *s;
23
int64_t offset;
24
int64_t bytes;
25
QLIST_ENTRY(BlockCopyTask) list;
26
@@ -XXX,XX +XXX,XX @@ static BlockCopyTask *block_copy_task_create(BlockCopyState *s,
27
bdrv_reset_dirty_bitmap(s->copy_bitmap, offset, bytes);
28
s->in_flight_bytes += bytes;
29
30
- task->offset = offset;
31
- task->bytes = bytes;
32
+ *task = (BlockCopyTask) {
33
+ .s = s,
34
+ .offset = offset,
35
+ .bytes = bytes,
36
+ };
37
qemu_co_queue_init(&task->wait_queue);
38
QLIST_INSERT_HEAD(&s->tasks, task, list);
39
40
@@ -XXX,XX +XXX,XX @@ static BlockCopyTask *block_copy_task_create(BlockCopyState *s,
41
* wake up all tasks waiting for us (may be some of them are not intersecting
42
* with shrunk task)
43
*/
44
-static void coroutine_fn block_copy_task_shrink(BlockCopyState *s,
45
- BlockCopyTask *task,
46
+static void coroutine_fn block_copy_task_shrink(BlockCopyTask *task,
47
int64_t new_bytes)
48
{
49
if (new_bytes == task->bytes) {
50
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn block_copy_task_shrink(BlockCopyState *s,
51
52
assert(new_bytes > 0 && new_bytes < task->bytes);
53
54
- s->in_flight_bytes -= task->bytes - new_bytes;
55
- bdrv_set_dirty_bitmap(s->copy_bitmap,
56
+ task->s->in_flight_bytes -= task->bytes - new_bytes;
57
+ bdrv_set_dirty_bitmap(task->s->copy_bitmap,
58
task->offset + new_bytes, task->bytes - new_bytes);
59
60
task->bytes = new_bytes;
61
qemu_co_queue_restart_all(&task->wait_queue);
62
}
63
64
-static void coroutine_fn block_copy_task_end(BlockCopyState *s,
65
- BlockCopyTask *task, int ret)
66
+static void coroutine_fn block_copy_task_end(BlockCopyTask *task, int ret)
67
{
68
- s->in_flight_bytes -= task->bytes;
69
+ task->s->in_flight_bytes -= task->bytes;
70
if (ret < 0) {
71
- bdrv_set_dirty_bitmap(s->copy_bitmap, task->offset, task->bytes);
72
+ bdrv_set_dirty_bitmap(task->s->copy_bitmap, task->offset, task->bytes);
73
}
40
}
74
QLIST_REMOVE(task, list);
41
if (l1_size > 512 * 1024 * 1024) {
75
qemu_co_queue_restart_all(&task->wait_queue);
42
- /* Although with big capacity and small l1_entry_sectors, we can get a
76
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn block_copy_dirty_clusters(BlockCopyState *s,
43
+ /*
77
ret = block_copy_block_status(s, offset, cur_bytes, &status_bytes);
44
+ * Although with big capacity and small l1_entry_sectors, we can get a
78
assert(ret >= 0); /* never fail */
45
* big l1_size, we don't want unbounded value to allocate the table.
79
cur_bytes = MIN(cur_bytes, status_bytes);
46
- * Limit it to 512M, which is 16PB for default cluster and L2 table
80
- block_copy_task_shrink(s, task, cur_bytes);
47
- * size */
81
+ block_copy_task_shrink(task, cur_bytes);
48
+ * Limit it to 512M, which is:
82
if (s->skip_unallocated && !(ret & BDRV_BLOCK_ALLOCATED)) {
49
+ * 16PB - for default "Hosted Sparse Extent" (VMDK4)
83
- block_copy_task_end(s, task, 0);
50
+ * cluster size: 64KB, L2 table size: 512 entries
84
+ block_copy_task_end(task, 0);
51
+ * 1PB - for default "ESXi Host Sparse Extent" (VMDK3/vmfsSparse)
85
progress_set_remaining(s->progress,
52
+ * cluster size: 512B, L2 table size: 4096 entries
86
bdrv_get_dirty_count(s->copy_bitmap) +
53
+ */
87
s->in_flight_bytes);
54
error_setg(errp, "L1 size too big");
88
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn block_copy_dirty_clusters(BlockCopyState *s,
55
return -EFBIG;
89
ret = block_copy_do_copy(s, offset, cur_bytes, ret & BDRV_BLOCK_ZERO,
56
}
90
error_is_read);
91
co_put_to_shres(s->mem, cur_bytes);
92
- block_copy_task_end(s, task, ret);
93
+ block_copy_task_end(task, ret);
94
if (ret < 0) {
95
return ret;
96
}
97
--
57
--
98
2.26.2
58
2.21.0
99
59
100
60
diff view generated by jsdifflib
1
From: Eric Blake <eblake@redhat.com>
1
From: Sam Eiderman <shmuel.eiderman@oracle.com>
2
2
3
There are several callers that need to create a new block backend from
3
512M of L1 entries is a very loose bound, only 32M are required to store
4
an existing BDS; make the task slightly easier with a common helper
4
the maximal supported VMDK file size of 2TB.
5
routine.
6
5
7
Suggested-by: Max Reitz <mreitz@redhat.com>
6
Fixed qemu-iotest 59# - now failure occures before on impossible L1
8
Signed-off-by: Eric Blake <eblake@redhat.com>
7
table size.
9
Message-Id: <20200424190903.522087-2-eblake@redhat.com>
8
10
[mreitz: Set @ret only in error paths, see
9
Reviewed-by: Karl Heubaum <karl.heubaum@oracle.com>
11
https://lists.nongnu.org/archive/html/qemu-block/2020-04/msg01216.html]
10
Reviewed-by: Eyal Moscovici <eyal.moscovici@oracle.com>
12
Signed-off-by: Max Reitz <mreitz@redhat.com>
11
Reviewed-by: Liran Alon <liran.alon@oracle.com>
13
Message-Id: <20200428192648.749066-2-eblake@redhat.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
15
Reviewed-by: Max Reitz <mreitz@redhat.com>
14
Signed-off-by: Max Reitz <mreitz@redhat.com>
16
Signed-off-by: Max Reitz <mreitz@redhat.com>
15
---
17
---
16
include/sysemu/block-backend.h | 2 ++
18
block/vmdk.c | 13 +++++++------
17
block/block-backend.c | 23 +++++++++++++++++++++++
19
tests/qemu-iotests/059.out | 2 +-
18
block/crypto.c | 9 ++++-----
20
2 files changed, 8 insertions(+), 7 deletions(-)
19
block/parallels.c | 8 ++++----
20
block/qcow.c | 8 ++++----
21
block/qcow2.c | 18 ++++++++----------
22
block/qed.c | 8 ++++----
23
block/sheepdog.c | 10 +++++-----
24
block/vdi.c | 8 ++++----
25
block/vhdx.c | 8 ++++----
26
block/vmdk.c | 9 ++++-----
27
block/vpc.c | 8 ++++----
28
blockdev.c | 8 +++-----
29
blockjob.c | 7 ++-----
30
14 files changed, 75 insertions(+), 59 deletions(-)
31
21
32
diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h
33
index XXXXXXX..XXXXXXX 100644
34
--- a/include/sysemu/block-backend.h
35
+++ b/include/sysemu/block-backend.h
36
@@ -XXX,XX +XXX,XX @@ typedef struct BlockBackendPublic {
37
} BlockBackendPublic;
38
39
BlockBackend *blk_new(AioContext *ctx, uint64_t perm, uint64_t shared_perm);
40
+BlockBackend *blk_new_with_bs(BlockDriverState *bs, uint64_t perm,
41
+ uint64_t shared_perm, Error **errp);
42
BlockBackend *blk_new_open(const char *filename, const char *reference,
43
QDict *options, int flags, Error **errp);
44
int blk_get_refcnt(BlockBackend *blk);
45
diff --git a/block/block-backend.c b/block/block-backend.c
46
index XXXXXXX..XXXXXXX 100644
47
--- a/block/block-backend.c
48
+++ b/block/block-backend.c
49
@@ -XXX,XX +XXX,XX @@ BlockBackend *blk_new(AioContext *ctx, uint64_t perm, uint64_t shared_perm)
50
return blk;
51
}
52
53
+/*
54
+ * Create a new BlockBackend connected to an existing BlockDriverState.
55
+ *
56
+ * @perm is a bitmasks of BLK_PERM_* constants which describes the
57
+ * permissions to request for @bs that is attached to this
58
+ * BlockBackend. @shared_perm is a bitmask which describes which
59
+ * permissions may be granted to other users of the attached node.
60
+ * Both sets of permissions can be changed later using blk_set_perm().
61
+ *
62
+ * Return the new BlockBackend on success, null on failure.
63
+ */
64
+BlockBackend *blk_new_with_bs(BlockDriverState *bs, uint64_t perm,
65
+ uint64_t shared_perm, Error **errp)
66
+{
67
+ BlockBackend *blk = blk_new(bdrv_get_aio_context(bs), perm, shared_perm);
68
+
69
+ if (blk_insert_bs(blk, bs, errp) < 0) {
70
+ blk_unref(blk);
71
+ return NULL;
72
+ }
73
+ return blk;
74
+}
75
+
76
/*
77
* Creates a new BlockBackend, opens a new BlockDriverState, and connects both.
78
* The new BlockBackend is in the main AioContext.
79
diff --git a/block/crypto.c b/block/crypto.c
80
index XXXXXXX..XXXXXXX 100644
81
--- a/block/crypto.c
82
+++ b/block/crypto.c
83
@@ -XXX,XX +XXX,XX @@ static int block_crypto_co_create_generic(BlockDriverState *bs,
84
QCryptoBlock *crypto = NULL;
85
struct BlockCryptoCreateData data;
86
87
- blk = blk_new(bdrv_get_aio_context(bs),
88
- BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
89
-
90
- ret = blk_insert_bs(blk, bs, errp);
91
- if (ret < 0) {
92
+ blk = blk_new_with_bs(bs, BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL,
93
+ errp);
94
+ if (!blk) {
95
+ ret = -EPERM;
96
goto cleanup;
97
}
98
99
diff --git a/block/parallels.c b/block/parallels.c
100
index XXXXXXX..XXXXXXX 100644
101
--- a/block/parallels.c
102
+++ b/block/parallels.c
103
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn parallels_co_create(BlockdevCreateOptions* opts,
104
return -EIO;
105
}
106
107
- blk = blk_new(bdrv_get_aio_context(bs),
108
- BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
109
- ret = blk_insert_bs(blk, bs, errp);
110
- if (ret < 0) {
111
+ blk = blk_new_with_bs(bs, BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL,
112
+ errp);
113
+ if (!blk) {
114
+ ret = -EPERM;
115
goto out;
116
}
117
blk_set_allow_write_beyond_eof(blk, true);
118
diff --git a/block/qcow.c b/block/qcow.c
119
index XXXXXXX..XXXXXXX 100644
120
--- a/block/qcow.c
121
+++ b/block/qcow.c
122
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow_co_create(BlockdevCreateOptions *opts,
123
return -EIO;
124
}
125
126
- qcow_blk = blk_new(bdrv_get_aio_context(bs),
127
- BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
128
- ret = blk_insert_bs(qcow_blk, bs, errp);
129
- if (ret < 0) {
130
+ qcow_blk = blk_new_with_bs(bs, BLK_PERM_WRITE | BLK_PERM_RESIZE,
131
+ BLK_PERM_ALL, errp);
132
+ if (!qcow_blk) {
133
+ ret = -EPERM;
134
goto exit;
135
}
136
blk_set_allow_write_beyond_eof(qcow_blk, true);
137
diff --git a/block/qcow2.c b/block/qcow2.c
138
index XXXXXXX..XXXXXXX 100644
139
--- a/block/qcow2.c
140
+++ b/block/qcow2.c
141
@@ -XXX,XX +XXX,XX @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp)
142
}
143
144
/* Create BlockBackend to write to the image */
145
- blk = blk_new(bdrv_get_aio_context(bs),
146
- BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
147
- ret = blk_insert_bs(blk, bs, errp);
148
- if (ret < 0) {
149
+ blk = blk_new_with_bs(bs, BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL,
150
+ errp);
151
+ if (!blk) {
152
+ ret = -EPERM;
153
goto out;
154
}
155
blk_set_allow_write_beyond_eof(blk, true);
156
@@ -XXX,XX +XXX,XX @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
157
}
158
159
if (new_size) {
160
- BlockBackend *blk = blk_new(bdrv_get_aio_context(bs),
161
- BLK_PERM_RESIZE, BLK_PERM_ALL);
162
- ret = blk_insert_bs(blk, bs, errp);
163
- if (ret < 0) {
164
- blk_unref(blk);
165
- return ret;
166
+ BlockBackend *blk = blk_new_with_bs(bs, BLK_PERM_RESIZE, BLK_PERM_ALL,
167
+ errp);
168
+ if (!blk) {
169
+ return -EPERM;
170
}
171
172
/*
173
diff --git a/block/qed.c b/block/qed.c
174
index XXXXXXX..XXXXXXX 100644
175
--- a/block/qed.c
176
+++ b/block/qed.c
177
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_qed_co_create(BlockdevCreateOptions *opts,
178
return -EIO;
179
}
180
181
- blk = blk_new(bdrv_get_aio_context(bs),
182
- BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
183
- ret = blk_insert_bs(blk, bs, errp);
184
- if (ret < 0) {
185
+ blk = blk_new_with_bs(bs, BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL,
186
+ errp);
187
+ if (!blk) {
188
+ ret = -EPERM;
189
goto out;
190
}
191
blk_set_allow_write_beyond_eof(blk, true);
192
diff --git a/block/sheepdog.c b/block/sheepdog.c
193
index XXXXXXX..XXXXXXX 100644
194
--- a/block/sheepdog.c
195
+++ b/block/sheepdog.c
196
@@ -XXX,XX +XXX,XX @@ static int sd_prealloc(BlockDriverState *bs, int64_t old_size, int64_t new_size,
197
void *buf = NULL;
198
int ret;
199
200
- blk = blk_new(bdrv_get_aio_context(bs),
201
- BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE | BLK_PERM_RESIZE,
202
- BLK_PERM_ALL);
203
+ blk = blk_new_with_bs(bs,
204
+ BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE | BLK_PERM_RESIZE,
205
+ BLK_PERM_ALL, errp);
206
207
- ret = blk_insert_bs(blk, bs, errp);
208
- if (ret < 0) {
209
+ if (!blk) {
210
+ ret = -EPERM;
211
goto out_with_err_set;
212
}
213
214
diff --git a/block/vdi.c b/block/vdi.c
215
index XXXXXXX..XXXXXXX 100644
216
--- a/block/vdi.c
217
+++ b/block/vdi.c
218
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn vdi_co_do_create(BlockdevCreateOptions *create_options,
219
goto exit;
220
}
221
222
- blk = blk_new(bdrv_get_aio_context(bs_file),
223
- BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
224
- ret = blk_insert_bs(blk, bs_file, errp);
225
- if (ret < 0) {
226
+ blk = blk_new_with_bs(bs_file, BLK_PERM_WRITE | BLK_PERM_RESIZE,
227
+ BLK_PERM_ALL, errp);
228
+ if (!blk) {
229
+ ret = -EPERM;
230
goto exit;
231
}
232
233
diff --git a/block/vhdx.c b/block/vhdx.c
234
index XXXXXXX..XXXXXXX 100644
235
--- a/block/vhdx.c
236
+++ b/block/vhdx.c
237
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn vhdx_co_create(BlockdevCreateOptions *opts,
238
return -EIO;
239
}
240
241
- blk = blk_new(bdrv_get_aio_context(bs),
242
- BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
243
- ret = blk_insert_bs(blk, bs, errp);
244
- if (ret < 0) {
245
+ blk = blk_new_with_bs(bs, BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL,
246
+ errp);
247
+ if (!blk) {
248
+ ret = -EPERM;
249
goto delete_and_exit;
250
}
251
blk_set_allow_write_beyond_eof(blk, true);
252
diff --git a/block/vmdk.c b/block/vmdk.c
22
diff --git a/block/vmdk.c b/block/vmdk.c
253
index XXXXXXX..XXXXXXX 100644
23
index XXXXXXX..XXXXXXX 100644
254
--- a/block/vmdk.c
24
--- a/block/vmdk.c
255
+++ b/block/vmdk.c
25
+++ b/block/vmdk.c
256
@@ -XXX,XX +XXX,XX @@ static BlockBackend *vmdk_co_create_cb(int64_t size, int idx,
26
@@ -XXX,XX +XXX,XX @@ static int vmdk_add_extent(BlockDriverState *bs,
257
if (!bs) {
27
error_setg(errp, "Invalid granularity, image may be corrupt");
258
return NULL;
28
return -EFBIG;
259
}
29
}
260
- blk = blk_new(bdrv_get_aio_context(bs),
30
- if (l1_size > 512 * 1024 * 1024) {
261
- BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE | BLK_PERM_RESIZE,
31
+ if (l1_size > 32 * 1024 * 1024) {
262
- BLK_PERM_ALL);
32
/*
263
- if (blk_insert_bs(blk, bs, errp)) {
33
* Although with big capacity and small l1_entry_sectors, we can get a
264
- bdrv_unref(bs);
34
* big l1_size, we don't want unbounded value to allocate the table.
265
+ blk = blk_new_with_bs(bs,
35
- * Limit it to 512M, which is:
266
+ BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE | BLK_PERM_RESIZE,
36
- * 16PB - for default "Hosted Sparse Extent" (VMDK4)
267
+ BLK_PERM_ALL, errp);
37
- * cluster size: 64KB, L2 table size: 512 entries
268
+ if (!blk) {
38
- * 1PB - for default "ESXi Host Sparse Extent" (VMDK3/vmfsSparse)
269
return NULL;
39
- * cluster size: 512B, L2 table size: 4096 entries
270
}
40
+ * Limit it to 32M, which is enough to store:
271
blk_set_allow_write_beyond_eof(blk, true);
41
+ * 8TB - for both VMDK3 & VMDK4 with
272
diff --git a/block/vpc.c b/block/vpc.c
42
+ * minimal cluster size: 512B
43
+ * minimal L2 table size: 512 entries
44
+ * 8 TB is still more than the maximal value supported for
45
+ * VMDK3 & VMDK4 which is 2TB.
46
*/
47
error_setg(errp, "L1 size too big");
48
return -EFBIG;
49
diff --git a/tests/qemu-iotests/059.out b/tests/qemu-iotests/059.out
273
index XXXXXXX..XXXXXXX 100644
50
index XXXXXXX..XXXXXXX 100644
274
--- a/block/vpc.c
51
--- a/tests/qemu-iotests/059.out
275
+++ b/block/vpc.c
52
+++ b/tests/qemu-iotests/059.out
276
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn vpc_co_create(BlockdevCreateOptions *opts,
53
@@ -XXX,XX +XXX,XX @@ Offset Length Mapped to File
277
return -EIO;
54
0x140000000 0x10000 0x50000 TEST_DIR/t-s003.vmdk
278
}
55
279
56
=== Testing afl image with a very large capacity ===
280
- blk = blk_new(bdrv_get_aio_context(bs),
57
-qemu-img: Can't get image size 'TEST_DIR/afl9.IMGFMT': File too large
281
- BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
58
+qemu-img: Could not open 'TEST_DIR/afl9.IMGFMT': L1 size too big
282
- ret = blk_insert_bs(blk, bs, errp);
59
*** done
283
- if (ret < 0) {
284
+ blk = blk_new_with_bs(bs, BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL,
285
+ errp);
286
+ if (!blk) {
287
+ ret = -EPERM;
288
goto out;
289
}
290
blk_set_allow_write_beyond_eof(blk, true);
291
diff --git a/blockdev.c b/blockdev.c
292
index XXXXXXX..XXXXXXX 100644
293
--- a/blockdev.c
294
+++ b/blockdev.c
295
@@ -XXX,XX +XXX,XX @@ void qmp_block_resize(bool has_device, const char *device,
296
BlockBackend *blk = NULL;
297
BlockDriverState *bs;
298
AioContext *aio_context;
299
- int ret;
300
301
bs = bdrv_lookup_bs(has_device ? device : NULL,
302
has_node_name ? node_name : NULL,
303
@@ -XXX,XX +XXX,XX @@ void qmp_block_resize(bool has_device, const char *device,
304
goto out;
305
}
306
307
- blk = blk_new(bdrv_get_aio_context(bs), BLK_PERM_RESIZE, BLK_PERM_ALL);
308
- ret = blk_insert_bs(blk, bs, errp);
309
- if (ret < 0) {
310
+ blk = blk_new_with_bs(bs, BLK_PERM_RESIZE, BLK_PERM_ALL, errp);
311
+ if (!blk) {
312
goto out;
313
}
314
315
bdrv_drained_begin(bs);
316
- ret = blk_truncate(blk, size, false, PREALLOC_MODE_OFF, 0, errp);
317
+ blk_truncate(blk, size, false, PREALLOC_MODE_OFF, 0, errp);
318
bdrv_drained_end(bs);
319
320
out:
321
diff --git a/blockjob.c b/blockjob.c
322
index XXXXXXX..XXXXXXX 100644
323
--- a/blockjob.c
324
+++ b/blockjob.c
325
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
326
{
327
BlockBackend *blk;
328
BlockJob *job;
329
- int ret;
330
331
if (job_id == NULL && !(flags & JOB_INTERNAL)) {
332
job_id = bdrv_get_device_name(bs);
333
}
334
335
- blk = blk_new(bdrv_get_aio_context(bs), perm, shared_perm);
336
- ret = blk_insert_bs(blk, bs, errp);
337
- if (ret < 0) {
338
- blk_unref(blk);
339
+ blk = blk_new_with_bs(bs, perm, shared_perm, errp);
340
+ if (!blk) {
341
return NULL;
342
}
343
344
--
60
--
345
2.26.2
61
2.21.0
346
62
347
63
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
From: Sam Eiderman <shmuel.eiderman@oracle.com>
2
2
3
Run block_copy iterations in parallel in aio tasks.
3
Until ESXi 6.5 VMware used the vmfsSparse format for snapshots (VMDK3 in
4
4
QEMU).
5
Changes:
5
6
- BlockCopyTask becomes aio task structure. Add zeroes field to pass
6
This format was lacking in the following:
7
it to block_copy_do_copy
7
8
- add call state - it's a state of one call of block_copy(), shared
8
* Grain directory (L1) and grain table (L2) entries were 32-bit,
9
between parallel tasks. For now used only to keep information about
9
allowing access to only 2TB (slightly less) of data.
10
first error: is it read or not.
10
* The grain size (default) was 512 bytes - leading to data
11
- convert block_copy_dirty_clusters to aio-task loop.
11
fragmentation and many grain tables.
12
12
* For space reclamation purposes, it was necessary to find all the
13
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
13
grains which are not pointed to by any grain table - so a reverse
14
Message-Id: <20200429130847.28124-6-vsementsov@virtuozzo.com>
14
mapping of "offset of grain in vmdk" to "grain table" must be
15
constructed - which takes large amounts of CPU/RAM.
16
17
The format specification can be found in VMware's documentation:
18
https://www.vmware.com/support/developer/vddk/vmdk_50_technote.pdf
19
20
In ESXi 6.5, to support snapshot files larger than 2TB, a new format was
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
15
Signed-off-by: Max Reitz <mreitz@redhat.com>
151
Signed-off-by: Max Reitz <mreitz@redhat.com>
16
---
152
---
17
block/block-copy.c | 119 ++++++++++++++++++++++++++++++++++++++++-----
153
block/vmdk.c | 358 ++++++++++++++++++++++++++++++++++++++++++++++++---
18
1 file changed, 106 insertions(+), 13 deletions(-)
154
1 file changed, 342 insertions(+), 16 deletions(-)
19
155
20
diff --git a/block/block-copy.c b/block/block-copy.c
156
diff --git a/block/vmdk.c b/block/vmdk.c
21
index XXXXXXX..XXXXXXX 100644
157
index XXXXXXX..XXXXXXX 100644
22
--- a/block/block-copy.c
158
--- a/block/vmdk.c
23
+++ b/block/block-copy.c
159
+++ b/block/vmdk.c
24
@@ -XXX,XX +XXX,XX @@
160
@@ -XXX,XX +XXX,XX @@ typedef struct {
25
#include "block/block-copy.h"
161
uint16_t compressAlgorithm;
26
#include "sysemu/block-backend.h"
162
} QEMU_PACKED VMDK4Header;
27
#include "qemu/units.h"
163
28
+#include "qemu/coroutine.h"
164
+typedef struct VMDKSESparseConstHeader {
29
+#include "block/aio_task.h"
165
+ uint64_t magic;
30
166
+ uint64_t version;
31
#define BLOCK_COPY_MAX_COPY_RANGE (16 * MiB)
167
+ uint64_t capacity;
32
#define BLOCK_COPY_MAX_BUFFER (1 * MiB)
168
+ uint64_t grain_size;
33
#define BLOCK_COPY_MAX_MEM (128 * MiB)
169
+ uint64_t grain_table_size;
34
+#define BLOCK_COPY_MAX_WORKERS 64
170
+ uint64_t flags;
35
+
171
+ uint64_t reserved1;
36
+static coroutine_fn int block_copy_task_entry(AioTask *task);
172
+ uint64_t reserved2;
37
+
173
+ uint64_t reserved3;
38
+typedef struct BlockCopyCallState {
174
+ uint64_t reserved4;
39
+ bool failed;
175
+ uint64_t volatile_header_offset;
40
+ bool error_is_read;
176
+ uint64_t volatile_header_size;
41
+} BlockCopyCallState;
177
+ uint64_t journal_header_offset;
42
178
+ uint64_t journal_header_size;
43
typedef struct BlockCopyTask {
179
+ uint64_t journal_offset;
44
+ AioTask task;
180
+ uint64_t journal_size;
45
+
181
+ uint64_t grain_dir_offset;
46
BlockCopyState *s;
182
+ uint64_t grain_dir_size;
47
+ BlockCopyCallState *call_state;
183
+ uint64_t grain_tables_offset;
48
int64_t offset;
184
+ uint64_t grain_tables_size;
49
int64_t bytes;
185
+ uint64_t free_bitmap_offset;
50
+ bool zeroes;
186
+ uint64_t free_bitmap_size;
51
QLIST_ENTRY(BlockCopyTask) list;
187
+ uint64_t backmap_offset;
52
CoQueue wait_queue; /* coroutines blocked on this task */
188
+ uint64_t backmap_size;
53
} BlockCopyTask;
189
+ uint64_t grains_offset;
54
@@ -XXX,XX +XXX,XX @@ static bool coroutine_fn block_copy_wait_one(BlockCopyState *s, int64_t offset,
190
+ uint64_t grains_size;
55
* the beginning of it.
191
+ uint8_t pad[304];
56
*/
192
+} QEMU_PACKED VMDKSESparseConstHeader;
57
static BlockCopyTask *block_copy_task_create(BlockCopyState *s,
193
+
58
+ BlockCopyCallState *call_state,
194
+typedef struct VMDKSESparseVolatileHeader {
59
int64_t offset, int64_t bytes)
195
+ uint64_t magic;
60
{
196
+ uint64_t free_gt_number;
61
BlockCopyTask *task;
197
+ uint64_t next_txn_seq_number;
62
@@ -XXX,XX +XXX,XX @@ static BlockCopyTask *block_copy_task_create(BlockCopyState *s,
198
+ uint64_t replay_journal;
63
199
+ uint8_t pad[480];
64
task = g_new(BlockCopyTask, 1);
200
+} QEMU_PACKED VMDKSESparseVolatileHeader;
65
*task = (BlockCopyTask) {
201
+
66
+ .task.func = block_copy_task_entry,
202
#define L2_CACHE_SIZE 16
67
.s = s,
203
68
+ .call_state = call_state,
204
typedef struct VmdkExtent {
69
.offset = offset,
205
@@ -XXX,XX +XXX,XX @@ typedef struct VmdkExtent {
70
.bytes = bytes,
206
bool compressed;
71
};
207
bool has_marker;
72
@@ -XXX,XX +XXX,XX @@ void block_copy_set_progress_meter(BlockCopyState *s, ProgressMeter *pm)
208
bool has_zero_grain;
73
s->progress = pm;
209
+ bool sesparse;
210
+ uint64_t sesparse_l2_tables_offset;
211
+ uint64_t sesparse_clusters_offset;
212
+ int32_t entry_size;
213
int version;
214
int64_t sectors;
215
int64_t end_sector;
216
int64_t flat_start_offset;
217
int64_t l1_table_offset;
218
int64_t l1_backup_table_offset;
219
- uint32_t *l1_table;
220
+ void *l1_table;
221
uint32_t *l1_backup_table;
222
unsigned int l1_size;
223
uint32_t l1_entry_sectors;
224
225
unsigned int l2_size;
226
- uint32_t *l2_cache;
227
+ void *l2_cache;
228
uint32_t l2_cache_offsets[L2_CACHE_SIZE];
229
uint32_t l2_cache_counts[L2_CACHE_SIZE];
230
231
@@ -XXX,XX +XXX,XX @@ static int vmdk_add_extent(BlockDriverState *bs,
232
* minimal L2 table size: 512 entries
233
* 8 TB is still more than the maximal value supported for
234
* VMDK3 & VMDK4 which is 2TB.
235
+ * 64TB - for "ESXi seSparse Extent"
236
+ * minimal cluster size: 512B (default is 4KB)
237
+ * L2 table size: 4096 entries (const).
238
+ * 64TB is more than the maximal value supported for
239
+ * seSparse VMDKs (which is slightly less than 64TB)
240
*/
241
error_setg(errp, "L1 size too big");
242
return -EFBIG;
243
@@ -XXX,XX +XXX,XX @@ static int vmdk_add_extent(BlockDriverState *bs,
244
extent->l2_size = l2_size;
245
extent->cluster_sectors = flat ? sectors : cluster_sectors;
246
extent->next_cluster_sector = ROUND_UP(nb_sectors, cluster_sectors);
247
+ extent->entry_size = sizeof(uint32_t);
248
249
if (s->num_extents > 1) {
250
extent->end_sector = (*(extent - 1)).end_sector + extent->sectors;
251
@@ -XXX,XX +XXX,XX @@ static int vmdk_init_tables(BlockDriverState *bs, VmdkExtent *extent,
252
int i;
253
254
/* read the L1 table */
255
- l1_size = extent->l1_size * sizeof(uint32_t);
256
+ l1_size = extent->l1_size * extent->entry_size;
257
extent->l1_table = g_try_malloc(l1_size);
258
if (l1_size && extent->l1_table == NULL) {
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);
270
+ }
271
}
272
273
if (extent->l1_backup_table_offset) {
274
+ assert(!extent->sesparse);
275
extent->l1_backup_table = g_try_malloc(l1_size);
276
if (l1_size && extent->l1_backup_table == NULL) {
277
ret = -ENOMEM;
278
@@ -XXX,XX +XXX,XX @@ static int vmdk_init_tables(BlockDriverState *bs, VmdkExtent *extent,
279
}
280
281
extent->l2_cache =
282
- g_new(uint32_t, extent->l2_size * L2_CACHE_SIZE);
283
+ g_malloc(extent->entry_size * extent->l2_size * L2_CACHE_SIZE);
284
return 0;
285
fail_l1b:
286
g_free(extent->l1_backup_table);
287
@@ -XXX,XX +XXX,XX @@ static int vmdk_open_vmfs_sparse(BlockDriverState *bs,
288
return ret;
74
}
289
}
75
290
76
+/*
291
+#define SESPARSE_CONST_HEADER_MAGIC UINT64_C(0x00000000cafebabe)
77
+ * Takes ownership of @task
292
+#define SESPARSE_VOLATILE_HEADER_MAGIC UINT64_C(0x00000000cafecafe)
78
+ *
293
+
79
+ * If pool is NULL directly run the task, otherwise schedule it into the pool.
294
+/* Strict checks - format not officially documented */
80
+ *
295
+static int check_se_sparse_const_header(VMDKSESparseConstHeader *header,
81
+ * Returns: task.func return code if pool is NULL
296
+ Error **errp)
82
+ * otherwise -ECANCELED if pool status is bad
83
+ * otherwise 0 (successfully scheduled)
84
+ */
85
+static coroutine_fn int block_copy_task_run(AioTaskPool *pool,
86
+ BlockCopyTask *task)
87
+{
297
+{
88
+ if (!pool) {
298
+ header->magic = le64_to_cpu(header->magic);
89
+ int ret = task->task.func(&task->task);
299
+ header->version = le64_to_cpu(header->version);
90
+
300
+ header->grain_size = le64_to_cpu(header->grain_size);
91
+ g_free(task);
301
+ header->grain_table_size = le64_to_cpu(header->grain_table_size);
92
+ return ret;
302
+ header->flags = le64_to_cpu(header->flags);
93
+ }
303
+ header->reserved1 = le64_to_cpu(header->reserved1);
94
+
304
+ header->reserved2 = le64_to_cpu(header->reserved2);
95
+ aio_task_pool_wait_slot(pool);
305
+ header->reserved3 = le64_to_cpu(header->reserved3);
96
+ if (aio_task_pool_status(pool) < 0) {
306
+ header->reserved4 = le64_to_cpu(header->reserved4);
97
+ co_put_to_shres(task->s->mem, task->bytes);
307
+
98
+ block_copy_task_end(task, -ECANCELED);
308
+ header->volatile_header_offset =
99
+ g_free(task);
309
+ le64_to_cpu(header->volatile_header_offset);
100
+ return -ECANCELED;
310
+ header->volatile_header_size = le64_to_cpu(header->volatile_header_size);
101
+ }
311
+
102
+
312
+ header->journal_header_offset = le64_to_cpu(header->journal_header_offset);
103
+ aio_task_pool_start_task(pool, &task->task);
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
+ }
104
+
378
+
105
+ return 0;
379
+ return 0;
106
+}
380
+}
107
+
381
+
108
/*
382
+static int check_se_sparse_volatile_header(VMDKSESparseVolatileHeader *header,
109
* block_copy_do_copy
383
+ Error **errp)
110
*
111
@@ -XXX,XX +XXX,XX @@ out:
112
return ret;
113
}
114
115
+static coroutine_fn int block_copy_task_entry(AioTask *task)
116
+{
384
+{
117
+ BlockCopyTask *t = container_of(task, BlockCopyTask, task);
385
+ header->magic = le64_to_cpu(header->magic);
118
+ bool error_is_read;
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
+{
119
+ int ret;
414
+ int ret;
120
+
415
+ VMDKSESparseConstHeader const_header;
121
+ ret = block_copy_do_copy(t->s, t->offset, t->bytes, t->zeroes,
416
+ VMDKSESparseVolatileHeader volatile_header;
122
+ &error_is_read);
417
+ VmdkExtent *extent;
123
+ if (ret < 0 && !t->call_state->failed) {
418
+
124
+ t->call_state->failed = true;
419
+ ret = bdrv_apply_auto_read_only(bs,
125
+ t->call_state->error_is_read = error_is_read;
420
+ "No write support for seSparse images available", errp);
126
+ } else {
421
+ if (ret < 0) {
127
+ progress_work_done(t->s->progress, t->bytes);
422
+ return ret;
128
+ t->s->progress_bytes_callback(t->bytes, t->s->progress_opaque);
423
+ }
129
+ }
424
+
130
+ co_put_to_shres(t->s->mem, t->bytes);
425
+ assert(sizeof(const_header) == SECTOR_SIZE);
131
+ block_copy_task_end(t, ret);
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
+ }
132
+
486
+
133
+ return ret;
487
+ return ret;
134
+}
488
+}
135
+
489
+
136
static int block_copy_block_status(BlockCopyState *s, int64_t offset,
490
static int vmdk_open_desc_file(BlockDriverState *bs, int flags, char *buf,
137
int64_t bytes, int64_t *pnum)
491
QDict *options, Error **errp);
138
{
492
139
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn block_copy_dirty_clusters(BlockCopyState *s,
493
@@ -XXX,XX +XXX,XX @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
140
int ret = 0;
494
* RW [size in sectors] SPARSE "file-name.vmdk"
141
bool found_dirty = false;
495
* RW [size in sectors] VMFS "file-name.vmdk"
142
int64_t end = offset + bytes;
496
* RW [size in sectors] VMFSSPARSE "file-name.vmdk"
143
+ AioTaskPool *aio = NULL;
497
+ * RW [size in sectors] SESPARSE "file-name.vmdk"
144
+ BlockCopyCallState call_state = {false, false};
498
*/
145
499
flat_offset = -1;
146
/*
500
matches = sscanf(p, "%10s %" SCNd64 " %10s \"%511[^\n\r\"]\" %" SCNd64,
147
* block_copy() user is responsible for keeping source and target in same
501
@@ -XXX,XX +XXX,XX @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
148
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn block_copy_dirty_clusters(BlockCopyState *s,
502
149
assert(QEMU_IS_ALIGNED(offset, s->cluster_size));
503
if (sectors <= 0 ||
150
assert(QEMU_IS_ALIGNED(bytes, s->cluster_size));
504
(strcmp(type, "FLAT") && strcmp(type, "SPARSE") &&
151
505
- strcmp(type, "VMFS") && strcmp(type, "VMFSSPARSE")) ||
152
- while (bytes) {
506
+ strcmp(type, "VMFS") && strcmp(type, "VMFSSPARSE") &&
153
- g_autofree BlockCopyTask *task = NULL;
507
+ strcmp(type, "SESPARSE")) ||
154
+ while (bytes && aio_task_pool_status(aio) == 0) {
508
(strcmp(access, "RW"))) {
155
+ BlockCopyTask *task;
156
int64_t status_bytes;
157
158
- task = block_copy_task_create(s, offset, bytes);
159
+ task = block_copy_task_create(s, &call_state, offset, bytes);
160
if (!task) {
161
/* No more dirty bits in the bitmap */
162
trace_block_copy_skip_range(s, offset, bytes);
163
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn block_copy_dirty_clusters(BlockCopyState *s,
164
}
165
if (s->skip_unallocated && !(ret & BDRV_BLOCK_ALLOCATED)) {
166
block_copy_task_end(task, 0);
167
+ g_free(task);
168
progress_set_remaining(s->progress,
169
bdrv_get_dirty_count(s->copy_bitmap) +
170
s->in_flight_bytes);
171
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn block_copy_dirty_clusters(BlockCopyState *s,
172
bytes = end - offset;
173
continue;
509
continue;
174
}
510
}
175
+ task->zeroes = ret & BDRV_BLOCK_ZERO;
511
@@ -XXX,XX +XXX,XX @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
176
512
return ret;
177
trace_block_copy_process(s, task->offset);
513
}
178
514
extent = &s->extents[s->num_extents - 1];
179
co_get_from_shres(s->mem, task->bytes);
515
+ } else if (!strcmp(type, "SESPARSE")) {
180
- ret = block_copy_do_copy(s, task->offset, task->bytes,
516
+ ret = vmdk_open_se_sparse(bs, extent_file, bs->open_flags, errp);
181
- ret & BDRV_BLOCK_ZERO, error_is_read);
517
+ if (ret) {
182
- co_put_to_shres(s->mem, task->bytes);
518
+ bdrv_unref_child(bs, extent_file);
183
- block_copy_task_end(task, ret);
519
+ return ret;
184
- if (ret < 0) {
520
+ }
185
- return ret;
521
+ extent = &s->extents[s->num_extents - 1];
186
- }
522
} else {
187
523
error_setg(errp, "Unsupported extent type '%s'", type);
188
- progress_work_done(s->progress, task->bytes);
524
bdrv_unref_child(bs, extent_file);
189
- s->progress_bytes_callback(task->bytes, s->progress_opaque);
525
@@ -XXX,XX +XXX,XX @@ static int vmdk_open_desc_file(BlockDriverState *bs, int flags, char *buf,
190
offset = task_end(task);
526
if (strcmp(ct, "monolithicFlat") &&
191
bytes = end - offset;
527
strcmp(ct, "vmfs") &&
192
+
528
strcmp(ct, "vmfsSparse") &&
193
+ if (!aio && bytes) {
529
+ strcmp(ct, "seSparse") &&
194
+ aio = aio_task_pool_new(BLOCK_COPY_MAX_WORKERS);
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,
534
{
535
unsigned int l1_index, l2_offset, l2_index;
536
int min_index, i, j;
537
- uint32_t min_count, *l2_table;
538
+ uint32_t min_count;
539
+ void *l2_table;
540
bool zeroed = false;
541
int64_t ret;
542
int64_t cluster_sector;
543
+ unsigned int l2_size_bytes = extent->l2_size * extent->entry_size;
544
545
if (m_data) {
546
m_data->valid = 0;
547
@@ -XXX,XX +XXX,XX @@ static int get_cluster_offset(BlockDriverState *bs,
548
if (l1_index >= extent->l1_size) {
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);
195
+ }
577
+ }
196
+
578
+ } else {
197
+ ret = block_copy_task_run(aio, task);
579
+ assert(extent->entry_size == sizeof(uint32_t));
198
+ if (ret < 0) {
580
+ l2_offset = ((uint32_t *)extent->l1_table)[l1_index];
199
+ goto out;
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;
592
}
593
}
594
@@ -XXX,XX +XXX,XX @@ static int get_cluster_offset(BlockDriverState *bs,
595
min_index = i;
596
}
597
}
598
- l2_table = extent->l2_cache + (min_index * extent->l2_size);
599
+ l2_table = (char *)extent->l2_cache + (min_index * l2_size_bytes);
600
BLKDBG_EVENT(extent->file, BLKDBG_L2_LOAD);
601
if (bdrv_pread(extent->file,
602
(int64_t)l2_offset * 512,
603
l2_table,
604
- extent->l2_size * sizeof(uint32_t)
605
- ) != extent->l2_size * sizeof(uint32_t)) {
606
+ l2_size_bytes
607
+ ) != l2_size_bytes) {
608
return VMDK_ERROR;
609
}
610
611
@@ -XXX,XX +XXX,XX @@ static int get_cluster_offset(BlockDriverState *bs,
612
extent->l2_cache_counts[min_index] = 1;
613
found:
614
l2_index = ((offset >> 9) / extent->cluster_sectors) % extent->l2_size;
615
- cluster_sector = le32_to_cpu(l2_table[l2_index]);
616
617
- if (extent->has_zero_grain && cluster_sector == VMDK_GTE_ZEROED) {
618
- zeroed = true;
619
+ if (extent->sesparse) {
620
+ cluster_sector = le64_to_cpu(((uint64_t *)l2_table)[l2_index]);
621
+ switch (cluster_sector & 0xf000000000000000) {
622
+ case 0x0000000000000000:
623
+ /* unallocated grain */
624
+ if (cluster_sector != 0) {
625
+ return VMDK_ERROR;
626
+ }
627
+ break;
628
+ case 0x1000000000000000:
629
+ /* scsi-unmapped grain - fallthrough */
630
+ case 0x2000000000000000:
631
+ /* zero grain */
632
+ zeroed = true;
633
+ break;
634
+ case 0x3000000000000000:
635
+ /* allocated grain */
636
+ cluster_sector = (((cluster_sector & 0x0fff000000000000) >> 48) |
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;
200
+ }
643
+ }
201
+ }
644
+ } else {
202
+
645
+ cluster_sector = le32_to_cpu(((uint32_t *)l2_table)[l2_index]);
203
+out:
646
+
204
+ if (aio) {
647
+ if (extent->has_zero_grain && cluster_sector == VMDK_GTE_ZEROED) {
205
+ aio_task_pool_wait_all(aio);
648
+ zeroed = true;
206
+
649
+ }
207
+ /*
650
}
208
+ * We are not really interested in -ECANCELED returned from
651
209
+ * block_copy_task_run. If it fails, it means some task already failed
652
if (!cluster_sector || zeroed) {
210
+ * for real reason, let's return first failure.
653
if (!allocate) {
211
+ * Still, assert that we don't rewrite failure by success.
654
return zeroed ? VMDK_ZEROED : VMDK_UNALLOC;
212
+ */
655
}
213
+ assert(ret == 0 || aio_task_pool_status(aio) < 0);
656
+ assert(!extent->sesparse);
214
+ ret = aio_task_pool_status(aio);
657
215
+
658
if (extent->next_cluster_sector >= VMDK_EXTENT_MAX_SECTORS) {
216
+ aio_task_pool_free(aio);
659
return VMDK_ERROR;
217
+ }
660
@@ -XXX,XX +XXX,XX @@ static int get_cluster_offset(BlockDriverState *bs,
218
+ if (error_is_read && ret < 0) {
661
m_data->l1_index = l1_index;
219
+ *error_is_read = call_state.error_is_read;
662
m_data->l2_index = l2_index;
220
}
663
m_data->l2_offset = l2_offset;
221
664
- m_data->l2_cache_entry = &l2_table[l2_index];
222
- return found_dirty;
665
+ m_data->l2_cache_entry = ((uint32_t *)l2_table) + l2_index;
223
+ return ret < 0 ? ret : found_dirty;
666
}
224
}
667
}
225
668
*cluster_offset = cluster_sector << BDRV_SECTOR_BITS;
226
/*
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);
227
--
679
--
228
2.26.2
680
2.21.0
229
681
230
682
diff view generated by jsdifflib
1
From: John Snow <jsnow@redhat.com>
1
From: Pino Toscano <ptoscano@redhat.com>
2
2
3
Like script_main, but doesn't require a single point of entry.
3
Rewrite the implementation of the ssh block driver to use libssh instead
4
Replace all existing initialization sections with this drop-in replacement.
4
of libssh2. The libssh library has various advantages over libssh2:
5
- easier API for authentication (for example for using ssh-agent)
6
- easier API for known_hosts handling
7
- supports newer types of keys in known_hosts
5
8
6
This brings debug support to all existing script-style iotests.
9
Use APIs/features available in libssh 0.8 conditionally, to support
10
older versions (which are not recommended though).
7
11
8
Signed-off-by: John Snow <jsnow@redhat.com>
12
Adjust the iotest 207 according to the different error message, and to
9
Reviewed-by: Max Reitz <mreitz@redhat.com>
13
find the default key type for localhost (to properly compare the
10
Message-Id: <20200331000014.11581-12-jsnow@redhat.com>
14
fingerprint with).
11
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
15
Contributed-by: Max Reitz <mreitz@redhat.com>
12
[mreitz: Give 274 the same treatment]
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
13
Signed-off-by: Max Reitz <mreitz@redhat.com>
27
Signed-off-by: Max Reitz <mreitz@redhat.com>
14
---
28
---
15
tests/qemu-iotests/149 | 3 +-
29
configure | 65 +-
16
tests/qemu-iotests/194 | 4 +-
30
block/Makefile.objs | 6 +-
17
tests/qemu-iotests/202 | 4 +-
31
block/ssh.c | 652 ++++++++++--------
18
tests/qemu-iotests/203 | 4 +-
32
.travis.yml | 4 +-
19
tests/qemu-iotests/206 | 2 +-
33
block/trace-events | 14 +-
20
tests/qemu-iotests/207 | 6 ++-
34
docs/qemu-block-drivers.texi | 2 +-
21
tests/qemu-iotests/208 | 2 +-
35
.../dockerfiles/debian-win32-cross.docker | 1 -
22
tests/qemu-iotests/209 | 2 +-
36
.../dockerfiles/debian-win64-cross.docker | 1 -
23
tests/qemu-iotests/210 | 6 ++-
37
tests/docker/dockerfiles/fedora.docker | 4 +-
24
tests/qemu-iotests/211 | 6 ++-
38
tests/docker/dockerfiles/ubuntu.docker | 2 +-
25
tests/qemu-iotests/212 | 6 ++-
39
tests/docker/dockerfiles/ubuntu1804.docker | 2 +-
26
tests/qemu-iotests/213 | 6 ++-
40
tests/qemu-iotests/207 | 54 +-
27
tests/qemu-iotests/216 | 4 +-
41
tests/qemu-iotests/207.out | 2 +-
28
tests/qemu-iotests/218 | 2 +-
42
13 files changed, 449 insertions(+), 360 deletions(-)
29
tests/qemu-iotests/219 | 2 +-
30
tests/qemu-iotests/222 | 7 ++--
31
tests/qemu-iotests/224 | 4 +-
32
tests/qemu-iotests/228 | 6 ++-
33
tests/qemu-iotests/234 | 4 +-
34
tests/qemu-iotests/235 | 4 +-
35
tests/qemu-iotests/236 | 2 +-
36
tests/qemu-iotests/237 | 2 +-
37
tests/qemu-iotests/238 | 2 +
38
tests/qemu-iotests/242 | 2 +-
39
tests/qemu-iotests/246 | 2 +-
40
tests/qemu-iotests/248 | 2 +-
41
tests/qemu-iotests/254 | 2 +-
42
tests/qemu-iotests/255 | 2 +-
43
tests/qemu-iotests/256 | 2 +-
44
tests/qemu-iotests/258 | 7 ++--
45
tests/qemu-iotests/260 | 4 +-
46
tests/qemu-iotests/262 | 4 +-
47
tests/qemu-iotests/264 | 4 +-
48
tests/qemu-iotests/274 | 4 +-
49
tests/qemu-iotests/277 | 2 +
50
tests/qemu-iotests/280 | 8 ++--
51
tests/qemu-iotests/283 | 4 +-
52
tests/qemu-iotests/iotests.py | 76 +++++++++++++++++++++++------------
53
38 files changed, 132 insertions(+), 83 deletions(-)
54
43
55
diff --git a/tests/qemu-iotests/149 b/tests/qemu-iotests/149
44
diff --git a/configure b/configure
56
index XXXXXXX..XXXXXXX 100755
45
index XXXXXXX..XXXXXXX 100755
57
--- a/tests/qemu-iotests/149
46
--- a/configure
58
+++ b/tests/qemu-iotests/149
47
+++ b/configure
59
@@ -XXX,XX +XXX,XX @@ def test_once(config, qemu_img=False):
48
@@ -XXX,XX +XXX,XX @@ auth_pam=""
60
49
vte=""
61
50
virglrenderer=""
62
# Obviously we only work with the luks image format
51
tpm=""
63
-iotests.verify_image_format(supported_fmts=['luks'])
52
-libssh2=""
64
-iotests.verify_platform()
53
+libssh=""
65
+iotests.script_initialize(supported_fmts=['luks'])
54
live_block_migration="yes"
66
55
numa=""
67
# We need sudo in order to run cryptsetup to create
56
tcmalloc="no"
68
# dm-crypt devices. This is safe to use on any
57
@@ -XXX,XX +XXX,XX @@ for opt do
69
diff --git a/tests/qemu-iotests/194 b/tests/qemu-iotests/194
58
;;
70
index XXXXXXX..XXXXXXX 100755
59
--enable-tpm) tpm="yes"
71
--- a/tests/qemu-iotests/194
60
;;
72
+++ b/tests/qemu-iotests/194
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
166
index XXXXXXX..XXXXXXX 100644
167
--- a/block/Makefile.objs
168
+++ b/block/Makefile.objs
169
@@ -XXX,XX +XXX,XX @@ block-obj-$(CONFIG_CURL) += curl.o
170
block-obj-$(CONFIG_RBD) += rbd.o
171
block-obj-$(CONFIG_GLUSTERFS) += gluster.o
172
block-obj-$(CONFIG_VXHS) += vxhs.o
173
-block-obj-$(CONFIG_LIBSSH2) += ssh.o
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
73
@@ -XXX,XX +XXX,XX @@
193
@@ -XXX,XX +XXX,XX @@
74
194
75
import iotests
195
#include "qemu/osdep.h"
76
196
77
-iotests.verify_image_format(supported_fmts=['qcow2', 'qed', 'raw'])
197
-#include <libssh2.h>
78
-iotests.verify_platform(['linux'])
198
-#include <libssh2_sftp.h>
79
+iotests.script_initialize(supported_fmts=['qcow2', 'qed', 'raw'],
199
+#include <libssh/libssh.h>
80
+ supported_platforms=['linux'])
200
+#include <libssh/sftp.h>
81
201
82
with iotests.FilePath('source.img') as source_img_path, \
202
#include "block/block_int.h"
83
iotests.FilePath('dest.img') as dest_img_path, \
203
#include "block/qdict.h"
84
diff --git a/tests/qemu-iotests/202 b/tests/qemu-iotests/202
85
index XXXXXXX..XXXXXXX 100755
86
--- a/tests/qemu-iotests/202
87
+++ b/tests/qemu-iotests/202
88
@@ -XXX,XX +XXX,XX @@
204
@@ -XXX,XX +XXX,XX @@
89
205
#include "trace.h"
90
import iotests
206
91
207
/*
92
-iotests.verify_image_format(supported_fmts=['qcow2'])
208
- * TRACE_LIBSSH2=<bitmask> enables tracing in libssh2 itself. Note
93
-iotests.verify_platform(['linux'])
209
- * that this requires that libssh2 was specially compiled with the
94
+iotests.script_initialize(supported_fmts=['qcow2'],
210
- * `./configure --enable-debug' option, so most likely you will have
95
+ supported_platforms=['linux'])
211
- * to compile it yourself. The meaning of <bitmask> is described
96
212
- * here: http://www.libssh2.org/libssh2_trace.html
97
with iotests.FilePath('disk0.img') as disk0_img_path, \
213
+ * TRACE_LIBSSH=<level> enables tracing in libssh itself.
98
iotests.FilePath('disk1.img') as disk1_img_path, \
214
+ * The meaning of <level> is described here:
99
diff --git a/tests/qemu-iotests/203 b/tests/qemu-iotests/203
215
+ * http://api.libssh.org/master/group__libssh__log.html
100
index XXXXXXX..XXXXXXX 100755
216
*/
101
--- a/tests/qemu-iotests/203
217
-#define TRACE_LIBSSH2 0 /* or try: LIBSSH2_TRACE_SFTP */
102
+++ b/tests/qemu-iotests/203
218
+#define TRACE_LIBSSH 0 /* see: SSH_LOG_* */
103
@@ -XXX,XX +XXX,XX @@
219
104
220
typedef struct BDRVSSHState {
105
import iotests
221
/* Coroutine. */
106
222
@@ -XXX,XX +XXX,XX @@ typedef struct BDRVSSHState {
107
-iotests.verify_image_format(supported_fmts=['qcow2'])
223
108
-iotests.verify_platform(['linux'])
224
/* SSH connection. */
109
+iotests.script_initialize(supported_fmts=['qcow2'],
225
int sock; /* socket */
110
+ supported_platforms=['linux'])
226
- LIBSSH2_SESSION *session; /* ssh session */
111
227
- LIBSSH2_SFTP *sftp; /* sftp session */
112
with iotests.FilePath('disk0.img') as disk0_img_path, \
228
- LIBSSH2_SFTP_HANDLE *sftp_handle; /* sftp remote file handle */
113
iotests.FilePath('disk1.img') as disk1_img_path, \
229
+ ssh_session session; /* ssh session */
114
diff --git a/tests/qemu-iotests/206 b/tests/qemu-iotests/206
230
+ sftp_session sftp; /* sftp session */
115
index XXXXXXX..XXXXXXX 100755
231
+ sftp_file sftp_handle; /* sftp remote file handle */
116
--- a/tests/qemu-iotests/206
232
117
+++ b/tests/qemu-iotests/206
233
- /* See ssh_seek() function below. */
118
@@ -XXX,XX +XXX,XX @@
234
- int64_t offset;
119
import iotests
235
- bool offset_op_read;
120
from iotests import imgfmt
236
-
121
237
- /* File attributes at open. We try to keep the .filesize field
122
-iotests.verify_image_format(supported_fmts=['qcow2'])
238
+ /*
123
+iotests.script_initialize(supported_fmts=['qcow2'])
239
+ * File attributes at open. We try to keep the .size field
124
240
* updated if it changes (eg by writing at the end of the file).
125
with iotests.FilePath('t.qcow2') as disk_path, \
241
*/
126
iotests.FilePath('t.qcow2.base') as backing_path, \
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;
672
goto out;
673
}
674
- /* Failed to authenticate with this identity, try the next one. */
675
- prev_identity = identity;
676
}
677
678
ret = -EPERM;
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;
1084
}
1085
- if (r < 0) {
1086
- sftp_error_trace(s, "read");
1087
- s->offset = -1;
1088
- return -EIO;
1089
- }
1090
- if (r == 0) {
1091
+ if (r == SSH_EOF || (r == 0 && sftp_get_error(s->sftp) == SSH_FX_EOF)) {
1092
/* EOF: Short read so pad the buffer with zeroes and return it. */
1093
qemu_iovec_memset(qiov, got, 0, size - got);
1094
return 0;
1095
}
1096
+ if (r <= 0) {
1097
+ sftp_error_trace(s, "read");
1098
+ return -EIO;
1099
+ }
1100
1101
got += r;
1102
buf += r;
1103
- s->offset += r;
1104
if (buf >= end_of_vec && got < size) {
1105
i++;
1106
buf = i->iov_base;
1107
@@ -XXX,XX +XXX,XX @@ static int ssh_write(BDRVSSHState *s, BlockDriverState *bs,
1108
1109
trace_ssh_write(offset, size);
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
1276
index XXXXXXX..XXXXXXX 100644
1277
--- a/.travis.yml
1278
+++ b/.travis.yml
1279
@@ -XXX,XX +XXX,XX @@ addons:
1280
- libseccomp-dev
1281
- libspice-protocol-dev
1282
- libspice-server-dev
1283
- - libssh2-1-dev
1284
+ - libssh-dev
1285
- liburcu-dev
1286
- libusb-1.0-0-dev
1287
- libvte-2.91-dev
1288
@@ -XXX,XX +XXX,XX @@ matrix:
1289
- libseccomp-dev
1290
- libspice-protocol-dev
1291
- libspice-server-dev
1292
- - libssh2-1-dev
1293
+ - libssh-dev
1294
- liburcu-dev
1295
- libusb-1.0-0-dev
1296
- libvte-2.91-dev
1297
diff --git a/block/trace-events b/block/trace-events
1298
index XXXXXXX..XXXXXXX 100644
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 \
127
diff --git a/tests/qemu-iotests/207 b/tests/qemu-iotests/207
1426
diff --git a/tests/qemu-iotests/207 b/tests/qemu-iotests/207
128
index XXXXXXX..XXXXXXX 100755
1427
index XXXXXXX..XXXXXXX 100755
129
--- a/tests/qemu-iotests/207
1428
--- a/tests/qemu-iotests/207
130
+++ b/tests/qemu-iotests/207
1429
+++ b/tests/qemu-iotests/207
131
@@ -XXX,XX +XXX,XX @@ import iotests
1430
@@ -XXX,XX +XXX,XX @@ with iotests.FilePath('t.img') as disk_path, \
132
import subprocess
1431
133
import re
1432
iotests.img_info_log(remote_path)
134
1433
135
-iotests.verify_image_format(supported_fmts=['raw'])
1434
- md5_key = subprocess.check_output(
136
-iotests.verify_protocol(supported=['ssh'])
1435
- 'ssh-keyscan -t rsa 127.0.0.1 2>/dev/null | grep -v "\\^#" | ' +
137
+iotests.script_initialize(
1436
- 'cut -d" " -f3 | base64 -d | md5sum -b | cut -d" " -f1',
138
+ supported_fmts=['raw'],
1437
- shell=True).rstrip().decode('ascii')
139
+ supported_protocols=['ssh'],
1438
+ keys = subprocess.check_output(
140
+)
1439
+ 'ssh-keyscan 127.0.0.1 2>/dev/null | grep -v "\\^#" | ' +
141
1440
+ 'cut -d" " -f3',
142
def filter_hash(qmsg):
1441
+ shell=True).rstrip().decode('ascii').split('\n')
143
def _filter(key, value):
1442
+
144
diff --git a/tests/qemu-iotests/208 b/tests/qemu-iotests/208
1443
+ # Mappings of base64 representations to digests
145
index XXXXXXX..XXXXXXX 100755
1444
+ md5_keys = {}
146
--- a/tests/qemu-iotests/208
1445
+ sha1_keys = {}
147
+++ b/tests/qemu-iotests/208
1446
+
148
@@ -XXX,XX +XXX,XX @@
1447
+ for key in keys:
149
1448
+ md5_keys[key] = subprocess.check_output(
150
import iotests
1449
+ 'echo %s | base64 -d | md5sum -b | cut -d" " -f1' % key,
151
1450
+ shell=True).rstrip().decode('ascii')
152
-iotests.verify_image_format(supported_fmts=['generic'])
1451
+
153
+iotests.script_initialize(supported_fmts=['generic'])
1452
+ sha1_keys[key] = subprocess.check_output(
154
1453
+ 'echo %s | base64 -d | sha1sum -b | cut -d" " -f1' % key,
155
with iotests.FilePath('disk.img') as disk_img_path, \
1454
+ shell=True).rstrip().decode('ascii')
156
iotests.FilePath('disk-snapshot.img') as disk_snapshot_img_path, \
1455
157
diff --git a/tests/qemu-iotests/209 b/tests/qemu-iotests/209
1456
vm.launch()
158
index XXXXXXX..XXXXXXX 100755
1457
+
159
--- a/tests/qemu-iotests/209
1458
+ # Find correct key first
160
+++ b/tests/qemu-iotests/209
1459
+ matching_key = None
161
@@ -XXX,XX +XXX,XX @@ import iotests
1460
+ for key in keys:
162
from iotests import qemu_img_create, qemu_io, qemu_img_verbose, qemu_nbd, \
1461
+ result = vm.qmp('blockdev-add',
163
file_path
1462
+ driver='ssh', node_name='node0', path=disk_path,
164
1463
+ server={
165
-iotests.verify_image_format(supported_fmts=['qcow2'])
1464
+ 'host': '127.0.0.1',
166
+iotests.script_initialize(supported_fmts=['qcow2'])
1465
+ 'port': '22',
167
1466
+ }, host_key_check={
168
disk = file_path('disk')
1467
+ 'mode': 'hash',
169
nbd_sock = file_path('nbd-sock', base_dir=iotests.sock_dir)
1468
+ 'type': 'md5',
170
diff --git a/tests/qemu-iotests/210 b/tests/qemu-iotests/210
1469
+ 'hash': md5_keys[key],
171
index XXXXXXX..XXXXXXX 100755
1470
+ })
172
--- a/tests/qemu-iotests/210
1471
+
173
+++ b/tests/qemu-iotests/210
1472
+ if 'error' not in result:
174
@@ -XXX,XX +XXX,XX @@
1473
+ vm.qmp('blockdev-del', node_name='node0')
175
import iotests
1474
+ matching_key = key
176
from iotests import imgfmt
1475
+ break
177
1476
+
178
-iotests.verify_image_format(supported_fmts=['luks'])
1477
+ if matching_key is None:
179
-iotests.verify_protocol(supported=['file'])
1478
+ vm.shutdown()
180
+iotests.script_initialize(
1479
+ iotests.notrun('Did not find a key that fits 127.0.0.1')
181
+ supported_fmts=['luks'],
1480
+
182
+ supported_protocols=['file'],
1481
blockdev_create(vm, { 'driver': 'ssh',
183
+)
1482
'location': {
184
1483
'path': disk_path,
185
with iotests.FilePath('t.luks') as disk_path, \
1484
@@ -XXX,XX +XXX,XX @@ with iotests.FilePath('t.img') as disk_path, \
186
iotests.VM() as vm:
1485
'host-key-check': {
187
diff --git a/tests/qemu-iotests/211 b/tests/qemu-iotests/211
1486
'mode': 'hash',
188
index XXXXXXX..XXXXXXX 100755
1487
'type': 'md5',
189
--- a/tests/qemu-iotests/211
1488
- 'hash': md5_key,
190
+++ b/tests/qemu-iotests/211
1489
+ 'hash': md5_keys[matching_key],
191
@@ -XXX,XX +XXX,XX @@
1490
}
192
import iotests
1491
},
193
from iotests import imgfmt
1492
'size': 8388608 })
194
1493
@@ -XXX,XX +XXX,XX @@ with iotests.FilePath('t.img') as disk_path, \
195
-iotests.verify_image_format(supported_fmts=['vdi'])
1494
196
-iotests.verify_protocol(supported=['file'])
1495
iotests.img_info_log(remote_path)
197
+iotests.script_initialize(
1496
198
+ supported_fmts=['vdi'],
1497
- sha1_key = subprocess.check_output(
199
+ supported_protocols=['file'],
1498
- 'ssh-keyscan -t rsa 127.0.0.1 2>/dev/null | grep -v "\\^#" | ' +
200
+)
1499
- 'cut -d" " -f3 | base64 -d | sha1sum -b | cut -d" " -f1',
201
1500
- shell=True).rstrip().decode('ascii')
202
def blockdev_create(vm, options):
203
error = vm.blockdev_create(options)
204
diff --git a/tests/qemu-iotests/212 b/tests/qemu-iotests/212
205
index XXXXXXX..XXXXXXX 100755
206
--- a/tests/qemu-iotests/212
207
+++ b/tests/qemu-iotests/212
208
@@ -XXX,XX +XXX,XX @@
209
import iotests
210
from iotests import imgfmt
211
212
-iotests.verify_image_format(supported_fmts=['parallels'])
213
-iotests.verify_protocol(supported=['file'])
214
+iotests.script_initialize(
215
+ supported_fmts=['parallels'],
216
+ supported_protocols=['file'],
217
+)
218
219
with iotests.FilePath('t.parallels') as disk_path, \
220
iotests.VM() as vm:
221
diff --git a/tests/qemu-iotests/213 b/tests/qemu-iotests/213
222
index XXXXXXX..XXXXXXX 100755
223
--- a/tests/qemu-iotests/213
224
+++ b/tests/qemu-iotests/213
225
@@ -XXX,XX +XXX,XX @@
226
import iotests
227
from iotests import imgfmt
228
229
-iotests.verify_image_format(supported_fmts=['vhdx'])
230
-iotests.verify_protocol(supported=['file'])
231
+iotests.script_initialize(
232
+ supported_fmts=['vhdx'],
233
+ supported_protocols=['file'],
234
+)
235
236
with iotests.FilePath('t.vhdx') as disk_path, \
237
iotests.VM() as vm:
238
diff --git a/tests/qemu-iotests/216 b/tests/qemu-iotests/216
239
index XXXXXXX..XXXXXXX 100755
240
--- a/tests/qemu-iotests/216
241
+++ b/tests/qemu-iotests/216
242
@@ -XXX,XX +XXX,XX @@ import iotests
243
from iotests import log, qemu_img, qemu_io_silent
244
245
# Need backing file support
246
-iotests.verify_image_format(supported_fmts=['qcow2', 'qcow', 'qed', 'vmdk'])
247
-iotests.verify_platform(['linux'])
248
+iotests.script_initialize(supported_fmts=['qcow2', 'qcow', 'qed', 'vmdk'],
249
+ supported_platforms=['linux'])
250
251
log('')
252
log('=== Copy-on-read across nodes ===')
253
diff --git a/tests/qemu-iotests/218 b/tests/qemu-iotests/218
254
index XXXXXXX..XXXXXXX 100755
255
--- a/tests/qemu-iotests/218
256
+++ b/tests/qemu-iotests/218
257
@@ -XXX,XX +XXX,XX @@
258
import iotests
259
from iotests import log, qemu_img, qemu_io_silent
260
261
-iotests.verify_image_format(supported_fmts=['qcow2', 'raw'])
262
+iotests.script_initialize(supported_fmts=['qcow2', 'raw'])
263
264
265
# Launches the VM, adds two null-co nodes (source and target), and
266
diff --git a/tests/qemu-iotests/219 b/tests/qemu-iotests/219
267
index XXXXXXX..XXXXXXX 100755
268
--- a/tests/qemu-iotests/219
269
+++ b/tests/qemu-iotests/219
270
@@ -XXX,XX +XXX,XX @@
271
272
import iotests
273
274
-iotests.verify_image_format(supported_fmts=['qcow2'])
275
+iotests.script_initialize(supported_fmts=['qcow2'])
276
277
img_size = 4 * 1024 * 1024
278
279
diff --git a/tests/qemu-iotests/222 b/tests/qemu-iotests/222
280
index XXXXXXX..XXXXXXX 100755
281
--- a/tests/qemu-iotests/222
282
+++ b/tests/qemu-iotests/222
283
@@ -XXX,XX +XXX,XX @@
284
import iotests
285
from iotests import log, qemu_img, qemu_io, qemu_io_silent
286
287
-iotests.verify_platform(['linux'])
288
-iotests.verify_image_format(supported_fmts=['qcow2', 'qcow', 'qed', 'vmdk',
289
- 'vhdx', 'raw'])
290
+iotests.script_initialize(
291
+ supported_fmts=['qcow2', 'qcow', 'qed', 'vmdk', 'vhdx', 'raw'],
292
+ supported_platforms=['linux'],
293
+)
294
295
patterns = [("0x5d", "0", "64k"),
296
("0xd5", "1M", "64k"),
297
diff --git a/tests/qemu-iotests/224 b/tests/qemu-iotests/224
298
index XXXXXXX..XXXXXXX 100755
299
--- a/tests/qemu-iotests/224
300
+++ b/tests/qemu-iotests/224
301
@@ -XXX,XX +XXX,XX @@ from iotests import log, qemu_img, qemu_io_silent, filter_qmp_testfiles, \
302
import json
303
304
# Need backing file support (for arbitrary backing formats)
305
-iotests.verify_image_format(supported_fmts=['qcow2', 'qcow', 'qed'])
306
-iotests.verify_platform(['linux'])
307
+iotests.script_initialize(supported_fmts=['qcow2', 'qcow', 'qed'],
308
+ supported_platforms=['linux'])
309
310
311
# There are two variations of this test:
312
diff --git a/tests/qemu-iotests/228 b/tests/qemu-iotests/228
313
index XXXXXXX..XXXXXXX 100755
314
--- a/tests/qemu-iotests/228
315
+++ b/tests/qemu-iotests/228
316
@@ -XXX,XX +XXX,XX @@ from iotests import log, qemu_img, filter_testfiles, filter_imgfmt, \
317
filter_qmp_testfiles, filter_qmp_imgfmt
318
319
# Need backing file and change-backing-file support
320
-iotests.verify_image_format(supported_fmts=['qcow2', 'qed'])
321
-iotests.verify_platform(['linux'])
322
+iotests.script_initialize(
323
+ supported_fmts=['qcow2', 'qed'],
324
+ supported_platforms=['linux'],
325
+)
326
327
328
def log_node_info(node):
329
diff --git a/tests/qemu-iotests/234 b/tests/qemu-iotests/234
330
index XXXXXXX..XXXXXXX 100755
331
--- a/tests/qemu-iotests/234
332
+++ b/tests/qemu-iotests/234
333
@@ -XXX,XX +XXX,XX @@
334
import iotests
335
import os
336
337
-iotests.verify_image_format(supported_fmts=['qcow2'])
338
-iotests.verify_platform(['linux'])
339
+iotests.script_initialize(supported_fmts=['qcow2'],
340
+ supported_platforms=['linux'])
341
342
with iotests.FilePath('img') as img_path, \
343
iotests.FilePath('backing') as backing_path, \
344
diff --git a/tests/qemu-iotests/235 b/tests/qemu-iotests/235
345
index XXXXXXX..XXXXXXX 100755
346
--- a/tests/qemu-iotests/235
347
+++ b/tests/qemu-iotests/235
348
@@ -XXX,XX +XXX,XX @@ sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
349
350
from qemu.machine import QEMUMachine
351
352
+iotests.script_initialize(supported_fmts=['qcow2'])
353
+
354
# Note:
355
# This test was added to check that mirror dead-lock was fixed (see previous
356
# commit before this test addition).
357
@@ -XXX,XX +XXX,XX @@ from qemu.machine import QEMUMachine
358
359
size = 1 * 1024 * 1024 * 1024
360
361
-iotests.verify_image_format(supported_fmts=['qcow2'])
362
-
1501
-
363
disk = file_path('disk')
1502
vm.launch()
364
1503
blockdev_create(vm, { 'driver': 'ssh',
365
# prepare source image
1504
'location': {
366
diff --git a/tests/qemu-iotests/236 b/tests/qemu-iotests/236
1505
@@ -XXX,XX +XXX,XX @@ with iotests.FilePath('t.img') as disk_path, \
367
index XXXXXXX..XXXXXXX 100755
1506
'host-key-check': {
368
--- a/tests/qemu-iotests/236
1507
'mode': 'hash',
369
+++ b/tests/qemu-iotests/236
1508
'type': 'sha1',
370
@@ -XXX,XX +XXX,XX @@
1509
- 'hash': sha1_key,
371
import iotests
1510
+ 'hash': sha1_keys[matching_key],
372
from iotests import log
1511
}
373
1512
},
374
-iotests.verify_image_format(supported_fmts=['generic'])
1513
'size': 4194304 })
375
+iotests.script_initialize(supported_fmts=['generic'])
1514
diff --git a/tests/qemu-iotests/207.out b/tests/qemu-iotests/207.out
376
size = 64 * 1024 * 1024
377
granularity = 64 * 1024
378
379
diff --git a/tests/qemu-iotests/237 b/tests/qemu-iotests/237
380
index XXXXXXX..XXXXXXX 100755
381
--- a/tests/qemu-iotests/237
382
+++ b/tests/qemu-iotests/237
383
@@ -XXX,XX +XXX,XX @@ import math
384
import iotests
385
from iotests import imgfmt
386
387
-iotests.verify_image_format(supported_fmts=['vmdk'])
388
+iotests.script_initialize(supported_fmts=['vmdk'])
389
390
with iotests.FilePath('t.vmdk') as disk_path, \
391
iotests.FilePath('t.vmdk.1') as extent1_path, \
392
diff --git a/tests/qemu-iotests/238 b/tests/qemu-iotests/238
393
index XXXXXXX..XXXXXXX 100755
394
--- a/tests/qemu-iotests/238
395
+++ b/tests/qemu-iotests/238
396
@@ -XXX,XX +XXX,XX @@ import os
397
import iotests
398
from iotests import log
399
400
+iotests.script_initialize()
401
+
402
virtio_scsi_device = iotests.get_virtio_scsi_device()
403
404
vm = iotests.VM()
405
diff --git a/tests/qemu-iotests/242 b/tests/qemu-iotests/242
406
index XXXXXXX..XXXXXXX 100755
407
--- a/tests/qemu-iotests/242
408
+++ b/tests/qemu-iotests/242
409
@@ -XXX,XX +XXX,XX @@ import struct
410
from iotests import qemu_img_create, qemu_io, qemu_img_pipe, \
411
file_path, img_info_log, log, filter_qemu_io
412
413
-iotests.verify_image_format(supported_fmts=['qcow2'])
414
+iotests.script_initialize(supported_fmts=['qcow2'])
415
416
disk = file_path('disk')
417
chunk = 256 * 1024
418
diff --git a/tests/qemu-iotests/246 b/tests/qemu-iotests/246
419
index XXXXXXX..XXXXXXX 100755
420
--- a/tests/qemu-iotests/246
421
+++ b/tests/qemu-iotests/246
422
@@ -XXX,XX +XXX,XX @@
423
import iotests
424
from iotests import log
425
426
-iotests.verify_image_format(supported_fmts=['qcow2'])
427
+iotests.script_initialize(supported_fmts=['qcow2'])
428
size = 64 * 1024 * 1024 * 1024
429
gran_small = 32 * 1024
430
gran_large = 128 * 1024
431
diff --git a/tests/qemu-iotests/248 b/tests/qemu-iotests/248
432
index XXXXXXX..XXXXXXX 100755
433
--- a/tests/qemu-iotests/248
434
+++ b/tests/qemu-iotests/248
435
@@ -XXX,XX +XXX,XX @@
436
import iotests
437
from iotests import qemu_img_create, qemu_io, file_path, filter_qmp_testfiles
438
439
-iotests.verify_image_format(supported_fmts=['qcow2'])
440
+iotests.script_initialize(supported_fmts=['qcow2'])
441
442
source, target = file_path('source', 'target')
443
size = 5 * 1024 * 1024
444
diff --git a/tests/qemu-iotests/254 b/tests/qemu-iotests/254
445
index XXXXXXX..XXXXXXX 100755
446
--- a/tests/qemu-iotests/254
447
+++ b/tests/qemu-iotests/254
448
@@ -XXX,XX +XXX,XX @@
449
import iotests
450
from iotests import qemu_img_create, file_path, log
451
452
-iotests.verify_image_format(supported_fmts=['qcow2'])
453
+iotests.script_initialize(supported_fmts=['qcow2'])
454
455
disk, top = file_path('disk', 'top')
456
size = 1024 * 1024
457
diff --git a/tests/qemu-iotests/255 b/tests/qemu-iotests/255
458
index XXXXXXX..XXXXXXX 100755
459
--- a/tests/qemu-iotests/255
460
+++ b/tests/qemu-iotests/255
461
@@ -XXX,XX +XXX,XX @@
462
import iotests
463
from iotests import imgfmt
464
465
-iotests.verify_image_format(supported_fmts=['qcow2'])
466
+iotests.script_initialize(supported_fmts=['qcow2'])
467
468
iotests.log('Finishing a commit job with background reads')
469
iotests.log('============================================')
470
diff --git a/tests/qemu-iotests/256 b/tests/qemu-iotests/256
471
index XXXXXXX..XXXXXXX 100755
472
--- a/tests/qemu-iotests/256
473
+++ b/tests/qemu-iotests/256
474
@@ -XXX,XX +XXX,XX @@ import os
475
import iotests
476
from iotests import log
477
478
-iotests.verify_image_format(supported_fmts=['qcow2'])
479
+iotests.script_initialize(supported_fmts=['qcow2'])
480
size = 64 * 1024 * 1024
481
482
with iotests.FilePath('img0') as img0_path, \
483
diff --git a/tests/qemu-iotests/258 b/tests/qemu-iotests/258
484
index XXXXXXX..XXXXXXX 100755
485
--- a/tests/qemu-iotests/258
486
+++ b/tests/qemu-iotests/258
487
@@ -XXX,XX +XXX,XX @@ from iotests import log, qemu_img, qemu_io_silent, \
488
filter_qmp_testfiles, filter_qmp_imgfmt
489
490
# Need backing file and change-backing-file support
491
-iotests.verify_image_format(supported_fmts=['qcow2', 'qed'])
492
-iotests.verify_platform(['linux'])
493
-
494
+iotests.script_initialize(
495
+ supported_fmts=['qcow2', 'qed'],
496
+ supported_platforms=['linux'],
497
+)
498
499
# Returns a node for blockdev-add
500
def node(node_name, path, backing=None, fmt=None, throttle=None):
501
diff --git a/tests/qemu-iotests/260 b/tests/qemu-iotests/260
502
index XXXXXXX..XXXXXXX 100755
503
--- a/tests/qemu-iotests/260
504
+++ b/tests/qemu-iotests/260
505
@@ -XXX,XX +XXX,XX @@
506
import iotests
507
from iotests import qemu_img_create, file_path, log, filter_qmp_event
508
509
-iotests.verify_image_format(supported_fmts=['qcow2'])
510
+iotests.script_initialize(
511
+ supported_fmts=['qcow2']
512
+)
513
514
base, top = file_path('base', 'top')
515
size = 64 * 1024 * 3
516
diff --git a/tests/qemu-iotests/262 b/tests/qemu-iotests/262
517
index XXXXXXX..XXXXXXX 100755
518
--- a/tests/qemu-iotests/262
519
+++ b/tests/qemu-iotests/262
520
@@ -XXX,XX +XXX,XX @@
521
import iotests
522
import os
523
524
-iotests.verify_image_format(supported_fmts=['qcow2'])
525
-iotests.verify_platform(['linux'])
526
+iotests.script_initialize(supported_fmts=['qcow2'],
527
+ supported_platforms=['linux'])
528
529
with iotests.FilePath('img') as img_path, \
530
iotests.FilePath('mig_fifo') as fifo, \
531
diff --git a/tests/qemu-iotests/264 b/tests/qemu-iotests/264
532
index XXXXXXX..XXXXXXX 100755
533
--- a/tests/qemu-iotests/264
534
+++ b/tests/qemu-iotests/264
535
@@ -XXX,XX +XXX,XX @@ import iotests
536
from iotests import qemu_img_create, qemu_io_silent_check, file_path, \
537
qemu_nbd_popen, log
538
539
-iotests.verify_image_format(supported_fmts=['qcow2'])
540
+iotests.script_initialize(
541
+ supported_fmts=['qcow2'],
542
+)
543
544
disk_a, disk_b, nbd_sock = file_path('disk_a', 'disk_b', 'nbd-sock')
545
nbd_uri = 'nbd+unix:///?socket=' + nbd_sock
546
diff --git a/tests/qemu-iotests/274 b/tests/qemu-iotests/274
547
index XXXXXXX..XXXXXXX 100755
548
--- a/tests/qemu-iotests/274
549
+++ b/tests/qemu-iotests/274
550
@@ -XXX,XX +XXX,XX @@
551
552
import iotests
553
554
-iotests.verify_image_format(supported_fmts=['qcow2'])
555
-iotests.verify_platform(['linux'])
556
+iotests.script_initialize(supported_fmts=['qcow2'],
557
+ supported_platforms=['linux'])
558
559
size_short = 1 * 1024 * 1024
560
size_long = 2 * 1024 * 1024
561
diff --git a/tests/qemu-iotests/277 b/tests/qemu-iotests/277
562
index XXXXXXX..XXXXXXX 100755
563
--- a/tests/qemu-iotests/277
564
+++ b/tests/qemu-iotests/277
565
@@ -XXX,XX +XXX,XX @@ import subprocess
566
import iotests
567
from iotests import file_path, log
568
569
+iotests.script_initialize()
570
+
571
572
nbd_sock, conf_file = file_path('nbd-sock', 'nbd-fault-injector.conf')
573
574
diff --git a/tests/qemu-iotests/280 b/tests/qemu-iotests/280
575
index XXXXXXX..XXXXXXX 100755
576
--- a/tests/qemu-iotests/280
577
+++ b/tests/qemu-iotests/280
578
@@ -XXX,XX +XXX,XX @@
579
import iotests
580
import os
581
582
-iotests.verify_image_format(supported_fmts=['qcow2'])
583
-iotests.verify_protocol(supported=['file'])
584
-iotests.verify_platform(['linux'])
585
+iotests.script_initialize(
586
+ supported_fmts=['qcow2'],
587
+ supported_protocols=['file'],
588
+ supported_platforms=['linux'],
589
+)
590
591
with iotests.FilePath('base') as base_path , \
592
iotests.FilePath('top') as top_path, \
593
diff --git a/tests/qemu-iotests/283 b/tests/qemu-iotests/283
594
index XXXXXXX..XXXXXXX 100644
1515
index XXXXXXX..XXXXXXX 100644
595
--- a/tests/qemu-iotests/283
1516
--- a/tests/qemu-iotests/207.out
596
+++ b/tests/qemu-iotests/283
1517
+++ b/tests/qemu-iotests/207.out
597
@@ -XXX,XX +XXX,XX @@
1518
@@ -XXX,XX +XXX,XX @@ virtual size: 4 MiB (4194304 bytes)
598
import iotests
1519
599
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}}}
600
# The test is unrelated to formats, restrict it to qcow2 to avoid extra runs
1521
{"return": {}}
601
-iotests.verify_image_format(supported_fmts=['qcow2'])
1522
-Job failed: failed to open remote file '/this/is/not/an/existing/path': Failed opening remote file (libssh2 error code: -31)
602
+iotests.script_initialize(
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)
603
+ supported_fmts=['qcow2'],
1524
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
604
+)
1525
{"return": {}}
605
1526
606
size = 1024 * 1024
607
608
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
609
index XXXXXXX..XXXXXXX 100644
610
--- a/tests/qemu-iotests/iotests.py
611
+++ b/tests/qemu-iotests/iotests.py
612
@@ -XXX,XX +XXX,XX @@ import signal
613
import struct
614
import subprocess
615
import sys
616
-from typing import (Any, Callable, Dict, Iterable, List, Optional, TypeVar)
617
+from typing import (Any, Callable, Dict, Iterable,
618
+ List, Optional, Sequence, TypeVar)
619
import unittest
620
621
# pylint: disable=import-error, wrong-import-position
622
@@ -XXX,XX +XXX,XX @@ def verify_protocol(supported=(), unsupported=()):
623
if not_sup or (imgproto in unsupported):
624
notrun('not suitable for this protocol: %s' % imgproto)
625
626
-def verify_platform(supported=None, unsupported=None):
627
- if unsupported is not None:
628
- if any((sys.platform.startswith(x) for x in unsupported)):
629
- notrun('not suitable for this OS: %s' % sys.platform)
630
+def verify_platform(supported=(), unsupported=()):
631
+ if any((sys.platform.startswith(x) for x in unsupported)):
632
+ notrun('not suitable for this OS: %s' % sys.platform)
633
634
- if supported is not None:
635
+ if supported:
636
if not any((sys.platform.startswith(x) for x in supported)):
637
notrun('not suitable for this OS: %s' % sys.platform)
638
639
@@ -XXX,XX +XXX,XX @@ def skip_if_user_is_root(func):
640
return func(*args, **kwargs)
641
return func_wrapper
642
643
-def execute_unittest(output, verbosity, debug):
644
+def execute_unittest(debug=False):
645
+ """Executes unittests within the calling module."""
646
+
647
+ verbosity = 2 if debug else 1
648
+
649
+ if debug:
650
+ output = sys.stdout
651
+ else:
652
+ # We need to filter out the time taken from the output so that
653
+ # qemu-iotest can reliably diff the results against master output.
654
+ output = io.StringIO()
655
+
656
runner = unittest.TextTestRunner(stream=output, descriptions=True,
657
verbosity=verbosity)
658
try:
659
@@ -XXX,XX +XXX,XX @@ def execute_unittest(output, verbosity, debug):
660
# exception
661
unittest.main(testRunner=runner)
662
finally:
663
+ # We need to filter out the time taken from the output so that
664
+ # qemu-iotest can reliably diff the results against master output.
665
if not debug:
666
out = output.getvalue()
667
out = re.sub(r'Ran (\d+) tests? in [\d.]+s', r'Ran \1 tests', out)
668
@@ -XXX,XX +XXX,XX @@ def execute_unittest(output, verbosity, debug):
669
670
sys.stderr.write(out)
671
672
-def execute_test(test_function=None,
673
- supported_fmts=(),
674
- supported_platforms=None,
675
- supported_cache_modes=(), supported_aio_modes=(),
676
- unsupported_fmts=(), supported_protocols=(),
677
- unsupported_protocols=()):
678
- """Run either unittest or script-style tests."""
679
+def execute_setup_common(supported_fmts: Sequence[str] = (),
680
+ supported_platforms: Sequence[str] = (),
681
+ supported_cache_modes: Sequence[str] = (),
682
+ supported_aio_modes: Sequence[str] = (),
683
+ unsupported_fmts: Sequence[str] = (),
684
+ supported_protocols: Sequence[str] = (),
685
+ unsupported_protocols: Sequence[str] = ()) -> bool:
686
+ """
687
+ Perform necessary setup for either script-style or unittest-style tests.
688
+
689
+ :return: Bool; Whether or not debug mode has been requested via the CLI.
690
+ """
691
+ # Note: Python 3.6 and pylint do not like 'Collection' so use 'Sequence'.
692
693
# We are using TEST_DIR and QEMU_DEFAULT_MACHINE as proxies to
694
# indicate that we're not being run via "check". There may be
695
@@ -XXX,XX +XXX,XX @@ def execute_test(test_function=None,
696
sys.stderr.write('Please run this test via the "check" script\n')
697
sys.exit(os.EX_USAGE)
698
699
- debug = '-d' in sys.argv
700
- verbosity = 1
701
verify_image_format(supported_fmts, unsupported_fmts)
702
verify_protocol(supported_protocols, unsupported_protocols)
703
verify_platform(supported=supported_platforms)
704
verify_cache_mode(supported_cache_modes)
705
verify_aio_mode(supported_aio_modes)
706
707
+ debug = '-d' in sys.argv
708
if debug:
709
- output = sys.stdout
710
- verbosity = 2
711
sys.argv.remove('-d')
712
- else:
713
- # We need to filter out the time taken from the output so that
714
- # qemu-iotest can reliably diff the results against master output.
715
- output = io.StringIO()
716
-
717
logging.basicConfig(level=(logging.DEBUG if debug else logging.WARN))
718
719
+ return debug
720
+
721
+def execute_test(*args, test_function=None, **kwargs):
722
+ """Run either unittest or script-style tests."""
723
+
724
+ debug = execute_setup_common(*args, **kwargs)
725
if not test_function:
726
- execute_unittest(output, verbosity, debug)
727
+ execute_unittest(debug)
728
else:
729
test_function()
730
731
+# This is called from script-style iotests without a single point of entry
732
+def script_initialize(*args, **kwargs):
733
+ """Initialize script-style tests without running any tests."""
734
+ execute_setup_common(*args, **kwargs)
735
+
736
+# This is called from script-style iotests with a single point of entry
737
def script_main(test_function, *args, **kwargs):
738
"""Run script-style tests outside of the unittest framework"""
739
- execute_test(test_function, *args, **kwargs)
740
+ execute_test(*args, test_function=test_function, **kwargs)
741
742
+# This is called from unittest style iotests
743
def main(*args, **kwargs):
744
"""Run tests using the unittest framework"""
745
- execute_test(None, *args, **kwargs)
746
+ execute_test(*args, **kwargs)
747
--
1527
--
748
2.26.2
1528
2.21.0
749
1529
750
1530
diff view generated by jsdifflib
Deleted patch
1
From: John Snow <jsnow@redhat.com>
2
1
3
Since this one is nicely factored to use a single entry point,
4
use script_main to run the tests.
5
6
Signed-off-by: John Snow <jsnow@redhat.com>
7
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
8
Reviewed-by: Max Reitz <mreitz@redhat.com>
9
Message-Id: <20200331000014.11581-13-jsnow@redhat.com>
10
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
11
Signed-off-by: Max Reitz <mreitz@redhat.com>
12
---
13
tests/qemu-iotests/258 | 11 ++++-------
14
1 file changed, 4 insertions(+), 7 deletions(-)
15
16
diff --git a/tests/qemu-iotests/258 b/tests/qemu-iotests/258
17
index XXXXXXX..XXXXXXX 100755
18
--- a/tests/qemu-iotests/258
19
+++ b/tests/qemu-iotests/258
20
@@ -XXX,XX +XXX,XX @@ import iotests
21
from iotests import log, qemu_img, qemu_io_silent, \
22
filter_qmp_testfiles, filter_qmp_imgfmt
23
24
-# Need backing file and change-backing-file support
25
-iotests.script_initialize(
26
- supported_fmts=['qcow2', 'qed'],
27
- supported_platforms=['linux'],
28
-)
29
-
30
# Returns a node for blockdev-add
31
def node(node_name, path, backing=None, fmt=None, throttle=None):
32
if fmt is None:
33
@@ -XXX,XX +XXX,XX @@ def main():
34
test_concurrent_finish(False)
35
36
if __name__ == '__main__':
37
- main()
38
+ # Need backing file and change-backing-file support
39
+ iotests.script_main(main,
40
+ supported_fmts=['qcow2', 'qed'],
41
+ supported_platforms=['linux'])
42
--
43
2.26.2
44
45
diff view generated by jsdifflib
Deleted patch
1
From: John Snow <jsnow@redhat.com>
2
1
3
Mark the verify functions as "private" with a leading underscore, to
4
discourage their use. Update type signatures while we're here.
5
6
(Also, make pending patches not yet using the new entry points fail in a
7
very obvious way.)
8
9
Signed-off-by: John Snow <jsnow@redhat.com>
10
Message-Id: <20200331000014.11581-14-jsnow@redhat.com>
11
Reviewed-by: Max Reitz <mreitz@redhat.com>
12
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
13
Signed-off-by: Max Reitz <mreitz@redhat.com>
14
---
15
tests/qemu-iotests/iotests.py | 23 +++++++++++++----------
16
1 file changed, 13 insertions(+), 10 deletions(-)
17
18
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
19
index XXXXXXX..XXXXXXX 100644
20
--- a/tests/qemu-iotests/iotests.py
21
+++ b/tests/qemu-iotests/iotests.py
22
@@ -XXX,XX +XXX,XX @@ def case_notrun(reason):
23
open('%s/%s.casenotrun' % (output_dir, seq), 'a').write(
24
' [case not run] ' + reason + '\n')
25
26
-def verify_image_format(supported_fmts=(), unsupported_fmts=()):
27
+def _verify_image_format(supported_fmts: Sequence[str] = (),
28
+ unsupported_fmts: Sequence[str] = ()) -> None:
29
assert not (supported_fmts and unsupported_fmts)
30
31
if 'generic' in supported_fmts and \
32
@@ -XXX,XX +XXX,XX @@ def verify_image_format(supported_fmts=(), unsupported_fmts=()):
33
if not_sup or (imgfmt in unsupported_fmts):
34
notrun('not suitable for this image format: %s' % imgfmt)
35
36
-def verify_protocol(supported=(), unsupported=()):
37
+def _verify_protocol(supported: Sequence[str] = (),
38
+ unsupported: Sequence[str] = ()) -> None:
39
assert not (supported and unsupported)
40
41
if 'generic' in supported:
42
@@ -XXX,XX +XXX,XX @@ def verify_protocol(supported=(), unsupported=()):
43
if not_sup or (imgproto in unsupported):
44
notrun('not suitable for this protocol: %s' % imgproto)
45
46
-def verify_platform(supported=(), unsupported=()):
47
+def _verify_platform(supported: Sequence[str] = (),
48
+ unsupported: Sequence[str] = ()) -> None:
49
if any((sys.platform.startswith(x) for x in unsupported)):
50
notrun('not suitable for this OS: %s' % sys.platform)
51
52
@@ -XXX,XX +XXX,XX @@ def verify_platform(supported=(), unsupported=()):
53
if not any((sys.platform.startswith(x) for x in supported)):
54
notrun('not suitable for this OS: %s' % sys.platform)
55
56
-def verify_cache_mode(supported_cache_modes=()):
57
+def _verify_cache_mode(supported_cache_modes: Sequence[str] = ()) -> None:
58
if supported_cache_modes and (cachemode not in supported_cache_modes):
59
notrun('not suitable for this cache mode: %s' % cachemode)
60
61
-def verify_aio_mode(supported_aio_modes=()):
62
+def _verify_aio_mode(supported_aio_modes: Sequence[str] = ()):
63
if supported_aio_modes and (aiomode not in supported_aio_modes):
64
notrun('not suitable for this aio mode: %s' % aiomode)
65
66
@@ -XXX,XX +XXX,XX @@ def execute_setup_common(supported_fmts: Sequence[str] = (),
67
sys.stderr.write('Please run this test via the "check" script\n')
68
sys.exit(os.EX_USAGE)
69
70
- verify_image_format(supported_fmts, unsupported_fmts)
71
- verify_protocol(supported_protocols, unsupported_protocols)
72
- verify_platform(supported=supported_platforms)
73
- verify_cache_mode(supported_cache_modes)
74
- verify_aio_mode(supported_aio_modes)
75
+ _verify_image_format(supported_fmts, unsupported_fmts)
76
+ _verify_protocol(supported_protocols, unsupported_protocols)
77
+ _verify_platform(supported=supported_platforms)
78
+ _verify_cache_mode(supported_cache_modes)
79
+ _verify_aio_mode(supported_aio_modes)
80
81
debug = '-d' in sys.argv
82
if debug:
83
--
84
2.26.2
85
86
diff view generated by jsdifflib
Deleted patch
1
From: John Snow <jsnow@redhat.com>
2
1
3
We can turn logging on/off globally instead of per-function.
4
5
Remove use_log from run_job, and use python logging to turn on
6
diffable output when we run through a script entry point.
7
8
iotest 245 changes output order due to buffering reasons.
9
10
An extended note on python logging:
11
12
A NullHandler is added to `qemu.iotests` to stop output from being
13
generated if this code is used as a library without configuring logging.
14
A NullHandler is only needed at the root, so a duplicate handler is not
15
needed for `qemu.iotests.diff_io`.
16
17
When logging is not configured, messages at the 'WARNING' levels or
18
above are printed with default settings. The NullHandler stops this from
19
occurring, which is considered good hygiene for code used as a library.
20
21
See https://docs.python.org/3/howto/logging.html#library-config
22
23
When logging is actually enabled (always at the behest of an explicit
24
call by a client script), a root logger is implicitly created at the
25
root, which allows messages to propagate upwards and be handled/emitted
26
from the root logger with default settings.
27
28
When we want iotest logging, we attach a handler to the
29
qemu.iotests.diff_io logger and disable propagation to avoid possible
30
double-printing.
31
32
For more information on python logging infrastructure, I highly
33
recommend downloading the pip package `logging_tree`, which provides
34
convenient visualizations of the hierarchical logging configuration
35
under different circumstances.
36
37
See https://pypi.org/project/logging_tree/ for more information.
38
39
Signed-off-by: John Snow <jsnow@redhat.com>
40
Reviewed-by: Max Reitz <mreitz@redhat.com>
41
Message-Id: <20200331000014.11581-15-jsnow@redhat.com>
42
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
43
Signed-off-by: Max Reitz <mreitz@redhat.com>
44
---
45
tests/qemu-iotests/030 | 4 +--
46
tests/qemu-iotests/155 | 2 +-
47
tests/qemu-iotests/245 | 1 +
48
tests/qemu-iotests/245.out | 10 +++----
49
tests/qemu-iotests/iotests.py | 53 ++++++++++++++++++++---------------
50
5 files changed, 39 insertions(+), 31 deletions(-)
51
52
diff --git a/tests/qemu-iotests/030 b/tests/qemu-iotests/030
53
index XXXXXXX..XXXXXXX 100755
54
--- a/tests/qemu-iotests/030
55
+++ b/tests/qemu-iotests/030
56
@@ -XXX,XX +XXX,XX @@ class TestParallelOps(iotests.QMPTestCase):
57
result = self.vm.qmp('block-job-set-speed', device='drive0', speed=0)
58
self.assert_qmp(result, 'return', {})
59
60
- self.vm.run_job(job='drive0', auto_dismiss=True, use_log=False)
61
- self.vm.run_job(job='node4', auto_dismiss=True, use_log=False)
62
+ self.vm.run_job(job='drive0', auto_dismiss=True)
63
+ self.vm.run_job(job='node4', auto_dismiss=True)
64
self.assert_no_active_block_jobs()
65
66
# Test a block-stream and a block-commit job in parallel
67
diff --git a/tests/qemu-iotests/155 b/tests/qemu-iotests/155
68
index XXXXXXX..XXXXXXX 100755
69
--- a/tests/qemu-iotests/155
70
+++ b/tests/qemu-iotests/155
71
@@ -XXX,XX +XXX,XX @@ class MirrorBaseClass(BaseClass):
72
73
self.assert_qmp(result, 'return', {})
74
75
- self.vm.run_job('mirror-job', use_log=False, auto_finalize=False,
76
+ self.vm.run_job('mirror-job', auto_finalize=False,
77
pre_finalize=self.openBacking, auto_dismiss=True)
78
79
def testFull(self):
80
diff --git a/tests/qemu-iotests/245 b/tests/qemu-iotests/245
81
index XXXXXXX..XXXXXXX 100755
82
--- a/tests/qemu-iotests/245
83
+++ b/tests/qemu-iotests/245
84
@@ -XXX,XX +XXX,XX @@ class TestBlockdevReopen(iotests.QMPTestCase):
85
self.run_test_iothreads(None, 'iothread0')
86
87
if __name__ == '__main__':
88
+ iotests.activate_logging()
89
iotests.main(supported_fmts=["qcow2"],
90
supported_protocols=["file"])
91
diff --git a/tests/qemu-iotests/245.out b/tests/qemu-iotests/245.out
92
index XXXXXXX..XXXXXXX 100644
93
--- a/tests/qemu-iotests/245.out
94
+++ b/tests/qemu-iotests/245.out
95
@@ -XXX,XX +XXX,XX @@
96
-.....................
97
-----------------------------------------------------------------------
98
-Ran 21 tests
99
-
100
-OK
101
{"execute": "job-finalize", "arguments": {"id": "commit0"}}
102
{"return": {}}
103
{"data": {"id": "commit0", "type": "commit"}, "event": "BLOCK_JOB_PENDING", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
104
@@ -XXX,XX +XXX,XX @@ OK
105
{"return": {}}
106
{"data": {"id": "stream0", "type": "stream"}, "event": "BLOCK_JOB_PENDING", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
107
{"data": {"device": "stream0", "len": 3145728, "offset": 3145728, "speed": 0, "type": "stream"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
108
+.....................
109
+----------------------------------------------------------------------
110
+Ran 21 tests
111
+
112
+OK
113
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
114
index XXXXXXX..XXXXXXX 100644
115
--- a/tests/qemu-iotests/iotests.py
116
+++ b/tests/qemu-iotests/iotests.py
117
@@ -XXX,XX +XXX,XX @@ assert sys.version_info >= (3, 6)
118
QMPResponse = Dict[str, Any]
119
120
121
+# Use this logger for logging messages directly from the iotests module
122
+logger = logging.getLogger('qemu.iotests')
123
+logger.addHandler(logging.NullHandler())
124
+
125
+# Use this logger for messages that ought to be used for diff output.
126
+test_logger = logging.getLogger('qemu.iotests.diff_io')
127
+
128
+
129
faulthandler.enable()
130
131
# This will not work if arguments contain spaces but is necessary if we
132
@@ -XXX,XX +XXX,XX @@ def log(msg: Msg,
133
if isinstance(msg, (dict, list)):
134
# Don't sort if it's already sorted
135
do_sort = not isinstance(msg, OrderedDict)
136
- print(json.dumps(msg, sort_keys=do_sort, indent=indent))
137
+ test_logger.info(json.dumps(msg, sort_keys=do_sort, indent=indent))
138
else:
139
- print(msg)
140
+ test_logger.info(msg)
141
142
class Timeout:
143
def __init__(self, seconds, errmsg="Timeout"):
144
@@ -XXX,XX +XXX,XX @@ class VM(qtest.QEMUQtestMachine):
145
146
# Returns None on success, and an error string on failure
147
def run_job(self, job, auto_finalize=True, auto_dismiss=False,
148
- pre_finalize=None, cancel=False, use_log=True, wait=60.0):
149
+ pre_finalize=None, cancel=False, wait=60.0):
150
"""
151
run_job moves a job from creation through to dismissal.
152
153
@@ -XXX,XX +XXX,XX @@ class VM(qtest.QEMUQtestMachine):
154
invoked prior to issuing job-finalize, if any.
155
:param cancel: Bool. When true, cancels the job after the pre_finalize
156
callback.
157
- :param use_log: Bool. When false, does not log QMP messages.
158
:param wait: Float. Timeout value specifying how long to wait for any
159
event, in seconds. Defaults to 60.0.
160
"""
161
@@ -XXX,XX +XXX,XX @@ class VM(qtest.QEMUQtestMachine):
162
while True:
163
ev = filter_qmp_event(self.events_wait(events, timeout=wait))
164
if ev['event'] != 'JOB_STATUS_CHANGE':
165
- if use_log:
166
- log(ev)
167
+ log(ev)
168
continue
169
status = ev['data']['status']
170
if status == 'aborting':
171
@@ -XXX,XX +XXX,XX @@ class VM(qtest.QEMUQtestMachine):
172
for j in result['return']:
173
if j['id'] == job:
174
error = j['error']
175
- if use_log:
176
- log('Job failed: %s' % (j['error']))
177
+ log('Job failed: %s' % (j['error']))
178
elif status == 'ready':
179
- if use_log:
180
- self.qmp_log('job-complete', id=job)
181
- else:
182
- self.qmp('job-complete', id=job)
183
+ self.qmp_log('job-complete', id=job)
184
elif status == 'pending' and not auto_finalize:
185
if pre_finalize:
186
pre_finalize()
187
- if cancel and use_log:
188
+ if cancel:
189
self.qmp_log('job-cancel', id=job)
190
- elif cancel:
191
- self.qmp('job-cancel', id=job)
192
- elif use_log:
193
- self.qmp_log('job-finalize', id=job)
194
else:
195
- self.qmp('job-finalize', id=job)
196
+ self.qmp_log('job-finalize', id=job)
197
elif status == 'concluded' and not auto_dismiss:
198
- if use_log:
199
- self.qmp_log('job-dismiss', id=job)
200
- else:
201
- self.qmp('job-dismiss', id=job)
202
+ self.qmp_log('job-dismiss', id=job)
203
elif status == 'null':
204
return error
205
206
@@ -XXX,XX +XXX,XX @@ def notrun(reason):
207
seq = os.path.basename(sys.argv[0])
208
209
open('%s/%s.notrun' % (output_dir, seq), 'w').write(reason + '\n')
210
- print('%s not run: %s' % (seq, reason))
211
+ logger.warning("%s not run: %s", seq, reason)
212
sys.exit(0)
213
214
def case_notrun(reason):
215
@@ -XXX,XX +XXX,XX @@ def execute_setup_common(supported_fmts: Sequence[str] = (),
216
if debug:
217
sys.argv.remove('-d')
218
logging.basicConfig(level=(logging.DEBUG if debug else logging.WARN))
219
+ logger.debug("iotests debugging messages active")
220
221
return debug
222
223
@@ -XXX,XX +XXX,XX @@ def execute_test(*args, test_function=None, **kwargs):
224
else:
225
test_function()
226
227
+def activate_logging():
228
+ """Activate iotests.log() output to stdout for script-style tests."""
229
+ handler = logging.StreamHandler(stream=sys.stdout)
230
+ formatter = logging.Formatter('%(message)s')
231
+ handler.setFormatter(formatter)
232
+ test_logger.addHandler(handler)
233
+ test_logger.setLevel(logging.INFO)
234
+ test_logger.propagate = False
235
+
236
# This is called from script-style iotests without a single point of entry
237
def script_initialize(*args, **kwargs):
238
"""Initialize script-style tests without running any tests."""
239
+ activate_logging()
240
execute_setup_common(*args, **kwargs)
241
242
# This is called from script-style iotests with a single point of entry
243
def script_main(test_function, *args, **kwargs):
244
"""Run script-style tests outside of the unittest framework"""
245
+ activate_logging()
246
execute_test(*args, test_function=test_function, **kwargs)
247
248
# This is called from unittest style iotests
249
--
250
2.26.2
251
252
diff view generated by jsdifflib
Deleted patch
1
From: Eric Blake <eblake@redhat.com>
2
1
3
We originally refused to allow resize of images with internal
4
snapshots because the v2 image format did not require the tracking of
5
snapshot size, making it impossible to safely revert to a snapshot
6
with a different size than the current view of the image. But the
7
snapshot size tracking was rectified in v3, and our recent fixes to
8
qemu-img amend (see 0a85af35) guarantee that we always have a valid
9
snapshot size. Thus, we no longer need to artificially limit image
10
resizes, but it does become one more thing that would prevent a
11
downgrade back to v2. And now that we support different-sized
12
snapshots, it's also easy to fix reverting to a snapshot to apply the
13
new size.
14
15
Upgrade iotest 61 to cover this (we previously had NO coverage of
16
refusal to resize while snapshots exist). Note that the amend process
17
can fail but still have effects: in particular, since we break things
18
into upgrade, resize, downgrade, a failure during resize does not roll
19
back changes made during upgrade, nor does failure in downgrade roll
20
back a resize. But this situation is pre-existing even without this
21
patch; and without journaling, the best we could do is minimize the
22
chance of partial failure by collecting all changes prior to doing any
23
writes - which adds a lot of complexity but could still fail with EIO.
24
On the other hand, we are careful that even if we have partial
25
modification but then fail, the image is left viable (that is, we are
26
careful to sequence things so that after each successful cluster
27
write, there may be transient leaked clusters but no corrupt
28
metadata). And complicating the code to make it more transaction-like
29
is not worth the effort: a user can always request multiple 'qemu-img
30
amend' changing one thing each, if they need finer-grained control
31
over detecting the first failure than what they get by letting qemu
32
decide how to sequence multiple changes.
33
34
Signed-off-by: Eric Blake <eblake@redhat.com>
35
Reviewed-by: Max Reitz <mreitz@redhat.com>
36
Message-Id: <20200428192648.749066-3-eblake@redhat.com>
37
Signed-off-by: Max Reitz <mreitz@redhat.com>
38
---
39
block/qcow2-snapshot.c | 20 ++++++++++++++++----
40
block/qcow2.c | 25 ++++++++++++++++++++++---
41
tests/qemu-iotests/061 | 35 +++++++++++++++++++++++++++++++++++
42
tests/qemu-iotests/061.out | 28 ++++++++++++++++++++++++++++
43
4 files changed, 101 insertions(+), 7 deletions(-)
44
45
diff --git a/block/qcow2-snapshot.c b/block/qcow2-snapshot.c
46
index XXXXXXX..XXXXXXX 100644
47
--- a/block/qcow2-snapshot.c
48
+++ b/block/qcow2-snapshot.c
49
@@ -XXX,XX +XXX,XX @@
50
*/
51
52
#include "qemu/osdep.h"
53
+#include "sysemu/block-backend.h"
54
#include "qapi/error.h"
55
#include "qcow2.h"
56
#include "qemu/bswap.h"
57
@@ -XXX,XX +XXX,XX @@ int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id)
58
}
59
60
if (sn->disk_size != bs->total_sectors * BDRV_SECTOR_SIZE) {
61
- error_report("qcow2: Loading snapshots with different disk "
62
- "size is not implemented");
63
- ret = -ENOTSUP;
64
- goto fail;
65
+ BlockBackend *blk = blk_new_with_bs(bs, BLK_PERM_RESIZE, BLK_PERM_ALL,
66
+ &local_err);
67
+ if (!blk) {
68
+ error_report_err(local_err);
69
+ ret = -ENOTSUP;
70
+ goto fail;
71
+ }
72
+
73
+ ret = blk_truncate(blk, sn->disk_size, true, PREALLOC_MODE_OFF, 0,
74
+ &local_err);
75
+ blk_unref(blk);
76
+ if (ret < 0) {
77
+ error_report_err(local_err);
78
+ goto fail;
79
+ }
80
}
81
82
/*
83
diff --git a/block/qcow2.c b/block/qcow2.c
84
index XXXXXXX..XXXXXXX 100644
85
--- a/block/qcow2.c
86
+++ b/block/qcow2.c
87
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset,
88
89
qemu_co_mutex_lock(&s->lock);
90
91
- /* cannot proceed if image has snapshots */
92
- if (s->nb_snapshots) {
93
- error_setg(errp, "Can't resize an image which has snapshots");
94
+ /*
95
+ * Even though we store snapshot size for all images, it was not
96
+ * required until v3, so it is not safe to proceed for v2.
97
+ */
98
+ if (s->nb_snapshots && s->qcow_version < 3) {
99
+ error_setg(errp, "Can't resize a v2 image which has snapshots");
100
ret = -ENOTSUP;
101
goto fail;
102
}
103
@@ -XXX,XX +XXX,XX @@ static int qcow2_downgrade(BlockDriverState *bs, int target_version,
104
BDRVQcow2State *s = bs->opaque;
105
int current_version = s->qcow_version;
106
int ret;
107
+ int i;
108
109
/* This is qcow2_downgrade(), not qcow2_upgrade() */
110
assert(target_version < current_version);
111
@@ -XXX,XX +XXX,XX @@ static int qcow2_downgrade(BlockDriverState *bs, int target_version,
112
return -ENOTSUP;
113
}
114
115
+ /*
116
+ * If any internal snapshot has a different size than the current
117
+ * image size, or VM state size that exceeds 32 bits, downgrading
118
+ * is unsafe. Even though we would still use v3-compliant output
119
+ * to preserve that data, other v2 programs might not realize
120
+ * those optional fields are important.
121
+ */
122
+ for (i = 0; i < s->nb_snapshots; i++) {
123
+ if (s->snapshots[i].vm_state_size > UINT32_MAX ||
124
+ s->snapshots[i].disk_size != bs->total_sectors * BDRV_SECTOR_SIZE) {
125
+ error_setg(errp, "Internal snapshots prevent downgrade of image");
126
+ return -ENOTSUP;
127
+ }
128
+ }
129
+
130
/* clear incompatible features */
131
if (s->incompatible_features & QCOW2_INCOMPAT_DIRTY) {
132
ret = qcow2_mark_clean(bs);
133
diff --git a/tests/qemu-iotests/061 b/tests/qemu-iotests/061
134
index XXXXXXX..XXXXXXX 100755
135
--- a/tests/qemu-iotests/061
136
+++ b/tests/qemu-iotests/061
137
@@ -XXX,XX +XXX,XX @@ $PYTHON qcow2.py "$TEST_IMG" dump-header
138
$QEMU_IO -c "read -P 0x2a 42M 64k" "$TEST_IMG" | _filter_qemu_io
139
_check_test_img
140
141
+echo
142
+echo "=== Testing resize with snapshots ==="
143
+echo
144
+_make_test_img -o "compat=0.10" 32M
145
+$QEMU_IO -c "write -P 0x2a 24M 64k" "$TEST_IMG" | _filter_qemu_io
146
+$QEMU_IMG snapshot -c foo "$TEST_IMG"
147
+$QEMU_IMG resize "$TEST_IMG" 64M &&
148
+ echo "unexpected pass"
149
+$PYTHON qcow2.py "$TEST_IMG" dump-header | grep '^\(version\|size\|nb_snap\)'
150
+
151
+$QEMU_IMG amend -o "compat=1.1,size=128M" "$TEST_IMG" ||
152
+ echo "unexpected fail"
153
+$PYTHON qcow2.py "$TEST_IMG" dump-header | grep '^\(version\|size\|nb_snap\)'
154
+
155
+$QEMU_IMG snapshot -c bar "$TEST_IMG"
156
+$QEMU_IMG resize --shrink "$TEST_IMG" 64M ||
157
+ echo "unexpected fail"
158
+$PYTHON qcow2.py "$TEST_IMG" dump-header | grep '^\(version\|size\|nb_snap\)'
159
+
160
+$QEMU_IMG amend -o "compat=0.10,size=32M" "$TEST_IMG" &&
161
+ echo "unexpected pass"
162
+$PYTHON qcow2.py "$TEST_IMG" dump-header | grep '^\(version\|size\|nb_snap\)'
163
+
164
+$QEMU_IMG snapshot -a bar "$TEST_IMG" ||
165
+ echo "unexpected fail"
166
+$PYTHON qcow2.py "$TEST_IMG" dump-header | grep '^\(version\|size\|nb_snap\)'
167
+
168
+$QEMU_IMG snapshot -d bar "$TEST_IMG"
169
+$QEMU_IMG amend -o "compat=0.10,size=32M" "$TEST_IMG" ||
170
+ echo "unexpected fail"
171
+$PYTHON qcow2.py "$TEST_IMG" dump-header | grep '^\(version\|size\|nb_snap\)'
172
+
173
+_check_test_img
174
+
175
+
176
echo
177
echo "=== Testing dirty lazy_refcounts=off ==="
178
echo
179
diff --git a/tests/qemu-iotests/061.out b/tests/qemu-iotests/061.out
180
index XXXXXXX..XXXXXXX 100644
181
--- a/tests/qemu-iotests/061.out
182
+++ b/tests/qemu-iotests/061.out
183
@@ -XXX,XX +XXX,XX @@ read 65536/65536 bytes at offset 44040192
184
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
185
No errors were found on the image.
186
187
+=== Testing resize with snapshots ===
188
+
189
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=33554432
190
+wrote 65536/65536 bytes at offset 25165824
191
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
192
+qemu-img: Can't resize a v2 image which has snapshots
193
+version 2
194
+size 33554432
195
+nb_snapshots 1
196
+version 3
197
+size 134217728
198
+nb_snapshots 1
199
+Image resized.
200
+version 3
201
+size 67108864
202
+nb_snapshots 2
203
+qemu-img: Internal snapshots prevent downgrade of image
204
+version 3
205
+size 33554432
206
+nb_snapshots 2
207
+version 3
208
+size 134217728
209
+nb_snapshots 2
210
+version 2
211
+size 33554432
212
+nb_snapshots 1
213
+No errors were found on the image.
214
+
215
=== Testing dirty lazy_refcounts=off ===
216
217
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
218
--
219
2.26.2
220
221
diff view generated by jsdifflib
Deleted patch
1
From: Eric Blake <eblake@redhat.com>
2
1
3
Our comment did not actually match the code. Rewrite the comment to
4
be less sensitive to any future changes to qcow2-bitmap.c that might
5
implement scenarios that we currently reject.
6
7
Signed-off-by: Eric Blake <eblake@redhat.com>
8
Reviewed-by: Max Reitz <mreitz@redhat.com>
9
Message-Id: <20200428192648.749066-4-eblake@redhat.com>
10
Signed-off-by: Max Reitz <mreitz@redhat.com>
11
---
12
block/qcow2.c | 2 +-
13
1 file changed, 1 insertion(+), 1 deletion(-)
14
15
diff --git a/block/qcow2.c b/block/qcow2.c
16
index XXXXXXX..XXXXXXX 100644
17
--- a/block/qcow2.c
18
+++ b/block/qcow2.c
19
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset,
20
goto fail;
21
}
22
23
- /* cannot proceed if image has bitmaps */
24
+ /* See qcow2-bitmap.c for which bitmap scenarios prevent a resize. */
25
if (qcow2_truncate_bitmaps_check(bs, errp)) {
26
ret = -ENOTSUP;
27
goto fail;
28
--
29
2.26.2
30
31
diff view generated by jsdifflib
1
From: Maxim Levitsky <mlevitsk@redhat.com>
1
Tests should place their files into the test directory. This includes
2
Unix sockets. 205 currently fails to do so, which prevents it from
3
being run concurrently.
2
4
3
Commit f62514b3def5fb2acbef64d0e053c0c31fa45aff made qemu-img reject -o "" but this test uses it.
5
Signed-off-by: Max Reitz <mreitz@redhat.com>
4
Since this test only tries to do a dry-run run of qemu-img amend,
6
Message-id: 20190618210238.9524-1-mreitz@redhat.com
5
replace the -o "" with dummy -o "size=$size".
7
Reviewed-by: Eric Blake <eblake@redhat.com>
6
7
Fixes: f62514b3def5fb2acbef64d0e053c0c31fa45aff
8
9
Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
10
Message-Id: <20200504131959.9533-1-mlevitsk@redhat.com>
11
Signed-off-by: Max Reitz <mreitz@redhat.com>
8
Signed-off-by: Max Reitz <mreitz@redhat.com>
12
---
9
---
13
tests/qemu-iotests/153 | 2 +-
10
tests/qemu-iotests/205 | 2 +-
14
tests/qemu-iotests/153.out | 12 ++++++------
11
1 file changed, 1 insertion(+), 1 deletion(-)
15
2 files changed, 7 insertions(+), 7 deletions(-)
16
12
17
diff --git a/tests/qemu-iotests/153 b/tests/qemu-iotests/153
13
diff --git a/tests/qemu-iotests/205 b/tests/qemu-iotests/205
18
index XXXXXXX..XXXXXXX 100755
14
index XXXXXXX..XXXXXXX 100755
19
--- a/tests/qemu-iotests/153
15
--- a/tests/qemu-iotests/205
20
+++ b/tests/qemu-iotests/153
16
+++ b/tests/qemu-iotests/205
21
@@ -XXX,XX +XXX,XX @@ for opts1 in "" "read-only=on" "read-only=on,force-share=on"; do
17
@@ -XXX,XX +XXX,XX @@ import iotests
22
_run_cmd $QEMU_IMG check $L "${TEST_IMG}"
18
import time
23
_run_cmd $QEMU_IMG compare $L "${TEST_IMG}" "${TEST_IMG}"
19
from iotests import qemu_img_create, qemu_io, filter_qemu_io, QemuIoInteractive
24
_run_cmd $QEMU_IMG map $L "${TEST_IMG}"
20
25
- _run_cmd $QEMU_IMG amend -o "" $L "${TEST_IMG}"
21
-nbd_sock = 'nbd_sock'
26
+ _run_cmd $QEMU_IMG amend -o "size=$size" $L "${TEST_IMG}"
22
+nbd_sock = os.path.join(iotests.test_dir, 'nbd_sock')
27
_run_cmd $QEMU_IMG commit $L "${TEST_IMG}"
23
nbd_uri = 'nbd+unix:///exp?socket=' + nbd_sock
28
_run_cmd $QEMU_IMG resize $L "${TEST_IMG}" $size
24
disk = os.path.join(iotests.test_dir, 'disk')
29
_run_cmd $QEMU_IMG rebase $L "${TEST_IMG}" -b "${TEST_IMG}.base"
30
diff --git a/tests/qemu-iotests/153.out b/tests/qemu-iotests/153.out
31
index XXXXXXX..XXXXXXX 100644
32
--- a/tests/qemu-iotests/153.out
33
+++ b/tests/qemu-iotests/153.out
34
@@ -XXX,XX +XXX,XX @@ _qemu_img_wrapper map TEST_DIR/t.qcow2
35
qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get shared "write" lock
36
Is another process using the image [TEST_DIR/t.qcow2]?
37
38
-_qemu_img_wrapper amend -o TEST_DIR/t.qcow2
39
+_qemu_img_wrapper amend -o size=32M TEST_DIR/t.qcow2
40
qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock
41
Is another process using the image [TEST_DIR/t.qcow2]?
42
43
@@ -XXX,XX +XXX,XX @@ _qemu_img_wrapper compare -U TEST_DIR/t.qcow2 TEST_DIR/t.qcow2
44
45
_qemu_img_wrapper map -U TEST_DIR/t.qcow2
46
47
-_qemu_img_wrapper amend -o -U TEST_DIR/t.qcow2
48
+_qemu_img_wrapper amend -o size=32M -U TEST_DIR/t.qcow2
49
qemu-img: unrecognized option '-U'
50
Try 'qemu-img --help' for more information
51
52
@@ -XXX,XX +XXX,XX @@ _qemu_img_wrapper compare TEST_DIR/t.qcow2 TEST_DIR/t.qcow2
53
54
_qemu_img_wrapper map TEST_DIR/t.qcow2
55
56
-_qemu_img_wrapper amend -o TEST_DIR/t.qcow2
57
+_qemu_img_wrapper amend -o size=32M TEST_DIR/t.qcow2
58
qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock
59
Is another process using the image [TEST_DIR/t.qcow2]?
60
61
@@ -XXX,XX +XXX,XX @@ _qemu_img_wrapper compare -U TEST_DIR/t.qcow2 TEST_DIR/t.qcow2
62
63
_qemu_img_wrapper map -U TEST_DIR/t.qcow2
64
65
-_qemu_img_wrapper amend -o -U TEST_DIR/t.qcow2
66
+_qemu_img_wrapper amend -o size=32M -U TEST_DIR/t.qcow2
67
qemu-img: unrecognized option '-U'
68
Try 'qemu-img --help' for more information
69
70
@@ -XXX,XX +XXX,XX @@ _qemu_img_wrapper compare TEST_DIR/t.qcow2 TEST_DIR/t.qcow2
71
72
_qemu_img_wrapper map TEST_DIR/t.qcow2
73
74
-_qemu_img_wrapper amend -o TEST_DIR/t.qcow2
75
+_qemu_img_wrapper amend -o size=32M TEST_DIR/t.qcow2
76
77
_qemu_img_wrapper commit TEST_DIR/t.qcow2
78
79
@@ -XXX,XX +XXX,XX @@ _qemu_img_wrapper compare -U TEST_DIR/t.qcow2 TEST_DIR/t.qcow2
80
81
_qemu_img_wrapper map -U TEST_DIR/t.qcow2
82
83
-_qemu_img_wrapper amend -o -U TEST_DIR/t.qcow2
84
+_qemu_img_wrapper amend -o size=32M -U TEST_DIR/t.qcow2
85
qemu-img: unrecognized option '-U'
86
Try 'qemu-img --help' for more information
87
25
88
--
26
--
89
2.26.2
27
2.21.0
90
28
91
29
diff view generated by jsdifflib
Deleted patch
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
1
3
We are going to use aio-task-pool API, so tasks will be handled in
4
parallel. We need therefore separate allocated task on each iteration.
5
Introduce this logic now.
6
7
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
8
Reviewed-by: Max Reitz <mreitz@redhat.com>
9
Message-Id: <20200429130847.28124-3-vsementsov@virtuozzo.com>
10
Signed-off-by: Max Reitz <mreitz@redhat.com>
11
---
12
block/block-copy.c | 18 +++++++++++-------
13
1 file changed, 11 insertions(+), 7 deletions(-)
14
15
diff --git a/block/block-copy.c b/block/block-copy.c
16
index XXXXXXX..XXXXXXX 100644
17
--- a/block/block-copy.c
18
+++ b/block/block-copy.c
19
@@ -XXX,XX +XXX,XX @@ static bool coroutine_fn block_copy_wait_one(BlockCopyState *s, int64_t offset,
20
}
21
22
/* Called only on full-dirty region */
23
-static void block_copy_task_begin(BlockCopyState *s, BlockCopyTask *task,
24
- int64_t offset, int64_t bytes)
25
+static BlockCopyTask *block_copy_task_create(BlockCopyState *s,
26
+ int64_t offset, int64_t bytes)
27
{
28
+ BlockCopyTask *task = g_new(BlockCopyTask, 1);
29
+
30
assert(!find_conflicting_task(s, offset, bytes));
31
32
bdrv_reset_dirty_bitmap(s->copy_bitmap, offset, bytes);
33
@@ -XXX,XX +XXX,XX @@ static void block_copy_task_begin(BlockCopyState *s, BlockCopyTask *task,
34
task->bytes = bytes;
35
qemu_co_queue_init(&task->wait_queue);
36
QLIST_INSERT_HEAD(&s->tasks, task, list);
37
+
38
+ return task;
39
}
40
41
/*
42
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn block_copy_dirty_clusters(BlockCopyState *s,
43
assert(QEMU_IS_ALIGNED(bytes, s->cluster_size));
44
45
while (bytes) {
46
- BlockCopyTask task;
47
+ g_autofree BlockCopyTask *task = NULL;
48
int64_t next_zero, cur_bytes, status_bytes;
49
50
if (!bdrv_dirty_bitmap_get(s->copy_bitmap, offset)) {
51
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn block_copy_dirty_clusters(BlockCopyState *s,
52
assert(next_zero < offset + cur_bytes); /* no need to do MIN() */
53
cur_bytes = next_zero - offset;
54
}
55
- block_copy_task_begin(s, &task, offset, cur_bytes);
56
+ task = block_copy_task_create(s, offset, cur_bytes);
57
58
ret = block_copy_block_status(s, offset, cur_bytes, &status_bytes);
59
assert(ret >= 0); /* never fail */
60
cur_bytes = MIN(cur_bytes, status_bytes);
61
- block_copy_task_shrink(s, &task, cur_bytes);
62
+ block_copy_task_shrink(s, task, cur_bytes);
63
if (s->skip_unallocated && !(ret & BDRV_BLOCK_ALLOCATED)) {
64
- block_copy_task_end(s, &task, 0);
65
+ block_copy_task_end(s, task, 0);
66
progress_set_remaining(s->progress,
67
bdrv_get_dirty_count(s->copy_bitmap) +
68
s->in_flight_bytes);
69
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn block_copy_dirty_clusters(BlockCopyState *s,
70
ret = block_copy_do_copy(s, offset, cur_bytes, ret & BDRV_BLOCK_ZERO,
71
error_is_read);
72
co_put_to_shres(s->mem, cur_bytes);
73
- block_copy_task_end(s, &task, ret);
74
+ block_copy_task_end(s, task, ret);
75
if (ret < 0) {
76
return ret;
77
}
78
--
79
2.26.2
80
81
diff view generated by jsdifflib