[PATCH] verify-pack updates.
Nico pointed out that having verify_pack.c and verify-pack.c was confusing. Rename verify_pack.c to pack-check.c as suggested, and enhances the verification done quite a bit. - Built-in sha1_file unpacking knows that a base object of a deltified object _must_ be in the same pack, and takes advantage of that fact. - Earlier verify-pack command only checked the SHA1 sum for the entire pack file and did not look into its contents. It now checks everything idx file claims to have unpacks correctly. - It now has a hook to give more detailed information for objects contained in the pack under -v flag. Signed-off-by: Junio C Hamano <junkio@cox.net> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
This commit is contained in:
parent
c62266f37c
commit
f3bf922409
2
Makefile
2
Makefile
@ -46,7 +46,7 @@ install: $(PROG) $(SCRIPTS)
|
|||||||
|
|
||||||
LIB_OBJS=read-cache.o sha1_file.o usage.o object.o commit.o tree.o blob.o \
|
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 \
|
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_FILE=libgit.a
|
||||||
LIB_H=cache.h object.h blob.h tree.h commit.h tag.h delta.h epoch.h csum-file.h \
|
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
|
pack.h pkt-line.h
|
||||||
|
9
cache.h
9
cache.h
@ -251,11 +251,20 @@ extern struct packed_git {
|
|||||||
unsigned int pack_use_cnt;
|
unsigned int pack_use_cnt;
|
||||||
char pack_name[0]; /* something like ".git/objects/pack/xxxxx.pack" */
|
char pack_name[0]; /* something like ".git/objects/pack/xxxxx.pack" */
|
||||||
} *packed_git;
|
} *packed_git;
|
||||||
|
|
||||||
|
struct pack_entry {
|
||||||
|
unsigned int offset;
|
||||||
|
unsigned char sha1[20];
|
||||||
|
struct packed_git *p;
|
||||||
|
};
|
||||||
|
|
||||||
extern void prepare_packed_git(void);
|
extern void prepare_packed_git(void);
|
||||||
extern int use_packed_git(struct packed_git *);
|
extern int use_packed_git(struct packed_git *);
|
||||||
extern void unuse_packed_git(struct packed_git *);
|
extern void unuse_packed_git(struct packed_git *);
|
||||||
extern struct packed_git *add_packed_git(char *, int);
|
extern struct packed_git *add_packed_git(char *, int);
|
||||||
extern int num_packed_objects(const struct packed_git *p);
|
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 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 */
|
#endif /* CACHE_H */
|
||||||
|
@ -440,7 +440,7 @@ int main(int argc, char **argv)
|
|||||||
prepare_packed_git();
|
prepare_packed_git();
|
||||||
for (p = packed_git; p; p = p->next)
|
for (p = packed_git; p; p = p->next)
|
||||||
/* verify gives error messages itself */
|
/* verify gives error messages itself */
|
||||||
verify_pack(p);
|
verify_pack(p, 0);
|
||||||
|
|
||||||
for (p = packed_git; p; p = p->next) {
|
for (p = packed_git; p; p = p->next) {
|
||||||
int num = num_packed_objects(p);
|
int num = num_packed_objects(p);
|
||||||
|
@ -10,8 +10,9 @@ static int verify_packfile(struct packed_git *p)
|
|||||||
unsigned long pack_size = p->pack_size;
|
unsigned long pack_size = p->pack_size;
|
||||||
void *pack_base;
|
void *pack_base;
|
||||||
struct pack_header *hdr;
|
struct pack_header *hdr;
|
||||||
int nr_objects;
|
int nr_objects, err, i;
|
||||||
|
|
||||||
|
/* Header consistency check */
|
||||||
hdr = p->pack_base;
|
hdr = p->pack_base;
|
||||||
if (hdr->hdr_signature != htonl(PACK_SIGNATURE))
|
if (hdr->hdr_signature != htonl(PACK_SIGNATURE))
|
||||||
return error("Packfile signature mismatch", p->pack_name);
|
return error("Packfile signature mismatch", p->pack_name);
|
||||||
@ -34,11 +35,47 @@ static int verify_packfile(struct packed_git *p)
|
|||||||
if (memcmp(sha1, pack_base + pack_size - 20, 20))
|
if (memcmp(sha1, pack_base + pack_size - 20, 20))
|
||||||
return error("Packfile %s SHA1 mismatch with itself",
|
return error("Packfile %s SHA1 mismatch with itself",
|
||||||
p->pack_name);
|
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;
|
unsigned long index_size = p->index_size;
|
||||||
void *index_base = p->index_base;
|
void *index_base = p->index_base;
|
||||||
@ -46,17 +83,30 @@ int verify_pack(struct packed_git *p)
|
|||||||
unsigned char sha1[20];
|
unsigned char sha1[20];
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
/* Verify SHA1 sum of the index file */
|
/* Verify SHA1 sum of the index file */
|
||||||
SHA1_Init(&ctx);
|
SHA1_Init(&ctx);
|
||||||
SHA1_Update(&ctx, index_base, index_size - 20);
|
SHA1_Update(&ctx, index_base, index_size - 20);
|
||||||
SHA1_Final(sha1, &ctx);
|
SHA1_Final(sha1, &ctx);
|
||||||
if (memcmp(sha1, index_base + index_size - 20, 20))
|
if (memcmp(sha1, index_base + index_size - 20, 20))
|
||||||
return error("Packfile index for %s SHA1 mismatch",
|
ret = error("Packfile index for %s SHA1 mismatch",
|
||||||
p->pack_name);
|
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;
|
return ret;
|
||||||
}
|
}
|
2
pack.h
2
pack.h
@ -27,6 +27,6 @@ struct pack_header {
|
|||||||
unsigned int hdr_entries;
|
unsigned int hdr_entries;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern int verify_pack(struct packed_git *);
|
extern int verify_pack(struct packed_git *, int);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
66
sha1_file.c
66
sha1_file.c
@ -272,12 +272,6 @@ static int pack_used_ctr;
|
|||||||
static unsigned long pack_mapped;
|
static unsigned long pack_mapped;
|
||||||
struct packed_git *packed_git;
|
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_,
|
static int check_packed_git_idx(const char *path, unsigned long *idx_size_,
|
||||||
void **idx_map_)
|
void **idx_map_)
|
||||||
{
|
{
|
||||||
@ -618,22 +612,34 @@ void * unpack_sha1_file(void *map, unsigned long mapsize, char *type, unsigned l
|
|||||||
return unpack_sha1_rest(&stream, hdr, *size);
|
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,
|
static int packed_delta_info(unsigned char *base_sha1,
|
||||||
unsigned long delta_size,
|
unsigned long delta_size,
|
||||||
unsigned long left,
|
unsigned long left,
|
||||||
char *type,
|
char *type,
|
||||||
unsigned long *sizep)
|
unsigned long *sizep,
|
||||||
|
struct packed_git *p)
|
||||||
{
|
{
|
||||||
|
struct pack_entry base_ent;
|
||||||
|
|
||||||
if (left < 20)
|
if (left < 20)
|
||||||
die("truncated pack file");
|
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
|
/* We choose to only get the type of the base object and
|
||||||
* ignore potentially corrupt pack file that expects the delta
|
* ignore potentially corrupt pack file that expects the delta
|
||||||
* based on a base with a wrong size. This saves tons of
|
* based on a base with a wrong size. This saves tons of
|
||||||
* inflate() calls.
|
* 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");
|
die("cannot get info for delta-pack base");
|
||||||
|
|
||||||
if (sizep) {
|
if (sizep) {
|
||||||
@ -716,7 +722,7 @@ static int packed_object_info(struct pack_entry *entry,
|
|||||||
|
|
||||||
switch (kind) {
|
switch (kind) {
|
||||||
case OBJ_DELTA:
|
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);
|
unuse_packed_git(p);
|
||||||
return retval;
|
return retval;
|
||||||
case OBJ_COMMIT:
|
case OBJ_COMMIT:
|
||||||
@ -747,8 +753,10 @@ static void *unpack_delta_entry(unsigned char *base_sha1,
|
|||||||
unsigned long delta_size,
|
unsigned long delta_size,
|
||||||
unsigned long left,
|
unsigned long left,
|
||||||
char *type,
|
char *type,
|
||||||
unsigned long *sizep)
|
unsigned long *sizep,
|
||||||
|
struct packed_git *p)
|
||||||
{
|
{
|
||||||
|
struct pack_entry base_ent;
|
||||||
void *data, *delta_data, *result, *base;
|
void *data, *delta_data, *result, *base;
|
||||||
unsigned long data_size, result_size, base_size;
|
unsigned long data_size, result_size, base_size;
|
||||||
z_stream stream;
|
z_stream stream;
|
||||||
@ -773,8 +781,11 @@ static void *unpack_delta_entry(unsigned char *base_sha1,
|
|||||||
if ((st != Z_STREAM_END) || stream.total_out != delta_size)
|
if ((st != Z_STREAM_END) || stream.total_out != delta_size)
|
||||||
die("delta data unpack failed");
|
die("delta data unpack failed");
|
||||||
|
|
||||||
/* This may recursively unpack the base, which is what we want */
|
/* The base entry _must_ be in the same pack */
|
||||||
base = read_sha1_file(base_sha1, type, &base_size);
|
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)
|
if (!base)
|
||||||
die("failed to read delta-pack base object %s",
|
die("failed to read delta-pack base object %s",
|
||||||
sha1_to_hex(base_sha1));
|
sha1_to_hex(base_sha1));
|
||||||
@ -820,21 +831,33 @@ static void *unpack_entry(struct pack_entry *entry,
|
|||||||
char *type, unsigned long *sizep)
|
char *type, unsigned long *sizep)
|
||||||
{
|
{
|
||||||
struct packed_git *p = entry->p;
|
struct packed_git *p = entry->p;
|
||||||
unsigned long offset, size, left;
|
|
||||||
unsigned char *pack;
|
|
||||||
enum object_type kind;
|
|
||||||
void *retval;
|
void *retval;
|
||||||
|
|
||||||
if (use_packed_git(p))
|
if (use_packed_git(p))
|
||||||
die("cannot map packed file");
|
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);
|
offset = unpack_object_header(p, entry->offset, &kind, &size);
|
||||||
pack = p->pack_base + offset;
|
pack = p->pack_base + offset;
|
||||||
left = p->pack_size - offset;
|
left = p->pack_size - offset;
|
||||||
switch (kind) {
|
switch (kind) {
|
||||||
case OBJ_DELTA:
|
case OBJ_DELTA:
|
||||||
retval = unpack_delta_entry(pack, size, left, type, sizep);
|
retval = unpack_delta_entry(pack, size, left, type, sizep, p);
|
||||||
unuse_packed_git(p);
|
|
||||||
return retval;
|
return retval;
|
||||||
case OBJ_COMMIT:
|
case OBJ_COMMIT:
|
||||||
strcpy(type, "commit");
|
strcpy(type, "commit");
|
||||||
@ -849,11 +872,10 @@ static void *unpack_entry(struct pack_entry *entry,
|
|||||||
strcpy(type, "tag");
|
strcpy(type, "tag");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
die("corrupted pack file");
|
return NULL;
|
||||||
}
|
}
|
||||||
*sizep = size;
|
*sizep = size;
|
||||||
retval = unpack_non_delta_entry(pack, size, left);
|
retval = unpack_non_delta_entry(pack, size, left);
|
||||||
unuse_packed_git(p);
|
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -873,8 +895,8 @@ int nth_packed_object_sha1(const struct packed_git *p, int n,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int find_pack_entry_1(const unsigned char *sha1,
|
int find_pack_entry_one(const unsigned char *sha1,
|
||||||
struct pack_entry *e, struct packed_git *p)
|
struct pack_entry *e, struct packed_git *p)
|
||||||
{
|
{
|
||||||
int *level1_ofs = p->index_base;
|
int *level1_ofs = p->index_base;
|
||||||
int hi = ntohl(level1_ofs[*sha1]);
|
int hi = ntohl(level1_ofs[*sha1]);
|
||||||
@ -904,7 +926,7 @@ static int find_pack_entry(const unsigned char *sha1, struct pack_entry *e)
|
|||||||
prepare_packed_git();
|
prepare_packed_git();
|
||||||
|
|
||||||
for (p = packed_git; p; p = p->next) {
|
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 1;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1,25 +1,56 @@
|
|||||||
#include "cache.h"
|
#include "cache.h"
|
||||||
#include "pack.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));
|
int len = strlen(arg);
|
||||||
if (!g)
|
struct packed_git *g;
|
||||||
return -1;
|
|
||||||
return verify_pack(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 main(int ac, char **av)
|
||||||
{
|
{
|
||||||
int errs = 0;
|
int errs = 0;
|
||||||
|
int verbose = 0;
|
||||||
|
int no_more_options = 0;
|
||||||
|
|
||||||
while (1 < ac) {
|
while (1 < ac) {
|
||||||
char path[PATH_MAX];
|
char path[PATH_MAX];
|
||||||
strcpy(path, av[1]);
|
|
||||||
if (verify_one_pack(path))
|
if (!no_more_options && av[1][0] == '-') {
|
||||||
errs++;
|
if (!strcmp("-v", av[1]))
|
||||||
else
|
verbose = 1;
|
||||||
printf("%s: OK\n", av[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++;
|
ac--; av++;
|
||||||
}
|
}
|
||||||
return !!errs;
|
return !!errs;
|
||||||
|
Loading…
Reference in New Issue
Block a user