Merge branch 'ew/rerere'
[git] / pack-check.c
1 #include "cache.h"
2 #include "pack.h"
3
4 static int verify_packfile(struct packed_git *p)
5 {
6         unsigned long index_size = p->index_size;
7         void *index_base = p->index_base;
8         SHA_CTX ctx;
9         unsigned char sha1[20];
10         unsigned long pack_size = p->pack_size;
11         void *pack_base;
12         struct pack_header *hdr;
13         int nr_objects, err, i;
14
15         /* Header consistency check */
16         hdr = p->pack_base;
17         if (hdr->hdr_signature != htonl(PACK_SIGNATURE))
18                 return error("Packfile %s signature mismatch", p->pack_name);
19         if (!pack_version_ok(hdr->hdr_version))
20                 return error("Packfile version %d unsupported",
21                              ntohl(hdr->hdr_version));
22         nr_objects = ntohl(hdr->hdr_entries);
23         if (num_packed_objects(p) != nr_objects)
24                 return error("Packfile claims to have %d objects, "
25                              "while idx size expects %d", nr_objects,
26                              num_packed_objects(p));
27
28         SHA1_Init(&ctx);
29         pack_base = p->pack_base;
30         SHA1_Update(&ctx, pack_base, pack_size - 20);
31         SHA1_Final(sha1, &ctx);
32         if (hashcmp(sha1, (unsigned char *)pack_base + pack_size - 20))
33                 return error("Packfile %s SHA1 mismatch with itself",
34                              p->pack_name);
35         if (hashcmp(sha1, (unsigned char *)index_base + index_size - 40))
36                 return error("Packfile %s SHA1 mismatch with idx",
37                              p->pack_name);
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         for (i = err = 0; i < nr_objects; i++) {
44                 unsigned char sha1[20];
45                 void *data;
46                 char type[20];
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_gently(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, 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         struct pack_header *hdr;
78         int nr_objects, i;
79         unsigned int chain_histogram[MAX_CHAIN];
80
81         hdr = p->pack_base;
82         nr_objects = ntohl(hdr->hdr_entries);
83         memset(chain_histogram, 0, sizeof(chain_histogram));
84
85         for (i = 0; i < nr_objects; i++) {
86                 unsigned char sha1[20], base_sha1[20];
87                 char type[20];
88                 unsigned long size;
89                 unsigned long store_size;
90                 unsigned long offset;
91                 unsigned int delta_chain_length;
92
93                 if (nth_packed_object_sha1(p, i, sha1))
94                         die("internal error pack-check nth-packed-object");
95                 offset = find_pack_entry_one(sha1, p);
96                 if (!offset)
97                         die("internal error pack-check find-pack-entry-one");
98
99                 packed_object_info_detail(p, offset, type, &size, &store_size,
100                                           &delta_chain_length,
101                                           base_sha1);
102                 printf("%s ", sha1_to_hex(sha1));
103                 if (!delta_chain_length)
104                         printf("%-6s %lu %lu\n", type, size, offset);
105                 else {
106                         printf("%-6s %lu %lu %u %s\n", type, size, offset,
107                                delta_chain_length, sha1_to_hex(base_sha1));
108                         if (delta_chain_length < MAX_CHAIN)
109                                 chain_histogram[delta_chain_length]++;
110                         else
111                                 chain_histogram[0]++;
112                 }
113         }
114
115         for (i = 0; i < MAX_CHAIN; i++) {
116                 if (!chain_histogram[i])
117                         continue;
118                 printf("chain length %s %d: %d object%s\n",
119                        i ? "=" : ">=",
120                        i ? i : MAX_CHAIN,
121                        chain_histogram[i],
122                        1 < chain_histogram[i] ? "s" : "");
123         }
124 }
125
126 int verify_pack(struct packed_git *p, int verbose)
127 {
128         unsigned long index_size = p->index_size;
129         void *index_base = p->index_base;
130         SHA_CTX ctx;
131         unsigned char sha1[20];
132         int ret;
133
134         ret = 0;
135         /* Verify SHA1 sum of the index file */
136         SHA1_Init(&ctx);
137         SHA1_Update(&ctx, index_base, index_size - 20);
138         SHA1_Final(sha1, &ctx);
139         if (hashcmp(sha1, (unsigned char *)index_base + index_size - 20))
140                 ret = error("Packfile index for %s SHA1 mismatch",
141                             p->pack_name);
142
143         if (!ret) {
144                 /* Verify pack file */
145                 use_packed_git(p);
146                 ret = verify_packfile(p);
147                 unuse_packed_git(p);
148         }
149
150         if (verbose) {
151                 if (ret)
152                         printf("%s: bad\n", p->pack_name);
153                 else {
154                         use_packed_git(p);
155                         show_pack_info(p);
156                         unuse_packed_git(p);
157                         printf("%s: ok\n", p->pack_name);
158                 }
159         }
160
161         return ret;
162 }