hw/block/fdc: Kludge missing floppy drive to fix CVE-2021-20196
[qemu.git] / tests / qemu-iotests / 055
blob5d6b607051d04f89b78ba7c31c57a70282c2d561
1 #!/usr/bin/env python3
2 # group: rw
4 # Tests for drive-backup and blockdev-backup
6 # Copyright (C) 2013, 2014 Red Hat, Inc.
8 # Based on 041.
10 # This program is free software; you can redistribute it and/or modify
11 # it under the terms of the GNU General Public License as published by
12 # the Free Software Foundation; either version 2 of the License, or
13 # (at your option) any later version.
15 # This program is distributed in the hope that it will be useful,
16 # but WITHOUT ANY WARRANTY; without even the implied warranty of
17 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 # GNU General Public License for more details.
20 # You should have received a copy of the GNU General Public License
21 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
24 import time
25 import os
26 import iotests
27 from iotests import qemu_img, qemu_io
29 test_img = os.path.join(iotests.test_dir, 'test.img')
30 target_img = os.path.join(iotests.test_dir, 'target.img')
31 blockdev_target_img = os.path.join(iotests.test_dir, 'blockdev-target.img')
33 image_len = 64 * 1024 * 1024 # MB
35 def setUpModule():
36     qemu_img('create', '-f', iotests.imgfmt, test_img, str(image_len))
37     qemu_io('-f', iotests.imgfmt, '-c', 'write -P0x11 0 64k', test_img)
38     qemu_io('-f', iotests.imgfmt, '-c', 'write -P0x00 64k 128k', test_img)
39     qemu_io('-f', iotests.imgfmt, '-c', 'write -P0x22 162k 32k', test_img)
40     qemu_io('-f', iotests.imgfmt, '-c', 'write -P0xd5 1M 32k', test_img)
41     qemu_io('-f', iotests.imgfmt, '-c', 'write -P0xdc 32M 124k', test_img)
42     qemu_io('-f', iotests.imgfmt, '-c', 'write -P0x33 67043328 64k', test_img)
44 def tearDownModule():
45     os.remove(test_img)
48 class TestSingleDrive(iotests.QMPTestCase):
49     def setUp(self):
50         qemu_img('create', '-f', iotests.imgfmt, blockdev_target_img, str(image_len))
52         self.vm = iotests.VM()
53         self.vm.add_drive('blkdebug::' + test_img, 'node-name=source')
54         self.vm.add_drive(blockdev_target_img, 'node-name=target',
55                           interface="none")
56         if iotests.qemu_default_machine == 'pc':
57             self.vm.add_drive(None, 'media=cdrom', 'ide')
58         self.vm.launch()
60     def tearDown(self):
61         self.vm.shutdown()
62         os.remove(blockdev_target_img)
63         try:
64             os.remove(target_img)
65         except OSError:
66             pass
68     def do_test_cancel(self, cmd, target):
69         self.assert_no_active_block_jobs()
71         self.vm.pause_drive('drive0')
72         result = self.vm.qmp(cmd, device='drive0', target=target, sync='full')
73         self.assert_qmp(result, 'return', {})
75         event = self.cancel_and_wait(resume=True)
76         self.assert_qmp(event, 'data/type', 'backup')
78     def test_cancel_drive_backup(self):
79         self.do_test_cancel('drive-backup', target_img)
81     def test_cancel_blockdev_backup(self):
82         self.do_test_cancel('blockdev-backup', 'drive1')
84     def do_test_pause(self, cmd, target, image):
85         self.assert_no_active_block_jobs()
87         self.vm.pause_drive('drive0')
88         result = self.vm.qmp(cmd, device='drive0',
89                              target=target, sync='full')
90         self.assert_qmp(result, 'return', {})
92         self.pause_job('drive0', wait=False)
93         self.vm.resume_drive('drive0')
94         self.pause_wait('drive0')
96         result = self.vm.qmp('query-block-jobs')
97         offset = self.dictpath(result, 'return[0]/offset')
99         time.sleep(0.5)
100         result = self.vm.qmp('query-block-jobs')
101         self.assert_qmp(result, 'return[0]/offset', offset)
103         result = self.vm.qmp('block-job-resume', device='drive0')
104         self.assert_qmp(result, 'return', {})
106         self.wait_until_completed()
108         self.vm.shutdown()
109         self.assertTrue(iotests.compare_images(test_img, image),
110                         'target image does not match source after backup')
112     def test_pause_drive_backup(self):
113         self.do_test_pause('drive-backup', target_img, target_img)
115     def test_pause_blockdev_backup(self):
116         self.do_test_pause('blockdev-backup', 'drive1', blockdev_target_img)
118     def do_test_resize_blockdev_backup(self, device, node):
119         def pre_finalize():
120             result = self.vm.qmp('block_resize', device=device, size=65536)
121             self.assert_qmp(result, 'error/class', 'GenericError')
123             result = self.vm.qmp('block_resize', node_name=node, size=65536)
124             self.assert_qmp(result, 'error/class', 'GenericError')
126         result = self.vm.qmp('blockdev-backup', job_id='job0', device='drive0',
127                              target='drive1', sync='full', auto_finalize=False,
128                              auto_dismiss=False)
129         self.assert_qmp(result, 'return', {})
131         self.vm.run_job('job0', auto_finalize=False, pre_finalize=pre_finalize)
133     def test_source_resize_blockdev_backup(self):
134         self.do_test_resize_blockdev_backup('drive0', 'source')
136     def test_target_resize_blockdev_backup(self):
137         self.do_test_resize_blockdev_backup('drive1', 'target')
139     def do_test_target_size(self, size):
140         result = self.vm.qmp('block_resize', device='drive1', size=size)
141         self.assert_qmp(result, 'return', {})
143         result = self.vm.qmp('blockdev-backup', job_id='job0', device='drive0',
144                              target='drive1', sync='full')
145         self.assert_qmp(result, 'error/class', 'GenericError')
147     def test_small_target(self):
148         self.do_test_target_size(image_len // 2)
150     def test_large_target(self):
151         self.do_test_target_size(image_len * 2)
153     def test_medium_not_found(self):
154         if iotests.qemu_default_machine != 'pc':
155             return
157         result = self.vm.qmp('drive-backup', device='drive2', # CD-ROM
158                              target=target_img, sync='full')
159         self.assert_qmp(result, 'error/class', 'GenericError')
161     def test_medium_not_found_blockdev_backup(self):
162         if iotests.qemu_default_machine != 'pc':
163             return
165         result = self.vm.qmp('blockdev-backup', device='drive2', # CD-ROM
166                              target='drive1', sync='full')
167         self.assert_qmp(result, 'error/class', 'GenericError')
169     def test_image_not_found(self):
170         result = self.vm.qmp('drive-backup', device='drive0',
171                              target=target_img, sync='full', mode='existing')
172         self.assert_qmp(result, 'error/class', 'GenericError')
174     def test_invalid_format(self):
175         result = self.vm.qmp('drive-backup', device='drive0',
176                              target=target_img, sync='full',
177                              format='spaghetti-noodles')
178         self.assert_qmp(result, 'error/class', 'GenericError')
180     def do_test_device_not_found(self, cmd, **args):
181         result = self.vm.qmp(cmd, **args)
182         self.assert_qmp(result, 'error/class', 'GenericError')
184     def test_device_not_found(self):
185         self.do_test_device_not_found('drive-backup', device='nonexistent',
186                                       target=target_img, sync='full')
188         self.do_test_device_not_found('blockdev-backup', device='nonexistent',
189                                       target='drive0', sync='full')
191         self.do_test_device_not_found('blockdev-backup', device='drive0',
192                                       target='nonexistent', sync='full')
194         self.do_test_device_not_found('blockdev-backup', device='nonexistent',
195                                       target='nonexistent', sync='full')
197     def test_target_is_source(self):
198         result = self.vm.qmp('blockdev-backup', device='drive0',
199                              target='drive0', sync='full')
200         self.assert_qmp(result, 'error/class', 'GenericError')
202 class TestSetSpeed(iotests.QMPTestCase):
203     def setUp(self):
204         qemu_img('create', '-f', iotests.imgfmt, blockdev_target_img, str(image_len))
206         self.vm = iotests.VM().add_drive('blkdebug::' + test_img)
207         self.vm.add_drive(blockdev_target_img, interface="none")
208         self.vm.launch()
210     def tearDown(self):
211         self.vm.shutdown()
212         os.remove(blockdev_target_img)
213         try:
214             os.remove(target_img)
215         except OSError:
216             pass
218     def do_test_set_speed(self, cmd, target):
219         self.assert_no_active_block_jobs()
221         self.vm.pause_drive('drive0')
222         result = self.vm.qmp(cmd, device='drive0', target=target, sync='full')
223         self.assert_qmp(result, 'return', {})
225         # Default speed is 0
226         result = self.vm.qmp('query-block-jobs')
227         self.assert_qmp(result, 'return[0]/device', 'drive0')
228         self.assert_qmp(result, 'return[0]/speed', 0)
230         result = self.vm.qmp('block-job-set-speed', device='drive0', speed=8 * 1024 * 1024)
231         self.assert_qmp(result, 'return', {})
233         # Ensure the speed we set was accepted
234         result = self.vm.qmp('query-block-jobs')
235         self.assert_qmp(result, 'return[0]/device', 'drive0')
236         self.assert_qmp(result, 'return[0]/speed', 8 * 1024 * 1024)
238         event = self.cancel_and_wait(resume=True)
239         self.assert_qmp(event, 'data/type', 'backup')
241         # Check setting speed option works
242         self.vm.pause_drive('drive0')
243         result = self.vm.qmp(cmd, device='drive0',
244                              target=target, sync='full', speed=4*1024*1024)
245         self.assert_qmp(result, 'return', {})
247         result = self.vm.qmp('query-block-jobs')
248         self.assert_qmp(result, 'return[0]/device', 'drive0')
249         self.assert_qmp(result, 'return[0]/speed', 4 * 1024 * 1024)
251         event = self.cancel_and_wait(resume=True)
252         self.assert_qmp(event, 'data/type', 'backup')
254     def test_set_speed_drive_backup(self):
255         self.do_test_set_speed('drive-backup', target_img)
257     def test_set_speed_blockdev_backup(self):
258         self.do_test_set_speed('blockdev-backup', 'drive1')
260     def do_test_set_speed_invalid(self, cmd, target):
261         self.assert_no_active_block_jobs()
263         result = self.vm.qmp(cmd, device='drive0',
264                              target=target, sync='full', speed=-1)
265         self.assert_qmp(result, 'error/class', 'GenericError')
267         self.assert_no_active_block_jobs()
269         self.vm.pause_drive('drive0')
270         result = self.vm.qmp(cmd, device='drive0',
271                              target=target, sync='full')
272         self.assert_qmp(result, 'return', {})
274         result = self.vm.qmp('block-job-set-speed', device='drive0', speed=-1)
275         self.assert_qmp(result, 'error/class', 'GenericError')
277         event = self.cancel_and_wait(resume=True)
278         self.assert_qmp(event, 'data/type', 'backup')
280     def test_set_speed_invalid_drive_backup(self):
281         self.do_test_set_speed_invalid('drive-backup', target_img)
283     def test_set_speed_invalid_blockdev_backup(self):
284         self.do_test_set_speed_invalid('blockdev-backup',  'drive1')
286 # Note: We cannot use pause_drive() here, or the transaction command
287 #       would stall.  Instead, we limit the block job speed here.
288 class TestSingleTransaction(iotests.QMPTestCase):
289     def setUp(self):
290         qemu_img('create', '-f', iotests.imgfmt, blockdev_target_img, str(image_len))
292         self.vm = iotests.VM().add_drive(test_img)
293         self.vm.add_drive(blockdev_target_img, interface="none")
294         if iotests.qemu_default_machine == 'pc':
295             self.vm.add_drive(None, 'media=cdrom', 'ide')
296         self.vm.launch()
298     def tearDown(self):
299         self.vm.shutdown()
300         os.remove(blockdev_target_img)
301         try:
302             os.remove(target_img)
303         except OSError:
304             pass
306     def do_test_cancel(self, cmd, target):
307         self.assert_no_active_block_jobs()
309         result = self.vm.qmp('transaction', actions=[{
310                 'type': cmd,
311                 'data': { 'device': 'drive0',
312                           'target': target,
313                           'sync': 'full',
314                           'speed': 64 * 1024 },
315             }
316         ])
318         self.assert_qmp(result, 'return', {})
320         event = self.cancel_and_wait()
321         self.assert_qmp(event, 'data/type', 'backup')
323     def test_cancel_drive_backup(self):
324         self.do_test_cancel('drive-backup', target_img)
326     def test_cancel_blockdev_backup(self):
327         self.do_test_cancel('blockdev-backup', 'drive1')
329     def do_test_pause(self, cmd, target, image):
330         self.assert_no_active_block_jobs()
332         result = self.vm.qmp('transaction', actions=[{
333                 'type': cmd,
334                 'data': { 'device': 'drive0',
335                           'target': target,
336                           'sync': 'full',
337                           'speed': 64 * 1024 },
338             }
339         ])
340         self.assert_qmp(result, 'return', {})
342         self.pause_job('drive0', wait=False)
344         result = self.vm.qmp('block-job-set-speed', device='drive0', speed=0)
345         self.assert_qmp(result, 'return', {})
347         self.pause_wait('drive0')
349         result = self.vm.qmp('query-block-jobs')
350         offset = self.dictpath(result, 'return[0]/offset')
352         time.sleep(0.5)
353         result = self.vm.qmp('query-block-jobs')
354         self.assert_qmp(result, 'return[0]/offset', offset)
356         result = self.vm.qmp('block-job-resume', device='drive0')
357         self.assert_qmp(result, 'return', {})
359         self.wait_until_completed()
361         self.vm.shutdown()
362         self.assertTrue(iotests.compare_images(test_img, image),
363                         'target image does not match source after backup')
365     def test_pause_drive_backup(self):
366         self.do_test_pause('drive-backup', target_img, target_img)
368     def test_pause_blockdev_backup(self):
369         self.do_test_pause('blockdev-backup', 'drive1', blockdev_target_img)
371     def do_test_medium_not_found(self, cmd, target):
372         if iotests.qemu_default_machine != 'pc':
373             return
375         result = self.vm.qmp('transaction', actions=[{
376                 'type': cmd,
377                 'data': { 'device': 'drive2', # CD-ROM
378                           'target': target,
379                           'sync': 'full' },
380             }
381         ])
382         self.assert_qmp(result, 'error/class', 'GenericError')
384     def test_medium_not_found_drive_backup(self):
385         self.do_test_medium_not_found('drive-backup', target_img)
387     def test_medium_not_found_blockdev_backup(self):
388         self.do_test_medium_not_found('blockdev-backup', 'drive1')
390     def test_image_not_found(self):
391         result = self.vm.qmp('transaction', actions=[{
392                 'type': 'drive-backup',
393                 'data': { 'device': 'drive0',
394                           'mode': 'existing',
395                           'target': target_img,
396                           'sync': 'full' },
397             }
398         ])
399         self.assert_qmp(result, 'error/class', 'GenericError')
401     def test_device_not_found(self):
402         result = self.vm.qmp('transaction', actions=[{
403                 'type': 'drive-backup',
404                 'data': { 'device': 'nonexistent',
405                           'mode': 'existing',
406                           'target': target_img,
407                           'sync': 'full' },
408             }
409         ])
410         self.assert_qmp(result, 'error/class', 'GenericError')
412         result = self.vm.qmp('transaction', actions=[{
413                 'type': 'blockdev-backup',
414                 'data': { 'device': 'nonexistent',
415                           'target': 'drive1',
416                           'sync': 'full' },
417             }
418         ])
419         self.assert_qmp(result, 'error/class', 'GenericError')
421         result = self.vm.qmp('transaction', actions=[{
422                 'type': 'blockdev-backup',
423                 'data': { 'device': 'drive0',
424                           'target': 'nonexistent',
425                           'sync': 'full' },
426             }
427         ])
428         self.assert_qmp(result, 'error/class', 'GenericError')
430         result = self.vm.qmp('transaction', actions=[{
431                 'type': 'blockdev-backup',
432                 'data': { 'device': 'nonexistent',
433                           'target': 'nonexistent',
434                           'sync': 'full' },
435             }
436         ])
437         self.assert_qmp(result, 'error/class', 'GenericError')
439     def test_target_is_source(self):
440         result = self.vm.qmp('transaction', actions=[{
441                 'type': 'blockdev-backup',
442                 'data': { 'device': 'drive0',
443                           'target': 'drive0',
444                           'sync': 'full' },
445             }
446         ])
447         self.assert_qmp(result, 'error/class', 'GenericError')
449     def test_abort(self):
450         result = self.vm.qmp('transaction', actions=[{
451                 'type': 'drive-backup',
452                 'data': { 'device': 'nonexistent',
453                           'mode': 'existing',
454                           'target': target_img,
455                           'sync': 'full' },
456             }, {
457                 'type': 'Abort',
458                 'data': {},
459             }
460         ])
461         self.assert_qmp(result, 'error/class', 'GenericError')
462         self.assert_no_active_block_jobs()
464         result = self.vm.qmp('transaction', actions=[{
465                 'type': 'blockdev-backup',
466                 'data': { 'device': 'nonexistent',
467                           'target': 'drive1',
468                           'sync': 'full' },
469             }, {
470                 'type': 'Abort',
471                 'data': {},
472             }
473         ])
474         self.assert_qmp(result, 'error/class', 'GenericError')
475         self.assert_no_active_block_jobs()
477         result = self.vm.qmp('transaction', actions=[{
478                 'type': 'blockdev-backup',
479                 'data': { 'device': 'drive0',
480                           'target': 'nonexistent',
481                           'sync': 'full' },
482             }, {
483                 'type': 'Abort',
484                 'data': {},
485             }
486         ])
487         self.assert_qmp(result, 'error/class', 'GenericError')
488         self.assert_no_active_block_jobs()
491 class TestCompressedToQcow2(iotests.QMPTestCase):
492     image_len = 64 * 1024 * 1024 # MB
493     target_fmt = {'type': 'qcow2', 'args': (), 'drive-opts': ''}
495     def tearDown(self):
496         self.vm.shutdown()
497         os.remove(blockdev_target_img)
498         try:
499             os.remove(target_img)
500         except OSError:
501             pass
503     def do_prepare_drives(self, attach_target):
504         self.vm = iotests.VM().add_drive('blkdebug::' + test_img,
505                                          opts=self.target_fmt['drive-opts'])
507         qemu_img('create', '-f', self.target_fmt['type'], blockdev_target_img,
508                  str(self.image_len), *self.target_fmt['args'])
509         if attach_target:
510             self.vm.add_drive(blockdev_target_img,
511                               img_format=self.target_fmt['type'],
512                               interface="none",
513                               opts=self.target_fmt['drive-opts'])
515         self.vm.launch()
517     def do_test_compress_complete(self, cmd, attach_target, **args):
518         self.do_prepare_drives(attach_target)
520         self.assert_no_active_block_jobs()
522         result = self.vm.qmp(cmd, device='drive0', sync='full', compress=True, **args)
523         self.assert_qmp(result, 'return', {})
525         self.wait_until_completed()
527         self.vm.shutdown()
528         self.assertTrue(iotests.compare_images(test_img, blockdev_target_img,
529                                                iotests.imgfmt,
530                                                self.target_fmt['type']),
531                         'target image does not match source after backup')
533     def test_complete_compress_drive_backup(self):
534         self.do_test_compress_complete('drive-backup', False,
535                                        target=blockdev_target_img,
536                                        mode='existing')
538     def test_complete_compress_blockdev_backup(self):
539         self.do_test_compress_complete('blockdev-backup',
540                                        True, target='drive1')
542     def do_test_compress_cancel(self, cmd, attach_target, **args):
543         self.do_prepare_drives(attach_target)
545         self.assert_no_active_block_jobs()
547         self.vm.pause_drive('drive0')
548         result = self.vm.qmp(cmd, device='drive0', sync='full', compress=True, **args)
549         self.assert_qmp(result, 'return', {})
551         event = self.cancel_and_wait(resume=True)
552         self.assert_qmp(event, 'data/type', 'backup')
554         self.vm.shutdown()
556     def test_compress_cancel_drive_backup(self):
557         self.do_test_compress_cancel('drive-backup', False,
558                                      target=blockdev_target_img,
559                                      mode='existing')
561     def test_compress_cancel_blockdev_backup(self):
562         self.do_test_compress_cancel('blockdev-backup', True,
563                                      target='drive1')
565     def do_test_compress_pause(self, cmd, attach_target, **args):
566         self.do_prepare_drives(attach_target)
568         self.assert_no_active_block_jobs()
570         self.vm.pause_drive('drive0')
571         result = self.vm.qmp(cmd, device='drive0', sync='full', compress=True, **args)
572         self.assert_qmp(result, 'return', {})
574         self.pause_job('drive0', wait=False)
575         self.vm.resume_drive('drive0')
576         self.pause_wait('drive0')
578         result = self.vm.qmp('query-block-jobs')
579         offset = self.dictpath(result, 'return[0]/offset')
581         time.sleep(0.5)
582         result = self.vm.qmp('query-block-jobs')
583         self.assert_qmp(result, 'return[0]/offset', offset)
585         result = self.vm.qmp('block-job-resume', device='drive0')
586         self.assert_qmp(result, 'return', {})
588         self.wait_until_completed()
590         self.vm.shutdown()
591         self.assertTrue(iotests.compare_images(test_img, blockdev_target_img,
592                                                iotests.imgfmt,
593                                                self.target_fmt['type']),
594                         'target image does not match source after backup')
596     def test_compress_pause_drive_backup(self):
597         self.do_test_compress_pause('drive-backup', False,
598                                     target=blockdev_target_img,
599                                     mode='existing')
601     def test_compress_pause_blockdev_backup(self):
602         self.do_test_compress_pause('blockdev-backup', True,
603                                     target='drive1')
606 class TestCompressedToVmdk(TestCompressedToQcow2):
607     target_fmt = {'type': 'vmdk', 'args': ('-o', 'subformat=streamOptimized'),
608                   'drive-opts': 'cache.no-flush=on'}
610     @iotests.skip_if_unsupported(['vmdk'])
611     def setUp(self):
612         pass
615 if __name__ == '__main__':
616     iotests.main(supported_fmts=['raw', 'qcow2'],
617                  supported_protocols=['file'])