index-pack: learn about pack index version 2
[gitweb.git] / sha1_file.c
index 8a19d7ee29c24b4f3df229b7cffc186d85ae573a..ebdd497badf21abd599ad50607c957c177f993f8 100644 (file)
@@ -494,6 +494,7 @@ static int check_packed_git_idx(const char *path,  struct packed_git *p)
        p->index_version = 1;
        p->index_data = idx_map;
        p->index_size = idx_size;
+       p->num_objects = nr;
        return 0;
 }
 
@@ -605,11 +606,11 @@ static int open_packed_git_1(struct packed_git *p)
                        p->pack_name, ntohl(hdr.hdr_version));
 
        /* Verify the pack matches its index. */
-       if (num_packed_objects(p) != ntohl(hdr.hdr_entries))
+       if (p->num_objects != ntohl(hdr.hdr_entries))
                return error("packfile %s claims to have %u objects"
-                       " while index size indicates %u objects",
-                       p->pack_name, ntohl(hdr.hdr_entries),
-                       num_packed_objects(p));
+                            " while index indicates %u objects",
+                            p->pack_name, ntohl(hdr.hdr_entries),
+                            p->num_objects);
        if (lseek(p->pack_fd, p->pack_size - sizeof(sha1), SEEK_SET) == -1)
                return error("end of packfile %s is unavailable", p->pack_name);
        if (read_in_full(p->pack_fd, sha1, sizeof(sha1)) != sizeof(sha1))
