mirror of https://github.com/xemu-project/xemu.git
122 lines
4.6 KiB
Python
Executable File
122 lines
4.6 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# group: rw
|
|
#
|
|
# Test permissions taken by the mirror-top filter
|
|
#
|
|
# Copyright (C) 2021 Red Hat, Inc.
|
|
#
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation; either version 2 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
#
|
|
|
|
import os
|
|
|
|
from qemu.machine import machine
|
|
|
|
import iotests
|
|
from iotests import change_log_level, qemu_img
|
|
|
|
|
|
image_size = 1 * 1024 * 1024
|
|
source = os.path.join(iotests.test_dir, 'source.img')
|
|
|
|
|
|
class TestMirrorTopPerms(iotests.QMPTestCase):
|
|
def setUp(self):
|
|
qemu_img('create', '-f', iotests.imgfmt, source, str(image_size))
|
|
self.vm = iotests.VM()
|
|
self.vm.add_drive(source)
|
|
self.vm.add_blockdev(f'null-co,node-name=null,size={image_size}')
|
|
self.vm.launch()
|
|
|
|
# Will be created by the test function itself
|
|
self.vm_b = None
|
|
|
|
def tearDown(self):
|
|
try:
|
|
self.vm.shutdown()
|
|
except machine.AbnormalShutdown:
|
|
pass
|
|
|
|
if self.vm_b is not None:
|
|
self.vm_b.shutdown()
|
|
|
|
os.remove(source)
|
|
|
|
def test_cancel(self):
|
|
"""
|
|
Before commit 53431b9086b28, mirror-top used to not take any
|
|
permissions but WRITE and share all permissions. Because it
|
|
is inserted between the source's original parents and the
|
|
source, there generally was no parent that would have taken or
|
|
unshared any permissions on the source, which means that an
|
|
external process could access the image unhindered by locks.
|
|
(Unless there was a parent above the protocol node that would
|
|
take its own locks, e.g. a format driver.)
|
|
This is bad enough, but if the mirror job is then cancelled,
|
|
the mirroring VM tries to take back the image, restores the
|
|
original permissions taken and unshared, and assumes this must
|
|
just work. But it will not, and so the VM aborts.
|
|
|
|
Commit 53431b9086b28 made mirror keep the original permissions
|
|
and so no other process can "steal" the image.
|
|
|
|
(Note that you cannot really do the same with the target image
|
|
and then completing the job, because the mirror job always
|
|
took/unshared the correct permissions on the target. For
|
|
example, it does not share READ_CONSISTENT, which makes it
|
|
difficult to let some other qemu process open the image.)
|
|
"""
|
|
|
|
result = self.vm.qmp('blockdev-mirror',
|
|
job_id='mirror',
|
|
device='drive0',
|
|
target='null',
|
|
sync='full')
|
|
self.assert_qmp(result, 'return', {})
|
|
|
|
self.vm.event_wait('BLOCK_JOB_READY')
|
|
|
|
# We want this to fail because the image cannot be locked.
|
|
# If it does not fail, continue still and see what happens.
|
|
self.vm_b = iotests.VM(path_suffix='b')
|
|
# Must use -blockdev -device so we can use share-rw.
|
|
# (And we need share-rw=on because mirror-top was always
|
|
# forced to take the WRITE permission so it can write to the
|
|
# source image.)
|
|
self.vm_b.add_blockdev(f'file,node-name=drive0,filename={source}')
|
|
self.vm_b.add_device('virtio-blk,drive=drive0,share-rw=on')
|
|
try:
|
|
# Silence QMP logging errors temporarily.
|
|
with change_log_level('qemu.qmp'):
|
|
self.vm_b.launch()
|
|
print('ERROR: VM B launched successfully, '
|
|
'this should not have happened')
|
|
except machine.VMLaunchFailure as exc:
|
|
assert 'Is another process using the image' in exc.output
|
|
|
|
result = self.vm.qmp('block-job-cancel',
|
|
device='mirror')
|
|
self.assert_qmp(result, 'return', {})
|
|
|
|
self.vm.event_wait('BLOCK_JOB_COMPLETED')
|
|
|
|
|
|
if __name__ == '__main__':
|
|
# No metadata format driver supported, because they would for
|
|
# example always unshare the WRITE permission. The raw driver
|
|
# just passes through the permissions from the guest device, and
|
|
# those are the permissions that we want to test.
|
|
iotests.main(supported_fmts=['raw'],
|
|
supported_protocols=['file'])
|