| 1 | #include "cache.h" |
| 2 | #include "pack.h" |
| 3 | |
| 4 | static int verify_packfile(struct packed_git *p, |
| 5 | struct pack_window **w_curs) |
| 6 | { |
| 7 | off_t index_size = p->index_size; |
| 8 | const unsigned char *index_base = p->index_data; |
| 9 | SHA_CTX ctx; |
| 10 | unsigned char sha1[20]; |
| 11 | off_t offset = 0, pack_sig = p->pack_size - 20; |
| 12 | uint32_t nr_objects, i; |
| 13 | int err; |
| 14 | |
| 15 | /* Note that the pack header checks are actually performed by |
| 16 | * use_pack when it first opens the pack file. If anything |
| 17 | * goes wrong during those checks then the call will die out |
| 18 | * immediately. |
| 19 | */ |
| 20 | |
| 21 | SHA1_Init(&ctx); |
| 22 | while (offset < pack_sig) { |
| 23 | unsigned int remaining; |
| 24 | unsigned char *in = use_pack(p, w_curs, offset, &remaining); |
| 25 | offset += remaining; |
| 26 | if (offset > pack_sig) |
| 27 | remaining -= (unsigned int)(offset - pack_sig); |
| 28 | SHA1_Update(&ctx, in, remaining); |
| 29 | } |
| 30 | SHA1_Final(sha1, &ctx); |
| 31 | if (hashcmp(sha1, use_pack(p, w_curs, pack_sig, NULL))) |
| 32 | return error("Packfile %s SHA1 mismatch with itself", |
| 33 | p->pack_name); |
| 34 | if (hashcmp(sha1, index_base + index_size - 40)) |
| 35 | return error("Packfile %s SHA1 mismatch with idx", |
| 36 | p->pack_name); |
| 37 | unuse_pack(w_curs); |
| 38 | |
| 39 | /* Make sure everything reachable from idx is valid. Since we |
| 40 | * have verified that nr_objects matches between idx and pack, |
| 41 | * we do not do scan-streaming check on the pack file. |
| 42 | */ |
| 43 | nr_objects = num_packed_objects(p); |
| 44 | for (i = 0, err = 0; i < nr_objects; i++) { |
| 45 | const unsigned char *sha1; |
| 46 | void *data; |
| 47 | enum object_type type; |
| 48 | unsigned long size; |
| 49 | off_t offset; |
| 50 | |
| 51 | sha1 = nth_packed_object_sha1(p, i); |
| 52 | if (!sha1) |
| 53 | die("internal error pack-check nth-packed-object"); |
| 54 | offset = find_pack_entry_one(sha1, p); |
| 55 | if (!offset) |
| 56 | die("internal error pack-check find-pack-entry-one"); |
| 57 | data = unpack_entry(p, offset, &type, &size); |
| 58 | if (!data) { |
| 59 | err = error("cannot unpack %s from %s", |
| 60 | sha1_to_hex(sha1), p->pack_name); |
| 61 | continue; |
| 62 | } |
| 63 | if (check_sha1_signature(sha1, data, size, typename(type))) { |
| 64 | err = error("packed %s from %s is corrupt", |
| 65 | sha1_to_hex(sha1), p->pack_name); |
| 66 | free(data); |
| 67 | continue; |
| 68 | } |
| 69 | free(data); |
| 70 | } |
| 71 | |
| 72 | return err; |
| 73 | } |
| 74 | |
| 75 | |
| 76 | #define MAX_CHAIN 40 |
| 77 | |
| 78 | static void show_pack_info(struct packed_git *p) |
| 79 | { |
| 80 | uint32_t nr_objects, i, chain_histogram[MAX_CHAIN]; |
| 81 | |
| 82 | nr_objects = num_packed_objects(p); |
| 83 | memset(chain_histogram, 0, sizeof(chain_histogram)); |
| 84 | |
| 85 | for (i = 0; i < nr_objects; i++) { |
| 86 | const unsigned char *sha1; |
| 87 | unsigned char base_sha1[20]; |
| 88 | const char *type; |
| 89 | unsigned long size; |
| 90 | unsigned long store_size; |
| 91 | off_t offset; |
| 92 | unsigned int delta_chain_length; |
| 93 | |
| 94 | sha1 = nth_packed_object_sha1(p, i); |
| 95 | if (!sha1) |
| 96 | die("internal error pack-check nth-packed-object"); |
| 97 | offset = find_pack_entry_one(sha1, p); |
| 98 | if (!offset) |
| 99 | die("internal error pack-check find-pack-entry-one"); |
| 100 | |
| 101 | type = packed_object_info_detail(p, offset, &size, &store_size, |
| 102 | &delta_chain_length, |
| 103 | base_sha1); |
| 104 | printf("%s ", sha1_to_hex(sha1)); |
| 105 | if (!delta_chain_length) |
| 106 | printf("%-6s %lu %"PRIuMAX"\n", |
| 107 | type, size, (uintmax_t)offset); |
| 108 | else { |
| 109 | printf("%-6s %lu %"PRIuMAX" %u %s\n", |
| 110 | type, size, (uintmax_t)offset, |
| 111 | delta_chain_length, sha1_to_hex(base_sha1)); |
| 112 | if (delta_chain_length < MAX_CHAIN) |
| 113 | chain_histogram[delta_chain_length]++; |
| 114 | else |
| 115 | chain_histogram[0]++; |
| 116 | } |
| 117 | } |
| 118 | |
| 119 | for (i = 0; i < MAX_CHAIN; i++) { |
| 120 | if (!chain_histogram[i]) |
| 121 | continue; |
| 122 | printf("chain length %s %d: %d object%s\n", |
| 123 | i ? "=" : ">=", |
| 124 | i ? i : MAX_CHAIN, |
| 125 | chain_histogram[i], |
| 126 | 1 < chain_histogram[i] ? "s" : ""); |
| 127 | } |
| 128 | } |
| 129 | |
| 130 | int verify_pack(struct packed_git *p, int verbose) |
| 131 | { |
| 132 | off_t index_size = p->index_size; |
| 133 | const unsigned char *index_base = p->index_data; |
| 134 | SHA_CTX ctx; |
| 135 | unsigned char sha1[20]; |
| 136 | int ret; |
| 137 | |
| 138 | ret = 0; |
| 139 | /* Verify SHA1 sum of the index file */ |
| 140 | SHA1_Init(&ctx); |
| 141 | SHA1_Update(&ctx, index_base, (unsigned int)(index_size - 20)); |
| 142 | SHA1_Final(sha1, &ctx); |
| 143 | if (hashcmp(sha1, index_base + index_size - 20)) |
| 144 | ret = error("Packfile index for %s SHA1 mismatch", |
| 145 | p->pack_name); |
| 146 | |
| 147 | if (!ret) { |
| 148 | /* Verify pack file */ |
| 149 | struct pack_window *w_curs = NULL; |
| 150 | ret = verify_packfile(p, &w_curs); |
| 151 | unuse_pack(&w_curs); |
| 152 | } |
| 153 | |
| 154 | if (verbose) { |
| 155 | if (ret) |
| 156 | printf("%s: bad\n", p->pack_name); |
| 157 | else { |
| 158 | show_pack_info(p); |
| 159 | printf("%s: ok\n", p->pack_name); |
| 160 | } |
| 161 | } |
| 162 | |
| 163 | return ret; |
| 164 | } |