@@ -1030,14 +1031,27 @@ static void *unpack_sha1_rest(z_stream *stream, void *buffer, unsigned long size
                n = size;
        memcpy(buf, (char *) buffer + bytes, n);
        bytes = n;
-       if (bytes < size) {
+       if (bytes <= size) {
+               /*
+                * The above condition must be (bytes <= size), not
+                * (bytes < size).  In other words, even though we
+                * expect no more output and set avail_out to zer0,
+                * the input zlib stream may have bytes that express
+                * "this concludes the stream", and we *do* want to
+                * eat that input.
+                *
+                * Otherwise we would not be able to test that we
+                * consumed all the input to reach the expected size;
+                * we also want to check that zlib tells us that all
+                * went well with status == Z_STREAM_END at the end.
+                */
                stream->next_out = buf + bytes;
                stream->avail_out = size - bytes;
                while (status == Z_OK)
                        status = inflate(stream, Z_FINISH);
        }
        buf[size] = 0;
-       if ((status == Z_OK || status == Z_STREAM_END) && !stream->avail_in) {
+       if (status == Z_STREAM_END && !stream->avail_in) {
                inflateEnd(stream);
                return buf;
        }
@@ -1136,7 +1150,7 @@ static off_t get_delta_base(struct packed_git *p,
                base_offset = c & 127;
                while (c & 128) {
                        base_offset += 1;
-                       if (!base_offset || base_offset & ~(~0UL >> 7))
+                       if (!base_offset || MSB(base_offset, 7))
                                die("offset value overflow for delta base object");
                        c = base_info[used++];
                        base_offset = (base_offset << 7) + (c & 127);
@@ -1355,11 +1369,18 @@ static void *unpack_compressed_entry(struct packed_git *p,
 #define MAX_DELTA_CACHE (256)
 
 static size_t delta_base_cached;
+
+static struct delta_base_cache_lru_list {
+       struct delta_base_cache_lru_list *prev;
+       struct delta_base_cache_lru_list *next;
+} delta_base_cache_lru = { &delta_base_cache_lru, &delta_base_cache_lru };
+
 static struct delta_base_cache_entry {
+       struct delta_base_cache_lru_list lru;
+       void *data;
        struct packed_git *p;
        off_t base_offset;
        unsigned long size;
-       void *data;
        enum object_type type;
 } delta_base_cache[MAX_DELTA_CACHE];
 
@@ -1387,6 +1408,8 @@ static void *cache_or_unpack_entry(struct packed_git *p, off_t base_offset,
 found_cache_entry:
        if (!keep_cache) {
                ent->data = NULL;
+               ent->lru.next->prev = ent->lru.prev;
+               ent->lru.prev->next = ent->lru.next;
                delta_base_cached -= ent->size;
        }
        else {
@@ -1404,6 +1427,8 @@ static inline void release_delta_base_cache(struct delta_base_cache_entry *ent)
        if (ent->data) {
                free(ent->data);
                ent->data = NULL;
+               ent->lru.next->prev = ent->lru.prev;
+               ent->lru.prev->next = ent->lru.next;
                delta_base_cached -= ent->size;
        }
 }
@@ -1411,26 +1436,38 @@ static inline void release_delta_base_cache(struct delta_base_cache_entry *ent)
 static void add_delta_base_cache(struct packed_git *p, off_t base_offset,
        void *base, unsigned long base_size, enum object_type type)
 {
-       unsigned long i, hash = pack_entry_hash(p, base_offset);
+       unsigned long hash = pack_entry_hash(p, base_offset);
        struct delta_base_cache_entry *ent = delta_base_cache + hash;
+       struct delta_base_cache_lru_list *lru;
 
        release_delta_base_cache(ent);
        delta_base_cached += base_size;
-       for (i = 0; delta_base_cached > delta_base_cache_limit
-                   && i < MAX_DELTA_CACHE ; i++) {
-               struct delta_base_cache_entry *f = delta_base_cache + i;
+
+       for (lru = delta_base_cache_lru.next;
+            delta_base_cached > delta_base_cache_limit
+            && lru != &delta_base_cache_lru;
+            lru = lru->next) {
+               struct delta_base_cache_entry *f = (void *)lru;
                if (f->type == OBJ_BLOB)
                        release_delta_base_cache(f);
        }
-       for (i = 0; delta_base_cached > delta_base_cache_limit
-                   && i < MAX_DELTA_CACHE ; i++)
-               release_delta_base_cache(delta_base_cache + i);
+       for (lru = delta_base_cache_lru.next;
+            delta_base_cached > delta_base_cache_limit
+            && lru != &delta_base_cache_lru;
+            lru = lru->next) {
+               struct delta_base_cache_entry *f = (void *)lru;
+               release_delta_base_cache(f);
+       }
 
        ent->p = p;
        ent->base_offset = base_offset;
        ent->type = type;
        ent->data = base;
        ent->size = base_size;
+       ent->lru.next = &delta_base_cache_lru;
+       ent->lru.prev = delta_base_cache_lru.prev;
+       delta_base_cache_lru.prev->next = &ent->lru;
+       delta_base_cache_lru.prev = &ent->lru;
 }
 
 static void *unpack_delta_entry(struct packed_git *p,
@@ -1490,21 +1527,14 @@ void *unpack_entry(struct packed_git *p, off_t obj_offset,
        return data;
 }
 
-uint32_t num_packed_objects(const struct packed_git *p)
-{
-       /* See check_packed_git_idx() */
-       return (uint32_t)((p->index_size - 20 - 20 - 4*256) / 24);
-}
-
-int nth_packed_object_sha1(const struct packed_git *p, uint32_t n,
-                          unsigned char* sha1)
+const unsigned char *nth_packed_object_sha1(const struct packed_git *p,
+                                           uint32_t n)
 {
        const unsigned char *index = p->index_data;
        index += 4 * 256;
-       if (num_packed_objects(p) <= n)
-               return -1;
-       hashcpy(sha1, index + 24 * n + 4);
-       return 0;
+       if (n >= p->num_objects)
+               return NULL;
+       return index + 24 * n + 4;
 }
 
 off_t find_pack_entry_one(const unsigned char *sha1,
@@ -1772,7 +1802,7 @@ void *read_object_with_reference(const unsigned char *sha1,
        }
 }
 
-static void write_sha1_file_prepare(void *buf, unsigned long len,
+static void write_sha1_file_prepare(const void *buf, unsigned long len,
                                     const char *type, unsigned char *sha1,
                                     char *hdr, int *hdrlen)
 {
@@ -1900,7 +1930,7 @@ static void setup_object_header(z_stream *stream, const char *type, unsigned lon
        stream->avail_out -= hdrlen;
 }
 
-int hash_sha1_file(void *buf, unsigned long len, const char *type,
+int hash_sha1_file(const void *buf, unsigned long len, const char *type,
                    unsigned char *sha1)
 {
        char hdr[32];
@@ -1911,7 +1941,7 @@ int hash_sha1_file(void *buf, unsigned long len, const char *type,
 
 int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *returnsha1)
 {
-       int size;
+       int size, ret;
        unsigned char *compressed;
        z_stream stream;
        unsigned char sha1[20];
@@ -1943,7 +1973,7 @@ int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned cha
                return error("sha1 file %s: %s\n", filename, strerror(errno));
        }
 
-       snprintf(tmpfile, sizeof(tmpfile), "%s/obj_XXXXXX", get_object_directory());
+       snprintf(tmpfile, sizeof(tmpfile), "%s/tmp_obj_XXXXXX", get_object_directory());
 
        fd = mkstemp(tmpfile);
        if (fd < 0) {
@@ -1971,15 +2001,21 @@ int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned cha
        /* Then the data itself.. */
        stream.next_in = buf;
        stream.avail_in = len;
-       while (deflate(&stream, Z_FINISH) == Z_OK)
-               /* nothing */;
-       deflateEnd(&stream);
+       ret = deflate(&stream, Z_FINISH);
+       if (ret != Z_STREAM_END)
+               die("unable to deflate new object %s (%d)", sha1_to_hex(sha1), ret);
+
+       ret = deflateEnd(&stream);
+       if (ret != Z_OK)
+               die("deflateEnd on object %s failed (%d)", sha1_to_hex(sha1), ret);
+
        size = stream.total_out;
 
        if (write_buffer(fd, compressed, size) < 0)
                die("unable to write sha1 file");
        fchmod(fd, 0444);
-       close(fd);
+       if (close(fd))
+               die("unable to write sha1 file");
        free(compressed);
 
        return move_temp_to_file(tmpfile, filename);
@@ -2064,7 +2100,7 @@ int write_sha1_from_fd(const unsigned char *sha1, int fd, char *buffer,
        int ret;
        SHA_CTX c;
 
-       snprintf(tmpfile, sizeof(tmpfile), "%s/obj_XXXXXX", get_object_directory());
+       snprintf(tmpfile, sizeof(tmpfile), "%s/tmp_obj_XXXXXX", get_object_directory());
 
        local = mkstemp(tmpfile);
        if (local < 0) {
@@ -2113,7 +2149,9 @@ int write_sha1_from_fd(const unsigned char *sha1, int fd, char *buffer,
        } while (1);
        inflateEnd(&stream);
 
-       close(local);
+       fchmod(local, 0444);
+       if (close(local) != 0)
+               die("unable to write sha1 file");
        SHA1_Final(real_sha1, &c);
        if (ret != Z_STREAM_END) {
                unlink(tmpfile);