LIB_OBJS=read-cache.o sha1_file.o usage.o object.o commit.o tree.o blob.o \
tag.o date.o index.o diff-delta.o patch-delta.o entry.o \
- epoch.o refs.o csum-file.o verify_pack.o pkt-line.o
+ epoch.o refs.o csum-file.o pack-check.o pkt-line.o
LIB_FILE=libgit.a
LIB_H=cache.h object.h blob.h tree.h commit.h tag.h delta.h epoch.h csum-file.h \
pack.h pkt-line.h
unsigned int pack_use_cnt;
char pack_name[0]; /* something like ".git/objects/pack/xxxxx.pack" */
} *packed_git;
+
+struct pack_entry {
+ unsigned int offset;
+ unsigned char sha1[20];
+ struct packed_git *p;
+};
+
extern void prepare_packed_git(void);
extern int use_packed_git(struct packed_git *);
extern void unuse_packed_git(struct packed_git *);
extern struct packed_git *add_packed_git(char *, int);
extern int num_packed_objects(const struct packed_git *p);
extern int nth_packed_object_sha1(const struct packed_git *, int, unsigned char*);
+extern int find_pack_entry_one(const unsigned char *, struct pack_entry *, struct packed_git *);
+extern void *unpack_entry_gently(struct pack_entry *, char *, unsigned long *);
#endif /* CACHE_H */
prepare_packed_git();
for (p = packed_git; p; p = p->next)
/* verify gives error messages itself */
- verify_pack(p);
+ verify_pack(p, 0);
for (p = packed_git; p; p = p->next) {
int num = num_packed_objects(p);
unsigned long pack_size = p->pack_size;
void *pack_base;
struct pack_header *hdr;
- int nr_objects;
+ int nr_objects, err, i;
+ /* Header consistency check */
hdr = p->pack_base;
if (hdr->hdr_signature != htonl(PACK_SIGNATURE))
return error("Packfile signature mismatch", p->pack_name);
if (memcmp(sha1, pack_base + pack_size - 20, 20))
return error("Packfile %s SHA1 mismatch with itself",
p->pack_name);
- return 0;
+
+ /* Make sure everything reachable from idx is valid. Since we
+ * have verified that nr_objects matches between idx and pack,
+ * we do not do scan-streaming check on the pack file.
+ */
+ for (i = err = 0; i < nr_objects; i++) {
+ unsigned char sha1[20];
+ struct pack_entry e;
+ void *data;
+ char type[20];
+ unsigned long size;
+
+ if (nth_packed_object_sha1(p, i, sha1))
+ die("internal error pack-check nth-packed-object");
+ if (!find_pack_entry_one(sha1, &e, p))
+ die("internal error pack-check find-pack-entry-one");
+ data = unpack_entry_gently(&e, type, &size);
+ if (!data) {
+ err = error("cannot unpack %s from %s",
+ sha1_to_hex(sha1), p->pack_name);
+ continue;
+ }
+ if (check_sha1_signature(sha1, data, size, type)) {
+ err = error("cannot packed %s from %s corrupt",
+ sha1_to_hex(sha1), p->pack_name);
+ free(data);
+ continue;
+ }
+ free(data);
+ }
+
+ return err;
}
-int verify_pack(struct packed_git *p)
+static void show_pack_info(struct packed_git *p)
+{
+ /* Next round */
+}
+
+int verify_pack(struct packed_git *p, int verbose)
{
unsigned long index_size = p->index_size;
void *index_base = p->index_base;
unsigned char sha1[20];
int ret;
+ ret = 0;
/* Verify SHA1 sum of the index file */
SHA1_Init(&ctx);
SHA1_Update(&ctx, index_base, index_size - 20);
SHA1_Final(sha1, &ctx);
if (memcmp(sha1, index_base + index_size - 20, 20))
- return error("Packfile index for %s SHA1 mismatch",
- p->pack_name);
+ ret = error("Packfile index for %s SHA1 mismatch",
+ p->pack_name);
+
+ if (!ret) {
+ /* Verify pack file */
+ use_packed_git(p);
+ ret = verify_packfile(p);
+ unuse_packed_git(p);
+ }
+
+ if (verbose) {
+ if (ret)
+ printf("%s: bad\n", p->pack_name);
+ else {
+ show_pack_info(p);
+ printf("%s: ok\n", p->pack_name);
+ }
+ }
- /* Verify pack file */
- use_packed_git(p);
- ret = verify_packfile(p);
- unuse_packed_git(p);
return ret;
}
unsigned int hdr_entries;
};
-extern int verify_pack(struct packed_git *);
+extern int verify_pack(struct packed_git *, int);
#endif
static unsigned long pack_mapped;
struct packed_git *packed_git;
-struct pack_entry {
- unsigned int offset;
- unsigned char sha1[20];
- struct packed_git *p;
-};
-
static int check_packed_git_idx(const char *path, unsigned long *idx_size_,
void **idx_map_)
{
return unpack_sha1_rest(&stream, hdr, *size);
}
+/* forward declaration for a mutually recursive function */
+static int packed_object_info(struct pack_entry *entry,
+ char *type, unsigned long *sizep);
+
static int packed_delta_info(unsigned char *base_sha1,
unsigned long delta_size,
unsigned long left,
char *type,
- unsigned long *sizep)
+ unsigned long *sizep,
+ struct packed_git *p)
{
+ struct pack_entry base_ent;
+
if (left < 20)
die("truncated pack file");
+ /* The base entry _must_ be in the same pack */
+ if (!find_pack_entry_one(base_sha1, &base_ent, p))
+ die("failed to find delta-pack base object %s",
+ sha1_to_hex(base_sha1));
+
/* We choose to only get the type of the base object and
* ignore potentially corrupt pack file that expects the delta
* based on a base with a wrong size. This saves tons of
* inflate() calls.
*/
- if (sha1_object_info(base_sha1, type, NULL))
+ if (packed_object_info(&base_ent, type, NULL))
die("cannot get info for delta-pack base");
if (sizep) {
switch (kind) {
case OBJ_DELTA:
- retval = packed_delta_info(pack, size, left, type, sizep);
+ retval = packed_delta_info(pack, size, left, type, sizep, p);
unuse_packed_git(p);
return retval;
case OBJ_COMMIT:
unsigned long delta_size,
unsigned long left,
char *type,
- unsigned long *sizep)
+ unsigned long *sizep,
+ struct packed_git *p)
{
+ struct pack_entry base_ent;
void *data, *delta_data, *result, *base;
unsigned long data_size, result_size, base_size;
z_stream stream;
if ((st != Z_STREAM_END) || stream.total_out != delta_size)
die("delta data unpack failed");
- /* This may recursively unpack the base, which is what we want */
- base = read_sha1_file(base_sha1, type, &base_size);
+ /* The base entry _must_ be in the same pack */
+ if (!find_pack_entry_one(base_sha1, &base_ent, p))
+ die("failed to find delta-pack base object %s",
+ sha1_to_hex(base_sha1));
+ base = unpack_entry_gently(&base_ent, type, &base_size);
if (!base)
die("failed to read delta-pack base object %s",
sha1_to_hex(base_sha1));
char *type, unsigned long *sizep)
{
struct packed_git *p = entry->p;
- unsigned long offset, size, left;
- unsigned char *pack;
- enum object_type kind;
void *retval;
if (use_packed_git(p))
die("cannot map packed file");
+ retval = unpack_entry_gently(entry, type, sizep);
+ unuse_packed_git(p);
+ if (!retval)
+ die("corrupted pack file");
+ return retval;
+}
+
+/* The caller is responsible for use_packed_git()/unuse_packed_git() pair */
+void *unpack_entry_gently(struct pack_entry *entry,
+ char *type, unsigned long *sizep)
+{
+ struct packed_git *p = entry->p;
+ unsigned long offset, size, left;
+ unsigned char *pack;
+ enum object_type kind;
+ void *retval;
offset = unpack_object_header(p, entry->offset, &kind, &size);
pack = p->pack_base + offset;
left = p->pack_size - offset;
switch (kind) {
case OBJ_DELTA:
- retval = unpack_delta_entry(pack, size, left, type, sizep);
- unuse_packed_git(p);
+ retval = unpack_delta_entry(pack, size, left, type, sizep, p);
return retval;
case OBJ_COMMIT:
strcpy(type, "commit");
strcpy(type, "tag");
break;
default:
- die("corrupted pack file");
+ return NULL;
}
*sizep = size;
retval = unpack_non_delta_entry(pack, size, left);
- unuse_packed_git(p);
return retval;
}
return 0;
}
-static int find_pack_entry_1(const unsigned char *sha1,
- struct pack_entry *e, struct packed_git *p)
+int find_pack_entry_one(const unsigned char *sha1,
+ struct pack_entry *e, struct packed_git *p)
{
int *level1_ofs = p->index_base;
int hi = ntohl(level1_ofs[*sha1]);
prepare_packed_git();
for (p = packed_git; p; p = p->next) {
- if (find_pack_entry_1(sha1, e, p))
+ if (find_pack_entry_one(sha1, e, p))
return 1;
}
return 0;
#include "cache.h"
#include "pack.h"
-static int verify_one_pack(char *arg)
+static int verify_one_pack(char *arg, int verbose)
{
- struct packed_git *g = add_packed_git(arg, strlen(arg));
- if (!g)
- return -1;
- return verify_pack(g);
+ int len = strlen(arg);
+ struct packed_git *g;
+
+ while (1) {
+ /* Should name foo.idx, but foo.pack may be named;
+ * convert it to foo.idx
+ */
+ if (!strcmp(arg + len - 5, ".pack")) {
+ strcpy(arg + len - 5, ".idx");
+ len--;
+ }
+ /* Should name foo.idx now */
+ if ((g = add_packed_git(arg, len)))
+ break;
+ /* No? did you name just foo? */
+ strcpy(arg + len, ".idx");
+ len += 4;
+ if ((g = add_packed_git(arg, len)))
+ break;
+ return error("packfile %s not found.", arg);
+ }
+ return verify_pack(g, verbose);
}
+static const char *verify_pack_usage = "git-verify-pack [-v] <pack>...";
+
int main(int ac, char **av)
{
int errs = 0;
+ int verbose = 0;
+ int no_more_options = 0;
while (1 < ac) {
char path[PATH_MAX];
- strcpy(path, av[1]);
- if (verify_one_pack(path))
- errs++;
- else
- printf("%s: OK\n", av[1]);
+
+ if (!no_more_options && av[1][0] == '-') {
+ if (!strcmp("-v", av[1]))
+ verbose = 1;
+ else if (!strcmp("--", av[1]))
+ no_more_options = 1;
+ else
+ usage(verify_pack_usage);
+ }
+ else {
+ strcpy(path, av[1]);
+ if (verify_one_pack(path, verbose))
+ errs++;
+ }
ac--; av++;
}
return !!errs;