Block layer patches:

- Relax restrictions for blockdev-snapshot (allows libvirt to do live
   storage migration with blockdev-mirror)
 - luks: Delete created files when block_crypto_co_create_opts_luks fails
 - Fix memleaks in qmp_object_add
 -----BEGIN PGP SIGNATURE-----
 
 iQIcBAABAgAGBQJeaQYTAAoJEH8JsnLIjy/WQwYP/3pzAjqVecL3dGmnPWAkBqCV
 CFpxT2nIMe+xCBvWQBoeekHsFJ7GQf4E1WVNRZgoAh9VQkvkajZsVNn8Auo2Veq2
 c7w/R4xf/Wet2hKGVRS0JXwbg69U5BbpcF7E2DRNfp+CvaDCafHSDNeGTb3hFUjT
 x1jwhK6VqfY7+LHU0B0QmX2KA66nDx1p8l8HJQYd1MlCKAbj8kv/swEbqBJn32hA
 32CIYfC4VCqkW5va1eOjd3Kyi/ugkFCHTI8+mOa45/BFBzIiKfCsDaFHh/DI59QB
 qcDKkUcO3+W788vCKgJQGnG070TwKPx2OnjhxFKiEGaoX3Sz+AY4wUvf3mfFl8GM
 zYqTdOy4Xh0ckvA6JCS0jtAKmANkeEGqnECAgub22Z+kyOzqC05B1FkYwqYDcFXY
 atWKm5Vr47jgD6Oq6O0OpZaZrAUWOfoBmq4ErnrBEHuW5329NEInmjYwxednK+43
 CwU/lSdX7ujRSsjS8Xi1dHS4pxHK/mg51dInL44zGFUayegiLPgA8cuESE0mHOfZ
 67X14rxu6D4Y5r0L+w7rsSGjByR29VynE1McL9fZ1Wp29JHaQ5fjdG6GMXEwYxmV
 R0YNXe85FAlNgqj0Bme+fR2YPxZ48NqHIMOvFFStNHyfD0qQN0TtT1iarfEcBR0u
 jm8MnSoIDLkRXEgBW9bW
 =ociU
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/kevin/tags/for-upstream' into staging

Block layer patches:

- Relax restrictions for blockdev-snapshot (allows libvirt to do live
  storage migration with blockdev-mirror)
- luks: Delete created files when block_crypto_co_create_opts_luks fails
- Fix memleaks in qmp_object_add

# gpg: Signature made Wed 11 Mar 2020 15:38:59 GMT
# gpg:                using RSA key 7F09B272C88F2FD6
# gpg: Good signature from "Kevin Wolf <kwolf@redhat.com>" [full]
# Primary key fingerprint: DC3D EB15 9A9A F95D 3D74  56FE 7F09 B272 C88F 2FD6

* remotes/kevin/tags/for-upstream:
  qemu-iotests: adding LUKS cleanup for non-UTF8 secret error
  crypto.c: cleanup created file when block_crypto_co_create_opts_luks fails
  block.c: adding bdrv_co_delete_file
  block: introducing 'bdrv_co_delete_file' interface
  tests/qemu-iotests: Fix socket_scm_helper build path
  qapi: Add '@allow-write-only-overlay' feature for 'blockdev-snapshot'
  iotests: Add iothread cases to 155
  block: Fix cross-AioContext blockdev-snapshot
  iotests: Test mirror with temporarily disabled target backing file
  iotests: Fix run_job() with use_log=False
  block: Relax restrictions for blockdev-snapshot
  block: Make bdrv_get_cumulative_perm() public
  qom-qmp-cmds: fix two memleaks in qmp_object_add

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2020-03-12 16:51:26 +00:00
commit 49780a582d
17 changed files with 262 additions and 57 deletions

33
block.c
View File

