aboutsummaryrefslogtreecommitdiff
path: root/packfile.c
diff options
context:
space:
mode:
authorJonathan Tan <jonathantanmy@google.com>2017-08-18 15:20:19 -0700
committerJunio C Hamano <gitster@pobox.com>2017-08-23 15:12:06 -0700
commit0317f45576a0b48c90c4b023fa572a000633946c (patch)
tree987b83cab92be9282e64c7011b2008884f373a51 /packfile.c
parent8e21176c3cff203c1b991fda55df3ac7904d09fa (diff)
downloadgit-0317f45576a0b48c90c4b023fa572a000633946c.tar.gz
git-0317f45576a0b48c90c4b023fa572a000633946c.tar.xz
pack: move open_pack_index(), parse_pack_index()
alloc_packed_git() in packfile.c is duplicated from sha1_file.c. In a subsequent commit, alloc_packed_git() will be removed from sha1_file.c. Signed-off-by: Jonathan Tan <jonathantanmy@google.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Diffstat (limited to 'packfile.c')
-rw-r--r--packfile.c149
1 files changed, 149 insertions, 0 deletions
diff --git a/packfile.c b/packfile.c
index 60d9fc3b0..6edc43228 100644
--- a/packfile.c
+++ b/packfile.c
@@ -1,5 +1,6 @@
#include "cache.h"
#include "mru.h"
+#include "pack.h"
char *odb_pack_name(struct strbuf *buf,
const unsigned char *sha1,
@@ -59,3 +60,151 @@ void pack_report(void)
pack_open_windows, peak_pack_open_windows,
sz_fmt(pack_mapped), sz_fmt(peak_pack_mapped));
}
+
+/*
+ * Open and mmap the index file at path, perform a couple of
+ * consistency checks, then record its information to p. Return 0 on
+ * success.
+ */
+static int check_packed_git_idx(const char *path, struct packed_git *p)
+{
+ void *idx_map;
+ struct pack_idx_header *hdr;
+ size_t idx_size;
+ uint32_t version, nr, i, *index;
+ int fd = git_open(path);
+ struct stat st;
+
+ if (fd < 0)
+ return -1;
+ if (fstat(fd, &st)) {
+ close(fd);
+ return -1;
+ }
+ idx_size = xsize_t(st.st_size);
+ if (idx_size < 4 * 256 + 20 + 20) {
+ close(fd);
+ return error("index file %s is too small", path);
+ }
+ idx_map = xmmap(NULL, idx_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ close(fd);
+
+ hdr = idx_map;
+ if (hdr->idx_signature == htonl(PACK_IDX_SIGNATURE)) {
+ version = ntohl(hdr->idx_version);
+ if (version < 2 || version > 2) {
+ munmap(idx_map, idx_size);
+ return error("index file %s is version %"PRIu32
+ " and is not supported by this binary"
+ " (try upgrading GIT to a newer version)",
+ path, version);
+ }
+ } else
+ version = 1;
+
+ nr = 0;
+ index = idx_map;
+ if (version > 1)
+ index += 2; /* skip index header */
+ for (i = 0; i < 256; i++) {
+ uint32_t n = ntohl(index[i]);
+ if (n < nr) {
+ munmap(idx_map, idx_size);
+ return error("non-monotonic index %s", path);
+ }
+ nr = n;
+ }
+
+ if (version == 1) {
+ /*
+ * Total size:
+ * - 256 index entries 4 bytes each
+ * - 24-byte entries * nr (20-byte sha1 + 4-byte offset)
+ * - 20-byte SHA1 of the packfile
+ * - 20-byte SHA1 file checksum
+ */
+ if (idx_size != 4*256 + nr * 24 + 20 + 20) {
+ munmap(idx_map, idx_size);
+ return error("wrong index v1 file size in %s", path);
+ }
+ } else if (version == 2) {
+ /*
+ * Minimum size:
+ * - 8 bytes of header
+ * - 256 index entries 4 bytes each
+ * - 20-byte sha1 entry * nr
+ * - 4-byte crc entry * nr
+ * - 4-byte offset entry * nr
+ * - 20-byte SHA1 of the packfile
+ * - 20-byte SHA1 file checksum
+ * And after the 4-byte offset table might be a
+ * variable sized table containing 8-byte entries
+ * for offsets larger than 2^31.
+ */
+ unsigned long min_size = 8 + 4*256 + nr*(20 + 4 + 4) + 20 + 20;
+ unsigned long max_size = min_size;
+ if (nr)
+ max_size += (nr - 1)*8;
+ if (idx_size < min_size || idx_size > max_size) {
+ munmap(idx_map, idx_size);
+ return error("wrong index v2 file size in %s", path);
+ }
+ if (idx_size != min_size &&
+ /*
+ * make sure we can deal with large pack offsets.
+ * 31-bit signed offset won't be enough, neither
+ * 32-bit unsigned one will be.
+ */
+ (sizeof(off_t) <= 4)) {
+ munmap(idx_map, idx_size);
+ return error("pack too large for current definition of off_t in %s", path);
+ }
+ }
+
+ p->index_version = version;
+ p->index_data = idx_map;
+ p->index_size = idx_size;
+ p->num_objects = nr;
+ return 0;
+}
+
+int open_pack_index(struct packed_git *p)
+{
+ char *idx_name;
+ size_t len;
+ int ret;
+
+ if (p->index_data)
+ return 0;
+
+ if (!strip_suffix(p->pack_name, ".pack", &len))
+ die("BUG: pack_name does not end in .pack");
+ idx_name = xstrfmt("%.*s.idx", (int)len, p->pack_name);
+ ret = check_packed_git_idx(idx_name, p);
+ free(idx_name);
+ return ret;
+}
+
+static struct packed_git *alloc_packed_git(int extra)
+{
+ struct packed_git *p = xmalloc(st_add(sizeof(*p), extra));
+ memset(p, 0, sizeof(*p));
+ p->pack_fd = -1;
+ return p;
+}
+
+struct packed_git *parse_pack_index(unsigned char *sha1, const char *idx_path)
+{
+ const char *path = sha1_pack_name(sha1);
+ size_t alloc = st_add(strlen(path), 1);
+ struct packed_git *p = alloc_packed_git(alloc);
+
+ memcpy(p->pack_name, path, alloc); /* includes NUL */
+ hashcpy(p->sha1, sha1);
+ if (check_packed_git_idx(idx_path, p)) {
+ free(p);
+ return NULL;
+ }
+
+ return p;
+}