t / t5303-pack-corruption-resilience.shon commit rebase --abort/--quit: cleanup refs/rewritten (d559f50)
   1#!/bin/sh
   2#
   3# Copyright (c) 2008 Nicolas Pitre
   4#
   5
   6test_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
  21create_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
  32create_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
  44do_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
  50do_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
  58printf '\0' > zero
  59
  60test_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
  69test_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
  76test_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
  85test_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
  96test_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
 106test_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
 115test_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
 126test_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
 135test_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
 144test_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
 153test_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
 163test_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
 172test_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
 181test_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
 190test_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
 199test_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
 208test_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
 217test_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
 226test_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
 235test_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
 244test_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
 253test_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
 262test_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
 278test_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
 287test_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
 296test_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
 305test_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)
 317test_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)
 325test_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)
 335test_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)
 344test_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)
 354test_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.
 367test_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
 377test_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
 388test_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
 398test_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
 403test_done