@ -668,6 +668,32 @@ int bdrv_create_file(const char *filename, QemuOpts *opts, Error **errp)
}
}
int coroutine_fn bdrv_co_delete_file(BlockDriverState *bs, Error **errp)
{
Error *local_err = NULL;
int ret;
assert(bs != NULL);
if (!bs->drv) {
error_setg(errp, "Block node '%s' is not opened", bs->filename);
return -ENOMEDIUM;
}
if (!bs->drv->bdrv_co_delete_file) {
error_setg(errp, "Driver '%s' does not support image deletion",
bs->drv->format_name);
return -ENOTSUP;
}
ret = bs->drv->bdrv_co_delete_file(bs, &local_err);
if (ret < 0) {
error_propagate(errp, local_err);
}
return ret;
}
/**
* Try to get @bs's logical and physical block size.
* On success, store them in @bsz struct and return 0.
@ -1872,8 +1898,6 @@ static int bdrv_child_check_perm(BdrvChild *c, BlockReopenQueue *q,
bool *tighten_restrictions, Error **errp);
static void bdrv_child_abort_perm_update(BdrvChild *c);
static void bdrv_child_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared);
static void bdrv_get_cumulative_perm(BlockDriverState *bs, uint64_t *perm,
uint64_t *shared_perm);
typedef struct BlockReopenQueueEntry {
bool prepared;
@ -2097,8 +2121,8 @@ static void bdrv_set_perm(BlockDriverState *bs, uint64_t cumulative_perms,
}
}
static void bdrv_get_cumulative_perm(BlockDriverState *bs, uint64_t *perm,
uint64_t *shared_perm)
void bdrv_get_cumulative_perm(BlockDriverState *bs, uint64_t *perm,
uint64_t *shared_perm)
{
BdrvChild *c;
uint64_t cumulative_perms = 0;
@ -4367,6 +4391,7 @@ void bdrv_replace_node(BlockDriverState *from, BlockDriverState *to,
bdrv_ref(from);
assert(qemu_get_current_aio_context() == qemu_get_aio_context());
assert(bdrv_get_aio_context(from) == bdrv_get_aio_context(to));
bdrv_drained_begin(from);
/* Put all parents into @list and calculate their cumulative permissions */

View File

