diff options
Diffstat (limited to 'tests')
-rw-r--r-- | tests/.gitignore | 1 | ||||
-rw-r--r-- | tests/Makefile.include | 4 | ||||
-rw-r--r-- | tests/check-qnull.c | 2 | ||||
-rw-r--r-- | tests/check-qobject.c | 328 | ||||
-rwxr-xr-x | tests/qemu-iotests/020 | 27 | ||||
-rw-r--r-- | tests/qemu-iotests/020.out | 17 | ||||
-rwxr-xr-x | tests/qemu-iotests/060 | 125 | ||||
-rw-r--r-- | tests/qemu-iotests/060.out | 115 | ||||
-rwxr-xr-x | tests/qemu-iotests/087 | 9 | ||||
-rwxr-xr-x | tests/qemu-iotests/133 | 9 | ||||
-rw-r--r-- | tests/qemu-iotests/133.out | 5 | ||||
-rwxr-xr-x | tests/qemu-iotests/176 | 55 | ||||
-rw-r--r-- | tests/qemu-iotests/176.out | 216 | ||||
-rwxr-xr-x | tests/qemu-iotests/182 | 2 | ||||
-rwxr-xr-x | tests/qemu-iotests/196 | 66 | ||||
-rw-r--r-- | tests/qemu-iotests/196.out | 5 | ||||
-rwxr-xr-x | tests/qemu-iotests/198 | 104 | ||||
-rw-r--r-- | tests/qemu-iotests/198.out | 126 | ||||
-rw-r--r-- | tests/qemu-iotests/common.filter | 4 | ||||
-rw-r--r-- | tests/qemu-iotests/group | 2 |
20 files changed, 1206 insertions, 16 deletions
diff --git a/tests/.gitignore b/tests/.gitignore index 53cb2efaee..74e55d7264 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -8,6 +8,7 @@ check-qjson check-qlist check-qlit check-qnull +check-qobject check-qstring check-qom-interface check-qom-proplist diff --git a/tests/Makefile.include b/tests/Makefile.include index 434a2ce868..c002352134 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -41,6 +41,7 @@ check-unit-y += tests/check-qlist$(EXESUF) gcov-files-check-qlist-y = qobject/qlist.c check-unit-y += tests/check-qnull$(EXESUF) gcov-files-check-qnull-y = qobject/qnull.c +check-unit-y += tests/check-qobject$(EXESUF) check-unit-y += tests/check-qjson$(EXESUF) gcov-files-check-qjson-y = qobject/qjson.c check-unit-y += tests/check-qlit$(EXESUF) @@ -546,7 +547,7 @@ GENERATED_FILES += tests/test-qapi-types.h tests/test-qapi-visit.h \ tests/test-qmp-introspect.h test-obj-y = tests/check-qnum.o tests/check-qstring.o tests/check-qdict.o \ - tests/check-qlist.o tests/check-qnull.o \ + tests/check-qlist.o tests/check-qnull.o tests/check-qobject.o \ tests/check-qjson.o tests/check-qlit.o \ tests/test-coroutine.o tests/test-string-output-visitor.o \ tests/test-string-input-visitor.o tests/test-qobject-output-visitor.o \ @@ -580,6 +581,7 @@ tests/check-qstring$(EXESUF): tests/check-qstring.o $(test-util-obj-y) tests/check-qdict$(EXESUF): tests/check-qdict.o $(test-util-obj-y) tests/check-qlist$(EXESUF): tests/check-qlist.o $(test-util-obj-y) tests/check-qnull$(EXESUF): tests/check-qnull.o $(test-util-obj-y) +tests/check-qobject$(EXESUF): tests/check-qobject.o $(test-util-obj-y) tests/check-qjson$(EXESUF): tests/check-qjson.o $(test-util-obj-y) tests/check-qlit$(EXESUF): tests/check-qlit.o $(test-util-obj-y) tests/check-qom-interface$(EXESUF): tests/check-qom-interface.o $(test-qom-obj-y) diff --git a/tests/check-qnull.c b/tests/check-qnull.c index 5c6eb0adc8..afa4400da1 100644 --- a/tests/check-qnull.c +++ b/tests/check-qnull.c @@ -8,7 +8,7 @@ */ #include "qemu/osdep.h" -#include "qapi/qmp/qobject.h" +#include "qapi/qmp/qnull.h" #include "qemu-common.h" #include "qapi/qobject-input-visitor.h" #include "qapi/qobject-output-visitor.h" diff --git a/tests/check-qobject.c b/tests/check-qobject.c new file mode 100644 index 0000000000..03e9175113 --- /dev/null +++ b/tests/check-qobject.c @@ -0,0 +1,328 @@ +/* + * Generic QObject unit-tests. + * + * Copyright (C) 2017 Red Hat Inc. + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + */ +#include "qemu/osdep.h" + +#include "qapi/qmp/types.h" +#include "qemu-common.h" + +#include <math.h> + +/* Marks the end of the test_equality() argument list. + * We cannot use NULL there because that is a valid argument. */ +static QObject test_equality_end_of_arguments; + +/** + * Test whether all variadic QObject *arguments are equal (@expected + * is true) or whether they are all not equal (@expected is false). + * Every QObject is tested to be equal to itself (to test + * reflexivity), all tests are done both ways (to test symmetry), and + * transitivity is not assumed but checked (each object is compared to + * every other one). + * + * Note that qobject_is_equal() is not really an equivalence relation, + * so this function may not be used for all objects (reflexivity is + * not guaranteed, e.g. in the case of a QNum containing NaN). + * + * The @_ argument is required because a boolean may not be the last + * argument before a variadic argument list (C11 7.16.1.4 para. 4). + */ +static void do_test_equality(bool expected, int _, ...) +{ + va_list ap_count, ap_extract; + QObject **args; + int arg_count = 0; + int i, j; + + va_start(ap_count, _); + va_copy(ap_extract, ap_count); + while (va_arg(ap_count, QObject *) != &test_equality_end_of_arguments) { + arg_count++; + } + va_end(ap_count); + + args = g_new(QObject *, arg_count); + for (i = 0; i < arg_count; i++) { + args[i] = va_arg(ap_extract, QObject *); + } + va_end(ap_extract); + + for (i = 0; i < arg_count; i++) { + g_assert(qobject_is_equal(args[i], args[i]) == true); + + for (j = i + 1; j < arg_count; j++) { + g_assert(qobject_is_equal(args[i], args[j]) == expected); + } + } +} + +#define check_equal(...) \ + do_test_equality(true, 0, __VA_ARGS__, &test_equality_end_of_arguments) +#define check_unequal(...) \ + do_test_equality(false, 0, __VA_ARGS__, &test_equality_end_of_arguments) + +static void do_free_all(int _, ...) +{ + va_list ap; + QObject *obj; + + va_start(ap, _); + while ((obj = va_arg(ap, QObject *)) != NULL) { + qobject_decref(obj); + } + va_end(ap); +} + +#define free_all(...) \ + do_free_all(0, __VA_ARGS__, NULL) + +static void qobject_is_equal_null_test(void) +{ + check_unequal(qnull(), NULL); +} + +static void qobject_is_equal_num_test(void) +{ + QNum *u0, *i0, *d0, *dnan, *um42, *im42, *dm42; + + u0 = qnum_from_uint(0u); + i0 = qnum_from_int(0); + d0 = qnum_from_double(0.0); + dnan = qnum_from_double(NAN); + um42 = qnum_from_uint((uint64_t)-42); + im42 = qnum_from_int(-42); + dm42 = qnum_from_double(-42.0); + + /* Integers representing a mathematically equal number should + * compare equal */ + check_equal(u0, i0); + /* Doubles, however, are always unequal to integers */ + check_unequal(u0, d0); + check_unequal(i0, d0); + + /* Do not assume any object is equal to itself -- note however + * that NaN cannot occur in a JSON object anyway. */ + g_assert(qobject_is_equal(QOBJECT(dnan), QOBJECT(dnan)) == false); + + /* No unsigned overflow */ + check_unequal(um42, im42); + check_unequal(um42, dm42); + check_unequal(im42, dm42); + + free_all(u0, i0, d0, dnan, um42, im42, dm42); +} + +static void qobject_is_equal_bool_test(void) +{ + QBool *btrue_0, *btrue_1, *bfalse_0, *bfalse_1; + + btrue_0 = qbool_from_bool(true); + btrue_1 = qbool_from_bool(true); + bfalse_0 = qbool_from_bool(false); + bfalse_1 = qbool_from_bool(false); + + check_equal(btrue_0, btrue_1); + check_equal(bfalse_0, bfalse_1); + check_unequal(btrue_0, bfalse_0); + + free_all(btrue_0, btrue_1, bfalse_0, bfalse_1); +} + +static void qobject_is_equal_string_test(void) +{ + QString *str_base, *str_whitespace_0, *str_whitespace_1, *str_whitespace_2; + QString *str_whitespace_3, *str_case, *str_built; + + str_base = qstring_from_str("foo"); + str_whitespace_0 = qstring_from_str(" foo"); + str_whitespace_1 = qstring_from_str("foo "); + str_whitespace_2 = qstring_from_str("foo\b"); + str_whitespace_3 = qstring_from_str("fooo\b"); + str_case = qstring_from_str("Foo"); + + /* Should yield "foo" */ + str_built = qstring_from_substr("form", 0, 1); + qstring_append_chr(str_built, 'o'); + + check_unequal(str_base, str_whitespace_0, str_whitespace_1, + str_whitespace_2, str_whitespace_3, str_case); + + check_equal(str_base, str_built); + + free_all(str_base, str_whitespace_0, str_whitespace_1, str_whitespace_2, + str_whitespace_3, str_case, str_built); +} + +static void qobject_is_equal_list_test(void) +{ + QList *list_0, *list_1, *list_cloned; + QList *list_reordered, *list_longer, *list_shorter; + + list_0 = qlist_new(); + list_1 = qlist_new(); + list_reordered = qlist_new(); + list_longer = qlist_new(); + list_shorter = qlist_new(); + + qlist_append_int(list_0, 1); + qlist_append_int(list_0, 2); + qlist_append_int(list_0, 3); + + qlist_append_int(list_1, 1); + qlist_append_int(list_1, 2); + qlist_append_int(list_1, 3); + + qlist_append_int(list_reordered, 1); + qlist_append_int(list_reordered, 3); + qlist_append_int(list_reordered, 2); + + qlist_append_int(list_longer, 1); + qlist_append_int(list_longer, 2); + qlist_append_int(list_longer, 3); + qlist_append_null(list_longer); + + qlist_append_int(list_shorter, 1); + qlist_append_int(list_shorter, 2); + + list_cloned = qlist_copy(list_0); + + check_equal(list_0, list_1, list_cloned); + check_unequal(list_0, list_reordered, list_longer, list_shorter); + + /* With a NaN in it, the list should no longer compare equal to + * itself */ + qlist_append(list_0, qnum_from_double(NAN)); + g_assert(qobject_is_equal(QOBJECT(list_0), QOBJECT(list_0)) == false); + + free_all(list_0, list_1, list_cloned, list_reordered, list_longer, + list_shorter); +} + +static void qobject_is_equal_dict_test(void) +{ + Error *local_err = NULL; + QDict *dict_0, *dict_1, *dict_cloned; + QDict *dict_different_key, *dict_different_value, *dict_different_null_key; + QDict *dict_longer, *dict_shorter, *dict_nested; + QDict *dict_crumpled; + + dict_0 = qdict_new(); + dict_1 = qdict_new(); + dict_different_key = qdict_new(); + dict_different_value = qdict_new(); + dict_different_null_key = qdict_new(); + dict_longer = qdict_new(); + dict_shorter = qdict_new(); + dict_nested = qdict_new(); + + qdict_put_int(dict_0, "f.o", 1); + qdict_put_int(dict_0, "bar", 2); + qdict_put_int(dict_0, "baz", 3); + qdict_put_null(dict_0, "null"); + + qdict_put_int(dict_1, "f.o", 1); + qdict_put_int(dict_1, "bar", 2); + qdict_put_int(dict_1, "baz", 3); + qdict_put_null(dict_1, "null"); + + qdict_put_int(dict_different_key, "F.o", 1); + qdict_put_int(dict_different_key, "bar", 2); + qdict_put_int(dict_different_key, "baz", 3); + qdict_put_null(dict_different_key, "null"); + + qdict_put_int(dict_different_value, "f.o", 42); + qdict_put_int(dict_different_value, "bar", 2); + qdict_put_int(dict_different_value, "baz", 3); + qdict_put_null(dict_different_value, "null"); + + qdict_put_int(dict_different_null_key, "f.o", 1); + qdict_put_int(dict_different_null_key, "bar", 2); + qdict_put_int(dict_different_null_key, "baz", 3); + qdict_put_null(dict_different_null_key, "none"); + + qdict_put_int(dict_longer, "f.o", 1); + qdict_put_int(dict_longer, "bar", 2); + qdict_put_int(dict_longer, "baz", 3); + qdict_put_int(dict_longer, "xyz", 4); + qdict_put_null(dict_longer, "null"); + + qdict_put_int(dict_shorter, "f.o", 1); + qdict_put_int(dict_shorter, "bar", 2); + qdict_put_int(dict_shorter, "baz", 3); + + qdict_put(dict_nested, "f", qdict_new()); + qdict_put_int(qdict_get_qdict(dict_nested, "f"), "o", 1); + qdict_put_int(dict_nested, "bar", 2); + qdict_put_int(dict_nested, "baz", 3); + qdict_put_null(dict_nested, "null"); + + dict_cloned = qdict_clone_shallow(dict_0); + + check_equal(dict_0, dict_1, dict_cloned); + check_unequal(dict_0, dict_different_key, dict_different_value, + dict_different_null_key, dict_longer, dict_shorter, + dict_nested); + + dict_crumpled = qobject_to_qdict(qdict_crumple(dict_1, &local_err)); + g_assert(!local_err); + check_equal(dict_crumpled, dict_nested); + + qdict_flatten(dict_nested); + check_equal(dict_0, dict_nested); + + /* Containing an NaN value will make this dict compare unequal to + * itself */ + qdict_put(dict_0, "NaN", qnum_from_double(NAN)); + g_assert(qobject_is_equal(QOBJECT(dict_0), QOBJECT(dict_0)) == false); + + free_all(dict_0, dict_1, dict_cloned, dict_different_key, + dict_different_value, dict_different_null_key, dict_longer, + dict_shorter, dict_nested, dict_crumpled); +} + +static void qobject_is_equal_conversion_test(void) +{ + QNum *u0, *i0, *d0; + QString *s0, *s_empty; + QBool *bfalse; + + u0 = qnum_from_uint(0u); + i0 = qnum_from_int(0); + d0 = qnum_from_double(0.0); + s0 = qstring_from_str("0"); + s_empty = qstring_new(); + bfalse = qbool_from_bool(false); + + /* No automatic type conversion */ + check_unequal(u0, s0, s_empty, bfalse, qnull(), NULL); + check_unequal(i0, s0, s_empty, bfalse, qnull(), NULL); + check_unequal(d0, s0, s_empty, bfalse, qnull(), NULL); + + free_all(u0, i0, d0, s0, s_empty, bfalse); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + g_test_add_func("/public/qobject_is_equal_null", + qobject_is_equal_null_test); + g_test_add_func("/public/qobject_is_equal_num", qobject_is_equal_num_test); + g_test_add_func("/public/qobject_is_equal_bool", + qobject_is_equal_bool_test); + g_test_add_func("/public/qobject_is_equal_string", + qobject_is_equal_string_test); + g_test_add_func("/public/qobject_is_equal_list", + qobject_is_equal_list_test); + g_test_add_func("/public/qobject_is_equal_dict", + qobject_is_equal_dict_test); + g_test_add_func("/public/qobject_is_equal_conversion", + qobject_is_equal_conversion_test); + + return g_test_run(); +} diff --git a/tests/qemu-iotests/020 b/tests/qemu-iotests/020 index 7a111100ec..cefe3a830e 100755 --- a/tests/qemu-iotests/020 +++ b/tests/qemu-iotests/020 @@ -110,6 +110,33 @@ for offset in $TEST_OFFSETS; do io_zero readv $(( offset + 64 * 1024 + 65536 * 4 )) 65536 65536 1 done _check_test_img +_cleanup +TEST_IMG=$TEST_IMG_SAVE + +echo +echo 'Testing failing commit' +echo + +# Create an image with a null backing file to which committing will fail (with +# ENOSPC so we can distinguish the result from some generic EIO which may be +# generated anywhere in the block layer) +_make_test_img -b "json:{'driver': 'raw', + 'file': { + 'driver': 'blkdebug', + 'inject-error': [{ + 'event': 'write_aio', + 'errno': 28, + 'once': true + }], + 'image': { + 'driver': 'null-co' + }}}" + +# Just write anything so committing will not be a no-op +$QEMU_IO -c 'writev 0 64k' "$TEST_IMG" | _filter_qemu_io + +$QEMU_IMG commit "$TEST_IMG" +_cleanup_test_img # success, all done echo "*** done" diff --git a/tests/qemu-iotests/020.out b/tests/qemu-iotests/020.out index 42f6c1b151..165b70aa49 100644 --- a/tests/qemu-iotests/020.out +++ b/tests/qemu-iotests/020.out @@ -1075,4 +1075,21 @@ read 65536/65536 bytes at offset 4295098368 read 65536/65536 bytes at offset 4295294976 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) No errors were found on the image. + +Testing failing commit + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 backing_file=json:{'driver': 'raw',, + 'file': { + 'driver': 'blkdebug',, + 'inject-error': [{ + 'event': 'write_aio',, + 'errno': 28,, + 'once': true + }],, + 'image': { + 'driver': 'null-co' + }}} +wrote 65536/65536 bytes at offset 0 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-img: Block job failed: No space left on device *** done diff --git a/tests/qemu-iotests/060 b/tests/qemu-iotests/060 index fae08b03bf..1eca09417b 100755 --- a/tests/qemu-iotests/060 +++ b/tests/qemu-iotests/060 @@ -301,6 +301,131 @@ _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 +echo +echo "=== Testing dirty corrupt image ===" +echo + +_make_test_img 64M + +# Let the refblock appear unaligned +poke_file "$TEST_IMG" "$rt_offset" "\x00\x00\x00\x00\xff\xff\x2a\x00" +# Mark the image dirty, thus forcing an automatic check when opening it +poke_file "$TEST_IMG" 72 "\x00\x00\x00\x00\x00\x00\x00\x01" +# Open the image (qemu should refuse to do so) +$QEMU_IO -c close "$TEST_IMG" 2>&1 | _filter_testdir | _filter_imgfmt + +echo '--- Repairing ---' + +# The actual repair should have happened (because of the dirty bit), +# but some cleanup may have failed (like freeing the old reftable) +# because the image was already marked corrupt by that point +_check_test_img -r all + +echo +echo "=== Writing to an unaligned preallocated zero cluster ===" +echo + +_make_test_img 64M + +# Allocate the L2 table +$QEMU_IO -c "write 0 64k" -c "discard 0 64k" "$TEST_IMG" | _filter_qemu_io +# Pretend there is a preallocated zero cluster somewhere inside the +# image header +poke_file "$TEST_IMG" "$l2_offset" "\x80\x00\x00\x00\x00\x00\x2a\x01" +# Let's write to it! +$QEMU_IO -c "write 0 64k" "$TEST_IMG" | _filter_qemu_io + +# Can't repair this yet (TODO: We can just deallocate the cluster) + +echo +echo '=== Discarding with an unaligned refblock ===' +echo + +_make_test_img 64M + +$QEMU_IO -c "write 0 128k" "$TEST_IMG" | _filter_qemu_io +# Make our refblock unaligned +poke_file "$TEST_IMG" "$(($rt_offset))" "\x00\x00\x00\x00\x00\x00\x2a\x00" +# Now try to discard something that will be submitted as two requests +# (main part + tail) +$QEMU_IO -c "discard 0 65537" "$TEST_IMG" + +echo '--- Repairing ---' +# Fails the first repair because the corruption prevents the check +# function from double-checking +# (Using -q for the first invocation, because otherwise the +# double-check error message appears above the summary for some +# reason -- so let's just hide the summary) +_check_test_img -q -r all +_check_test_img -r all + +echo +echo "=== Discarding an out-of-bounds refblock ===" +echo + +_make_test_img 64M + +# Pretend there's a refblock really up high +poke_file "$TEST_IMG" "$(($rt_offset+8))" "\x00\xff\xff\xff\x00\x00\x00\x00" +# Let's try to shrink the qcow2 image so that the block driver tries +# to discard that refblock (and see what happens!) +$QEMU_IMG resize --shrink "$TEST_IMG" 32M + +echo '--- Checking and retrying ---' +# Image should not be resized +_img_info | grep 'virtual size' +# But it should pass this check, because the "partial" resize has +# already overwritten refblocks past the end +_check_test_img -r all +# So let's try again +$QEMU_IMG resize --shrink "$TEST_IMG" 32M +_img_info | grep 'virtual size' + +echo +echo "=== Discarding a non-covered in-bounds refblock ===" +echo + +IMGOPTS='refcount_bits=1' _make_test_img 64M + +# Pretend there's a refblock somewhere where there is no refblock to +# cover it (but the covering refblock has a valid index in the +# reftable) +# Every refblock covers 65536 * 8 * 65536 = 32 GB, so we have to point +# to 0x10_0000_0000 (64G) to point to the third refblock +poke_file "$TEST_IMG" "$(($rt_offset+8))" "\x00\x00\x00\x10\x00\x00\x00\x00" +$QEMU_IMG resize --shrink "$TEST_IMG" 32M + +echo '--- Checking and retrying ---' +# Image should not be resized +_img_info | grep 'virtual size' +# But it should pass this check, because the "partial" resize has +# already overwritten refblocks past the end +_check_test_img -r all +# So let's try again +$QEMU_IMG resize --shrink "$TEST_IMG" 32M +_img_info | grep 'virtual size' + +echo +echo "=== Discarding a refblock covered by an unaligned refblock ===" +echo + +IMGOPTS='refcount_bits=1' _make_test_img 64M + +# Same as above +poke_file "$TEST_IMG" "$(($rt_offset+8))" "\x00\x00\x00\x10\x00\x00\x00\x00" +# But now we actually "create" an unaligned third refblock +poke_file "$TEST_IMG" "$(($rt_offset+16))" "\x00\x00\x00\x00\x00\x00\x02\x00" +$QEMU_IMG resize --shrink "$TEST_IMG" 32M + +echo '--- Repairing ---' +# Fails the first repair because the corruption prevents the check +# function from double-checking +# (Using -q for the first invocation, because otherwise the +# double-check error message appears above the summary for some +# reason -- so let's just hide the summary) +_check_test_img -q -r all +_check_test_img -r all + # success, all done echo "*** done" rm -f $seq.full diff --git a/tests/qemu-iotests/060.out b/tests/qemu-iotests/060.out index 62c22701b8..56f5eb15d8 100644 --- a/tests/qemu-iotests/060.out +++ b/tests/qemu-iotests/060.out @@ -284,4 +284,119 @@ No errors were found on the image. 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 + +=== Testing dirty corrupt image === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +ERROR refcount block 0 is not cluster aligned; refcount table entry corrupted +IMGFMT: Marking image as corrupt: Refblock offset 0xffff2a00 unaligned (reftable index: 0); further corruption events will be suppressed +Can't get refcount for cluster 0: Input/output error +Can't get refcount for cluster 1: Input/output error +Can't get refcount for cluster 2: Input/output error +Can't get refcount for cluster 3: Input/output error +Rebuilding refcount structure +Repairing cluster 1 refcount=1 reference=0 +can't open device TEST_DIR/t.IMGFMT: Could not repair dirty image: Input/output error +--- Repairing --- +Leaked cluster 1 refcount=1 reference=0 +Repairing cluster 1 refcount=1 reference=0 +The following inconsistencies were found and repaired: + + 1 leaked clusters + 0 corruptions + +Double checking the fixed image now... +No errors were found on the image. + +=== Writing to an unaligned preallocated zero cluster === + +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) +discard 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: Preallocated zero cluster offset 0x2a00 unaligned (guest offset: 0); further corruption events will be suppressed +write failed: Input/output error + +=== Discarding with an unaligned refblock === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +wrote 131072/131072 bytes at offset 0 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qcow2: Marking image as corrupt: Refblock offset 0x2a00 unaligned (reftable index: 0); further corruption events will be suppressed +qcow2_free_clusters failed: Input/output error +discard failed: No medium found +--- Repairing --- +ERROR refcount block 0 is not cluster aligned; refcount table entry corrupted +qcow2: Marking image as corrupt: Refblock offset 0x2a00 unaligned (reftable index: 0); further corruption events will be suppressed +Can't get refcount for cluster 0: Input/output error +Can't get refcount for cluster 1: Input/output error +Can't get refcount for cluster 2: Input/output error +Can't get refcount for cluster 3: Input/output error +Can't get refcount for cluster 4: Input/output error +Can't get refcount for cluster 5: Input/output error +Can't get refcount for cluster 6: Input/output error +Rebuilding refcount structure +Repairing cluster 1 refcount=1 reference=0 +qemu-img: Check failed: No medium found +Leaked cluster 1 refcount=1 reference=0 +Repairing cluster 1 refcount=1 reference=0 +The following inconsistencies were found and repaired: + + 1 leaked clusters + 0 corruptions + +Double checking the fixed image now... +No errors were found on the image. + +=== Discarding an out-of-bounds refblock === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +qcow2: Marking image as corrupt: Refblock at 0xffffff00000000 is not covered by the refcount structures; further corruption events will be suppressed +qemu-img: Failed to discard unused refblocks: Input/output error +--- Checking and retrying --- +virtual size: 64M (67108864 bytes) +No errors were found on the image. +Image resized. +virtual size: 32M (33554432 bytes) + +=== Discarding a non-covered in-bounds refblock === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +qcow2: Marking image as corrupt: Refblock at 0x1000000000 is not covered by the refcount structures; further corruption events will be suppressed +qemu-img: Failed to discard unused refblocks: Input/output error +--- Checking and retrying --- +virtual size: 64M (67108864 bytes) +No errors were found on the image. +Image resized. +virtual size: 32M (33554432 bytes) + +=== Discarding a refblock covered by an unaligned refblock === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +qcow2: Marking image as corrupt: Cannot get entry from refcount block cache: Offset 0x200 is unaligned; further corruption events will be suppressed +qemu-img: Failed to discard unused refblocks: Input/output error +--- Repairing --- +Repairing refcount block 1 is outside image +ERROR refcount block 2 is not cluster aligned; refcount table entry corrupted +qcow2: Marking image as corrupt: Refblock offset 0x200 unaligned (reftable index: 0x2); further corruption events will be suppressed +Can't get refcount for cluster 1048576: Input/output error +Rebuilding refcount structure +Repairing cluster 1 refcount=1 reference=0 +Repairing cluster 2 refcount=1 reference=0 +Repairing cluster 1048576 refcount=1 reference=0 +qemu-img: Check failed: No medium found +Leaked cluster 1 refcount=1 reference=0 +Leaked cluster 2 refcount=1 reference=0 +Leaked cluster 1048576 refcount=1 reference=0 +Repairing cluster 1 refcount=1 reference=0 +Repairing cluster 2 refcount=1 reference=0 +Repairing cluster 1048576 refcount=1 reference=0 +The following inconsistencies were found and repaired: + + 3 leaked clusters + 0 corruptions + +Double checking the fixed image now... +No errors were found on the image. *** done diff --git a/tests/qemu-iotests/087 b/tests/qemu-iotests/087 index 27ab6c5151..2561a14456 100755 --- a/tests/qemu-iotests/087 +++ b/tests/qemu-iotests/087 @@ -102,7 +102,14 @@ echo echo === aio=native without O_DIRECT === echo -run_qemu <<EOF +# Skip this test if AIO is not enabled in this build +function run_qemu_filter_aio() +{ + run_qemu "$@" | \ + sed -e 's/is not supported in this build/it requires cache.direct=on, which was not specified/' +} + +run_qemu_filter_aio <<EOF { "execute": "qmp_capabilities" } { "execute": "blockdev-add", "arguments": { diff --git a/tests/qemu-iotests/133 b/tests/qemu-iotests/133 index 9d35a6a1ca..af6b3e1dd4 100755 --- a/tests/qemu-iotests/133 +++ b/tests/qemu-iotests/133 @@ -83,6 +83,15 @@ $QEMU_IO -c 'reopen -o driver=qcow2' $TEST_IMG $QEMU_IO -c 'reopen -o file.driver=file' $TEST_IMG $QEMU_IO -c 'reopen -o backing.driver=qcow2' $TEST_IMG +echo +echo "=== Check that reopening works with non-string options ===" +echo + +# Using the json: pseudo-protocol we can create non-string options +# (Invoke 'info' just so we get some output afterwards) +IMGOPTSSYNTAX=false $QEMU_IO -f null-co -c 'reopen' -c 'info' \ + "json:{'driver': 'null-co', 'size': 65536}" + # success, all done echo "*** done" rm -f $seq.full diff --git a/tests/qemu-iotests/133.out b/tests/qemu-iotests/133.out index cc86b94880..f4a85aeb63 100644 --- a/tests/qemu-iotests/133.out +++ b/tests/qemu-iotests/133.out @@ -19,4 +19,9 @@ Cannot change the option 'driver' === Check that unchanged driver is okay === + +=== Check that reopening works with non-string options === + +format name: null-co +format name: null-co *** done diff --git a/tests/qemu-iotests/176 b/tests/qemu-iotests/176 index 950b28720e..0f31a20294 100755 --- a/tests/qemu-iotests/176 +++ b/tests/qemu-iotests/176 @@ -3,10 +3,11 @@ # Commit changes into backing chains and empty the top image if the # backing image is not explicitly specified. # -# Variant of 097, which includes snapshots to test different codepath -# in qcow2 +# Variant of 097, which includes snapshots and persistent bitmaps, to +# tickle the slow codepath in qcow2. See also 198, for another feature +# that tickles the slow codepath. # -# Copyright (C) 2014 Red Hat, Inc. +# Copyright (C) 2014, 2017 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 @@ -43,11 +44,18 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 . ./common.filter . ./common.pattern -# Any format supporting backing files and bdrv_make_empty +# This test is specific to qcow2 _supported_fmt qcow2 _supported_proto file _supported_os Linux +function run_qemu() +{ + $QEMU -nographic -qmp stdio -serial none "$@" 2>&1 \ + | _filter_testdir | _filter_qmp | _filter_qemu +} + +for reason in snapshot bitmap; do # Four passes: # 0: Two-layer backing chain, commit to upper backing file (implicitly) @@ -66,14 +74,29 @@ _supported_os Linux for i in 0 1 2 3; do echo -echo "=== Test pass $i ===" +echo "=== Test pass $reason.$i ===" echo len=$((2100 * 1024 * 1024 + 512)) # larger than 2G, and not cluster aligned TEST_IMG="$TEST_IMG.base" _make_test_img $len TEST_IMG="$TEST_IMG.itmd" _make_test_img -b "$TEST_IMG.base" $len _make_test_img -b "$TEST_IMG.itmd" $len -$QEMU_IMG snapshot -c snap "$TEST_IMG" +# Update the top image to use a feature that is incompatible with fast path +case $reason in + snapshot) $QEMU_IMG snapshot -c snap "$TEST_IMG" ;; + bitmap) + run_qemu <<EOF +{ "execute": "qmp_capabilities" } +{ "execute": "blockdev-add", + "arguments": { "driver": "qcow2", "node-name": "drive0", + "file": { "driver": "file", "filename": "$TEST_IMG" } } } +{ "execute": "block-dirty-bitmap-add", + "arguments": { "node": "drive0", "name": "bitmap0", + "persistent": true, "autoload": true } } +{ "execute": "quit" } +EOF + ;; +esac $QEMU_IO -c "write -P 1 0x7ffd0000 192k" "$TEST_IMG.base" | _filter_qemu_io $QEMU_IO -c "write -P 2 0x7ffe0000 128k" "$TEST_IMG.itmd" | _filter_qemu_io @@ -122,8 +145,26 @@ $QEMU_IMG map "$TEST_IMG.base" | _filter_qemu_img_map $QEMU_IMG map "$TEST_IMG.itmd" | _filter_qemu_img_map $QEMU_IMG map "$TEST_IMG" | _filter_qemu_img_map -done +# Check that the reason for slow path is still present, as appropriate +case $reason in + snapshot) + $QEMU_IMG snapshot -l "$TEST_IMG" | + sed 's/^\(.\{20\}\).*/\1/; s/ *$//' ;; + bitmap) + run_qemu <<EOF +{ "execute": "qmp_capabilities" } +{ "execute": "blockdev-add", + "arguments": { "driver": "qcow2", "node-name": "drive0", + "file": { "driver": "file", "filename": "$TEST_IMG" } } } +{ "execute": "x-debug-block-dirty-bitmap-sha256", + "arguments": { "node": "drive0", "name": "bitmap0" } } +{ "execute": "quit" } +EOF + ;; +esac +done +done # success, all done echo "*** done" diff --git a/tests/qemu-iotests/176.out b/tests/qemu-iotests/176.out index 6271fa7d6f..e62085cd0a 100644 --- a/tests/qemu-iotests/176.out +++ b/tests/qemu-iotests/176.out @@ -1,6 +1,6 @@ QA output created by 176 -=== Test pass 0 === +=== Test pass snapshot.0 === Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=2202010112 Formatting 'TEST_DIR/t.IMGFMT.itmd', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.base @@ -36,8 +36,11 @@ Offset Length File 0x7ffd0000 0x10000 TEST_DIR/t.IMGFMT.base 0x7ffe0000 0x20000 TEST_DIR/t.IMGFMT.itmd 0x83400000 0x200 TEST_DIR/t.IMGFMT.itmd +Snapshot list: +ID TAG +1 snap -=== Test pass 1 === +=== Test pass snapshot.1 === Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=2202010112 Formatting 'TEST_DIR/t.IMGFMT.itmd', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.base @@ -74,8 +77,11 @@ Offset Length File 0x7ffe0000 0x10000 TEST_DIR/t.IMGFMT.itmd 0x7fff0000 0x10000 TEST_DIR/t.IMGFMT 0x83400000 0x200 TEST_DIR/t.IMGFMT +Snapshot list: +ID TAG +1 snap -=== Test pass 2 === +=== Test pass snapshot.2 === Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=2202010112 Formatting 'TEST_DIR/t.IMGFMT.itmd', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.base @@ -112,8 +118,11 @@ Offset Length File 0x7ffe0000 0x10000 TEST_DIR/t.IMGFMT.itmd 0x7fff0000 0x10000 TEST_DIR/t.IMGFMT 0x83400000 0x200 TEST_DIR/t.IMGFMT +Snapshot list: +ID TAG +1 snap -=== Test pass 3 === +=== Test pass snapshot.3 === Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=2202010112 Formatting 'TEST_DIR/t.IMGFMT.itmd', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.base @@ -147,4 +156,203 @@ Offset Length File 0x7ffe0000 0x10000 TEST_DIR/t.IMGFMT.itmd 0x7fff0000 0x10000 TEST_DIR/t.IMGFMT 0x83400000 0x200 TEST_DIR/t.IMGFMT +Snapshot list: +ID TAG +1 snap + +=== Test pass bitmap.0 === + +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=2202010112 +Formatting 'TEST_DIR/t.IMGFMT.itmd', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.base +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.itmd +QMP_VERSION +{"return": {}} +{"return": {}} +{"return": {}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +wrote 196608/196608 bytes at offset 2147287040 +192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 131072/131072 bytes at offset 2147352576 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 65536/65536 bytes at offset 2147418112 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 512/512 bytes at offset 2202009600 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Image committed. +read 196608/196608 bytes at offset 2147287040 +192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 512/512 bytes at offset 2202009600 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 65536/65536 bytes at offset 2147287040 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 65536/65536 bytes at offset 2147352576 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 65536/65536 bytes at offset 2147418112 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 512/512 bytes at offset 2202009600 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Offset Length File +0x7ffd0000 0x30000 TEST_DIR/t.IMGFMT.base +Offset Length File +0x7ffd0000 0x10000 TEST_DIR/t.IMGFMT.base +0x7ffe0000 0x20000 TEST_DIR/t.IMGFMT.itmd +0x83400000 0x200 TEST_DIR/t.IMGFMT.itmd +Offset Length File +0x7ffd0000 0x10000 TEST_DIR/t.IMGFMT.base +0x7ffe0000 0x20000 TEST_DIR/t.IMGFMT.itmd +0x83400000 0x200 TEST_DIR/t.IMGFMT.itmd +QMP_VERSION +{"return": {}} +{"return": {}} +{"return": {"sha256": "e12600978d86b5a453861ae5c17d275204673fef3874b7c3c5433c6153d84706"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} + +=== Test pass bitmap.1 === + +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=2202010112 +Formatting 'TEST_DIR/t.IMGFMT.itmd', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.base +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.itmd +QMP_VERSION +{"return": {}} +{"return": {}} +{"return": {}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +wrote 196608/196608 bytes at offset 2147287040 +192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 131072/131072 bytes at offset 2147352576 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 65536/65536 bytes at offset 2147418112 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 512/512 bytes at offset 2202009600 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Image committed. +read 196608/196608 bytes at offset 2147287040 +192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 512/512 bytes at offset 2202009600 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 65536/65536 bytes at offset 2147287040 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 65536/65536 bytes at offset 2147352576 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 65536/65536 bytes at offset 2147418112 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 512/512 bytes at offset 2202009600 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Offset Length File +0x7ffd0000 0x30000 TEST_DIR/t.IMGFMT.base +Offset Length File +0x7ffd0000 0x10000 TEST_DIR/t.IMGFMT.base +0x7ffe0000 0x20000 TEST_DIR/t.IMGFMT.itmd +0x83400000 0x200 TEST_DIR/t.IMGFMT.itmd +Offset Length File +0x7ffd0000 0x10000 TEST_DIR/t.IMGFMT.base +0x7ffe0000 0x10000 TEST_DIR/t.IMGFMT.itmd +0x7fff0000 0x10000 TEST_DIR/t.IMGFMT +0x83400000 0x200 TEST_DIR/t.IMGFMT +QMP_VERSION +{"return": {}} +{"return": {}} +{"return": {"sha256": "e12600978d86b5a453861ae5c17d275204673fef3874b7c3c5433c6153d84706"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} + +=== Test pass bitmap.2 === + +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=2202010112 +Formatting 'TEST_DIR/t.IMGFMT.itmd', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.base +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.itmd +QMP_VERSION +{"return": {}} +{"return": {}} +{"return": {}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +wrote 196608/196608 bytes at offset 2147287040 +192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 131072/131072 bytes at offset 2147352576 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 65536/65536 bytes at offset 2147418112 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 512/512 bytes at offset 2202009600 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Image committed. +read 196608/196608 bytes at offset 2147287040 +192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 512/512 bytes at offset 2202009600 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 65536/65536 bytes at offset 2147287040 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 65536/65536 bytes at offset 2147352576 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 65536/65536 bytes at offset 2147418112 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 512/512 bytes at offset 2202009600 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Offset Length File +0x7ffd0000 0x30000 TEST_DIR/t.IMGFMT.base +Offset Length File +0x7ffd0000 0x10000 TEST_DIR/t.IMGFMT.base +0x7ffe0000 0x20000 TEST_DIR/t.IMGFMT.itmd +0x83400000 0x200 TEST_DIR/t.IMGFMT.itmd +Offset Length File +0x7ffd0000 0x10000 TEST_DIR/t.IMGFMT.base +0x7ffe0000 0x10000 TEST_DIR/t.IMGFMT.itmd +0x7fff0000 0x10000 TEST_DIR/t.IMGFMT +0x83400000 0x200 TEST_DIR/t.IMGFMT +QMP_VERSION +{"return": {}} +{"return": {}} +{"return": {"sha256": "e12600978d86b5a453861ae5c17d275204673fef3874b7c3c5433c6153d84706"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} + +=== Test pass bitmap.3 === + +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=2202010112 +Formatting 'TEST_DIR/t.IMGFMT.itmd', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.base +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.itmd +QMP_VERSION +{"return": {}} +{"return": {}} +{"return": {}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +wrote 196608/196608 bytes at offset 2147287040 +192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 131072/131072 bytes at offset 2147352576 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 65536/65536 bytes at offset 2147418112 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 512/512 bytes at offset 2202009600 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Image committed. +read 65536/65536 bytes at offset 2147287040 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 65536/65536 bytes at offset 2147352576 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 65536/65536 bytes at offset 2147418112 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 512/512 bytes at offset 2202009600 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Offset Length File +0x7ffd0000 0x30000 TEST_DIR/t.IMGFMT.base +0x83400000 0x200 TEST_DIR/t.IMGFMT.base +Offset Length File +0x7ffd0000 0x10000 TEST_DIR/t.IMGFMT.base +0x7ffe0000 0x20000 TEST_DIR/t.IMGFMT.itmd +0x83400000 0x200 TEST_DIR/t.IMGFMT.base +Offset Length File +0x7ffd0000 0x10000 TEST_DIR/t.IMGFMT.base +0x7ffe0000 0x10000 TEST_DIR/t.IMGFMT.itmd +0x7fff0000 0x10000 TEST_DIR/t.IMGFMT +0x83400000 0x200 TEST_DIR/t.IMGFMT +QMP_VERSION +{"return": {}} +{"return": {}} +{"return": {"sha256": "e12600978d86b5a453861ae5c17d275204673fef3874b7c3c5433c6153d84706"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} *** done diff --git a/tests/qemu-iotests/182 b/tests/qemu-iotests/182 index 2e078ceed8..4b31592fb8 100755 --- a/tests/qemu-iotests/182 +++ b/tests/qemu-iotests/182 @@ -62,7 +62,7 @@ _launch_qemu -drive file=$TEST_IMG,if=none,id=drive0,file.locking=on \ echo echo "Starting a second QEMU using the same image should fail" -echo 'quit' | $QEMU -monitor stdio \ +echo 'quit' | $QEMU -nographic -monitor stdio \ -drive file=$TEST_IMG,if=none,id=drive0,file.locking=on \ -device $virtioblk,drive=drive0 2>&1 | _filter_testdir 2>&1 | _filter_qemu | diff --git a/tests/qemu-iotests/196 b/tests/qemu-iotests/196 new file mode 100755 index 0000000000..4116ebc92b --- /dev/null +++ b/tests/qemu-iotests/196 @@ -0,0 +1,66 @@ +#!/usr/bin/env python +# +# Test clearing unknown autoclear_features flag by qcow2 after +# migration. This test mimics migration to older qemu. +# +# Copyright (c) 2017 Parallels International GmbH +# +# 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 +import iotests +from iotests import qemu_img + +disk = os.path.join(iotests.test_dir, 'disk') +migfile = os.path.join(iotests.test_dir, 'migfile') + +class TestInvalidateAutoclear(iotests.QMPTestCase): + + def tearDown(self): + self.vm_a.shutdown() + self.vm_b.shutdown() + os.remove(disk) + os.remove(migfile) + + def setUp(self): + qemu_img('create', '-f', iotests.imgfmt, disk, '1M') + + self.vm_a = iotests.VM(path_suffix='a').add_drive(disk) + self.vm_a.launch() + + self.vm_b = iotests.VM(path_suffix='b').add_drive(disk) + self.vm_b.add_incoming("exec: cat '" + migfile + "'") + + def test_migration(self): + result = self.vm_a.qmp('migrate', uri='exec:cat>' + migfile) + self.assert_qmp(result, 'return', {}); + self.assertNotEqual(self.vm_a.event_wait("STOP"), None) + + with open(disk, 'r+b') as f: + f.seek(88) # first byte of autoclear_features field + f.write(b'\xff') + + self.vm_b.launch() + while True: + result = self.vm_b.qmp('query-status') + if result['return']['status'] == 'running': + break + + with open(disk, 'rb') as f: + f.seek(88) + self.assertEqual(f.read(1), b'\x00') + +if __name__ == '__main__': + iotests.main(supported_fmts=['qcow2']) diff --git a/tests/qemu-iotests/196.out b/tests/qemu-iotests/196.out new file mode 100644 index 0000000000..ae1213e6f8 --- /dev/null +++ b/tests/qemu-iotests/196.out @@ -0,0 +1,5 @@ +. +---------------------------------------------------------------------- +Ran 1 tests + +OK diff --git a/tests/qemu-iotests/198 b/tests/qemu-iotests/198 new file mode 100755 index 0000000000..34ef666381 --- /dev/null +++ b/tests/qemu-iotests/198 @@ -0,0 +1,104 @@ +#!/bin/bash +# +# Test commit of encrypted qcow2 files +# +# Copyright (C) 2017 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/>. +# + +# creator +owner=berrange@redhat.com + +seq=`basename $0` +echo "QA output created by $seq" + +here=`pwd` +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +_supported_fmt qcow2 +_supported_proto generic +_supported_os Linux + + +size=16M +TEST_IMG_BASE=$TEST_IMG.base +SECRET0="secret,id=sec0,data=astrochicken" +SECRET1="secret,id=sec1,data=furby" + +TEST_IMG_SAVE=$TEST_IMG +TEST_IMG=$TEST_IMG_BASE +echo "== create base ==" +_make_test_img --object $SECRET0 -o "encrypt.format=luks,encrypt.key-secret=sec0,encrypt.iter-time=10" $size +TEST_IMG=$TEST_IMG_SAVE + +IMGSPECBASE="driver=$IMGFMT,file.filename=$TEST_IMG_BASE,encrypt.key-secret=sec0" +IMGSPECLAYER="driver=$IMGFMT,file.filename=$TEST_IMG,encrypt.key-secret=sec1" +IMGSPEC="$IMGSPECLAYER,backing.driver=$IMGFMT,backing.file.filename=$TEST_IMG_BASE,backing.encrypt.key-secret=sec0" +QEMU_IO_OPTIONS=$QEMU_IO_OPTIONS_NO_FMT + +echo +echo "== writing whole image base ==" +$QEMU_IO --object $SECRET0 -c "write -P 0xa 0 $size" --image-opts $IMGSPECBASE | _filter_qemu_io | _filter_testdir + +echo "== create overlay ==" +_make_test_img --object $SECRET1 -o "encrypt.format=luks,encrypt.key-secret=sec1,encrypt.iter-time=10" -u -b "$TEST_IMG_BASE" $size + +echo +echo "== writing whole image layer ==" +$QEMU_IO --object $SECRET0 --object $SECRET1 -c "write -P 0x9 0 $size" --image-opts $IMGSPEC | _filter_qemu_io | _filter_testdir + +echo +echo "== verify pattern base ==" +$QEMU_IO --object $SECRET0 -c "read -P 0xa 0 $size" --image-opts $IMGSPECBASE | _filter_qemu_io | _filter_testdir + +echo +echo "== verify pattern layer ==" +$QEMU_IO --object $SECRET0 --object $SECRET1 -c "read -P 0x9 0 $size" --image-opts $IMGSPEC | _filter_qemu_io | _filter_testdir + +echo +echo "== committing layer into base ==" +$QEMU_IMG commit --object $SECRET0 --object $SECRET1 --image-opts $IMGSPEC | _filter_testdir + +echo +echo "== verify pattern base ==" +$QEMU_IO --object $SECRET0 -c "read -P 0x9 0 $size" --image-opts $IMGSPECBASE | _filter_qemu_io | _filter_testdir + +echo +echo "== verify pattern layer ==" +$QEMU_IO --object $SECRET0 --object $SECRET1 -c "read -P 0x9 0 $size" --image-opts $IMGSPEC | _filter_qemu_io | _filter_testdir + +echo +echo "== checking image base ==" +$QEMU_IMG info --image-opts $IMGSPECBASE | _filter_img_info | _filter_testdir | sed -e "/^disk size:/ D" + +echo +echo "== checking image layer ==" +$QEMU_IMG info --image-opts $IMGSPECLAYER | _filter_img_info | _filter_testdir | sed -e "/^disk size:/ D" + + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/198.out b/tests/qemu-iotests/198.out new file mode 100644 index 0000000000..2db565e16b --- /dev/null +++ b/tests/qemu-iotests/198.out @@ -0,0 +1,126 @@ +QA output created by 198 +== create base == +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=16777216 encrypt.format=luks encrypt.key-secret=sec0 encrypt.iter-time=10 + +== writing whole image base == +wrote 16777216/16777216 bytes at offset 0 +16 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +== create overlay == +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=16777216 backing_file=TEST_DIR/t.IMGFMT.base encrypt.format=luks encrypt.key-secret=sec1 encrypt.iter-time=10 + +== writing whole image layer == +wrote 16777216/16777216 bytes at offset 0 +16 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== verify pattern base == +read 16777216/16777216 bytes at offset 0 +16 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== verify pattern layer == +read 16777216/16777216 bytes at offset 0 +16 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== committing layer into base == +Image committed. + +== verify pattern base == +read 16777216/16777216 bytes at offset 0 +16 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== verify pattern layer == +read 16777216/16777216 bytes at offset 0 +16 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== checking image base == +image: json:{"encrypt.key-secret": "sec0", "driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/t.IMGFMT.base"}} +file format: IMGFMT +virtual size: 16M (16777216 bytes) +Format specific information: + compat: 1.1 + lazy refcounts: false + refcount bits: 16 + encrypt: + ivgen alg: plain64 + hash alg: sha256 + cipher alg: aes-256 + uuid: 00000000-0000-0000-0000-000000000000 + format: luks + cipher mode: xts + slots: + [0]: + active: true + iters: 1024 + key offset: 4096 + stripes: 4000 + [1]: + active: false + key offset: 262144 + [2]: + active: false + key offset: 520192 + [3]: + active: false + key offset: 778240 + [4]: + active: false + key offset: 1036288 + [5]: + active: false + key offset: 1294336 + [6]: + active: false + key offset: 1552384 + [7]: + active: false + key offset: 1810432 + payload offset: 2068480 + master key iters: 1024 + corrupt: false + +== checking image layer == +image: json:{"encrypt.key-secret": "sec1", "driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/t.IMGFMT"}} +file format: IMGFMT +virtual size: 16M (16777216 bytes) +backing file: TEST_DIR/t.IMGFMT.base +Format specific information: + compat: 1.1 + lazy refcounts: false + refcount bits: 16 + encrypt: + ivgen alg: plain64 + hash alg: sha256 + cipher alg: aes-256 + uuid: 00000000-0000-0000-0000-000000000000 + format: luks + cipher mode: xts + slots: + [0]: + active: true + iters: 1024 + key offset: 4096 + stripes: 4000 + [1]: + active: false + key offset: 262144 + [2]: + active: false + key offset: 520192 + [3]: + active: false + key offset: 778240 + [4]: + active: false + key offset: 1036288 + [5]: + active: false + key offset: 1294336 + [6]: + active: false + key offset: 1552384 + [7]: + active: false + key offset: 1810432 + payload offset: 2068480 + master key iters: 1024 + corrupt: false +*** done diff --git a/tests/qemu-iotests/common.filter b/tests/qemu-iotests/common.filter index 873ca6b104..d9237799e9 100644 --- a/tests/qemu-iotests/common.filter +++ b/tests/qemu-iotests/common.filter @@ -157,7 +157,9 @@ _filter_img_info() -e "/lazy_refcounts: \\(on\\|off\\)/d" \ -e "/block_size: [0-9]\\+/d" \ -e "/block_state_zero: \\(on\\|off\\)/d" \ - -e "/log_size: [0-9]\\+/d" + -e "/log_size: [0-9]\\+/d" \ + -e "s/iters: [0-9]\\+/iters: 1024/" \ + -e "s/uuid: [-a-f0-9]\\+/uuid: 00000000-0000-0000-0000-000000000000/" } # filter out offsets and file names from qemu-img map; good for both diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group index 24e5ad1b79..1fad602152 100644 --- a/tests/qemu-iotests/group +++ b/tests/qemu-iotests/group @@ -193,4 +193,6 @@ 192 rw auto quick 194 rw auto migration quick 195 rw auto quick +196 rw auto quick 197 rw auto quick +198 rw auto |