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