17c477526SPhilippe Mathieu-Daudé#!/usr/bin/env python3 29dd003a9SVladimir Sementsov-Ogievskiy# group: auto quick 3a541fcc2SVladimir Sementsov-Ogievskiy# 4d003e0aeSVladimir Sementsov-Ogievskiy# Test for copy-before-write filter permission conflict 5a541fcc2SVladimir Sementsov-Ogievskiy# 6a541fcc2SVladimir Sementsov-Ogievskiy# Copyright (c) 2019 Virtuozzo International GmbH. 7a541fcc2SVladimir Sementsov-Ogievskiy# 8a541fcc2SVladimir Sementsov-Ogievskiy# This program is free software; you can redistribute it and/or modify 9a541fcc2SVladimir Sementsov-Ogievskiy# it under the terms of the GNU General Public License as published by 10a541fcc2SVladimir Sementsov-Ogievskiy# the Free Software Foundation; either version 2 of the License, or 11a541fcc2SVladimir Sementsov-Ogievskiy# (at your option) any later version. 12a541fcc2SVladimir Sementsov-Ogievskiy# 13a541fcc2SVladimir Sementsov-Ogievskiy# This program is distributed in the hope that it will be useful, 14a541fcc2SVladimir Sementsov-Ogievskiy# but WITHOUT ANY WARRANTY; without even the implied warranty of 15a541fcc2SVladimir Sementsov-Ogievskiy# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16a541fcc2SVladimir Sementsov-Ogievskiy# GNU General Public License for more details. 17a541fcc2SVladimir Sementsov-Ogievskiy# 18a541fcc2SVladimir Sementsov-Ogievskiy# You should have received a copy of the GNU General Public License 19a541fcc2SVladimir Sementsov-Ogievskiy# along with this program. If not, see <http://www.gnu.org/licenses/>. 20a541fcc2SVladimir Sementsov-Ogievskiy# 21a541fcc2SVladimir Sementsov-Ogievskiy 22a541fcc2SVladimir Sementsov-Ogievskiyimport iotests 23a541fcc2SVladimir Sementsov-Ogievskiy 24a541fcc2SVladimir Sementsov-Ogievskiy# The test is unrelated to formats, restrict it to qcow2 to avoid extra runs 257d814059SJohn Snowiotests.script_initialize( 267d814059SJohn Snow supported_fmts=['qcow2'], 277d814059SJohn Snow) 28a541fcc2SVladimir Sementsov-Ogievskiy 29a541fcc2SVladimir Sementsov-Ogievskiysize = 1024 * 1024 30a541fcc2SVladimir Sementsov-Ogievskiy 31a541fcc2SVladimir Sementsov-Ogievskiy""" Test description 32a541fcc2SVladimir Sementsov-Ogievskiy 33a541fcc2SVladimir Sementsov-OgievskiyWhen performing a backup, all writes on the source subtree must go through the 34d003e0aeSVladimir Sementsov-Ogievskiycopy-before-write filter so it can copy all data to the target before it is 35d003e0aeSVladimir Sementsov-Ogievskiychanged. copy-before-write filter is appended above source node, to achieve 36d003e0aeSVladimir Sementsov-Ogievskiythis thing, so all parents of source node are handled. A configuration with 37d003e0aeSVladimir Sementsov-Ogievskiyside parents of source sub-tree with write permission is unsupported (we'd have 38d003e0aeSVladimir Sementsov-Ogievskiyappend several copy-before-write filter like nodes to handle such parents). The 39d003e0aeSVladimir Sementsov-Ogievskiytest create an example of such configuration and checks that a backup is then 40d003e0aeSVladimir Sementsov-Ogievskiynot allowed (blockdev-backup command should fail). 41a541fcc2SVladimir Sementsov-Ogievskiy 42a541fcc2SVladimir Sementsov-OgievskiyThe configuration: 43a541fcc2SVladimir Sementsov-Ogievskiy 44a541fcc2SVladimir Sementsov-Ogievskiy ┌────────┐ target ┌─────────────┐ 45a541fcc2SVladimir Sementsov-Ogievskiy │ target │ ◀─────── │ backup_top │ 46a541fcc2SVladimir Sementsov-Ogievskiy └────────┘ └─────────────┘ 47a541fcc2SVladimir Sementsov-Ogievskiy │ 48a541fcc2SVladimir Sementsov-Ogievskiy │ backing 49a541fcc2SVladimir Sementsov-Ogievskiy ▼ 50a541fcc2SVladimir Sementsov-Ogievskiy ┌─────────────┐ 51a541fcc2SVladimir Sementsov-Ogievskiy │ source │ 52a541fcc2SVladimir Sementsov-Ogievskiy └─────────────┘ 53a541fcc2SVladimir Sementsov-Ogievskiy │ 54a541fcc2SVladimir Sementsov-Ogievskiy │ file 55a541fcc2SVladimir Sementsov-Ogievskiy ▼ 56a541fcc2SVladimir Sementsov-Ogievskiy ┌─────────────┐ write perm ┌───────┐ 57a541fcc2SVladimir Sementsov-Ogievskiy │ base │ ◀──────────── │ other │ 58a541fcc2SVladimir Sementsov-Ogievskiy └─────────────┘ └───────┘ 59a541fcc2SVladimir Sementsov-Ogievskiy 60d003e0aeSVladimir Sementsov-Ogievskiycopy-before-write filter wants to unshare write permission on its source child. 61d003e0aeSVladimir Sementsov-OgievskiyWrite unsharing will be propagated to the "source->base" link and will conflict 62d003e0aeSVladimir Sementsov-Ogievskiywith other node write permission. So permission update will fail and backup job 63d003e0aeSVladimir Sementsov-Ogievskiywill not be started. 64a541fcc2SVladimir Sementsov-Ogievskiy 65a541fcc2SVladimir Sementsov-OgievskiyNote, that the only thing which prevents backup of running on such 66a541fcc2SVladimir Sementsov-Ogievskiyconfiguration is default permission propagation scheme. It may be altered by 67a541fcc2SVladimir Sementsov-Ogievskiydifferent block drivers, so backup will run in invalid configuration. But 68a541fcc2SVladimir Sementsov-Ogievskiysomething is better than nothing. Also, before the previous commit (commit 69a541fcc2SVladimir Sementsov-Ogievskiypreceding this test creation), starting backup on such configuration led to 70a541fcc2SVladimir Sementsov-Ogievskiycrash, so current "something" is a lot better, and this test actual goal is 71a541fcc2SVladimir Sementsov-Ogievskiyto check that crash is fixed :) 72a541fcc2SVladimir Sementsov-Ogievskiy""" 73a541fcc2SVladimir Sementsov-Ogievskiy 74a541fcc2SVladimir Sementsov-Ogievskiyvm = iotests.VM() 75a541fcc2SVladimir Sementsov-Ogievskiyvm.launch() 76a541fcc2SVladimir Sementsov-Ogievskiy 77813cc254SKevin Wolfvm.qmp_log('blockdev-add', **{ 78813cc254SKevin Wolf 'node-name': 'target', 79813cc254SKevin Wolf 'driver': 'null-co', 80813cc254SKevin Wolf 'size': size, 81813cc254SKevin Wolf}) 82a541fcc2SVladimir Sementsov-Ogievskiy 83a541fcc2SVladimir Sementsov-Ogievskiyvm.qmp_log('blockdev-add', **{ 84a541fcc2SVladimir Sementsov-Ogievskiy 'node-name': 'source', 85a541fcc2SVladimir Sementsov-Ogievskiy 'driver': 'blkdebug', 86a541fcc2SVladimir Sementsov-Ogievskiy 'image': {'node-name': 'base', 'driver': 'null-co', 'size': size} 87a541fcc2SVladimir Sementsov-Ogievskiy}) 88a541fcc2SVladimir Sementsov-Ogievskiy 89a541fcc2SVladimir Sementsov-Ogievskiyvm.qmp_log('blockdev-add', **{ 90a541fcc2SVladimir Sementsov-Ogievskiy 'node-name': 'other', 91a541fcc2SVladimir Sementsov-Ogievskiy 'driver': 'blkdebug', 92a541fcc2SVladimir Sementsov-Ogievskiy 'image': 'base', 93a541fcc2SVladimir Sementsov-Ogievskiy 'take-child-perms': ['write'] 94a541fcc2SVladimir Sementsov-Ogievskiy}) 95a541fcc2SVladimir Sementsov-Ogievskiy 96*985cac8fSVladimir Sementsov-Ogievskiyvm.qmp_log('blockdev-backup', sync='full', device='source', target='target', 97*985cac8fSVladimir Sementsov-Ogievskiy job_id="backup0") 98a541fcc2SVladimir Sementsov-Ogievskiy 99a541fcc2SVladimir Sementsov-Ogievskiyvm.shutdown() 100e4179940SMax Reitz 101e4179940SMax Reitz 102d003e0aeSVladimir Sementsov-Ogievskiyprint('\n=== copy-before-write filter should be gone after job-finalize ===\n') 103e4179940SMax Reitz 104d003e0aeSVladimir Sementsov-Ogievskiy# Check that the copy-before-write node is gone after job-finalize. 105e4179940SMax Reitz 106e4179940SMax Reitzvm = iotests.VM() 107e4179940SMax Reitzvm.launch() 108e4179940SMax Reitz 109e4179940SMax Reitzvm.qmp_log('blockdev-add', **{ 110e4179940SMax Reitz 'node-name': 'source', 111e4179940SMax Reitz 'driver': 'null-co', 112e4179940SMax Reitz}) 113e4179940SMax Reitz 114e4179940SMax Reitzvm.qmp_log('blockdev-add', **{ 115e4179940SMax Reitz 'node-name': 'target', 116e4179940SMax Reitz 'driver': 'null-co', 117e4179940SMax Reitz}) 118e4179940SMax Reitz 119e4179940SMax Reitzvm.qmp_log('blockdev-backup', 120e4179940SMax Reitz job_id='backup', 121e4179940SMax Reitz device='source', 122e4179940SMax Reitz target='target', 123e4179940SMax Reitz sync='full', 124e4179940SMax Reitz filter_node_name='backup-filter', 125e4179940SMax Reitz auto_finalize=False, 126e4179940SMax Reitz auto_dismiss=False) 127e4179940SMax Reitz 128e4179940SMax Reitzvm.event_wait('BLOCK_JOB_PENDING', 5.0) 129e4179940SMax Reitz 130d003e0aeSVladimir Sementsov-Ogievskiy# The copy-before-write filter should still be present prior to finalization 131e4179940SMax Reitzassert vm.node_info('backup-filter') is not None 132e4179940SMax Reitz 133e4179940SMax Reitzvm.qmp_log('job-finalize', id='backup') 134e4179940SMax Reitzvm.event_wait('BLOCK_JOB_COMPLETED', 5.0) 135e4179940SMax Reitz 136e4179940SMax Reitz# The filter should be gone now. Check that by trying to access it 137e4179940SMax Reitz# with qemu-io (which will most likely crash qemu if it is still 138e4179940SMax Reitz# there.). 139e4179940SMax Reitzvm.qmp_log('human-monitor-command', 140e4179940SMax Reitz command_line='qemu-io backup-filter "write 0 1M"') 141e4179940SMax Reitz 142e4179940SMax Reitz# (Also, do an explicit check.) 143e4179940SMax Reitzassert vm.node_info('backup-filter') is None 144e4179940SMax Reitz 145e4179940SMax Reitzvm.qmp_log('job-dismiss', id='backup') 146e4179940SMax Reitzvm.event_wait('JOB_STATUS_CHANGE', 5.0, {'data': {'status': 'null'}}) 147e4179940SMax Reitz 148e4179940SMax Reitzvm.shutdown() 149