summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cache.h1
-rw-r--r--sha1_file.c86
2 files changed, 47 insertions, 40 deletions
diff --git a/cache.h b/cache.h
index e6b7164..f8a8928 100644
--- a/cache.h
+++ b/cache.h
@@ -351,6 +351,7 @@ extern struct packed_git {
unsigned int *index_base;
off_t index_size;
off_t pack_size;
+ int pack_fd;
int pack_local;
unsigned char sha1[20];
/* something like ".git/objects/pack/xxxxx.pack" */
diff --git a/sha1_file.c b/sha1_file.c
index a9f374e..79d2d9d 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -465,6 +465,8 @@ static int unuse_one_packed_git(void)
munmap(lru->windows->base, lru->windows->len);
free(lru->windows);
lru->windows = NULL;
+ close(p->pack_fd);
+ p->pack_fd = -1;
return 1;
}
@@ -474,62 +476,64 @@ void unuse_packed_git(struct packed_git *p)
p->windows->inuse_cnt--;
}
-int use_packed_git(struct packed_git *p)
+static void open_packed_git(struct packed_git *p)
{
+ struct stat st;
+ struct pack_header hdr;
+ unsigned char sha1[20];
+ unsigned char *idx_sha1;
+
+ p->pack_fd = open(p->pack_name, O_RDONLY);
+ if (p->pack_fd < 0 || fstat(p->pack_fd, &st))
+ die("packfile %s cannot be opened", p->pack_name);
+
+ /* If we created the struct before we had the pack we lack size. */
if (!p->pack_size) {
- struct stat st;
- /* We created the struct before we had the pack */
- stat(p->pack_name, &st);
if (!S_ISREG(st.st_mode))
die("packfile %s not a regular file", p->pack_name);
p->pack_size = st.st_size;
- }
+ } else if (p->pack_size != st.st_size)
+ die("packfile %s size changed", p->pack_name);
+
+ /* Verify we recognize this pack file format. */
+ read_or_die(p->pack_fd, &hdr, sizeof(hdr));
+ if (hdr.hdr_signature != htonl(PACK_SIGNATURE))
+ die("file %s is not a GIT packfile", p->pack_name);
+ if (!pack_version_ok(hdr.hdr_version))
+ die("packfile %s is version %u and not supported"
+ " (try upgrading GIT to a newer version)",
+ p->pack_name, ntohl(hdr.hdr_version));
+
+ /* Verify the pack matches its index. */
+ if (num_packed_objects(p) != ntohl(hdr.hdr_entries))
+ die("packfile %s claims to have %u objects"
+ " while index size indicates %u objects",
+ p->pack_name, ntohl(hdr.hdr_entries),
+ num_packed_objects(p));
+ if (lseek(p->pack_fd, p->pack_size - sizeof(sha1), SEEK_SET) == -1)
+ die("end of packfile %s is unavailable", p->pack_name);
+ read_or_die(p->pack_fd, sha1, sizeof(sha1));
+ idx_sha1 = ((unsigned char *)p->index_base) + p->index_size - 40;
+ if (hashcmp(sha1, idx_sha1))
+ die("packfile %s does not match index", p->pack_name);
+}
+
+int use_packed_git(struct packed_git *p)
+{
+ if (p->pack_fd == -1)
+ open_packed_git(p);
if (!p->windows) {
- int fd;
- struct stat st;
struct pack_window *win;
- struct pack_header *hdr;
pack_mapped += p->pack_size;
while (packed_git_limit < pack_mapped && unuse_one_packed_git())
; /* nothing */
- fd = open(p->pack_name, O_RDONLY);
- if (fd < 0)
- die("packfile %s cannot be opened", p->pack_name);
- if (fstat(fd, &st)) {
- close(fd);
- die("packfile %s cannot be opened", p->pack_name);
- }
- if (st.st_size != p->pack_size)
- die("packfile %s size mismatch.", p->pack_name);
win = xcalloc(1, sizeof(*win));
win->len = p->pack_size;
- win->base = mmap(NULL, p->pack_size, PROT_READ, MAP_PRIVATE, fd, 0);
- close(fd);
+ win->base = mmap(NULL, p->pack_size, PROT_READ, MAP_PRIVATE, p->pack_fd, 0);
if (win->base == MAP_FAILED)
die("packfile %s cannot be mapped.", p->pack_name);
p->windows = win;
-
- /* Check if we understand this pack file. If we don't we're
- * likely too old to handle it.
- */
- hdr = (struct pack_header*)win->base;
- if (hdr->hdr_signature != htonl(PACK_SIGNATURE))
- die("packfile %s isn't actually a pack.", p->pack_name);
- if (!pack_version_ok(hdr->hdr_version))
- die("packfile %s is version %i and not supported"
- " (try upgrading GIT to a newer version)",
- p->pack_name, ntohl(hdr->hdr_version));
-
- /* Check if the pack file matches with the index file.
- * this is cheap.
- */
- if (hashcmp((unsigned char *)(p->index_base) +
- p->index_size - 40,
- p->windows->base +
- p->pack_size - 20)) {
- die("packfile %s does not match index.", p->pack_name);
- }
}
p->windows->last_used = pack_used_ctr++;
p->windows->inuse_cnt++;
@@ -563,6 +567,7 @@ struct packed_git *add_packed_git(char *path, int path_len, int local)
p->index_base = idx_map;
p->next = NULL;
p->windows = NULL;
+ p->pack_fd = -1;
p->pack_local = local;
if ((path_len > 44) && !get_sha1_hex(path + path_len - 44, sha1))
hashcpy(p->sha1, sha1);
@@ -594,6 +599,7 @@ struct packed_git *parse_pack_index_file(const unsigned char *sha1, char *idx_pa
p->index_base = idx_map;
p->next = NULL;
p->windows = NULL;
+ p->pack_fd = -1;
hashcpy(p->sha1, sha1);
return p;
}