@ -30,6 +30,7 @@
#include "qapi/error.h"
#include "qemu/module.h"
#include "qemu/option.h"
#include "qemu/cutils.h"
#include "crypto.h"
typedef struct BlockCrypto BlockCrypto;
@ -657,6 +658,23 @@ static int coroutine_fn block_crypto_co_create_opts_luks(const char *filename,
ret = 0;
fail:
/*
* If an error occurred, delete 'filename'. Even if the file existed
* beforehand, it has been truncated and corrupted in the process.
*/
if (ret && bs) {
Error *local_delete_err = NULL;
int r_del = bdrv_co_delete_file(bs, &local_delete_err);
/*
* ENOTSUP will happen if the block driver doesn't support
* the 'bdrv_co_delete_file' interface. This is a predictable
* scenario and shouldn't be reported back to the user.
*/
if ((r_del < 0) && (r_del != -ENOTSUP)) {
error_report_err(local_delete_err);
}
}
bdrv_unref(bs);
qapi_free_QCryptoBlockCreateOptions(create_opts);
qobject_unref(cryptoopts);

View File

@ -2445,6 +2445,28 @@ static int coroutine_fn raw_co_create_opts(const char *filename, QemuOpts *opts,
return raw_co_create(&options, errp);
}
static int coroutine_fn raw_co_delete_file(BlockDriverState *bs,
Error **errp)
{
struct stat st;
int ret;
if (!(stat(bs->filename, &st) == 0) || !S_ISREG(st.st_mode)) {
error_setg_errno(errp, ENOENT, "%s is not a regular file",
bs->filename);
return -ENOENT;
}
ret = unlink(bs->filename);
if (ret < 0) {
ret = -errno;
error_setg_errno(errp, -ret, "Error when deleting file %s",
bs->filename);
}
return ret;
}
/*
* Find allocation range in @bs around offset @start.
* May change underlying file descriptor's file offset.
@ -3075,6 +3097,7 @@ BlockDriver bdrv_file = {
.bdrv_co_block_status = raw_co_block_status,
.bdrv_co_invalidate_cache = raw_co_invalidate_cache,
.bdrv_co_pwrite_zeroes = raw_co_pwrite_zeroes,
.bdrv_co_delete_file = raw_co_delete_file,
.bdrv_co_preadv = raw_co_preadv,
.bdrv_co_pwritev = raw_co_pwritev,

View File

@ -1470,8 +1470,7 @@ static void external_snapshot_prepare(BlkActionState *common,
DO_UPCAST(ExternalSnapshotState, common, common);
TransactionAction *action = common->action;
AioContext *aio_context;
AioContext *old_context;
int ret;
uint64_t perm, shared;
/* 'blockdev-snapshot' and 'blockdev-snapshot-sync' have similar
* purpose but a different set of parameters */
@ -1586,16 +1585,17 @@ static void external_snapshot_prepare(BlkActionState *common,
goto out;
}
if (bdrv_has_blk(state->new_bs)) {
/*
* Allow attaching a backing file to an overlay that's already in use only
* if the parents don't assume that they are already seeing a valid image.
* (Specifically, allow it as a mirror target, which is write-only access.)
*/
bdrv_get_cumulative_perm(state->new_bs, &perm, &shared);
if (perm & BLK_PERM_CONSISTENT_READ) {
error_setg(errp, "The overlay is already in use");
goto out;
}
if (bdrv_op_is_blocked(state->new_bs, BLOCK_OP_TYPE_EXTERNAL_SNAPSHOT,
errp)) {
goto out;
}
if (state->new_bs->backing != NULL) {
error_setg(errp, "The overlay already has a backing image");
goto out;
@ -1606,20 +1606,6 @@ static void external_snapshot_prepare(BlkActionState *common,
goto out;
}
/* Honor bdrv_try_set_aio_context() context acquisition requirements. */
old_context = bdrv_get_aio_context(state->new_bs);
aio_context_release(aio_context);
aio_context_acquire(old_context);
ret = bdrv_try_set_aio_context(state->new_bs, aio_context, errp);
aio_context_release(old_context);
aio_context_acquire(aio_context);
if (ret < 0) {
goto out;
}
/* This removes our old bs and adds the new bs. This is an operation that
* can fail, so we need to do it in .prepare; undoing it for abort is
* always possible. */

View File

@ -363,6 +363,7 @@ bool bdrv_is_backing_chain_frozen(BlockDriverState *bs, BlockDriverState *base,
int bdrv_freeze_backing_chain(BlockDriverState *bs, BlockDriverState *base,
Error **errp);
void bdrv_unfreeze_backing_chain(BlockDriverState *bs, BlockDriverState *base);
int coroutine_fn bdrv_co_delete_file(BlockDriverState *bs, Error **errp);
typedef struct BdrvCheckResult {

View File

@ -314,6 +314,10 @@ struct BlockDriver {
*/
int coroutine_fn (*bdrv_co_flush)(BlockDriverState *bs);
/* Delete a created file. */
int coroutine_fn (*bdrv_co_delete_file)(BlockDriverState *bs,
Error **errp);
/*
* Flushes all data that was already written to the OS all the way down to
* the disk (for example file-posix.c calls fsync()).
@ -1224,6 +1228,9 @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs,
void *opaque, Error **errp);
void bdrv_root_unref_child(BdrvChild *child);
void bdrv_get_cumulative_perm(BlockDriverState *bs, uint64_t *perm,
uint64_t *shared_perm);
/**
* Sets a BdrvChild's permissions. Avoid if the parent is a BDS; use
* bdrv_child_refresh_perms() instead and make the parent's

View File

@ -1472,6 +1472,12 @@
#
# For the arguments, see the documentation of BlockdevSnapshot.
#
# Features:
# @allow-write-only-overlay: If present, the check whether this operation is safe
# was relaxed so that it can be used to change
# backing file of a destination of a blockdev-mirror.
# (since 5.0)
#
# Since: 2.5
#
# Example:
@ -1492,7 +1498,8 @@
#
##
{ 'command': 'blockdev-snapshot',
'data': 'BlockdevSnapshot' }
'data': 'BlockdevSnapshot',
'features': [ 'allow-write-only-overlay' ] }
##
# @change-backing-file:

View File

@ -247,26 +247,22 @@ void qmp_object_add(QDict *qdict, QObject **ret_data, Error **errp)
QDict *pdict;
Visitor *v;
Object *obj;
const char *type;
const char *id;
g_autofree char *type = NULL;
g_autofree char *id = NULL;
type = qdict_get_try_str(qdict, "qom-type");
type = g_strdup(qdict_get_try_str(qdict, "qom-type"));
if (!type) {
error_setg(errp, QERR_MISSING_PARAMETER, "qom-type");
return;
} else {
type = g_strdup(type);
qdict_del(qdict, "qom-type");
}
qdict_del(qdict, "qom-type");
id = qdict_get_try_str(qdict, "id");
id = g_strdup(qdict_get_try_str(qdict, "id"));
if (!id) {
error_setg(errp, QERR_MISSING_PARAMETER, "id");
return;
} else {
id = g_strdup(id);
qdict_del(qdict, "id");
}
qdict_del(qdict, "id");
props = qdict_get(qdict, "props");
if (props) {

View File

@ -589,6 +589,7 @@ include $(SRC_PATH)/tests/qtest/Makefile.include
tests/test-qga$(EXESUF): qemu-ga$(EXESUF)
tests/test-qga$(EXESUF): tests/test-qga.o $(qtest-obj-y)
tests/vhost-user-bridge$(EXESUF): tests/vhost-user-bridge.o $(test-util-obj-y) libvhost-user.a
tests/qemu-iotests/socket_scm_helper$(EXESUF): tests/qemu-iotests/socket_scm_helper.o
SPEED = quick

View File

@ -82,7 +82,7 @@ Formatting 'TEST_DIR/12-snapshot-v0.IMGFMT', fmt=IMGFMT size=134217728 backing_f
=== Invalid command - cannot create a snapshot using a file BDS ===
{ 'execute': 'blockdev-snapshot', 'arguments': { 'node':'virtio0', 'overlay':'file_12' } }
{"error": {"class": "GenericError", "desc": "The overlay does not support backing images"}}
{"error": {"class": "GenericError", "desc": "The overlay is already in use"}}
=== Invalid command - snapshot node used as active layer ===
@ -96,7 +96,7 @@ Formatting 'TEST_DIR/12-snapshot-v0.IMGFMT', fmt=IMGFMT size=134217728 backing_f
=== Invalid command - snapshot node used as backing hd ===
{ 'execute': 'blockdev-snapshot', 'arguments': { 'node': 'virtio0', 'overlay':'snap_11' } }
{"error": {"class": "GenericError", "desc": "Node 'snap_11' is busy: node is used as backing hd of 'snap_12'"}}
{"error": {"class": "GenericError", "desc": "The overlay is already in use"}}
=== Invalid command - snapshot node has a backing image ===

View File

@ -45,10 +45,18 @@ target_img = os.path.join(iotests.test_dir, 'target.' + iotests.imgfmt)
# image during runtime, only makes sense if
# target_blockdev_backing is not None
# (None: same as target_backing)
# target_open_with_backing: If True, the target image is added with its backing
# chain opened right away. If False, blockdev-add
# opens it without a backing file and job completion
# is supposed to open the backing chain.
# use_iothread: If True, an iothread is configured for the virtio-blk device
# that uses the image being mirrored
class BaseClass(iotests.QMPTestCase):
target_blockdev_backing = None
target_real_backing = None
target_open_with_backing = True
use_iothread = False
def setUp(self):
qemu_img('create', '-f', iotests.imgfmt, back0_img, '1440K')
@ -64,7 +72,16 @@ class BaseClass(iotests.QMPTestCase):
'file': {'driver': 'file',
'filename': source_img}}
self.vm.add_blockdev(self.vm.qmp_to_opts(blockdev))
self.vm.add_device('virtio-blk,id=qdev0,drive=source')
if self.use_iothread:
self.vm.add_object('iothread,id=iothread0')
iothread = ",iothread=iothread0"
else:
iothread = ""
self.vm.add_device('virtio-scsi%s' % iothread)
self.vm.add_device('scsi-hd,id=qdev0,drive=source')
self.vm.launch()
self.assertIntactSourceBackingChain()
@ -80,9 +97,13 @@ class BaseClass(iotests.QMPTestCase):
options = { 'node-name': 'target',
'driver': iotests.imgfmt,
'file': { 'driver': 'file',
'node-name': 'target-file',
'filename': target_img } }
if self.target_blockdev_backing:
options['backing'] = self.target_blockdev_backing
if not self.target_open_with_backing:
options['backing'] = None
elif self.target_blockdev_backing:
options['backing'] = self.target_blockdev_backing
result = self.vm.qmp('blockdev-add', **options)
self.assert_qmp(result, 'return', {})
@ -147,10 +168,14 @@ class BaseClass(iotests.QMPTestCase):
# cmd: Mirroring command to execute, either drive-mirror or blockdev-mirror
class MirrorBaseClass(BaseClass):
def openBacking(self):
pass
def runMirror(self, sync):
if self.cmd == 'blockdev-mirror':
result = self.vm.qmp(self.cmd, job_id='mirror-job', device='source',
sync=sync, target='target')
sync=sync, target='target',
auto_finalize=False)
else:
if self.existing:
mode = 'existing'
@ -159,33 +184,31 @@ class MirrorBaseClass(BaseClass):
result = self.vm.qmp(self.cmd, job_id='mirror-job', device='source',
sync=sync, target=target_img,
format=iotests.imgfmt, mode=mode,
node_name='target')
node_name='target', auto_finalize=False)
self.assert_qmp(result, 'return', {})
self.complete_and_wait('mirror-job')
self.vm.run_job('mirror-job', use_log=False, auto_finalize=False,
pre_finalize=self.openBacking, auto_dismiss=True)
def testFull(self):
self.runMirror('full')
node = self.findBlockNode('target',
'/machine/peripheral/qdev0/virtio-backend')
node = self.findBlockNode('target', 'qdev0')
self.assertCorrectBackingImage(node, None)
self.assertIntactSourceBackingChain()
def testTop(self):
self.runMirror('top')
node = self.findBlockNode('target',
'/machine/peripheral/qdev0/virtio-backend')
node = self.findBlockNode('target', 'qdev0')
self.assertCorrectBackingImage(node, back2_img)
self.assertIntactSourceBackingChain()
def testNone(self):
self.runMirror('none')
node = self.findBlockNode('target',
'/machine/peripheral/qdev0/virtio-backend')
node = self.findBlockNode('target', 'qdev0')
self.assertCorrectBackingImage(node, source_img)
self.assertIntactSourceBackingChain()
@ -221,6 +244,44 @@ class TestBlockdevMirrorForcedBacking(MirrorBaseClass):
target_blockdev_backing = { 'driver': 'null-co' }
target_real_backing = 'null-co://'
# Attach the backing chain only during completion, with blockdev-reopen
class TestBlockdevMirrorReopen(MirrorBaseClass):
cmd = 'blockdev-mirror'
existing = True
target_backing = 'null-co://'
target_open_with_backing = False
def openBacking(self):
if not self.target_open_with_backing:
result = self.vm.qmp('blockdev-add', node_name="backing",
driver="null-co")
self.assert_qmp(result, 'return', {})
result = self.vm.qmp('x-blockdev-reopen', node_name="target",
driver=iotests.imgfmt, file="target-file",
backing="backing")
self.assert_qmp(result, 'return', {})
class TestBlockdevMirrorReopenIothread(TestBlockdevMirrorReopen):
use_iothread = True
# Attach the backing chain only during completion, with blockdev-snapshot
class TestBlockdevMirrorSnapshot(MirrorBaseClass):
cmd = 'blockdev-mirror'
existing = True
target_backing = 'null-co://'
target_open_with_backing = False
def openBacking(self):
if not self.target_open_with_backing:
result = self.vm.qmp('blockdev-add', node_name="backing",
driver="null-co")
self.assert_qmp(result, 'return', {})
result = self.vm.qmp('blockdev-snapshot', node="backing",
overlay="target")
self.assert_qmp(result, 'return', {})
class TestBlockdevMirrorSnapshotIothread(TestBlockdevMirrorSnapshot):
use_iothread = True
class TestCommit(BaseClass):
existing = False
@ -237,8 +298,7 @@ class TestCommit(BaseClass):
self.vm.event_wait('BLOCK_JOB_COMPLETED')
node = self.findBlockNode(None,
'/machine/peripheral/qdev0/virtio-backend')
node = self.findBlockNode(None, 'qdev0')
self.assert_qmp(node, 'image' + '/backing-image' * 0 + '/filename',
back1_img)
self.assert_qmp(node, 'image' + '/backing-image' * 1 + '/filename',

View File

@ -1,5 +1,5 @@
...................
...............................
----------------------------------------------------------------------
Ran 19 tests
Ran 31 tests
OK

67
tests/qemu-iotests/282 Executable file
View File

@ -0,0 +1,67 @@
#!/usr/bin/env bash
#
# Test qemu-img file cleanup for LUKS when using a non-UTF8 secret
#
# Copyright (C) 2020, IBM Corporation.
#
# 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/>.
#
seq=`basename $0`
echo "QA output created by $seq"
status=1 # failure is the default!
TEST_IMAGE_FILE='vol.img'
_cleanup()
{
_cleanup_test_img
rm non_utf8_secret
rm -f $TEST_IMAGE_FILE
}
trap "_cleanup; exit \$status" 0 1 2 3 15
# get standard environment, filters and checks
. ./common.rc
. ./common.filter
_supported_fmt luks
_supported_proto generic
_unsupported_proto vxhs
echo "== Create non-UTF8 secret =="
echo -n -e '\x3a\x3c\x3b\xff' > non_utf8_secret
SECRET="secret,id=sec0,file=non_utf8_secret"
echo "== Throws an error because of invalid UTF-8 secret =="
$QEMU_IMG create -f $IMGFMT --object $SECRET -o "key-secret=sec0" $TEST_IMAGE_FILE 4M
echo "== Image file should not exist after the error =="
if test -f "$TEST_IMAGE_FILE"; then
exit 1
fi
echo "== Create a stub image file and run qemu-img again =="
touch $TEST_IMAGE_FILE
$QEMU_IMG create -f $IMGFMT --object $SECRET -o "key-secret=sec0" $TEST_IMAGE_FILE 4M
echo "== Pre-existing image file should also be deleted after the error =="
if test -f "$TEST_IMAGE_FILE"; then
exit 1
fi
# success, all done
echo "*** done"
rm -f $seq.full
status=0

View File

@ -0,0 +1,11 @@
QA output created by 282
== Create non-UTF8 secret ==
== Throws an error because of invalid UTF-8 secret ==
qemu-img: vol.img: Data from secret sec0 is not valid UTF-8
Formatting 'vol.img', fmt=luks size=4194304 key-secret=sec0
== Image file should not exist after the error ==
== Create a stub image file and run qemu-img again ==
qemu-img: vol.img: Data from secret sec0 is not valid UTF-8
Formatting 'vol.img', fmt=luks size=4194304 key-secret=sec0
== Pre-existing image file should also be deleted after the error ==
*** done

View File

@ -290,6 +290,7 @@
279 rw backing quick
280 rw migration quick
281 rw quick
282 rw img quick
283 auto quick
284 rw
286 rw quick

View File

@ -624,7 +624,10 @@ class VM(qtest.QEMUQtestMachine):
if use_log:
log('Job failed: %s' % (j['error']))
elif status == 'ready':
self.qmp_log('job-complete', id=job)
if use_log:
self.qmp_log('job-complete', id=job)
else:
self.qmp('job-complete', id=job)
elif status == 'pending' and not auto_finalize:
if pre_finalize:
pre_finalize()

View File

@ -288,7 +288,6 @@ tests/qtest/usb-hcd-ehci-test$(EXESUF): tests/qtest/usb-hcd-ehci-test.o $(libqos
tests/qtest/usb-hcd-xhci-test$(EXESUF): tests/qtest/usb-hcd-xhci-test.o $(libqos-usb-obj-y)
tests/qtest/cpu-plug-test$(EXESUF): tests/qtest/cpu-plug-test.o
tests/qtest/migration-test$(EXESUF): tests/qtest/migration-test.o tests/qtest/migration-helpers.o
tests/qtest/qemu-iotests/qtest/socket_scm_helper$(EXESUF): tests/qtest/qemu-iotests/qtest/socket_scm_helper.o
tests/qtest/test-netfilter$(EXESUF): tests/qtest/test-netfilter.o $(qtest-obj-y)
tests/qtest/test-filter-mirror$(EXESUF): tests/qtest/test-filter-mirror.o $(qtest-obj-y)
tests/qtest/test-filter-redirector$(EXESUF): tests/qtest/test-filter-redirector.o $(qtest-obj-y)