Merge branch 'np/maint-safer-pack' into maint
[git] / builtin-verify-pack.c
1 #include "builtin.h"
2 #include "cache.h"
3 #include "pack.h"
4 #include "pack-revindex.h"
5
6 #define MAX_CHAIN 50
7
8 static void show_pack_info(struct packed_git *p)
9 {
10         uint32_t nr_objects, i, chain_histogram[MAX_CHAIN+1];
11
12         nr_objects = p->num_objects;
13         memset(chain_histogram, 0, sizeof(chain_histogram));
14
15         for (i = 0; i < nr_objects; i++) {
16                 const unsigned char *sha1;
17                 unsigned char base_sha1[20];
18                 const char *type;
19                 unsigned long size;
20                 unsigned long store_size;
21                 off_t offset;
22                 unsigned int delta_chain_length;
23
24                 sha1 = nth_packed_object_sha1(p, i);
25                 if (!sha1)
26                         die("internal error pack-check nth-packed-object");
27                 offset = nth_packed_object_offset(p, i);
28                 type = packed_object_info_detail(p, offset, &size, &store_size,
29                                                  &delta_chain_length,
30                                                  base_sha1);
31                 printf("%s ", sha1_to_hex(sha1));
32                 if (!delta_chain_length)
33                         printf("%-6s %lu %lu %"PRIuMAX"\n",
34                                type, size, store_size, (uintmax_t)offset);
35                 else {
36                         printf("%-6s %lu %lu %"PRIuMAX" %u %s\n",
37                                type, size, store_size, (uintmax_t)offset,
38                                delta_chain_length, sha1_to_hex(base_sha1));
39                         if (delta_chain_length <= MAX_CHAIN)
40                                 chain_histogram[delta_chain_length]++;
41                         else
42                                 chain_histogram[0]++;
43                 }
44         }
45
46         for (i = 0; i <= MAX_CHAIN; i++) {
47                 if (!chain_histogram[i])
48                         continue;
49                 printf("chain length = %"PRIu32": %"PRIu32" object%s\n", i,
50                        chain_histogram[i], chain_histogram[i] > 1 ? "s" : "");
51         }
52         if (chain_histogram[0])
53                 printf("chain length > %d: %"PRIu32" object%s\n", MAX_CHAIN,
54                        chain_histogram[0], chain_histogram[0] > 1 ? "s" : "");
55 }
56
57 static int verify_one_pack(const char *path, int verbose)
58 {
59         char arg[PATH_MAX];
60         int len;
61         struct packed_git *pack;
62         int err;
63
64         len = strlcpy(arg, path, PATH_MAX);
65         if (len >= PATH_MAX)
66                 return error("name too long: %s", path);
67
68         /*
69          * In addition to "foo.idx" we accept "foo.pack" and "foo";
70          * normalize these forms to "foo.idx" for add_packed_git().
71          */
72         if (has_extension(arg, ".pack")) {
73                 strcpy(arg + len - 5, ".idx");
74                 len--;
75         } else if (!has_extension(arg, ".idx")) {
76                 if (len + 4 >= PATH_MAX)
77                         return error("name too long: %s.idx", arg);
78                 strcpy(arg + len, ".idx");
79                 len += 4;
80         }
81
82         /*
83          * add_packed_git() uses our buffer (containing "foo.idx") to
84          * build the pack filename ("foo.pack").  Make sure it fits.
85          */
86         if (len + 1 >= PATH_MAX) {
87                 arg[len - 4] = '\0';
88                 return error("name too long: %s.pack", arg);
89         }
90
91         pack = add_packed_git(arg, len, 1);
92         if (!pack)
93                 return error("packfile %s not found.", arg);
94
95         install_packed_git(pack);
96         err = verify_pack(pack);
97
98         if (verbose) {
99                 if (err)
100                         printf("%s: bad\n", pack->pack_name);
101                 else {
102                         show_pack_info(pack);
103                         printf("%s: ok\n", pack->pack_name);
104                 }
105         }
106
107         return err;
108 }
109
110 static const char verify_pack_usage[] = "git-verify-pack [-v] <pack>...";
111
112 int cmd_verify_pack(int argc, const char **argv, const char *prefix)
113 {
114         int err = 0;
115         int verbose = 0;
116         int no_more_options = 0;
117         int nothing_done = 1;
118
119         git_config(git_default_config, NULL);
120         while (1 < argc) {
121                 if (!no_more_options && argv[1][0] == '-') {
122                         if (!strcmp("-v", argv[1]))
123                                 verbose = 1;
124                         else if (!strcmp("--", argv[1]))
125                                 no_more_options = 1;
126                         else
127                                 usage(verify_pack_usage);
128                 }
129                 else {
130                         if (verify_one_pack(argv[1], verbose))
131                                 err = 1;
132                         discard_revindex();
133                         nothing_done = 0;
134                 }
135                 argc--; argv++;
136         }
137
138         if (nothing_done)
139                 usage(verify_pack_usage);
140
141         return err;
142 }