]> Git Repo - qemu.git/blob - tests/qemu-iotests/261
iotests: Backup with different source/target size
[qemu.git] / tests / qemu-iotests / 261
1 #!/usr/bin/env bash
2 #
3 # Test case for qcow2's handling of extra data in snapshot table entries
4 # (and more generally, how certain cases of broken snapshot tables are
5 # handled)
6 #
7 # Copyright (C) 2019 Red Hat, Inc.
8 #
9 # This program is free software; you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation; either version 2 of the License, or
12 # (at your option) any later version.
13 #
14 # This program is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 # GNU General Public License for more details.
18 #
19 # You should have received a copy of the GNU General Public License
20 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
21 #
22
23 # creator
24 [email protected]
25
26 seq=$(basename $0)
27 echo "QA output created by $seq"
28
29 status=1        # failure is the default!
30
31 _cleanup()
32 {
33     _cleanup_test_img
34     rm -f "$TEST_IMG".v{2,3}.orig
35     rm -f "$TEST_DIR"/sn{0,1,2}{,-pre,-extra,-post}
36 }
37 trap "_cleanup; exit \$status" 0 1 2 3 15
38
39 # get standard environment, filters and checks
40 . ./common.rc
41 . ./common.filter
42
43 # This tests qcow2-specific low-level functionality
44 _supported_fmt qcow2
45 _supported_proto file
46 _supported_os Linux
47 # (1) We create a v2 image that supports nothing but refcount_bits=16
48 # (2) We do some refcount management on our own which expects
49 #     refcount_bits=16
50 # As for data files, they do not support snapshots at all.
51 _unsupported_imgopts 'refcount_bits=\([^1]\|.\([^6]\|$\)\)' data_file
52
53 # Parameters:
54 #   $1: image filename
55 #   $2: snapshot table entry offset in the image
56 snapshot_table_entry_size()
57 {
58     id_len=$(peek_file_be "$1" $(($2 + 12)) 2)
59     name_len=$(peek_file_be "$1" $(($2 + 14)) 2)
60     extra_len=$(peek_file_be "$1" $(($2 + 36)) 4)
61
62     full_len=$((40 + extra_len + id_len + name_len))
63     echo $(((full_len + 7) / 8 * 8))
64 }
65
66 # Parameter:
67 #   $1: image filename
68 print_snapshot_table()
69 {
70     nb_entries=$(peek_file_be "$1" 60 4)
71     offset=$(peek_file_be "$1" 64 8)
72
73     echo "Snapshots in $1:" | _filter_testdir | _filter_imgfmt
74
75     for ((i = 0; i < nb_entries; i++)); do
76         id_len=$(peek_file_be "$1" $((offset + 12)) 2)
77         name_len=$(peek_file_be "$1" $((offset + 14)) 2)
78         extra_len=$(peek_file_be "$1" $((offset + 36)) 4)
79
80         extra_ofs=$((offset + 40))
81         id_ofs=$((extra_ofs + extra_len))
82         name_ofs=$((id_ofs + id_len))
83
84         echo "  [$i]"
85         echo "    ID: $(peek_file_raw "$1" $id_ofs $id_len)"
86         echo "    Name: $(peek_file_raw "$1" $name_ofs $name_len)"
87         echo "    Extra data size: $extra_len"
88         if [ $extra_len -ge 8 ]; then
89             echo "    VM state size: $(peek_file_be "$1" $extra_ofs 8)"
90         fi
91         if [ $extra_len -ge 16 ]; then
92             echo "    Disk size: $(peek_file_be "$1" $((extra_ofs + 8)) 8)"
93         fi
94         if [ $extra_len -gt 16 ]; then
95             echo '    Unknown extra data:' \
96                 "$(peek_file_raw "$1" $((extra_ofs + 16)) $((extra_len - 16)) \
97                    | tr -d '\0')"
98         fi
99
100         offset=$((offset + $(snapshot_table_entry_size "$1" $offset)))
101     done
102 }
103
104 # Mark clusters as allocated; works only in refblock 0 (i.e. before
105 # cluster #32768).
106 # Parameters:
107 #   $1: Start offset of what to allocate
108 #   $2: End offset (exclusive)
109 refblock0_allocate()
110 {
111     reftable_ofs=$(peek_file_be "$TEST_IMG" 48 8)
112     refblock_ofs=$(peek_file_be "$TEST_IMG" $reftable_ofs 8)
113
114     cluster=$(($1 / 65536))
115     ecluster=$((($2 + 65535) / 65536))
116
117     while [ $cluster -lt $ecluster ]; do
118         if [ $cluster -ge 32768 ]; then
119             echo "*** Abort: Cluster $cluster exceeds refblock 0 ***"
120             exit 1
121         fi
122         poke_file "$TEST_IMG" $((refblock_ofs + cluster * 2)) '\x00\x01'
123         cluster=$((cluster + 1))
124     done
125 }
126
127
128 echo
129 echo '=== Create v2 template ==='
130 echo
131
132 # Create v2 image with a snapshot table with three entries:
133 # [0]: No extra data (valid with v2, not valid with v3)
134 # [1]: Has extra data unknown to qemu
135 # [2]: Has the 64-bit VM state size, but not the disk size (again,
136 #      valid with v2, not valid with v3)
137
138 TEST_IMG="$TEST_IMG.v2.orig" IMGOPTS='compat=0.10' _make_test_img 64M
139 $QEMU_IMG snapshot -c sn0 "$TEST_IMG.v2.orig"
140 $QEMU_IMG snapshot -c sn1 "$TEST_IMG.v2.orig"
141 $QEMU_IMG snapshot -c sn2 "$TEST_IMG.v2.orig"
142
143 # Copy out all existing snapshot table entries
144 sn_table_ofs=$(peek_file_be "$TEST_IMG.v2.orig" 64 8)
145
146 # ofs: Snapshot table entry offset
147 # eds: Extra data size
148 # ids: Name + ID size
149 # len: Total entry length
150 sn0_ofs=$sn_table_ofs
151 sn0_eds=$(peek_file_be "$TEST_IMG.v2.orig" $((sn0_ofs + 36)) 4)
152 sn0_ids=$(($(peek_file_be "$TEST_IMG.v2.orig" $((sn0_ofs + 12)) 2) +
153            $(peek_file_be "$TEST_IMG.v2.orig" $((sn0_ofs + 14)) 2)))
154 sn0_len=$(snapshot_table_entry_size "$TEST_IMG.v2.orig" $sn0_ofs)
155 sn1_ofs=$((sn0_ofs + sn0_len))
156 sn1_eds=$(peek_file_be "$TEST_IMG.v2.orig" $((sn1_ofs + 36)) 4)
157 sn1_ids=$(($(peek_file_be "$TEST_IMG.v2.orig" $((sn1_ofs + 12)) 2) +
158            $(peek_file_be "$TEST_IMG.v2.orig" $((sn1_ofs + 14)) 2)))
159 sn1_len=$(snapshot_table_entry_size "$TEST_IMG.v2.orig" $sn1_ofs)
160 sn2_ofs=$((sn1_ofs + sn1_len))
161 sn2_eds=$(peek_file_be "$TEST_IMG.v2.orig" $((sn2_ofs + 36)) 4)
162 sn2_ids=$(($(peek_file_be "$TEST_IMG.v2.orig" $((sn2_ofs + 12)) 2) +
163            $(peek_file_be "$TEST_IMG.v2.orig" $((sn2_ofs + 14)) 2)))
164 sn2_len=$(snapshot_table_entry_size "$TEST_IMG.v2.orig" $sn2_ofs)
165
166 # Data before extra data
167 dd if="$TEST_IMG.v2.orig" of="$TEST_DIR/sn0-pre" bs=1 skip=$sn0_ofs count=40 \
168     &> /dev/null
169 dd if="$TEST_IMG.v2.orig" of="$TEST_DIR/sn1-pre" bs=1 skip=$sn1_ofs count=40 \
170     &> /dev/null
171 dd if="$TEST_IMG.v2.orig" of="$TEST_DIR/sn2-pre" bs=1 skip=$sn2_ofs count=40 \
172     &> /dev/null
173
174 # Extra data
175 dd if="$TEST_IMG.v2.orig" of="$TEST_DIR/sn0-extra" bs=1 \
176     skip=$((sn0_ofs + 40)) count=$sn0_eds &> /dev/null
177 dd if="$TEST_IMG.v2.orig" of="$TEST_DIR/sn1-extra" bs=1 \
178     skip=$((sn1_ofs + 40)) count=$sn1_eds &> /dev/null
179 dd if="$TEST_IMG.v2.orig" of="$TEST_DIR/sn2-extra" bs=1 \
180     skip=$((sn2_ofs + 40)) count=$sn2_eds &> /dev/null
181
182 # Data after extra data
183 dd if="$TEST_IMG.v2.orig" of="$TEST_DIR/sn0-post" bs=1 \
184     skip=$((sn0_ofs + 40 + sn0_eds)) count=$sn0_ids \
185     &> /dev/null
186 dd if="$TEST_IMG.v2.orig" of="$TEST_DIR/sn1-post" bs=1 \
187     skip=$((sn1_ofs + 40 + sn1_eds)) count=$sn1_ids \
188     &> /dev/null
189 dd if="$TEST_IMG.v2.orig" of="$TEST_DIR/sn2-post" bs=1 \
190     skip=$((sn2_ofs + 40 + sn2_eds)) count=$sn2_ids \
191     &> /dev/null
192
193 # Amend them, one by one
194 # Set sn0's extra data size to 0
195 poke_file "$TEST_DIR/sn0-pre" 36 '\x00\x00\x00\x00'
196 truncate -s 0 "$TEST_DIR/sn0-extra"
197 # Grow sn0-post to pad
198 truncate -s $(($(snapshot_table_entry_size "$TEST_DIR/sn0-pre") - 40)) \
199     "$TEST_DIR/sn0-post"
200
201 # Set sn1's extra data size to 42
202 poke_file "$TEST_DIR/sn1-pre" 36 '\x00\x00\x00\x2a'
203 truncate -s 42 "$TEST_DIR/sn1-extra"
204 poke_file "$TEST_DIR/sn1-extra" 16 'very important data'
205 # Grow sn1-post to pad
206 truncate -s $(($(snapshot_table_entry_size "$TEST_DIR/sn1-pre") - 82)) \
207     "$TEST_DIR/sn1-post"
208
209 # Set sn2's extra data size to 8
210 poke_file "$TEST_DIR/sn2-pre" 36 '\x00\x00\x00\x08'
211 truncate -s 8 "$TEST_DIR/sn2-extra"
212 # Grow sn2-post to pad
213 truncate -s $(($(snapshot_table_entry_size "$TEST_DIR/sn2-pre") - 48)) \
214     "$TEST_DIR/sn2-post"
215
216 # Construct snapshot table
217 cat "$TEST_DIR"/sn0-{pre,extra,post} \
218     "$TEST_DIR"/sn1-{pre,extra,post} \
219     "$TEST_DIR"/sn2-{pre,extra,post} \
220     | dd of="$TEST_IMG.v2.orig" bs=1 seek=$sn_table_ofs conv=notrunc \
221           &> /dev/null
222
223 # Done!
224 TEST_IMG="$TEST_IMG.v2.orig" _check_test_img
225 print_snapshot_table "$TEST_IMG.v2.orig"
226
227 echo
228 echo '=== Upgrade to v3 ==='
229 echo
230
231 cp "$TEST_IMG.v2.orig" "$TEST_IMG.v3.orig"
232 $QEMU_IMG amend -o compat=1.1 "$TEST_IMG.v3.orig"
233 TEST_IMG="$TEST_IMG.v3.orig" _check_test_img
234 print_snapshot_table "$TEST_IMG.v3.orig"
235
236 echo
237 echo '=== Repair botched v3 ==='
238 echo
239
240 # Force the v2 file to be v3.  v3 requires each snapshot table entry
241 # to have at least 16 bytes of extra data, so it will not comply to
242 # the qcow2 v3 specification; but we can fix that.
243 cp "$TEST_IMG.v2.orig" "$TEST_IMG"
244
245 # Set version
246 poke_file "$TEST_IMG" 4 '\x00\x00\x00\x03'
247 # Increase header length (necessary for v3)
248 poke_file "$TEST_IMG" 100 '\x00\x00\x00\x68'
249 # Set refcount order (necessary for v3)
250 poke_file "$TEST_IMG" 96 '\x00\x00\x00\x04'
251
252 _check_test_img -r all
253 print_snapshot_table "$TEST_IMG"
254
255
256 # From now on, just test the qcow2 version we are supposed to test.
257 # (v3 by default, v2 by choice through $IMGOPTS.)
258 # That works because we always write all known extra data when
259 # updating the snapshot table, independent of the version.
260
261 if echo "$IMGOPTS" | grep -q 'compat=\(0\.10\|v2\)' 2> /dev/null; then
262     subver=v2
263 else
264     subver=v3
265 fi
266
267 echo
268 echo '=== Add new snapshot ==='
269 echo
270
271 cp "$TEST_IMG.$subver.orig" "$TEST_IMG"
272 $QEMU_IMG snapshot -c sn3 "$TEST_IMG"
273 _check_test_img
274 print_snapshot_table "$TEST_IMG"
275
276 echo
277 echo '=== Remove different snapshots ==='
278
279 for sn in sn0 sn1 sn2; do
280     echo
281     echo "--- $sn ---"
282
283     cp "$TEST_IMG.$subver.orig" "$TEST_IMG"
284     $QEMU_IMG snapshot -d $sn "$TEST_IMG"
285     _check_test_img
286     print_snapshot_table "$TEST_IMG"
287 done
288
289 echo
290 echo '=== Reject too much unknown extra data ==='
291 echo
292
293 cp "$TEST_IMG.$subver.orig" "$TEST_IMG"
294 $QEMU_IMG snapshot -c sn3 "$TEST_IMG"
295
296 sn_table_ofs=$(peek_file_be "$TEST_IMG" 64 8)
297 sn0_ofs=$sn_table_ofs
298 sn1_ofs=$((sn0_ofs + $(snapshot_table_entry_size "$TEST_IMG" $sn0_ofs)))
299 sn2_ofs=$((sn1_ofs + $(snapshot_table_entry_size "$TEST_IMG" $sn1_ofs)))
300 sn3_ofs=$((sn2_ofs + $(snapshot_table_entry_size "$TEST_IMG" $sn2_ofs)))
301
302 # 64 kB of extra data should be rejected
303 # (Note that this also induces a refcount error, because it spills
304 # over to the next cluster.  That's a good way to test that we can
305 # handle simultaneous snapshot table and refcount errors.)
306 poke_file "$TEST_IMG" $((sn3_ofs + 36)) '\x00\x01\x00\x00'
307
308 # Print error
309 _img_info
310 echo
311 _check_test_img
312 echo
313
314 # Should be repairable
315 _check_test_img -r all
316
317 echo
318 echo '=== Snapshot table too big ==='
319 echo
320
321 sn_table_ofs=$(peek_file_be "$TEST_IMG.v3.orig" 64 8)
322
323 # Fill a snapshot with 1 kB of extra data, a 65535-char ID, and a
324 # 65535-char name, and repeat it as many times as necessary to fill
325 # 64 MB (the maximum supported by qemu)
326
327 touch "$TEST_DIR/sn0"
328
329 # Full size (fixed + extra + ID + name + padding)
330 sn_size=$((40 + 1024 + 65535 + 65535 + 2))
331
332 # We only need the fixed part, though.
333 truncate -s 40 "$TEST_DIR/sn0"
334
335 # 65535-char ID string
336 poke_file "$TEST_DIR/sn0" 12 '\xff\xff'
337 # 65535-char name
338 poke_file "$TEST_DIR/sn0" 14 '\xff\xff'
339 # 1 kB of extra data
340 poke_file "$TEST_DIR/sn0" 36 '\x00\x00\x04\x00'
341
342 # Create test image
343 _make_test_img 64M
344
345 # Hook up snapshot table somewhere safe (at 1 MB)
346 poke_file "$TEST_IMG" 64 '\x00\x00\x00\x00\x00\x10\x00\x00'
347
348 offset=1048576
349 size_written=0
350 sn_count=0
351 while [ $size_written -le $((64 * 1048576)) ]; do
352     dd if="$TEST_DIR/sn0" of="$TEST_IMG" bs=1 seek=$offset conv=notrunc \
353         &> /dev/null
354     offset=$((offset + sn_size))
355     size_written=$((size_written + sn_size))
356     sn_count=$((sn_count + 1))
357 done
358 truncate -s "$offset" "$TEST_IMG"
359
360 # Give the last snapshot (the one to be removed) an L1 table so we can
361 # see how that is handled when repairing the image
362 # (Put it two clusters before 1 MB, and one L2 table one cluster
363 # before 1 MB)
364 poke_file "$TEST_IMG" $((offset - sn_size + 0)) \
365     '\x00\x00\x00\x00\x00\x0e\x00\x00'
366 poke_file "$TEST_IMG" $((offset - sn_size + 8)) \
367     '\x00\x00\x00\x01'
368
369 # Hook up the L2 table
370 poke_file "$TEST_IMG" $((1048576 - 2 * 65536)) \
371     '\x80\x00\x00\x00\x00\x0f\x00\x00'
372
373 # Make sure all of the clusters we just hooked up are allocated:
374 # - The snapshot table
375 # - The last snapshot's L1 and L2 table
376 refblock0_allocate $((1048576 - 2 * 65536)) $offset
377
378 poke_file "$TEST_IMG" 60 \
379     "$(printf '%08x' $sn_count | sed -e 's/\(..\)/\\x\1/g')"
380
381 # Print error
382 _img_info
383 echo
384 _check_test_img
385 echo
386
387 # Should be repairable
388 _check_test_img -r all
389
390 echo
391 echo "$((sn_count - 1)) snapshots should remain:"
392 echo "  qemu-img info reports $(_img_info | grep -c '^ \{34\}') snapshots"
393 echo "  Image header reports $(peek_file_be "$TEST_IMG" 60 4) snapshots"
394
395 echo
396 echo '=== Snapshot table too big with one entry with too much extra data ==='
397 echo
398
399 # For this test, we reuse the image from the previous case, which has
400 # a snapshot table that is right at the limit.
401 # Our layout looks like this:
402 # - (a number of snapshot table entries)
403 # - One snapshot with $extra_data_size extra data
404 # - One normal snapshot that breaks the 64 MB boundary
405 # - One normal snapshot beyond the 64 MB boundary
406 #
407 # $extra_data_size is calculated so that simply by virtue of it
408 # decreasing to 1 kB, the penultimate snapshot will fit into 64 MB
409 # limit again.  The final snapshot will always be beyond the limit, so
410 # that we can see that the repair algorithm does still determine the
411 # limit to be somewhere, even when truncating one snapshot's extra
412 # data.
413
414 # The last case has removed the last snapshot, so calculate
415 # $old_offset to get the current image's real length
416 old_offset=$((offset - sn_size))
417
418 # The layout from the previous test had one snapshot beyond the 64 MB
419 # limit; we want the same (after the oversized extra data has been
420 # truncated to 1 kB), so we drop the last three snapshots and
421 # construct them from scratch.
422 offset=$((offset - 3 * sn_size))
423 sn_count=$((sn_count - 3))
424
425 # Assuming we had already written one of the three snapshots
426 # (necessary so we can calculate $extra_data_size next).
427 size_written=$((size_written - 2 * sn_size))
428
429 # Increase the extra data size so we go past the limit
430 # (The -1024 comes from the 1 kB of extra data we already have)
431 extra_data_size=$((64 * 1048576 + 8 - sn_size - (size_written - 1024)))
432
433 poke_file "$TEST_IMG" $((offset + 36)) \
434     "$(printf '%08x' $extra_data_size | sed -e 's/\(..\)/\\x\1/g')"
435
436 offset=$((offset + sn_size - 1024 + extra_data_size))
437 size_written=$((size_written - 1024 + extra_data_size))
438 sn_count=$((sn_count + 1))
439
440 # Write the two normal snapshots
441 for ((i = 0; i < 2; i++)); do
442     dd if="$TEST_DIR/sn0" of="$TEST_IMG" bs=1 seek=$offset conv=notrunc \
443         &> /dev/null
444     offset=$((offset + sn_size))
445     size_written=$((size_written + sn_size))
446     sn_count=$((sn_count + 1))
447
448     if [ $i = 0 ]; then
449         # Check that the penultimate snapshot is beyond the 64 MB limit
450         echo "Snapshot table size should equal $((64 * 1048576 + 8)):" \
451             $size_written
452         echo
453     fi
454 done
455
456 truncate -s $offset "$TEST_IMG"
457 refblock0_allocate $old_offset $offset
458
459 poke_file "$TEST_IMG" 60 \
460     "$(printf '%08x' $sn_count | sed -e 's/\(..\)/\\x\1/g')"
461
462 # Print error
463 _img_info
464 echo
465 _check_test_img
466 echo
467
468 # Just truncating the extra data should be sufficient to shorten the
469 # snapshot table so only one snapshot exceeds the extra size
470 _check_test_img -r all
471
472 echo
473 echo '=== Too many snapshots ==='
474 echo
475
476 # Create a v2 image, for speeds' sake: All-zero snapshot table entries
477 # are only valid in v2.
478 IMGOPTS='compat=0.10' _make_test_img 64M
479
480 # Hook up snapshot table somewhere safe (at 1 MB)
481 poke_file "$TEST_IMG" 64 '\x00\x00\x00\x00\x00\x10\x00\x00'
482 # "Create" more than 65536 snapshots (twice that many here)
483 poke_file "$TEST_IMG" 60 '\x00\x02\x00\x00'
484
485 # 40-byte all-zero snapshot table entries are valid snapshots, but
486 # only in v2 (v3 needs 16 bytes of extra data, so we would have to
487 # write 131072x '\x10').
488 truncate -s $((1048576 + 40 * 131072)) "$TEST_IMG"
489
490 # But let us give one of the snapshots to be removed an L1 table so
491 # we can see how that is handled when repairing the image.
492 # (Put it two clusters before 1 MB, and one L2 table one cluster
493 # before 1 MB)
494 poke_file "$TEST_IMG" $((1048576 + 40 * 65536 + 0)) \
495     '\x00\x00\x00\x00\x00\x0e\x00\x00'
496 poke_file "$TEST_IMG" $((1048576 + 40 * 65536 + 8)) \
497     '\x00\x00\x00\x01'
498
499 # Hook up the L2 table
500 poke_file "$TEST_IMG" $((1048576 - 2 * 65536)) \
501     '\x80\x00\x00\x00\x00\x0f\x00\x00'
502
503 # Make sure all of the clusters we just hooked up are allocated:
504 # - The snapshot table
505 # - The last snapshot's L1 and L2 table
506 refblock0_allocate $((1048576 - 2 * 65536)) $((1048576 + 40 * 131072))
507
508 # Print error
509 _img_info
510 echo
511 _check_test_img
512 echo
513
514 # Should be repairable
515 _check_test_img -r all
516
517 echo
518 echo '65536 snapshots should remain:'
519 echo "  qemu-img info reports $(_img_info | grep -c '^ \{34\}') snapshots"
520 echo "  Image header reports $(peek_file_be "$TEST_IMG" 60 4) snapshots"
521
522 # success, all done
523 echo "*** done"
524 status=0
This page took 0.052501 seconds and 4 git commands to generate.