]>
Commit | Line | Data |
---|---|---|
29b0d019 NP |
1 | #!/bin/sh |
2 | # | |
3 | # Copyright (c) 2008 Nicolas Pitre | |
4 | # | |
5 | ||
6 | test_description='resilience to pack corruptions with redundant objects' | |
f40a6934 ÆAB |
7 | |
8 | TEST_PASSES_SANITIZE_LEAK=true | |
29b0d019 NP |
9 | . ./test-lib.sh |
10 | ||
11 | # Note: the test objects are created with knowledge of their pack encoding | |
12 | # to ensure good code path coverage, and to facilitate direct alteration | |
13 | # later on. The assumed characteristics are: | |
14 | # | |
15 | # 1) blob_2 is a delta with blob_1 for base and blob_3 is a delta with blob2 | |
16 | # for base, such that blob_3 delta depth is 2; | |
17 | # | |
18 | # 2) the bulk of object data is uncompressible so the text part remains | |
19 | # visible; | |
20 | # | |
21 | # 3) object header is always 2 bytes. | |
22 | ||
23 | create_test_files() { | |
c680668d NTND |
24 | test-tool genrandom "foo" 2000 > file_1 && |
25 | test-tool genrandom "foo" 1800 > file_2 && | |
26 | test-tool genrandom "foo" 1800 > file_3 && | |
29b0d019 NP |
27 | echo " base " >> file_1 && |
28 | echo " delta1 " >> file_2 && | |
29 | echo " delta delta2 " >> file_3 && | |
c680668d NTND |
30 | test-tool genrandom "bar" 150 >> file_2 && |
31 | test-tool genrandom "baz" 100 >> file_3 | |
29b0d019 NP |
32 | } |
33 | ||
34 | create_new_pack() { | |
35 | rm -rf .git && | |
36 | git init && | |
a64d080f EP |
37 | blob_1=$(git hash-object -t blob -w file_1) && |
38 | blob_2=$(git hash-object -t blob -w file_2) && | |
39 | blob_3=$(git hash-object -t blob -w file_3) && | |
40 | pack=$(printf "$blob_1\n$blob_2\n$blob_3\n" | | |
41 | git pack-objects $@ .git/objects/pack/pack) && | |
29b0d019 NP |
42 | pack=".git/objects/pack/pack-${pack}" && |
43 | git verify-pack -v ${pack}.pack | |
44 | } | |
45 | ||
538cf6b6 | 46 | do_repack() { |
a64d080f EP |
47 | pack=$(printf "$blob_1\n$blob_2\n$blob_3\n" | |
48 | git pack-objects $@ .git/objects/pack/pack) && | |
538cf6b6 NP |
49 | pack=".git/objects/pack/pack-${pack}" |
50 | } | |
51 | ||
29b0d019 | 52 | do_corrupt_object() { |
a64d080f | 53 | ofs=$(git show-index < ${pack}.idx | grep $1 | cut -f1 -d" ") && |
29b0d019 NP |
54 | ofs=$(($ofs + $2)) && |
55 | chmod +w ${pack}.pack && | |
50b72ede | 56 | dd of=${pack}.pack bs=1 conv=notrunc seek=$ofs && |
29b0d019 NP |
57 | test_must_fail git verify-pack ${pack}.pack |
58 | } | |
59 | ||
b689ccf6 JS |
60 | printf '\0' > zero |
61 | ||
aac86405 JC |
62 | test_expect_success 'initial setup validation' ' |
63 | create_test_files && | |
64 | create_new_pack && | |
65 | git prune-packed && | |
66 | git cat-file blob $blob_1 > /dev/null && | |
67 | git cat-file blob $blob_2 > /dev/null && | |
68 | git cat-file blob $blob_3 > /dev/null | |
69 | ' | |
70 | ||
71 | test_expect_success 'create corruption in header of first object' ' | |
72 | do_corrupt_object $blob_1 0 < zero && | |
73 | test_must_fail git cat-file blob $blob_1 > /dev/null && | |
74 | test_must_fail git cat-file blob $blob_2 > /dev/null && | |
75 | test_must_fail git cat-file blob $blob_3 > /dev/null | |
76 | ' | |
77 | ||
78 | test_expect_success '... but having a loose copy allows for full recovery' ' | |
79 | mv ${pack}.idx tmp && | |
80 | git hash-object -t blob -w file_1 && | |
81 | mv tmp ${pack}.idx && | |
82 | git cat-file blob $blob_1 > /dev/null && | |
83 | git cat-file blob $blob_2 > /dev/null && | |
84 | git cat-file blob $blob_3 > /dev/null | |
85 | ' | |
86 | ||
87 | test_expect_success '... and loose copy of first delta allows for partial recovery' ' | |
88 | git prune-packed && | |
89 | test_must_fail git cat-file blob $blob_2 > /dev/null && | |
90 | mv ${pack}.idx tmp && | |
91 | git hash-object -t blob -w file_2 && | |
92 | mv tmp ${pack}.idx && | |
93 | test_must_fail git cat-file blob $blob_1 > /dev/null && | |
94 | git cat-file blob $blob_2 > /dev/null && | |
95 | git cat-file blob $blob_3 > /dev/null | |
96 | ' | |
97 | ||
98 | test_expect_success 'create corruption in data of first object' ' | |
99 | create_new_pack && | |
100 | git prune-packed && | |
101 | chmod +w ${pack}.pack && | |
102 | perl -i.bak -pe "s/ base /abcdef/" ${pack}.pack && | |
103 | test_must_fail git cat-file blob $blob_1 > /dev/null && | |
104 | test_must_fail git cat-file blob $blob_2 > /dev/null && | |
105 | test_must_fail git cat-file blob $blob_3 > /dev/null | |
106 | ' | |
107 | ||
108 | test_expect_success '... but having a loose copy allows for full recovery' ' | |
109 | mv ${pack}.idx tmp && | |
110 | git hash-object -t blob -w file_1 && | |
111 | mv tmp ${pack}.idx && | |
112 | git cat-file blob $blob_1 > /dev/null && | |
113 | git cat-file blob $blob_2 > /dev/null && | |
114 | git cat-file blob $blob_3 > /dev/null | |
115 | ' | |
116 | ||
117 | test_expect_success '... and loose copy of second object allows for partial recovery' ' | |
118 | git prune-packed && | |
119 | test_must_fail git cat-file blob $blob_2 > /dev/null && | |
120 | mv ${pack}.idx tmp && | |
121 | git hash-object -t blob -w file_2 && | |
122 | mv tmp ${pack}.idx && | |
123 | test_must_fail git cat-file blob $blob_1 > /dev/null && | |
124 | git cat-file blob $blob_2 > /dev/null && | |
125 | git cat-file blob $blob_3 > /dev/null | |
126 | ' | |
127 | ||
128 | test_expect_success 'create corruption in header of first delta' ' | |
129 | create_new_pack && | |
130 | git prune-packed && | |
131 | do_corrupt_object $blob_2 0 < zero && | |
132 | git cat-file blob $blob_1 > /dev/null && | |
133 | test_must_fail git cat-file blob $blob_2 > /dev/null && | |
134 | test_must_fail git cat-file blob $blob_3 > /dev/null | |
135 | ' | |
136 | ||
137 | test_expect_success '... but having a loose copy allows for full recovery' ' | |
138 | mv ${pack}.idx tmp && | |
139 | git hash-object -t blob -w file_2 && | |
140 | mv tmp ${pack}.idx && | |
141 | git cat-file blob $blob_1 > /dev/null && | |
142 | git cat-file blob $blob_2 > /dev/null && | |
143 | git cat-file blob $blob_3 > /dev/null | |
144 | ' | |
145 | ||
146 | test_expect_success '... and then a repack "clears" the corruption' ' | |
147 | do_repack && | |
148 | git prune-packed && | |
149 | git verify-pack ${pack}.pack && | |
150 | git cat-file blob $blob_1 > /dev/null && | |
151 | git cat-file blob $blob_2 > /dev/null && | |
152 | git cat-file blob $blob_3 > /dev/null | |
153 | ' | |
154 | ||
155 | test_expect_success 'create corruption in data of first delta' ' | |
156 | create_new_pack && | |
157 | git prune-packed && | |
158 | chmod +w ${pack}.pack && | |
159 | perl -i.bak -pe "s/ delta1 /abcdefgh/" ${pack}.pack && | |
160 | git cat-file blob $blob_1 > /dev/null && | |
161 | test_must_fail git cat-file blob $blob_2 > /dev/null && | |
162 | test_must_fail git cat-file blob $blob_3 > /dev/null | |
163 | ' | |
164 | ||
165 | test_expect_success '... but having a loose copy allows for full recovery' ' | |
166 | mv ${pack}.idx tmp && | |
167 | git hash-object -t blob -w file_2 && | |
168 | mv tmp ${pack}.idx && | |
169 | git cat-file blob $blob_1 > /dev/null && | |
170 | git cat-file blob $blob_2 > /dev/null && | |
171 | git cat-file blob $blob_3 > /dev/null | |
172 | ' | |
173 | ||
174 | test_expect_success '... and then a repack "clears" the corruption' ' | |
175 | do_repack && | |
176 | git prune-packed && | |
177 | git verify-pack ${pack}.pack && | |
178 | git cat-file blob $blob_1 > /dev/null && | |
179 | git cat-file blob $blob_2 > /dev/null && | |
180 | git cat-file blob $blob_3 > /dev/null | |
181 | ' | |
182 | ||
183 | test_expect_success 'corruption in delta base reference of first delta (OBJ_REF_DELTA)' ' | |
184 | create_new_pack && | |
185 | git prune-packed && | |
186 | do_corrupt_object $blob_2 2 < zero && | |
187 | git cat-file blob $blob_1 > /dev/null && | |
188 | test_must_fail git cat-file blob $blob_2 > /dev/null && | |
189 | test_must_fail git cat-file blob $blob_3 > /dev/null | |
190 | ' | |
191 | ||
192 | test_expect_success '... but having a loose copy allows for full recovery' ' | |
193 | mv ${pack}.idx tmp && | |
194 | git hash-object -t blob -w file_2 && | |
195 | mv tmp ${pack}.idx && | |
196 | git cat-file blob $blob_1 > /dev/null && | |
197 | git cat-file blob $blob_2 > /dev/null && | |
198 | git cat-file blob $blob_3 > /dev/null | |
199 | ' | |
200 | ||
201 | test_expect_success '... and then a repack "clears" the corruption' ' | |
202 | do_repack && | |
203 | git prune-packed && | |
204 | git verify-pack ${pack}.pack && | |
205 | git cat-file blob $blob_1 > /dev/null && | |
206 | git cat-file blob $blob_2 > /dev/null && | |
207 | git cat-file blob $blob_3 > /dev/null | |
208 | ' | |
209 | ||
210 | test_expect_success 'corruption #0 in delta base reference of first delta (OBJ_OFS_DELTA)' ' | |
211 | create_new_pack --delta-base-offset && | |
212 | git prune-packed && | |
213 | do_corrupt_object $blob_2 2 < zero && | |
214 | git cat-file blob $blob_1 > /dev/null && | |
215 | test_must_fail git cat-file blob $blob_2 > /dev/null && | |
216 | test_must_fail git cat-file blob $blob_3 > /dev/null | |
217 | ' | |
218 | ||
219 | test_expect_success '... but having a loose copy allows for full recovery' ' | |
220 | mv ${pack}.idx tmp && | |
221 | git hash-object -t blob -w file_2 && | |
222 | mv tmp ${pack}.idx && | |
223 | git cat-file blob $blob_1 > /dev/null && | |
224 | git cat-file blob $blob_2 > /dev/null && | |
225 | git cat-file blob $blob_3 > /dev/null | |
226 | ' | |
227 | ||
228 | test_expect_success '... and then a repack "clears" the corruption' ' | |
229 | do_repack --delta-base-offset && | |
230 | git prune-packed && | |
231 | git verify-pack ${pack}.pack && | |
232 | git cat-file blob $blob_1 > /dev/null && | |
233 | git cat-file blob $blob_2 > /dev/null && | |
234 | git cat-file blob $blob_3 > /dev/null | |
235 | ' | |
236 | ||
237 | test_expect_success 'corruption #1 in delta base reference of first delta (OBJ_OFS_DELTA)' ' | |
238 | create_new_pack --delta-base-offset && | |
239 | git prune-packed && | |
240 | printf "\001" | do_corrupt_object $blob_2 2 && | |
241 | git cat-file blob $blob_1 > /dev/null && | |
242 | test_must_fail git cat-file blob $blob_2 > /dev/null && | |
243 | test_must_fail git cat-file blob $blob_3 > /dev/null | |
244 | ' | |
245 | ||
246 | test_expect_success '... but having a loose copy allows for full recovery' ' | |
247 | mv ${pack}.idx tmp && | |
248 | git hash-object -t blob -w file_2 && | |
249 | mv tmp ${pack}.idx && | |
250 | git cat-file blob $blob_1 > /dev/null && | |
251 | git cat-file blob $blob_2 > /dev/null && | |
252 | git cat-file blob $blob_3 > /dev/null | |
253 | ' | |
254 | ||
255 | test_expect_success '... and then a repack "clears" the corruption' ' | |
256 | do_repack --delta-base-offset && | |
257 | git prune-packed && | |
258 | git verify-pack ${pack}.pack && | |
259 | git cat-file blob $blob_1 > /dev/null && | |
260 | git cat-file blob $blob_2 > /dev/null && | |
261 | git cat-file blob $blob_3 > /dev/null | |
262 | ' | |
263 | ||
264 | test_expect_success '... and a redundant pack allows for full recovery too' ' | |
265 | do_corrupt_object $blob_2 2 < zero && | |
266 | git cat-file blob $blob_1 > /dev/null && | |
267 | test_must_fail git cat-file blob $blob_2 > /dev/null && | |
268 | test_must_fail git cat-file blob $blob_3 > /dev/null && | |
269 | mv ${pack}.idx tmp && | |
270 | git hash-object -t blob -w file_1 && | |
271 | git hash-object -t blob -w file_2 && | |
272 | printf "$blob_1\n$blob_2\n" | git pack-objects .git/objects/pack/pack && | |
273 | git prune-packed && | |
274 | mv tmp ${pack}.idx && | |
275 | git cat-file blob $blob_1 > /dev/null && | |
276 | git cat-file blob $blob_2 > /dev/null && | |
277 | git cat-file blob $blob_3 > /dev/null | |
278 | ' | |
279 | ||
280 | test_expect_success 'corruption of delta base reference pointing to wrong object' ' | |
281 | create_new_pack --delta-base-offset && | |
282 | git prune-packed && | |
283 | printf "\220\033" | do_corrupt_object $blob_3 2 && | |
284 | git cat-file blob $blob_1 >/dev/null && | |
285 | git cat-file blob $blob_2 >/dev/null && | |
286 | test_must_fail git cat-file blob $blob_3 >/dev/null | |
287 | ' | |
288 | ||
289 | test_expect_success '... but having a loose copy allows for full recovery' ' | |
290 | mv ${pack}.idx tmp && | |
291 | git hash-object -t blob -w file_3 && | |
292 | mv tmp ${pack}.idx && | |
293 | git cat-file blob $blob_1 > /dev/null && | |
294 | git cat-file blob $blob_2 > /dev/null && | |
295 | git cat-file blob $blob_3 > /dev/null | |
296 | ' | |
297 | ||
298 | test_expect_success '... and then a repack "clears" the corruption' ' | |
299 | do_repack --delta-base-offset --no-reuse-delta && | |
300 | git prune-packed && | |
301 | git verify-pack ${pack}.pack && | |
302 | git cat-file blob $blob_1 > /dev/null && | |
303 | git cat-file blob $blob_2 > /dev/null && | |
304 | git cat-file blob $blob_3 > /dev/null | |
305 | ' | |
306 | ||
307 | test_expect_success 'corrupting header to have too small output buffer fails unpack' ' | |
308 | create_new_pack && | |
309 | git prune-packed && | |
310 | printf "\262\001" | do_corrupt_object $blob_1 0 && | |
311 | test_must_fail git cat-file blob $blob_1 > /dev/null && | |
312 | test_must_fail git cat-file blob $blob_2 > /dev/null && | |
313 | test_must_fail git cat-file blob $blob_3 > /dev/null | |
314 | ' | |
b3118bdc | 315 | |
9caf0107 JK |
316 | # \0 - empty base |
317 | # \1 - one byte in result | |
318 | # \1 - one literal byte (X) | |
aac86405 JC |
319 | test_expect_success 'apply good minimal delta' ' |
320 | printf "\0\1\1X" > minimal_delta && | |
321 | test-tool delta -p /dev/null minimal_delta /dev/null | |
322 | ' | |
9caf0107 JK |
323 | |
324 | # \0 - empty base | |
325 | # \1 - 1 byte in result | |
326 | # \2 - two literal bytes (one too many) | |
aac86405 JC |
327 | test_expect_success 'apply delta with too many literal bytes' ' |
328 | printf "\0\1\2XX" > too_big_literal && | |
329 | test_must_fail test-tool delta -p /dev/null too_big_literal /dev/null | |
330 | ' | |
9caf0107 | 331 | |
18f60f2d | 332 | # \4 - four bytes in base |
9caf0107 JK |
333 | # \1 - one byte in result |
334 | # \221 - copy, one byte offset, one byte size | |
335 | # \0 - copy from offset 0 | |
336 | # \2 - copy two bytes (one too many) | |
aac86405 JC |
337 | test_expect_success 'apply delta with too many copied bytes' ' |
338 | printf "\4\1\221\0\2" > too_big_copy && | |
339 | printf base >base && | |
340 | test_must_fail test-tool delta -p base too_big_copy /dev/null | |
341 | ' | |
9caf0107 JK |
342 | |
343 | # \0 - empty base | |
344 | # \2 - two bytes in result | |
345 | # \2 - two literal bytes (we are short one) | |
aac86405 JC |
346 | test_expect_success 'apply delta with too few literal bytes' ' |
347 | printf "\0\2\2X" > truncated_delta && | |
348 | test_must_fail test-tool delta -p /dev/null truncated_delta /dev/null | |
349 | ' | |
9caf0107 JK |
350 | |
351 | # \0 - empty base | |
352 | # \1 - one byte in result | |
353 | # \221 - copy, one byte offset, one byte size | |
354 | # \0 - copy from offset 0 | |
355 | # \1 - copy one byte (we are short one) | |
aac86405 JC |
356 | test_expect_success 'apply delta with too few bytes in base' ' |
357 | printf "\0\1\221\0\1" > truncated_base && | |
358 | test_must_fail test-tool delta -p /dev/null truncated_base /dev/null | |
359 | ' | |
9caf0107 | 360 | |
18f60f2d JK |
361 | # \4 - four bytes in base |
362 | # \2 - two bytes in result | |
9caf0107 JK |
363 | # \1 - one literal byte (X) |
364 | # \221 - copy, one byte offset, one byte size | |
365 | # (offset/size missing) | |
366 | # | |
367 | # Note that the literal byte is necessary to get past the uninteresting minimum | |
368 | # delta size check. | |
aac86405 JC |
369 | test_expect_success 'apply delta with truncated copy parameters' ' |
370 | printf "\4\2\1X\221" > truncated_copy_delta && | |
371 | printf base >base && | |
372 | test_must_fail test-tool delta -p base truncated_copy_delta /dev/null | |
373 | ' | |
9caf0107 | 374 | |
fa72f90e JH |
375 | # \0 - empty base |
376 | # \1 - one byte in result | |
377 | # \1 - one literal byte (X) | |
378 | # \1 - trailing garbage command | |
aac86405 JC |
379 | test_expect_success 'apply delta with trailing garbage literal' ' |
380 | printf "\0\1\1X\1" > tail_garbage_literal && | |
381 | test_must_fail test-tool delta -p /dev/null tail_garbage_literal /dev/null | |
382 | ' | |
fa72f90e | 383 | |
18f60f2d | 384 | # \4 - four bytes in base |
fa72f90e JH |
385 | # \1 - one byte in result |
386 | # \1 - one literal byte (X) | |
387 | # \221 - copy, one byte offset, one byte size | |
388 | # \0 - copy from offset 0 | |
389 | # \1 - copy 1 byte | |
aac86405 JC |
390 | test_expect_success 'apply delta with trailing garbage copy' ' |
391 | printf "\4\1\1X\221\0\1" > tail_garbage_copy && | |
392 | printf base >base && | |
393 | test_must_fail test-tool delta -p /dev/null tail_garbage_copy /dev/null | |
394 | ' | |
fa72f90e JH |
395 | |
396 | # \0 - empty base | |
397 | # \1 - one byte in result | |
398 | # \1 - one literal byte (X) | |
399 | # \0 - bogus opcode | |
aac86405 JC |
400 | test_expect_success 'apply delta with trailing garbage opcode' ' |
401 | printf "\0\1\1X\0" > tail_garbage_opcode && | |
402 | test_must_fail test-tool delta -p /dev/null tail_garbage_opcode /dev/null | |
403 | ' | |
fa72f90e | 404 | |
29b0d019 | 405 | test_done |