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