Make "git clone" pack-fetching download statistics better
[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 (memcmp(sha1, index_base + index_size - 40, 20))
33                 return error("Packfile %s SHA1 mismatch with idx",
34                              p->pack_name);
35         if (memcmp(sha1, pack_base + pack_size - 20, 20))
36                 return error("Packfile %s SHA1 mismatch with itself",
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                 struct pack_entry e;
46                 void *data;
47                 char type[20];
48                 unsigned long size;
49
50                 if (nth_packed_object_sha1(p, i, sha1))
51                         die("internal error pack-check nth-packed-object");
52                 if (!find_pack_entry_one(sha1, &e, p))
53                         die("internal error pack-check find-pack-entry-one");
54                 data = unpack_entry_gently(&e, 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 static void show_pack_info(struct packed_git *p)
74 {
75         struct pack_header *hdr;
76         int nr_objects, i;
77
78         hdr = p->pack_base;
79         nr_objects = ntohl(hdr->hdr_entries);
80
81         for (i = 0; i < nr_objects; i++) {
82                 unsigned char sha1[20], base_sha1[20];
83                 struct pack_entry e;
84                 char type[20];
85                 unsigned long size;
86                 unsigned long store_size;
87                 int delta_chain_length;
88
89                 if (nth_packed_object_sha1(p, i, sha1))
90                         die("internal error pack-check nth-packed-object");
91                 if (!find_pack_entry_one(sha1, &e, p))
92                         die("internal error pack-check find-pack-entry-one");
93
94                 packed_object_info_detail(&e, type, &size, &store_size,
95                                           &delta_chain_length,
96                                           base_sha1);
97                 printf("%s ", sha1_to_hex(sha1));
98                 if (!delta_chain_length)
99                         printf("%-6s %lu %u\n", type, size, e.offset);
100                 else
101                         printf("%-6s %lu %u %d %s\n", type, size, e.offset,
102                                delta_chain_length, sha1_to_hex(base_sha1));
103         }
104
105 }
106
107 int verify_pack(struct packed_git *p, int verbose)
108 {
109         unsigned long index_size = p->index_size;
110         void *index_base = p->index_base;
111         SHA_CTX ctx;
112         unsigned char sha1[20];
113         int ret;
114
115         ret = 0;
116         /* Verify SHA1 sum of the index file */
117         SHA1_Init(&ctx);
118         SHA1_Update(&ctx, index_base, index_size - 20);
119         SHA1_Final(sha1, &ctx);
120         if (memcmp(sha1, index_base + index_size - 20, 20))
121                 ret = error("Packfile index for %s SHA1 mismatch",
122                             p->pack_name);
123
124         if (!ret) {
125                 /* Verify pack file */
126                 use_packed_git(p);
127                 ret = verify_packfile(p);
128                 unuse_packed_git(p);
129         }
130
131         if (verbose) {
132                 if (ret)
133                         printf("%s: bad\n", p->pack_name);
134                 else {
135                         use_packed_git(p);
136                         show_pack_info(p);
137                         unuse_packed_git(p);
138                         printf("%s: ok\n", p->pack_name);
139                 }
140         }
141
142         return ret;
143 }