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