]>
Commit | Line | Data |
---|---|---|
f53b25df HR |
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 | owner=mreitz@redhat.com | |
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 | ||
e696f335 | 43 | # This tests qcow2-specific low-level functionality |
f53b25df HR |
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 | |
3be2024a HR |
50 | # As for data files, they do not support snapshots at all. |
51 | _unsupported_imgopts 'refcount_bits=\([^1]\|.\([^6]\|$\)\)' data_file | |
f53b25df HR |
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 |