Block patches for 2.11.0-rc1
-----BEGIN PGP SIGNATURE----- iQEcBAABAgAGBQJaCyZJAAoJEPQH2wBh1c9A9MgH/1NIx+nUKQwYnwvLADmbA/iB 2EtFTLf4yq3B0tK0DTQxmCltB2ZkmA3wyA7Njp0FbWQ1fpOmyfjr1ceQZYV1hLMm TcpkCgbPffaBcfhosulwFfM4i8pG6zrm9jp/FUl3Qy1Ja2iwW+wPs/oh7P41516l VlwiL3BlqZ/1c6uUpavGpAahsN1SU+i1JZZ4fM+xOXjtdqq9rMuvje6RDwXLkoCh 7FLky2Kw1M6InoGh5Er/J9qrQtb/WoaXAtzTRFtzGmWEAZ/ow9/OLYn0BLXp1ct6 BScQbiElrVfpKdyZ1IC1SwjdIV5PtyMeJGSZdcvbSpFspcFCgqyAA0y+HixexYc= =J5Qp -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/maxreitz/tags/pull-block-2017-11-14' into staging Block patches for 2.11.0-rc1 # gpg: Signature made Tue 14 Nov 2017 17:22:17 GMT # gpg: using RSA key 0xF407DB0061D5CF40 # gpg: Good signature from "Max Reitz <mreitz@redhat.com>" # Primary key fingerprint: 91BE B60A 30DB 3E88 57D1 1829 F407 DB00 61D5 CF40 * remotes/maxreitz/tags/pull-block-2017-11-14: qemu-iotests: update unsupported image formats in 194 block/parallels: add migration blocker block/parallels: Do not update header or truncate image when INMIGRATE block/vhdx.c: Don't blindly update the header iotests: 077: Filter out 'resume' lines block/snapshot: dirty all dirty bitmaps on snapshot-switch qcow2: Check that corrupted images can be repaired in iotest 060 iotests: Use new-style NBD connections iotests: Make 136 less flaky iotests: Make 083 less flaky iotests: Make 055 less flaky iotests: Add missing 'blkdebug::' in 040 iotests: Make 030 less flaky qcow2: Assert that the crypto header does not overlap other metadata qcow2: Add iotest for an empty refcount table qcow2: Add iotest for an image with header.refcount_table_offset == 0 qcow2: Don't open images with header.refcount_table_clusters == 0 qcow2: Prevent allocating compressed clusters at offset 0 qcow2: Prevent allocating L2 tables at offset 0 qcow2: Prevent allocating refcount blocks at offset 0 Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
03d1cbe320
|
@ -35,6 +35,7 @@
|
|||
#include "qemu/module.h"
|
||||
#include "qemu/bswap.h"
|
||||
#include "qemu/bitmap.h"
|
||||
#include "migration/blocker.h"
|
||||
|
||||
/**************************************************************/
|
||||
|
||||
|
@ -100,6 +101,7 @@ typedef struct BDRVParallelsState {
|
|||
unsigned int tracks;
|
||||
|
||||
unsigned int off_multiplier;
|
||||
Error *migration_blocker;
|
||||
} BDRVParallelsState;
|
||||
|
||||
|
||||
|
@ -708,7 +710,7 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags,
|
|||
s->prealloc_mode = PRL_PREALLOC_MODE_FALLOCATE;
|
||||
}
|
||||
|
||||
if (flags & BDRV_O_RDWR) {
|
||||
if ((flags & BDRV_O_RDWR) && !(flags & BDRV_O_INACTIVE)) {
|
||||
s->header->inuse = cpu_to_le32(HEADER_INUSE_MAGIC);
|
||||
ret = parallels_update_header(bs);
|
||||
if (ret < 0) {
|
||||
|
@ -720,6 +722,16 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags,
|
|||
s->bat_dirty_bmap =
|
||||
bitmap_new(DIV_ROUND_UP(s->header_size, s->bat_dirty_block));
|
||||
|
||||
/* Disable migration until bdrv_invalidate_cache method is added */
|
||||
error_setg(&s->migration_blocker, "The Parallels format used by node '%s' "
|
||||
"does not support live migration",
|
||||
bdrv_get_device_or_node_name(bs));
|
||||
ret = migrate_add_blocker(s->migration_blocker, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
error_free(s->migration_blocker);
|
||||
goto fail;
|
||||
}
|
||||
qemu_co_mutex_init(&s->lock);
|
||||
return 0;
|
||||
|
||||
|
@ -741,18 +753,18 @@ static void parallels_close(BlockDriverState *bs)
|
|||
{
|
||||
BDRVParallelsState *s = bs->opaque;
|
||||
|
||||
if (bs->open_flags & BDRV_O_RDWR) {
|
||||
if ((bs->open_flags & BDRV_O_RDWR) && !(bs->open_flags & BDRV_O_INACTIVE)) {
|
||||
s->header->inuse = 0;
|
||||
parallels_update_header(bs);
|
||||
}
|
||||
|
||||
if (bs->open_flags & BDRV_O_RDWR) {
|
||||
bdrv_truncate(bs->file, s->data_end << BDRV_SECTOR_BITS,
|
||||
PREALLOC_MODE_OFF, NULL);
|
||||
}
|
||||
|
||||
g_free(s->bat_dirty_bmap);
|
||||
qemu_vfree(s->header);
|
||||
|
||||
migrate_del_blocker(s->migration_blocker);
|
||||
error_free(s->migration_blocker);
|
||||
}
|
||||
|
||||
static QemuOptsList parallels_create_opts = {
|
||||
|
|
|
@ -278,6 +278,14 @@ static int l2_allocate(BlockDriverState *bs, int l1_index, uint64_t **table)
|
|||
goto fail;
|
||||
}
|
||||
|
||||
/* If we're allocating the table at offset 0 then something is wrong */
|
||||
if (l2_offset == 0) {
|
||||
qcow2_signal_corruption(bs, true, -1, -1, "Preventing invalid "
|
||||
"allocation of L2 table at offset 0");
|
||||
ret = -EIO;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = qcow2_cache_flush(bs, s->refcount_block_cache);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
|
|
|
@ -367,6 +367,13 @@ static int alloc_refcount_block(BlockDriverState *bs,
|
|||
return new_block;
|
||||
}
|
||||
|
||||
/* If we're allocating the block at offset 0 then something is wrong */
|
||||
if (new_block == 0) {
|
||||
qcow2_signal_corruption(bs, true, -1, -1, "Preventing invalid "
|
||||
"allocation of refcount block at offset 0");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_ALLOC2
|
||||
fprintf(stderr, "qcow2: Allocate refcount block %d for %" PRIx64
|
||||
" at %" PRIx64 "\n",
|
||||
|
@ -1075,6 +1082,13 @@ int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size)
|
|||
return new_cluster;
|
||||
}
|
||||
|
||||
if (new_cluster == 0) {
|
||||
qcow2_signal_corruption(bs, true, -1, -1, "Preventing invalid "
|
||||
"allocation of compressed cluster "
|
||||
"at offset 0");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (!offset || ROUND_UP(offset, s->cluster_size) != new_cluster) {
|
||||
offset = new_cluster;
|
||||
free_in_cluster = s->cluster_size;
|
||||
|
|
|
@ -126,6 +126,7 @@ static ssize_t qcow2_crypto_hdr_init_func(QCryptoBlock *block, size_t headerlen,
|
|||
/* Zero fill remaining space in cluster so it has predictable
|
||||
* content in case of future spec changes */
|
||||
clusterlen = size_to_clusters(s, headerlen) * s->cluster_size;
|
||||
assert(qcow2_pre_write_overlap_check(bs, 0, ret, clusterlen) == 0);
|
||||
ret = bdrv_pwrite_zeroes(bs->file,
|
||||
ret + headerlen,
|
||||
clusterlen - headerlen, 0);
|
||||
|
@ -1280,6 +1281,12 @@ static int qcow2_do_open(BlockDriverState *bs, QDict *options, int flags,
|
|||
goto fail;
|
||||
}
|
||||
|
||||
if (header.refcount_table_clusters == 0 && !(flags & BDRV_O_CHECK)) {
|
||||
error_setg(errp, "Image does not contain a reference count table");
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = validate_table_offset(bs, s->refcount_table_offset,
|
||||
s->refcount_table_size, sizeof(uint64_t));
|
||||
if (ret < 0) {
|
||||
|
|
|
@ -181,10 +181,24 @@ int bdrv_snapshot_goto(BlockDriverState *bs,
|
|||
{
|
||||
BlockDriver *drv = bs->drv;
|
||||
int ret, open_ret;
|
||||
int64_t len;
|
||||
|
||||
if (!drv) {
|
||||
return -ENOMEDIUM;
|
||||
}
|
||||
|
||||
len = bdrv_getlength(bs);
|
||||
if (len < 0) {
|
||||
return len;
|
||||
}
|
||||
/* We should set all bits in all enabled dirty bitmaps, because dirty
|
||||
* bitmaps reflect active state of disk and snapshot switch operation
|
||||
* actually dirties active state.
|
||||
* TODO: It may make sense not to set all bits but analyze block status of
|
||||
* current state and destination snapshot and do not set bits corresponding
|
||||
* to both-zero or both-unallocated areas. */
|
||||
bdrv_set_dirty(bs, 0, len);
|
||||
|
||||
if (drv->bdrv_snapshot_goto) {
|
||||
return drv->bdrv_snapshot_goto(bs, snapshot_id);
|
||||
}
|
||||
|
|
|
@ -1008,13 +1008,6 @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags,
|
|||
goto fail;
|
||||
}
|
||||
|
||||
if (flags & BDRV_O_RDWR) {
|
||||
ret = vhdx_update_headers(bs, s, false, NULL);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO: differencing files */
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -666,6 +666,7 @@ class TestENOSPC(TestErrors):
|
|||
if event['event'] == 'BLOCK_JOB_ERROR':
|
||||
self.assert_qmp(event, 'data/device', 'drive0')
|
||||
self.assert_qmp(event, 'data/operation', 'read')
|
||||
error = True
|
||||
|
||||
result = self.vm.qmp('query-block-jobs')
|
||||
self.assert_qmp(result, 'return[0]/paused', True)
|
||||
|
@ -676,9 +677,11 @@ class TestENOSPC(TestErrors):
|
|||
self.assert_qmp(result, 'return', {})
|
||||
|
||||
result = self.vm.qmp('query-block-jobs')
|
||||
if result == {'return': []}:
|
||||
# Race; likely already finished. Check.
|
||||
continue
|
||||
self.assert_qmp(result, 'return[0]/paused', False)
|
||||
self.assert_qmp(result, 'return[0]/io-status', 'ok')
|
||||
error = True
|
||||
elif event['event'] == 'BLOCK_JOB_COMPLETED':
|
||||
self.assertTrue(error, 'job completed unexpectedly')
|
||||
self.assert_qmp(event, 'data/type', 'stream')
|
||||
|
@ -792,13 +795,14 @@ class TestSetSpeed(iotests.QMPTestCase):
|
|||
|
||||
self.assert_no_active_block_jobs()
|
||||
|
||||
self.vm.pause_drive('drive0')
|
||||
result = self.vm.qmp('block-stream', device='drive0')
|
||||
self.assert_qmp(result, 'return', {})
|
||||
|
||||
result = self.vm.qmp('block-job-set-speed', device='drive0', speed=-1)
|
||||
self.assert_qmp(result, 'error/class', 'GenericError')
|
||||
|
||||
self.cancel_and_wait()
|
||||
self.cancel_and_wait(resume=True)
|
||||
|
||||
if __name__ == '__main__':
|
||||
iotests.main(supported_fmts=['qcow2', 'qed'])
|
||||
|
|
|
@ -289,7 +289,7 @@ class TestSetSpeed(ImageCommitTestCase):
|
|||
qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % mid_img, test_img)
|
||||
qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0x1 0 512', test_img)
|
||||
qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0xef 524288 524288', mid_img)
|
||||
self.vm = iotests.VM().add_drive(test_img)
|
||||
self.vm = iotests.VM().add_drive('blkdebug::' + test_img)
|
||||
self.vm.launch()
|
||||
|
||||
def tearDown(self):
|
||||
|
|
|
@ -48,7 +48,7 @@ class TestSingleDrive(iotests.QMPTestCase):
|
|||
def setUp(self):
|
||||
qemu_img('create', '-f', iotests.imgfmt, blockdev_target_img, str(image_len))
|
||||
|
||||
self.vm = iotests.VM().add_drive(test_img)
|
||||
self.vm = iotests.VM().add_drive('blkdebug::' + test_img)
|
||||
self.vm.add_drive(blockdev_target_img, interface="none")
|
||||
if iotests.qemu_default_machine == 'pc':
|
||||
self.vm.add_drive(None, 'media=cdrom', 'ide')
|
||||
|
@ -65,10 +65,11 @@ class TestSingleDrive(iotests.QMPTestCase):
|
|||
def do_test_cancel(self, cmd, target):
|
||||
self.assert_no_active_block_jobs()
|
||||
|
||||
self.vm.pause_drive('drive0')
|
||||
result = self.vm.qmp(cmd, device='drive0', target=target, sync='full')
|
||||
self.assert_qmp(result, 'return', {})
|
||||
|
||||
event = self.cancel_and_wait()
|
||||
event = self.cancel_and_wait(resume=True)
|
||||
self.assert_qmp(event, 'data/type', 'backup')
|
||||
|
||||
def test_cancel_drive_backup(self):
|
||||
|
@ -166,7 +167,7 @@ class TestSetSpeed(iotests.QMPTestCase):
|
|||
def setUp(self):
|
||||
qemu_img('create', '-f', iotests.imgfmt, blockdev_target_img, str(image_len))
|
||||
|
||||
self.vm = iotests.VM().add_drive(test_img)
|
||||
self.vm = iotests.VM().add_drive('blkdebug::' + test_img)
|
||||
self.vm.add_drive(blockdev_target_img, interface="none")
|
||||
self.vm.launch()
|
||||
|
||||
|
@ -246,6 +247,8 @@ class TestSetSpeed(iotests.QMPTestCase):
|
|||
def test_set_speed_invalid_blockdev_backup(self):
|
||||
self.do_test_set_speed_invalid('blockdev-backup', 'drive1')
|
||||
|
||||
# Note: We cannot use pause_drive() here, or the transaction command
|
||||
# would stall. Instead, we limit the block job speed here.
|
||||
class TestSingleTransaction(iotests.QMPTestCase):
|
||||
def setUp(self):
|
||||
qemu_img('create', '-f', iotests.imgfmt, blockdev_target_img, str(image_len))
|
||||
|
@ -271,7 +274,8 @@ class TestSingleTransaction(iotests.QMPTestCase):
|
|||
'type': cmd,
|
||||
'data': { 'device': 'drive0',
|
||||
'target': target,
|
||||
'sync': 'full' },
|
||||
'sync': 'full',
|
||||
'speed': 64 * 1024 },
|
||||
}
|
||||
])
|
||||
|
||||
|
@ -289,12 +293,12 @@ class TestSingleTransaction(iotests.QMPTestCase):
|
|||
def do_test_pause(self, cmd, target, image):
|
||||
self.assert_no_active_block_jobs()
|
||||
|
||||
self.vm.pause_drive('drive0')
|
||||
result = self.vm.qmp('transaction', actions=[{
|
||||
'type': cmd,
|
||||
'data': { 'device': 'drive0',
|
||||
'target': target,
|
||||
'sync': 'full' },
|
||||
'sync': 'full',
|
||||
'speed': 64 * 1024 },
|
||||
}
|
||||
])
|
||||
self.assert_qmp(result, 'return', {})
|
||||
|
@ -302,7 +306,9 @@ class TestSingleTransaction(iotests.QMPTestCase):
|
|||
result = self.vm.qmp('block-job-pause', device='drive0')
|
||||
self.assert_qmp(result, 'return', {})
|
||||
|
||||
self.vm.resume_drive('drive0')
|
||||
result = self.vm.qmp('block-job-set-speed', device='drive0', speed=0)
|
||||
self.assert_qmp(result, 'return', {})
|
||||
|
||||
self.pause_job('drive0')
|
||||
|
||||
result = self.vm.qmp('query-block-jobs')
|
||||
|
@ -461,7 +467,7 @@ class TestDriveCompression(iotests.QMPTestCase):
|
|||
pass
|
||||
|
||||
def do_prepare_drives(self, fmt, args, attach_target):
|
||||
self.vm = iotests.VM().add_drive(test_img)
|
||||
self.vm = iotests.VM().add_drive('blkdebug::' + test_img)
|
||||
|
||||
qemu_img('create', '-f', fmt, blockdev_target_img,
|
||||
str(TestDriveCompression.image_len), *args)
|
||||
|
@ -500,10 +506,11 @@ class TestDriveCompression(iotests.QMPTestCase):
|
|||
|
||||
self.assert_no_active_block_jobs()
|
||||
|
||||
self.vm.pause_drive('drive0')
|
||||
result = self.vm.qmp(cmd, device='drive0', sync='full', compress=True, **args)
|
||||
self.assert_qmp(result, 'return', {})
|
||||
|
||||
event = self.cancel_and_wait()
|
||||
event = self.cancel_and_wait(resume=True)
|
||||
self.assert_qmp(event, 'data/type', 'backup')
|
||||
|
||||
self.vm.shutdown()
|
||||
|
|
|
@ -242,6 +242,65 @@ poke_file "$TEST_IMG" "$(($l2_offset+8))" "\x80\x00\x00\x00\x00\x06\x2a\x00"
|
|||
# Should emit two error messages
|
||||
$QEMU_IO -c "discard 0 64k" -c "read 64k 64k" "$TEST_IMG" | _filter_qemu_io
|
||||
|
||||
echo
|
||||
echo "=== Testing empty refcount table ==="
|
||||
echo
|
||||
_make_test_img 64M
|
||||
poke_file "$TEST_IMG" "$rt_offset" "\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
$QEMU_IO -c "write 0 64k" "$TEST_IMG" | _filter_qemu_io
|
||||
# Repair the image
|
||||
_check_test_img -r all
|
||||
|
||||
echo
|
||||
echo "=== Testing empty refcount table with valid L1 and L2 tables ==="
|
||||
echo
|
||||
_make_test_img 64M
|
||||
$QEMU_IO -c "write 0 64k" "$TEST_IMG" | _filter_qemu_io
|
||||
poke_file "$TEST_IMG" "$rt_offset" "\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
# Since the first data cluster is already allocated this triggers an
|
||||
# allocation with an explicit offset (using qcow2_alloc_clusters_at())
|
||||
# causing a refcount block to be allocated at offset 0
|
||||
$QEMU_IO -c "write 0 128k" "$TEST_IMG" | _filter_qemu_io
|
||||
# Repair the image
|
||||
_check_test_img -r all
|
||||
|
||||
echo
|
||||
echo "=== Testing empty refcount block ==="
|
||||
echo
|
||||
_make_test_img 64M
|
||||
poke_file "$TEST_IMG" "$rb_offset" "\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
$QEMU_IO -c "write 0 64k" "$TEST_IMG" | _filter_qemu_io
|
||||
# Repair the image
|
||||
_check_test_img -r all
|
||||
|
||||
echo
|
||||
echo "=== Testing empty refcount block with compressed write ==="
|
||||
echo
|
||||
_make_test_img 64M
|
||||
$QEMU_IO -c "write 64k 64k" "$TEST_IMG" | _filter_qemu_io
|
||||
poke_file "$TEST_IMG" "$rb_offset" "\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
# The previous write already allocated an L2 table, so now this new
|
||||
# write will try to allocate a compressed data cluster at offset 0.
|
||||
$QEMU_IO -c "write -c 0k 64k" "$TEST_IMG" | _filter_qemu_io
|
||||
# Repair the image
|
||||
_check_test_img -r all
|
||||
|
||||
echo
|
||||
echo "=== Testing zero refcount table size ==="
|
||||
echo
|
||||
_make_test_img 64M
|
||||
poke_file "$TEST_IMG" "56" "\x00\x00\x00\x00"
|
||||
$QEMU_IO -c "write 0 64k" "$TEST_IMG" 2>&1 | _filter_testdir | _filter_imgfmt
|
||||
# Repair the image
|
||||
_check_test_img -r all
|
||||
|
||||
echo
|
||||
echo "=== Testing incorrect refcount table offset ==="
|
||||
echo
|
||||
_make_test_img 64M
|
||||
poke_file "$TEST_IMG" "48" "\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
$QEMU_IO -c "write 0 64k" "$TEST_IMG" | _filter_qemu_io
|
||||
|
||||
# success, all done
|
||||
echo "*** done"
|
||||
rm -f $seq.full
|
||||
|
|
|
@ -181,4 +181,107 @@ qcow2: Marking image as corrupt: Cluster allocation offset 0x62a00 unaligned (L2
|
|||
discard 65536/65536 bytes at offset 0
|
||||
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
read failed: Input/output error
|
||||
|
||||
=== Testing empty refcount table ===
|
||||
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
|
||||
qcow2: Marking image as corrupt: Preventing invalid write on metadata (overlaps with refcount table); further corruption events will be suppressed
|
||||
write failed: Input/output error
|
||||
ERROR cluster 0 refcount=0 reference=1
|
||||
ERROR cluster 1 refcount=0 reference=1
|
||||
ERROR cluster 3 refcount=0 reference=1
|
||||
Rebuilding refcount structure
|
||||
Repairing cluster 1 refcount=1 reference=0
|
||||
The following inconsistencies were found and repaired:
|
||||
|
||||
0 leaked clusters
|
||||
3 corruptions
|
||||
|
||||
Double checking the fixed image now...
|
||||
No errors were found on the image.
|
||||
|
||||
=== Testing empty refcount table with valid L1 and L2 tables ===
|
||||
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
|
||||
wrote 65536/65536 bytes at offset 0
|
||||
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qcow2: Marking image as corrupt: Preventing invalid allocation of refcount block at offset 0; further corruption events will be suppressed
|
||||
write failed: Input/output error
|
||||
ERROR cluster 0 refcount=0 reference=1
|
||||
ERROR cluster 1 refcount=0 reference=1
|
||||
ERROR cluster 3 refcount=0 reference=1
|
||||
ERROR cluster 4 refcount=0 reference=1
|
||||
ERROR cluster 5 refcount=0 reference=1
|
||||
Rebuilding refcount structure
|
||||
Repairing cluster 1 refcount=1 reference=0
|
||||
The following inconsistencies were found and repaired:
|
||||
|
||||
0 leaked clusters
|
||||
5 corruptions
|
||||
|
||||
Double checking the fixed image now...
|
||||
No errors were found on the image.
|
||||
|
||||
=== Testing empty refcount block ===
|
||||
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
|
||||
qcow2: Marking image as corrupt: Preventing invalid allocation of L2 table at offset 0; further corruption events will be suppressed
|
||||
write failed: Input/output error
|
||||
ERROR cluster 0 refcount=0 reference=1
|
||||
ERROR cluster 1 refcount=0 reference=1
|
||||
ERROR cluster 2 refcount=0 reference=1
|
||||
ERROR cluster 3 refcount=0 reference=1
|
||||
Rebuilding refcount structure
|
||||
Repairing cluster 1 refcount=1 reference=0
|
||||
Repairing cluster 2 refcount=1 reference=0
|
||||
The following inconsistencies were found and repaired:
|
||||
|
||||
0 leaked clusters
|
||||
4 corruptions
|
||||
|
||||
Double checking the fixed image now...
|
||||
No errors were found on the image.
|
||||
|
||||
=== Testing empty refcount block with compressed write ===
|
||||
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
|
||||
wrote 65536/65536 bytes at offset 65536
|
||||
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
qcow2: Marking image as corrupt: Preventing invalid allocation of compressed cluster at offset 0; further corruption events will be suppressed
|
||||
write failed: Input/output error
|
||||
ERROR cluster 0 refcount=0 reference=1
|
||||
ERROR cluster 1 refcount=0 reference=1
|
||||
ERROR cluster 2 refcount=0 reference=1
|
||||
ERROR cluster 3 refcount=0 reference=1
|
||||
Rebuilding refcount structure
|
||||
Repairing cluster 1 refcount=1 reference=0
|
||||
Repairing cluster 2 refcount=1 reference=0
|
||||
The following inconsistencies were found and repaired:
|
||||
|
||||
0 leaked clusters
|
||||
4 corruptions
|
||||
|
||||
Double checking the fixed image now...
|
||||
No errors were found on the image.
|
||||
|
||||
=== Testing zero refcount table size ===
|
||||
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
|
||||
can't open device TEST_DIR/t.IMGFMT: Image does not contain a reference count table
|
||||
ERROR cluster 0 refcount=0 reference=1
|
||||
ERROR cluster 3 refcount=0 reference=1
|
||||
Rebuilding refcount structure
|
||||
The following inconsistencies were found and repaired:
|
||||
|
||||
0 leaked clusters
|
||||
2 corruptions
|
||||
|
||||
Double checking the fixed image now...
|
||||
No errors were found on the image.
|
||||
|
||||
=== Testing incorrect refcount table offset ===
|
||||
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
|
||||
qcow2: Marking image as corrupt: Preventing invalid allocation of L2 table at offset 0; further corruption events will be suppressed
|
||||
write failed: Input/output error
|
||||
*** done
|
||||
|
|
|
@ -188,7 +188,8 @@ EOF
|
|||
test_io | $QEMU_IO | _filter_qemu_io | \
|
||||
sed -e 's,[0-9/]* bytes at offset [0-9]*,XXX/XXX bytes at offset XXX,g' \
|
||||
-e 's/^[0-9]* \(bytes\|KiB\)/XXX bytes/' \
|
||||
-e '/Suspended/d'
|
||||
-e '/Suspended/d' \
|
||||
-e '/blkdebug: Resuming request/d'
|
||||
|
||||
echo
|
||||
echo "== Verify image content =="
|
||||
|
|
|
@ -4,17 +4,6 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
|
|||
== Some concurrent requests involving RMW ==
|
||||
wrote XXX/XXX bytes at offset XXX
|
||||
XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
blkdebug: Resuming request 'A'
|
||||
wrote XXX/XXX bytes at offset XXX
|
||||
XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote XXX/XXX bytes at offset XXX
|
||||
XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
blkdebug: Resuming request 'A'
|
||||
wrote XXX/XXX bytes at offset XXX
|
||||
XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote XXX/XXX bytes at offset XXX
|
||||
XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
blkdebug: Resuming request 'A'
|
||||
wrote XXX/XXX bytes at offset XXX
|
||||
XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote XXX/XXX bytes at offset XXX
|
||||
|
@ -31,51 +20,46 @@ wrote XXX/XXX bytes at offset XXX
|
|||
XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote XXX/XXX bytes at offset XXX
|
||||
XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
blkdebug: Resuming request 'A'
|
||||
wrote XXX/XXX bytes at offset XXX
|
||||
XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
blkdebug: Resuming request 'B'
|
||||
wrote XXX/XXX bytes at offset XXX
|
||||
XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote XXX/XXX bytes at offset XXX
|
||||
XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
blkdebug: Resuming request 'B'
|
||||
wrote XXX/XXX bytes at offset XXX
|
||||
XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
blkdebug: Resuming request 'A'
|
||||
wrote XXX/XXX bytes at offset XXX
|
||||
XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote XXX/XXX bytes at offset XXX
|
||||
XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
blkdebug: Resuming request 'A'
|
||||
wrote XXX/XXX bytes at offset XXX
|
||||
XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
blkdebug: Resuming request 'B'
|
||||
wrote XXX/XXX bytes at offset XXX
|
||||
XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote XXX/XXX bytes at offset XXX
|
||||
XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
blkdebug: Resuming request 'B'
|
||||
wrote XXX/XXX bytes at offset XXX
|
||||
XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
blkdebug: Resuming request 'A'
|
||||
wrote XXX/XXX bytes at offset XXX
|
||||
XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote XXX/XXX bytes at offset XXX
|
||||
XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
blkdebug: Resuming request 'A'
|
||||
wrote XXX/XXX bytes at offset XXX
|
||||
XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote XXX/XXX bytes at offset XXX
|
||||
XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
blkdebug: Resuming request 'A'
|
||||
blkdebug: Resuming request 'C'
|
||||
wrote XXX/XXX bytes at offset XXX
|
||||
XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
blkdebug: Resuming request 'B'
|
||||
wrote XXX/XXX bytes at offset XXX
|
||||
XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
blkdebug: Resuming request 'A'
|
||||
wrote XXX/XXX bytes at offset XXX
|
||||
XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote XXX/XXX bytes at offset XXX
|
||||
XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote XXX/XXX bytes at offset XXX
|
||||
XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote XXX/XXX bytes at offset XXX
|
||||
XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote XXX/XXX bytes at offset XXX
|
||||
XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote XXX/XXX bytes at offset XXX
|
||||
XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote XXX/XXX bytes at offset XXX
|
||||
XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote XXX/XXX bytes at offset XXX
|
||||
XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote XXX/XXX bytes at offset XXX
|
||||
XXX bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
wrote XXX/XXX bytes at offset XXX
|
||||
|
|
|
@ -86,6 +86,7 @@ EOF
|
|||
|
||||
rm -f "$TEST_DIR/nbd.sock"
|
||||
|
||||
echo > "$TEST_DIR/nbd-fault-injector.out"
|
||||
$PYTHON nbd-fault-injector.py $extra_args "$nbd_addr" "$TEST_DIR/nbd-fault-injector.conf" >"$TEST_DIR/nbd-fault-injector.out" 2>&1 &
|
||||
|
||||
# Wait for server to be ready
|
||||
|
@ -94,7 +95,8 @@ EOF
|
|||
done
|
||||
|
||||
# Extract the final address (port number has now been assigned in tcp case)
|
||||
nbd_addr=$(sed 's/Listening on \(.*\)$/\1/' "$TEST_DIR/nbd-fault-injector.out")
|
||||
nbd_addr=$(sed -n 's/^Listening on //p' \
|
||||
"$TEST_DIR/nbd-fault-injector.out")
|
||||
|
||||
if [ "$proto" = "tcp" ]; then
|
||||
nbd_url="nbd+tcp://$nbd_addr/$export_name"
|
||||
|
|
|
@ -238,6 +238,18 @@ sector = "%d"
|
|||
for i in range(failed_wr_ops):
|
||||
ops.append("aio_write %d 512" % bad_offset)
|
||||
|
||||
# We need an extra aio_flush to settle all outstanding AIO
|
||||
# operations before we can advance the virtual clock, so that
|
||||
# the last access happens before clock_step and idle_time_ns
|
||||
# will be greater than 0
|
||||
extra_flush = 0
|
||||
if rd_ops + wr_ops + invalid_rd_ops + invalid_wr_ops + \
|
||||
failed_rd_ops + failed_wr_ops > 0:
|
||||
extra_flush = 1
|
||||
|
||||
if extra_flush > 0:
|
||||
ops.append("aio_flush")
|
||||
|
||||
if failed_wr_ops > 0:
|
||||
highest_offset = max(highest_offset, bad_offset + 512)
|
||||
|
||||
|
@ -251,7 +263,7 @@ sector = "%d"
|
|||
self.total_wr_bytes += wr_ops * wr_size
|
||||
self.total_wr_ops += wr_ops
|
||||
self.total_wr_merged += wr_merged
|
||||
self.total_flush_ops += flush_ops
|
||||
self.total_flush_ops += flush_ops + extra_flush
|
||||
self.invalid_rd_ops += invalid_rd_ops
|
||||
self.invalid_wr_ops += invalid_wr_ops
|
||||
self.failed_rd_ops += failed_rd_ops
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
|
||||
import iotests
|
||||
|
||||
iotests.verify_image_format(unsupported_fmts=['luks'])
|
||||
iotests.verify_image_format(supported_fmts=['qcow2', 'qed', 'raw', 'dmg'])
|
||||
iotests.verify_platform(['linux'])
|
||||
|
||||
with iotests.FilePath('source.img') as source_img_path, \
|
||||
|
|
|
@ -242,7 +242,7 @@ _make_test_img()
|
|||
if [ $IMGPROTO = "nbd" ]; then
|
||||
# Pass a sufficiently high number to -e that should be enough for all
|
||||
# tests
|
||||
eval "$QEMU_NBD -v -t -b 127.0.0.1 -p 10810 -f $IMGFMT -e 42 $TEST_IMG_FILE >/dev/null &"
|
||||
eval "$QEMU_NBD -v -t -b 127.0.0.1 -p 10810 -f $IMGFMT -e 42 -x '' $TEST_IMG_FILE >/dev/null &"
|
||||
sleep 1 # FIXME: qemu-nbd needs to be listening before we continue
|
||||
fi
|
||||
|
||||
|
|
Loading…
Reference in a new issue