master
text 307 lines 10.3 KB
Raw
1 #!/usr/bin/env bash
2 # group: rw quick
3 #
4 # Test qemu-img check for parallels format
5 #
6 # Copyright (C) 2022 Virtuozzo International GmbH
7 #
8 # This program is free software; you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation; either version 2 of the License, or
11 # (at your option) any later version.
12 #
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
17 #
18 # You should have received a copy of the GNU General Public License
19 # along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #
21
22 # creator
23 owner=alexander.ivanov@virtuozzo.com
24
25 seq=`basename $0`
26 echo "QA output created by $seq"
27
28 status=1 # failure is the default!
29
30 _cleanup()
31 {
32 _cleanup_test_img
33 }
34 trap "_cleanup; exit \$status" 0 1 2 3 15
35
36 # get standard environment, filters and checks
37 . ../common.rc
38 . ../common.filter
39
40 _supported_fmt parallels
41 _supported_proto file
42 _supported_os Linux
43
44 SIZE=$((4 * 1024 * 1024))
45 IMGFMT=parallels
46 CLUSTER_SIZE_OFFSET=28
47 BAT_ENTRIES_OFFSET=32
48 DATA_OFF_OFFSET=48
49 BAT_OFFSET=64
50
51 _make_test_img $SIZE
52
53 CLUSTER_SIZE=$(peek_file_le $TEST_IMG $CLUSTER_SIZE_OFFSET 4)
54 CLUSTER_SIZE=$((CLUSTER_SIZE * 512))
55 LAST_CLUSTER_OFF=$((SIZE - CLUSTER_SIZE))
56 LAST_CLUSTER=$((LAST_CLUSTER_OFF/CLUSTER_SIZE))
57
58 echo "== TEST OUT OF IMAGE CHECK =="
59
60 echo "== write pattern =="
61 { $QEMU_IO -c "write -P 0x11 0 $SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir
62
63 echo "== corrupt image =="
64 cluster=$(($LAST_CLUSTER + 2))
65 poke_file "$TEST_IMG" "$BAT_OFFSET" "\x$cluster\x00\x00\x00"
66
67 echo "== read corrupted image with repairing =="
68 { $QEMU_IO -c "read -P 0x00 0 $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir
69
70 # Clear image
71 _make_test_img $SIZE
72
73 echo "== TEST LEAK CHECK =="
74
75 echo "== write pattern to last cluster =="
76 echo "write -P 0x11 $LAST_CLUSTER_OFF $CLUSTER_SIZE"
77 { $QEMU_IO -c "write -P 0x11 $LAST_CLUSTER_OFF $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir
78
79 file_size=`stat --printf="%s" "$TEST_IMG"`
80 echo "file size: $file_size"
81
82 echo "== extend image by 1 cluster =="
83 fallocate -xl $((file_size + CLUSTER_SIZE)) "$TEST_IMG"
84
85 file_size=`stat --printf="%s" "$TEST_IMG"`
86 echo "file size: $file_size"
87
88 echo "== repair image =="
89 _check_test_img -r all
90
91 file_size=`stat --printf="%s" "$TEST_IMG"`
92 echo "file size: $file_size"
93
94 echo "== check last cluster =="
95 { $QEMU_IO -r -c "read -P 0x11 $LAST_CLUSTER_OFF $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir
96
97 # Clear image
98 _make_test_img $SIZE
99
100 echo "== TEST DUPLICATION CHECK =="
101
102 echo "== write pattern to whole image =="
103 { $QEMU_IO -c "write -P 0x11 0 $SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir
104
105 echo "== write another pattern to second cluster =="
106 { $QEMU_IO -c "write -P 0x55 $CLUSTER_SIZE $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir
107
108 echo "== check second cluster =="
109 { $QEMU_IO -r -c "read -P 0x55 $CLUSTER_SIZE $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir
110
111
112 echo "== corrupt image =="
113 poke_file "$TEST_IMG" "$(($BAT_OFFSET + 4))" "\x01\x00\x00\x00"
114
115 echo "== check second cluster =="
116 { $QEMU_IO -r -c "read -P 0x11 $CLUSTER_SIZE $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir
117
118 echo "== repair image =="
119 _check_test_img -r all
120
121 echo "== check the first cluster =="
122 { $QEMU_IO -r -c "read -P 0x11 0 $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir
123
124 echo "== check second cluster =="
125 { $QEMU_IO -r -c "read -P 0x11 $CLUSTER_SIZE $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir
126
127 echo "== write another pattern to the first clusters =="
128 { $QEMU_IO -c "write -P 0x66 0 $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir
129
130 echo "== check the first cluster =="
131 { $QEMU_IO -r -c "read -P 0x66 0 $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir
132
133 echo "== check the second cluster (deduplicated) =="
134 { $QEMU_IO -r -c "read -P 0x11 $CLUSTER_SIZE $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir
135
136 # Clear image
137 _make_test_img $SIZE
138
139 echo "== TEST DUPLICATION SELF-CURE =="
140
141 echo "== write pattern to whole image =="
142 { $QEMU_IO -c "write -P 0x11 0 $SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir
143
144 echo "== write another pattern to second cluster =="
145 { $QEMU_IO -c "write -P 0x55 $CLUSTER_SIZE $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir
146
147 echo "== check second cluster =="
148 { $QEMU_IO -r -c "read -P 0x55 $CLUSTER_SIZE $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir
149
150
151 echo "== corrupt image =="
152 poke_file "$TEST_IMG" "$(($BAT_OFFSET + 4))" "\x01\x00\x00\x00"
153
154 echo "== check second cluster =="
155 { $QEMU_IO -r -c "read -P 0x11 $CLUSTER_SIZE $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir
156
157 echo "== check the first cluster with self-repair =="
158 { $QEMU_IO -c "read -P 0x11 0 $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir
159
160 echo "== check second cluster =="
161 { $QEMU_IO -r -c "read -P 0x11 $CLUSTER_SIZE $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir
162
163 echo "== write another pattern to the first clusters =="
164 { $QEMU_IO -c "write -P 0x66 0 $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir
165
166 echo "== check the first cluster =="
167 { $QEMU_IO -r -c "read -P 0x66 0 $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir
168
169 echo "== check the second cluster (deduplicated) =="
170 { $QEMU_IO -r -c "read -P 0x11 $CLUSTER_SIZE $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir
171
172 # Clear image
173 _make_test_img $SIZE
174
175 echo "== TEST DATA_OFF CHECK =="
176
177 echo "== write pattern to first cluster =="
178 { $QEMU_IO -c "write -P 0x55 0 $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir
179
180 echo "== spoil data_off field =="
181 poke_file "$TEST_IMG" "$DATA_OFF_OFFSET" "\xff\xff\xff\xff"
182
183 echo "== check first cluster =="
184 { $QEMU_IO -c "read -P 0x55 0 $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir
185
186 # Clear image
187 _make_test_img $SIZE
188
189 echo "== TEST DATA_OFF THROUGH REPAIR =="
190
191 echo "== write pattern to first cluster =="
192 { $QEMU_IO -c "write -P 0x55 0 $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir
193
194 echo "== spoil data_off field =="
195 poke_file "$TEST_IMG" "$DATA_OFF_OFFSET" "\xff\xff\xff\xff"
196
197 echo "== repair image =="
198 _check_test_img -r all
199
200 echo "== check first cluster =="
201 { $QEMU_IO -r -c "read -P 0x55 0 $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir
202
203 # Clear image
204 _make_test_img $SIZE
205
206 echo "== TEST HUGE BAT TABLE OPEN =="
207
208 # Overflows a single read request, but stays under parallels_open()'s
209 # own catalog-size cap.
210 BAT_ENTRIES=536870896
211 HEADER_SIZE=$((64 + 4 * BAT_ENTRIES))
212
213 echo "== advertise a BAT table larger than BDRV_REQUEST_MAX_BYTES =="
214 poke_file "$TEST_IMG" "$BAT_ENTRIES_OFFSET" "\xf0\xff\xff\x1f"
215
216 echo "== grow the file to match, without writing real data =="
217 truncate -s $HEADER_SIZE "$TEST_IMG"
218
219 echo "== open must succeed: the header/BAT read is chunked =="
220 _img_info
221
222 echo "== an unallocated cluster still reads as zeroes =="
223 { $QEMU_IO -r -c "read -P 0x00 0 $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir
224
225 # Clear image
226 _make_test_img $SIZE
227
228 echo "== TEST OVERSIZED VIRTUAL DISK CHECK =="
229
230 BAT_ENTRIES_OFFSET=32
231 NB_SECTORS_OFFSET=36
232
233 TRACKS=$(peek_file_le $TEST_IMG $CLUSTER_SIZE_OFFSET 4)
234 BAT_ENTRIES=$(peek_file_le $TEST_IMG $BAT_ENTRIES_OFFSET 4)
235 COVERED_SECTORS=$((BAT_ENTRIES * TRACKS))
236
237 echo "== advertise one more cluster than the BAT covers =="
238 poke_file_le "$TEST_IMG" $NB_SECTORS_OFFSET 8 $((COVERED_SECTORS + TRACKS))
239
240 echo "== open must fail cleanly instead of aborting =="
241 _img_info
242
243 echo "== write into the uncovered range must fail cleanly too =="
244 { $QEMU_IO -c "write -P 0x41 $((COVERED_SECTORS * 512)) $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir
245
246 # Clear image
247 _make_test_img $SIZE
248
249 echo "== TEST BAT ENTRY POINTING OUTSIDE IMAGE =="
250
251 echo "== corrupt image: point first cluster far outside the file =="
252 poke_file_le "$TEST_IMG" $BAT_OFFSET 4 1000000
253
254 echo "== read-only read must return zeroes, not an I/O error =="
255 { $QEMU_IO -r -c "read -P 0x00 0 $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir
256
257 echo "== write must allocate a fresh cluster instead of trusting the entry =="
258 { $QEMU_IO -c "write -P 0x77 0 $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir
259
260 echo "== file did not grow anywhere near the bogus offset =="
261 file_size=`stat --printf="%s" "$TEST_IMG"`
262 if [ "$file_size" -lt $((16 * 1024 * 1024)) ]; then
263 echo "file size sane: yes"
264 else
265 echo "file size sane: no ($file_size bytes)"
266 fi
267
268 echo "== data reads back correctly =="
269 { $QEMU_IO -r -c "read -P 0x77 0 $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir
270
271 # Clear image, with a small cluster size so the BAT table itself spans
272 # more than one cluster and there is room to point before data_off.
273 _make_test_img -o cluster_size=512 65536
274
275 SMALL_CLUSTER_SIZE=$(peek_file_le $TEST_IMG $CLUSTER_SIZE_OFFSET 4)
276 SMALL_CLUSTER_SIZE=$((SMALL_CLUSTER_SIZE * 512))
277 DATA_OFF=$(peek_file_le $TEST_IMG $DATA_OFF_OFFSET 4)
278 echo "cluster size: $SMALL_CLUSTER_SIZE, data offset (sectors): $DATA_OFF"
279
280 # Cluster index 1 starts at this byte offset, which must be < data_off
281 # in sectors * 512 for this test to actually exercise the bug.
282 VICTIM_OFFSET=$SMALL_CLUSTER_SIZE
283
284 echo "== TEST BAT ENTRY POINTING BEFORE DATA AREA =="
285
286 echo "== corrupt image: point first cluster into the BAT table itself =="
287 poke_file_le "$TEST_IMG" $BAT_OFFSET 4 1
288
289 echo "== qemu-img check detects it without repairing =="
290 _check_test_img
291
292 echo "== bytes at the victim offset before write =="
293 echo "$(peek_file_le "$TEST_IMG" $VICTIM_OFFSET 4)"
294
295 echo "== write must allocate a fresh cluster instead of clobbering the BAT =="
296 { $QEMU_IO -c "write -P 0x88 0 $SMALL_CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir
297
298 echo "== bytes at the victim offset are unchanged =="
299 echo "$(peek_file_le "$TEST_IMG" $VICTIM_OFFSET 4)"
300
301 echo "== data reads back correctly =="
302 { $QEMU_IO -r -c "read -P 0x88 0 $SMALL_CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir
303
304 # success, all done
305 echo "*** done"
306 rm -f $seq.full
307 status=0