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