Merge branch 'mv/clonev'
[gitweb.git] / pack-check.c
index 5f02a65b6eea7c1c505a39c31bc31298cd595ca2..90c33b1b84ef793bf017927026863de41e31fa2c 100644 (file)
@@ -4,8 +4,9 @@
 
 struct idx_entry
 {
-       const unsigned char *sha1;
        off_t                offset;
+       const unsigned char *sha1;
+       unsigned int nr;
 };
 
 static int compare_entries(const void *e1, const void *e2)
@@ -19,12 +20,34 @@ static int compare_entries(const void *e1, const void *e2)
        return 0;
 }
 
+int check_pack_crc(struct packed_git *p, struct pack_window **w_curs,
+                  off_t offset, off_t len, unsigned int nr)
+{
+       const uint32_t *index_crc;
+       uint32_t data_crc = crc32(0, Z_NULL, 0);
+
+       do {
+               unsigned int avail;
+               void *data = use_pack(p, w_curs, offset, &avail);
+               if (avail > len)
+                       avail = len;
+               data_crc = crc32(data_crc, data, avail);
+               offset += avail;
+               len -= avail;
+       } while (len);
+
+       index_crc = p->index_data;
+       index_crc += 2 + 256 + p->num_objects * (20/4) + nr;
+
+       return data_crc != ntohl(*index_crc);
+}
+
 static int verify_packfile(struct packed_git *p,
                struct pack_window **w_curs)
 {
        off_t index_size = p->index_size;
        const unsigned char *index_base = p->index_data;
-       SHA_CTX ctx;
+       git_SHA_CTX ctx;
        unsigned char sha1[20], *pack_sig;
        off_t offset = 0, pack_sig_ofs = p->pack_size - 20;
        uint32_t nr_objects, i;
@@ -37,16 +60,16 @@ static int verify_packfile(struct packed_git *p,
         * immediately.
         */
 
-       SHA1_Init(&ctx);
+       git_SHA1_Init(&ctx);
        while (offset < pack_sig_ofs) {
                unsigned int remaining;
                unsigned char *in = use_pack(p, w_curs, offset, &remaining);
                offset += remaining;
                if (offset > pack_sig_ofs)
                        remaining -= (unsigned int)(offset - pack_sig_ofs);
-               SHA1_Update(&ctx, in, remaining);
+               git_SHA1_Update(&ctx, in, remaining);
        }
-       SHA1_Final(sha1, &ctx);
+       git_SHA1_Final(sha1, &ctx);
        pack_sig = use_pack(p, w_curs, pack_sig_ofs, NULL);
        if (hashcmp(sha1, pack_sig))
                err = error("%s SHA1 checksum mismatch",
@@ -61,13 +84,15 @@ static int verify_packfile(struct packed_git *p,
         * we do not do scan-streaming check on the pack file.
         */
        nr_objects = p->num_objects;
-       entries = xmalloc(nr_objects * sizeof(*entries));
+       entries = xmalloc((nr_objects + 1) * sizeof(*entries));
+       entries[nr_objects].offset = pack_sig_ofs;
        /* first sort entries by pack offset, since unpacking them is more efficient that way */
        for (i = 0; i < nr_objects; i++) {
                entries[i].sha1 = nth_packed_object_sha1(p, i);
                if (!entries[i].sha1)
                        die("internal error pack-check nth-packed-object");
                entries[i].offset = nth_packed_object_offset(p, i);
+               entries[i].nr = i;
        }
        qsort(entries, nr_objects, sizeof(*entries), compare_entries);
 
@@ -76,6 +101,16 @@ static int verify_packfile(struct packed_git *p,
                enum object_type type;
                unsigned long size;
 
+               if (p->index_version > 1) {
+                       off_t offset = entries[i].offset;
+                       off_t len = entries[i+1].offset - offset;
+                       unsigned int nr = entries[i].nr;
+                       if (check_pack_crc(p, w_curs, offset, len, nr))
+                               err = error("index CRC mismatch for object %s "
+                                           "from %s at offset %"PRIuMAX"",
+                                           sha1_to_hex(entries[i].sha1),
+                                           p->pack_name, (uintmax_t)offset);
+               }
                data = unpack_entry(p, entries[i].offset, &type, &size);
                if (!data) {
                        err = error("cannot unpack %s from %s at offset %"PRIuMAX"",
@@ -100,7 +135,7 @@ int verify_pack(struct packed_git *p)
 {
        off_t index_size;
        const unsigned char *index_base;
-       SHA_CTX ctx;
+       git_SHA_CTX ctx;
        unsigned char sha1[20];
        int err = 0;
        struct pack_window *w_curs = NULL;
@@ -111,9 +146,9 @@ int verify_pack(struct packed_git *p)
        index_base = p->index_data;
 
        /* Verify SHA1 sum of the index file */
-       SHA1_Init(&ctx);
-       SHA1_Update(&ctx, index_base, (unsigned int)(index_size - 20));
-       SHA1_Final(sha1, &ctx);
+       git_SHA1_Init(&ctx);
+       git_SHA1_Update(&ctx, index_base, (unsigned int)(index_size - 20));
+       git_SHA1_Final(sha1, &ctx);
        if (hashcmp(sha1, index_base + index_size - 20))
                err = error("Packfile index for %s SHA1 mismatch",
                            p->pack_name);