worktree remove: new command
[gitweb.git] / packfile.c
index d4a78b1ac907d47bcab3e85ac568a6956b6a1280..4a5fe7ab1883843a389ce74bf1c7bd89890d8e51 100644 (file)
@@ -5,6 +5,9 @@
 #include "mergesort.h"
 #include "packfile.h"
 #include "delta.h"
+#include "list.h"
+#include "streaming.h"
+#include "sha1-lookup.h"
 
 char *odb_pack_name(struct strbuf *buf,
                    const unsigned char *sha1,
@@ -37,9 +40,7 @@ static unsigned int pack_max_fds;
 static size_t peak_pack_mapped;
 static size_t pack_mapped;
 struct packed_git *packed_git;
-
-static struct mru packed_git_mru_storage;
-struct mru *packed_git_mru = &packed_git_mru_storage;
+struct mru packed_git_mru;
 
 #define SZ_FMT PRIuMAX
 static inline uintmax_t sz_fmt(size_t s) { return s; }
@@ -441,6 +442,7 @@ static int open_packed_git_1(struct packed_git *p)
        unsigned char sha1[20];
        unsigned char *idx_sha1;
        long fd_flag;
+       ssize_t read_result;
 
        if (!p->index_data && open_pack_index(p))
                return error("packfile %s index unavailable", p->pack_name);
@@ -482,7 +484,10 @@ static int open_packed_git_1(struct packed_git *p)
                return error("cannot set FD_CLOEXEC");
 
        /* Verify we recognize this pack file format. */
-       if (read_in_full(p->pack_fd, &hdr, sizeof(hdr)) != sizeof(hdr))
+       read_result = read_in_full(p->pack_fd, &hdr, sizeof(hdr));
+       if (read_result < 0)
+               return error_errno("error reading from %s", p->pack_name);
+       if (read_result != sizeof(hdr))
                return error("file %s is far too short to be a packfile", p->pack_name);
        if (hdr.hdr_signature != htonl(PACK_SIGNATURE))
                return error("file %s is not a GIT packfile", p->pack_name);
@@ -499,7 +504,10 @@ static int open_packed_git_1(struct packed_git *p)
                             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))
+       read_result = read_in_full(p->pack_fd, sha1, sizeof(sha1));
+       if (read_result < 0)
+               return error_errno("error reading from %s", p->pack_name);
+       if (read_result != sizeof(sha1))
                return error("packfile %s signature is unavailable", p->pack_name);
        idx_sha1 = ((unsigned char *)p->index_data) + p->index_size - 40;
        if (hashcmp(sha1, idx_sha1))
@@ -507,7 +515,7 @@ static int open_packed_git_1(struct packed_git *p)
        return 0;
 }
 
-int open_packed_git(struct packed_git *p)
+static int open_packed_git(struct packed_git *p)
 {
        if (!open_packed_git_1(p))
                return 0;
@@ -858,9 +866,9 @@ static void prepare_packed_git_mru(void)
 {
        struct packed_git *p;
 
-       mru_clear(packed_git_mru);
+       mru_clear(&packed_git_mru);
        for (p = packed_git; p; p = p->next)
-               mru_append(packed_git_mru, p);
+               mru_append(&packed_git_mru, p);
 }
 
 static int prepare_packed_git_run_once = 0;
@@ -975,3 +983,919 @@ int unpack_object_header(struct packed_git *p,
 
        return type;
 }
+
+void mark_bad_packed_object(struct packed_git *p, const unsigned char *sha1)
+{
+       unsigned i;
+       for (i = 0; i < p->num_bad_objects; i++)
+               if (!hashcmp(sha1, p->bad_object_sha1 + GIT_SHA1_RAWSZ * i))
+                       return;
+       p->bad_object_sha1 = xrealloc(p->bad_object_sha1,
+                                     st_mult(GIT_MAX_RAWSZ,
+                                             st_add(p->num_bad_objects, 1)));
+       hashcpy(p->bad_object_sha1 + GIT_SHA1_RAWSZ * p->num_bad_objects, sha1);
+       p->num_bad_objects++;
+}
+
+const struct packed_git *has_packed_and_bad(const unsigned char *sha1)
+{
+       struct packed_git *p;
+       unsigned i;
+
+       for (p = packed_git; p; p = p->next)
+               for (i = 0; i < p->num_bad_objects; i++)
+                       if (!hashcmp(sha1, p->bad_object_sha1 + 20 * i))
+                               return p;
+       return NULL;
+}
+
+static off_t get_delta_base(struct packed_git *p,
+                                   struct pack_window **w_curs,
+                                   off_t *curpos,
+                                   enum object_type type,
+                                   off_t delta_obj_offset)
+{
+       unsigned char *base_info = use_pack(p, w_curs, *curpos, NULL);
+       off_t base_offset;
+
+       /* use_pack() assured us we have [base_info, base_info + 20)
+        * as a range that we can look at without walking off the
+        * end of the mapped window.  Its actually the hash size
+        * that is assured.  An OFS_DELTA longer than the hash size
+        * is stupid, as then a REF_DELTA would be smaller to store.
+        */
+       if (type == OBJ_OFS_DELTA) {
+               unsigned used = 0;
+               unsigned char c = base_info[used++];
+               base_offset = c & 127;
+               while (c & 128) {
+                       base_offset += 1;
+                       if (!base_offset || MSB(base_offset, 7))
+                               return 0;  /* overflow */
+                       c = base_info[used++];
+                       base_offset = (base_offset << 7) + (c & 127);
+               }
+               base_offset = delta_obj_offset - base_offset;
+               if (base_offset <= 0 || base_offset >= delta_obj_offset)
+                       return 0;  /* out of bound */
+               *curpos += used;
+       } else if (type == OBJ_REF_DELTA) {
+               /* The base entry _must_ be in the same pack */
+               base_offset = find_pack_entry_one(base_info, p);
+               *curpos += 20;
+       } else
+               die("I am totally screwed");
+       return base_offset;
+}
+
+/*
+ * Like get_delta_base above, but we return the sha1 instead of the pack
+ * offset. This means it is cheaper for REF deltas (we do not have to do
+ * the final object lookup), but more expensive for OFS deltas (we
+ * have to load the revidx to convert the offset back into a sha1).
+ */
+static const unsigned char *get_delta_base_sha1(struct packed_git *p,
+                                               struct pack_window **w_curs,
+                                               off_t curpos,
+                                               enum object_type type,
+                                               off_t delta_obj_offset)
+{
+       if (type == OBJ_REF_DELTA) {
+               unsigned char *base = use_pack(p, w_curs, curpos, NULL);
+               return base;
+       } else if (type == OBJ_OFS_DELTA) {
+               struct revindex_entry *revidx;
+               off_t base_offset = get_delta_base(p, w_curs, &curpos,
+                                                  type, delta_obj_offset);
+
+               if (!base_offset)
+                       return NULL;
+
+               revidx = find_pack_revindex(p, base_offset);
+               if (!revidx)
+                       return NULL;
+
+               return nth_packed_object_sha1(p, revidx->nr);
+       } else
+               return NULL;
+}
+
+static int retry_bad_packed_offset(struct packed_git *p, off_t obj_offset)
+{
+       int type;
+       struct revindex_entry *revidx;
+       const unsigned char *sha1;
+       revidx = find_pack_revindex(p, obj_offset);
+       if (!revidx)
+               return OBJ_BAD;
+       sha1 = nth_packed_object_sha1(p, revidx->nr);
+       mark_bad_packed_object(p, sha1);
+       type = sha1_object_info(sha1, NULL);
+       if (type <= OBJ_NONE)
+               return OBJ_BAD;
+       return type;
+}
+
+#define POI_STACK_PREALLOC 64
+
+static enum object_type packed_to_object_type(struct packed_git *p,
+                                             off_t obj_offset,
+                                             enum object_type type,
+                                             struct pack_window **w_curs,
+                                             off_t curpos)
+{
+       off_t small_poi_stack[POI_STACK_PREALLOC];
+       off_t *poi_stack = small_poi_stack;
+       int poi_stack_nr = 0, poi_stack_alloc = POI_STACK_PREALLOC;
+
+       while (type == OBJ_OFS_DELTA || type == OBJ_REF_DELTA) {
+               off_t base_offset;
+               unsigned long size;
+               /* Push the object we're going to leave behind */
+               if (poi_stack_nr >= poi_stack_alloc && poi_stack == small_poi_stack) {
+                       poi_stack_alloc = alloc_nr(poi_stack_nr);
+                       ALLOC_ARRAY(poi_stack, poi_stack_alloc);
+                       memcpy(poi_stack, small_poi_stack, sizeof(off_t)*poi_stack_nr);
+               } else {
+                       ALLOC_GROW(poi_stack, poi_stack_nr+1, poi_stack_alloc);
+               }
+               poi_stack[poi_stack_nr++] = obj_offset;
+               /* If parsing the base offset fails, just unwind */
+               base_offset = get_delta_base(p, w_curs, &curpos, type, obj_offset);
+               if (!base_offset)
+                       goto unwind;
+               curpos = obj_offset = base_offset;
+               type = unpack_object_header(p, w_curs, &curpos, &size);
+               if (type <= OBJ_NONE) {
+                       /* If getting the base itself fails, we first
+                        * retry the base, otherwise unwind */
+                       type = retry_bad_packed_offset(p, base_offset);
+                       if (type > OBJ_NONE)
+                               goto out;
+                       goto unwind;
+               }
+       }
+
+       switch (type) {
+       case OBJ_BAD:
+       case OBJ_COMMIT:
+       case OBJ_TREE:
+       case OBJ_BLOB:
+       case OBJ_TAG:
+               break;
+       default:
+               error("unknown object type %i at offset %"PRIuMAX" in %s",
+                     type, (uintmax_t)obj_offset, p->pack_name);
+               type = OBJ_BAD;
+       }
+
+out:
+       if (poi_stack != small_poi_stack)
+               free(poi_stack);
+       return type;
+
+unwind:
+       while (poi_stack_nr) {
+               obj_offset = poi_stack[--poi_stack_nr];
+               type = retry_bad_packed_offset(p, obj_offset);
+               if (type > OBJ_NONE)
+                       goto out;
+       }
+       type = OBJ_BAD;
+       goto out;
+}
+
+static struct hashmap delta_base_cache;
+static size_t delta_base_cached;
+
+static LIST_HEAD(delta_base_cache_lru);
+
+struct delta_base_cache_key {
+       struct packed_git *p;
+       off_t base_offset;
+};
+
+struct delta_base_cache_entry {
+       struct hashmap hash;
+       struct delta_base_cache_key key;
+       struct list_head lru;
+       void *data;
+       unsigned long size;
+       enum object_type type;
+};
+
+static unsigned int pack_entry_hash(struct packed_git *p, off_t base_offset)
+{
+       unsigned int hash;
+
+       hash = (unsigned int)(intptr_t)p + (unsigned int)base_offset;
+       hash += (hash >> 8) + (hash >> 16);
+       return hash;
+}
+
+static struct delta_base_cache_entry *
+get_delta_base_cache_entry(struct packed_git *p, off_t base_offset)
+{
+       struct hashmap_entry entry;
+       struct delta_base_cache_key key;
+
+       if (!delta_base_cache.cmpfn)
+               return NULL;
+
+       hashmap_entry_init(&entry, pack_entry_hash(p, base_offset));
+       key.p = p;
+       key.base_offset = base_offset;
+       return hashmap_get(&delta_base_cache, &entry, &key);
+}
+
+static int delta_base_cache_key_eq(const struct delta_base_cache_key *a,
+                                  const struct delta_base_cache_key *b)
+{
+       return a->p == b->p && a->base_offset == b->base_offset;
+}
+
+static int delta_base_cache_hash_cmp(const void *unused_cmp_data,
+                                    const void *va, const void *vb,
+                                    const void *vkey)
+{
+       const struct delta_base_cache_entry *a = va, *b = vb;
+       const struct delta_base_cache_key *key = vkey;
+       if (key)
+               return !delta_base_cache_key_eq(&a->key, key);
+       else
+               return !delta_base_cache_key_eq(&a->key, &b->key);
+}
+
+static int in_delta_base_cache(struct packed_git *p, off_t base_offset)
+{
+       return !!get_delta_base_cache_entry(p, base_offset);
+}
+
+/*
+ * Remove the entry from the cache, but do _not_ free the associated
+ * entry data. The caller takes ownership of the "data" buffer, and
+ * should copy out any fields it wants before detaching.
+ */
+static void detach_delta_base_cache_entry(struct delta_base_cache_entry *ent)
+{
+       hashmap_remove(&delta_base_cache, ent, &ent->key);
+       list_del(&ent->lru);
+       delta_base_cached -= ent->size;
+       free(ent);
+}
+
+static void *cache_or_unpack_entry(struct packed_git *p, off_t base_offset,
+       unsigned long *base_size, enum object_type *type)
+{
+       struct delta_base_cache_entry *ent;
+
+       ent = get_delta_base_cache_entry(p, base_offset);
+       if (!ent)
+               return unpack_entry(p, base_offset, type, base_size);
+
+       if (type)
+               *type = ent->type;
+       if (base_size)
+               *base_size = ent->size;
+       return xmemdupz(ent->data, ent->size);
+}
+
+static inline void release_delta_base_cache(struct delta_base_cache_entry *ent)
+{
+       free(ent->data);
+       detach_delta_base_cache_entry(ent);
+}
+
+void clear_delta_base_cache(void)
+{
+       struct list_head *lru, *tmp;
+       list_for_each_safe(lru, tmp, &delta_base_cache_lru) {
+               struct delta_base_cache_entry *entry =
+                       list_entry(lru, struct delta_base_cache_entry, lru);
+               release_delta_base_cache(entry);
+       }
+}
+
+static void add_delta_base_cache(struct packed_git *p, off_t base_offset,
+       void *base, unsigned long base_size, enum object_type type)
+{
+       struct delta_base_cache_entry *ent = xmalloc(sizeof(*ent));
+       struct list_head *lru, *tmp;
+
+       delta_base_cached += base_size;
+
+       list_for_each_safe(lru, tmp, &delta_base_cache_lru) {
+               struct delta_base_cache_entry *f =
+                       list_entry(lru, struct delta_base_cache_entry, lru);
+               if (delta_base_cached <= delta_base_cache_limit)
+                       break;
+               release_delta_base_cache(f);
+       }
+
+       ent->key.p = p;
+       ent->key.base_offset = base_offset;
+       ent->type = type;
+       ent->data = base;
+       ent->size = base_size;
+       list_add_tail(&ent->lru, &delta_base_cache_lru);
+
+       if (!delta_base_cache.cmpfn)
+               hashmap_init(&delta_base_cache, delta_base_cache_hash_cmp, NULL, 0);
+       hashmap_entry_init(ent, pack_entry_hash(p, base_offset));
+       hashmap_add(&delta_base_cache, ent);
+}
+
+int packed_object_info(struct packed_git *p, off_t obj_offset,
+                      struct object_info *oi)
+{
+       struct pack_window *w_curs = NULL;
+       unsigned long size;
+       off_t curpos = obj_offset;
+       enum object_type type;
+
+       /*
+        * We always get the representation type, but only convert it to
+        * a "real" type later if the caller is interested.
+        */
+       if (oi->contentp) {
+               *oi->contentp = cache_or_unpack_entry(p, obj_offset, oi->sizep,
+                                                     &type);
+               if (!*oi->contentp)
+                       type = OBJ_BAD;
+       } else {
+               type = unpack_object_header(p, &w_curs, &curpos, &size);
+       }
+
+       if (!oi->contentp && oi->sizep) {
+               if (type == OBJ_OFS_DELTA || type == OBJ_REF_DELTA) {
+                       off_t tmp_pos = curpos;
+                       off_t base_offset = get_delta_base(p, &w_curs, &tmp_pos,
+                                                          type, obj_offset);
+                       if (!base_offset) {
+                               type = OBJ_BAD;
+                               goto out;
+                       }
+                       *oi->sizep = get_size_from_delta(p, &w_curs, tmp_pos);
+                       if (*oi->sizep == 0) {
+                               type = OBJ_BAD;
+                               goto out;
+                       }
+               } else {
+                       *oi->sizep = size;
+               }
+       }
+
+       if (oi->disk_sizep) {
+               struct revindex_entry *revidx = find_pack_revindex(p, obj_offset);
+               *oi->disk_sizep = revidx[1].offset - obj_offset;
+       }
+
+       if (oi->typep || oi->typename) {
+               enum object_type ptot;
+               ptot = packed_to_object_type(p, obj_offset, type, &w_curs,
+                                            curpos);
+               if (oi->typep)
+                       *oi->typep = ptot;
+               if (oi->typename) {
+                       const char *tn = typename(ptot);
+                       if (tn)
+                               strbuf_addstr(oi->typename, tn);
+               }
+               if (ptot < 0) {
+                       type = OBJ_BAD;
+                       goto out;
+               }
+       }
+
+       if (oi->delta_base_sha1) {
+               if (type == OBJ_OFS_DELTA || type == OBJ_REF_DELTA) {
+                       const unsigned char *base;
+
+                       base = get_delta_base_sha1(p, &w_curs, curpos,
+                                                  type, obj_offset);
+                       if (!base) {
+                               type = OBJ_BAD;
+                               goto out;
+                       }
+
+                       hashcpy(oi->delta_base_sha1, base);
+               } else
+                       hashclr(oi->delta_base_sha1);
+       }
+
+       oi->whence = in_delta_base_cache(p, obj_offset) ? OI_DBCACHED :
+                                                         OI_PACKED;
+
+out:
+       unuse_pack(&w_curs);
+       return type;
+}
+
+static void *unpack_compressed_entry(struct packed_git *p,
+                                   struct pack_window **w_curs,
+                                   off_t curpos,
+                                   unsigned long size)
+{
+       int st;
+       git_zstream stream;
+       unsigned char *buffer, *in;
+
+       buffer = xmallocz_gently(size);
+       if (!buffer)
+               return NULL;
+       memset(&stream, 0, sizeof(stream));
+       stream.next_out = buffer;
+       stream.avail_out = size + 1;
+
+       git_inflate_init(&stream);
+       do {
+               in = use_pack(p, w_curs, curpos, &stream.avail_in);
+               stream.next_in = in;
+               st = git_inflate(&stream, Z_FINISH);
+               if (!stream.avail_out)
+                       break; /* the payload is larger than it should be */
+               curpos += stream.next_in - in;
+       } while (st == Z_OK || st == Z_BUF_ERROR);
+       git_inflate_end(&stream);
+       if ((st != Z_STREAM_END) || stream.total_out != size) {
+               free(buffer);
+               return NULL;
+       }
+
+       return buffer;
+}
+
+static void write_pack_access_log(struct packed_git *p, off_t obj_offset)
+{
+       static struct trace_key pack_access = TRACE_KEY_INIT(PACK_ACCESS);
+       trace_printf_key(&pack_access, "%s %"PRIuMAX"\n",
+                        p->pack_name, (uintmax_t)obj_offset);
+}
+
+int do_check_packed_object_crc;
+
+#define UNPACK_ENTRY_STACK_PREALLOC 64
+struct unpack_entry_stack_ent {
+       off_t obj_offset;
+       off_t curpos;
+       unsigned long size;
+};
+
+static void *read_object(const unsigned char *sha1, enum object_type *type,
+                        unsigned long *size)
+{
+       struct object_info oi = OBJECT_INFO_INIT;
+       void *content;
+       oi.typep = type;
+       oi.sizep = size;
+       oi.contentp = &content;
+
+       if (sha1_object_info_extended(sha1, &oi, 0) < 0)
+               return NULL;
+       return content;
+}
+
+void *unpack_entry(struct packed_git *p, off_t obj_offset,
+                  enum object_type *final_type, unsigned long *final_size)
+{
+       struct pack_window *w_curs = NULL;
+       off_t curpos = obj_offset;
+       void *data = NULL;
+       unsigned long size;
+       enum object_type type;
+       struct unpack_entry_stack_ent small_delta_stack[UNPACK_ENTRY_STACK_PREALLOC];
+       struct unpack_entry_stack_ent *delta_stack = small_delta_stack;
+       int delta_stack_nr = 0, delta_stack_alloc = UNPACK_ENTRY_STACK_PREALLOC;
+       int base_from_cache = 0;
+
+       write_pack_access_log(p, obj_offset);
+
+       /* PHASE 1: drill down to the innermost base object */
+       for (;;) {
+               off_t base_offset;
+               int i;
+               struct delta_base_cache_entry *ent;
+
+               ent = get_delta_base_cache_entry(p, curpos);
+               if (ent) {
+                       type = ent->type;
+                       data = ent->data;
+                       size = ent->size;
+                       detach_delta_base_cache_entry(ent);
+                       base_from_cache = 1;
+                       break;
+               }
+
+               if (do_check_packed_object_crc && p->index_version > 1) {
+                       struct revindex_entry *revidx = find_pack_revindex(p, obj_offset);
+                       off_t len = revidx[1].offset - obj_offset;
+                       if (check_pack_crc(p, &w_curs, obj_offset, len, revidx->nr)) {
+                               const unsigned char *sha1 =
+                                       nth_packed_object_sha1(p, revidx->nr);
+                               error("bad packed object CRC for %s",
+                                     sha1_to_hex(sha1));
+                               mark_bad_packed_object(p, sha1);
+                               data = NULL;
+                               goto out;
+                       }
+               }
+
+               type = unpack_object_header(p, &w_curs, &curpos, &size);
+               if (type != OBJ_OFS_DELTA && type != OBJ_REF_DELTA)
+                       break;
+
+               base_offset = get_delta_base(p, &w_curs, &curpos, type, obj_offset);
+               if (!base_offset) {
+                       error("failed to validate delta base reference "
+                             "at offset %"PRIuMAX" from %s",
+                             (uintmax_t)curpos, p->pack_name);
+                       /* bail to phase 2, in hopes of recovery */
+                       data = NULL;
+                       break;
+               }
+
+               /* push object, proceed to base */
+               if (delta_stack_nr >= delta_stack_alloc
+                   && delta_stack == small_delta_stack) {
+                       delta_stack_alloc = alloc_nr(delta_stack_nr);
+                       ALLOC_ARRAY(delta_stack, delta_stack_alloc);
+                       memcpy(delta_stack, small_delta_stack,
+                              sizeof(*delta_stack)*delta_stack_nr);
+               } else {
+                       ALLOC_GROW(delta_stack, delta_stack_nr+1, delta_stack_alloc);
+               }
+               i = delta_stack_nr++;
+               delta_stack[i].obj_offset = obj_offset;
+               delta_stack[i].curpos = curpos;
+               delta_stack[i].size = size;
+
+               curpos = obj_offset = base_offset;
+       }
+
+       /* PHASE 2: handle the base */
+       switch (type) {
+       case OBJ_OFS_DELTA:
+       case OBJ_REF_DELTA:
+               if (data)
+                       die("BUG: unpack_entry: left loop at a valid delta");
+               break;
+       case OBJ_COMMIT:
+       case OBJ_TREE:
+       case OBJ_BLOB:
+       case OBJ_TAG:
+               if (!base_from_cache)
+                       data = unpack_compressed_entry(p, &w_curs, curpos, size);
+               break;
+       default:
+               data = NULL;
+               error("unknown object type %i at offset %"PRIuMAX" in %s",
+                     type, (uintmax_t)obj_offset, p->pack_name);
+       }
+
+       /* PHASE 3: apply deltas in order */
+
+       /* invariants:
+        *   'data' holds the base data, or NULL if there was corruption
+        */
+       while (delta_stack_nr) {
+               void *delta_data;
+               void *base = data;
+               void *external_base = NULL;
+               unsigned long delta_size, base_size = size;
+               int i;
+
+               data = NULL;
+
+               if (base)
+                       add_delta_base_cache(p, obj_offset, base, base_size, type);
+
+               if (!base) {
+                       /*
+                        * We're probably in deep shit, but let's try to fetch
+                        * the required base anyway from another pack or loose.
+                        * This is costly but should happen only in the presence
+                        * of a corrupted pack, and is better than failing outright.
+                        */
+                       struct revindex_entry *revidx;
+                       const unsigned char *base_sha1;
+                       revidx = find_pack_revindex(p, obj_offset);
+                       if (revidx) {
+                               base_sha1 = nth_packed_object_sha1(p, revidx->nr);
+                               error("failed to read delta base object %s"
+                                     " at offset %"PRIuMAX" from %s",
+                                     sha1_to_hex(base_sha1), (uintmax_t)obj_offset,
+                                     p->pack_name);
+                               mark_bad_packed_object(p, base_sha1);
+                               base = read_object(base_sha1, &type, &base_size);
+                               external_base = base;
+                       }
+               }
+
+               i = --delta_stack_nr;
+               obj_offset = delta_stack[i].obj_offset;
+               curpos = delta_stack[i].curpos;
+               delta_size = delta_stack[i].size;
+
+               if (!base)
+                       continue;
+
+               delta_data = unpack_compressed_entry(p, &w_curs, curpos, delta_size);
+
+               if (!delta_data) {
+                       error("failed to unpack compressed delta "
+                             "at offset %"PRIuMAX" from %s",
+                             (uintmax_t)curpos, p->pack_name);
+                       data = NULL;
+                       free(external_base);
+                       continue;
+               }
+
+               data = patch_delta(base, base_size,
+                                  delta_data, delta_size,
+                                  &size);
+
+               /*
+                * We could not apply the delta; warn the user, but keep going.
+                * Our failure will be noticed either in the next iteration of
+                * the loop, or if this is the final delta, in the caller when
+                * we return NULL. Those code paths will take care of making
+                * a more explicit warning and retrying with another copy of
+                * the object.
+                */
+               if (!data)
+                       error("failed to apply delta");
+
+               free(delta_data);
+               free(external_base);
+       }
+
+       if (final_type)
+               *final_type = type;
+       if (final_size)
+               *final_size = size;
+
+out:
+       unuse_pack(&w_curs);
+
+       if (delta_stack != small_delta_stack)
+               free(delta_stack);
+
+       return data;
+}
+
+const unsigned char *nth_packed_object_sha1(struct packed_git *p,
+                                           uint32_t n)
+{
+       const unsigned char *index = p->index_data;
+       if (!index) {
+               if (open_pack_index(p))
+                       return NULL;
+               index = p->index_data;
+       }
+       if (n >= p->num_objects)
+               return NULL;
+       index += 4 * 256;
+       if (p->index_version == 1) {
+               return index + 24 * n + 4;
+       } else {
+               index += 8;
+               return index + 20 * n;
+       }
+}
+
+const struct object_id *nth_packed_object_oid(struct object_id *oid,
+                                             struct packed_git *p,
+                                             uint32_t n)
+{
+       const unsigned char *hash = nth_packed_object_sha1(p, n);
+       if (!hash)
+               return NULL;
+       hashcpy(oid->hash, hash);
+       return oid;
+}
+
+void check_pack_index_ptr(const struct packed_git *p, const void *vptr)
+{
+       const unsigned char *ptr = vptr;
+       const unsigned char *start = p->index_data;
+       const unsigned char *end = start + p->index_size;
+       if (ptr < start)
+               die(_("offset before start of pack index for %s (corrupt index?)"),
+                   p->pack_name);
+       /* No need to check for underflow; .idx files must be at least 8 bytes */
+       if (ptr >= end - 8)
+               die(_("offset beyond end of pack index for %s (truncated index?)"),
+                   p->pack_name);
+}
+
+off_t nth_packed_object_offset(const struct packed_git *p, uint32_t n)
+{
+       const unsigned char *index = p->index_data;
+       index += 4 * 256;
+       if (p->index_version == 1) {
+               return ntohl(*((uint32_t *)(index + 24 * n)));
+       } else {
+               uint32_t off;
+               index += 8 + p->num_objects * (20 + 4);
+               off = ntohl(*((uint32_t *)(index + 4 * n)));
+               if (!(off & 0x80000000))
+                       return off;
+               index += p->num_objects * 4 + (off & 0x7fffffff) * 8;
+               check_pack_index_ptr(p, index);
+               return (((uint64_t)ntohl(*((uint32_t *)(index + 0)))) << 32) |
+                                  ntohl(*((uint32_t *)(index + 4)));
+       }
+}
+
+off_t find_pack_entry_one(const unsigned char *sha1,
+                                 struct packed_git *p)
+{
+       const uint32_t *level1_ofs = p->index_data;
+       const unsigned char *index = p->index_data;
+       unsigned hi, lo, stride;
+       static int debug_lookup = -1;
+
+       if (debug_lookup < 0)
+               debug_lookup = !!getenv("GIT_DEBUG_LOOKUP");
+
+       if (!index) {
+               if (open_pack_index(p))
+                       return 0;
+               level1_ofs = p->index_data;
+               index = p->index_data;
+       }
+       if (p->index_version > 1) {
+               level1_ofs += 2;
+               index += 8;
+       }
+       index += 4 * 256;
+       hi = ntohl(level1_ofs[*sha1]);
+       lo = ((*sha1 == 0x0) ? 0 : ntohl(level1_ofs[*sha1 - 1]));
+       if (p->index_version > 1) {
+               stride = 20;
+       } else {
+               stride = 24;
+               index += 4;
+       }
+
+       if (debug_lookup)
+               printf("%02x%02x%02x... lo %u hi %u nr %"PRIu32"\n",
+                      sha1[0], sha1[1], sha1[2], lo, hi, p->num_objects);
+
+       while (lo < hi) {
+               unsigned mi = lo + (hi - lo) / 2;
+               int cmp = hashcmp(index + mi * stride, sha1);
+
+               if (debug_lookup)
+                       printf("lo %u hi %u rg %u mi %u\n",
+                              lo, hi, hi - lo, mi);
+               if (!cmp)
+                       return nth_packed_object_offset(p, mi);
+               if (cmp > 0)
+                       hi = mi;
+               else
+                       lo = mi+1;
+       }
+       return 0;
+}
+
+int is_pack_valid(struct packed_git *p)
+{
+       /* An already open pack is known to be valid. */
+       if (p->pack_fd != -1)
+               return 1;
+
+       /* If the pack has one window completely covering the
+        * file size, the pack is known to be valid even if
+        * the descriptor is not currently open.
+        */
+       if (p->windows) {
+               struct pack_window *w = p->windows;
+
+               if (!w->offset && w->len == p->pack_size)
+                       return 1;
+       }
+
+       /* Force the pack to open to prove its valid. */
+       return !open_packed_git(p);
+}
+
+struct packed_git *find_sha1_pack(const unsigned char *sha1,
+                                 struct packed_git *packs)
+{
+       struct packed_git *p;
+
+       for (p = packs; p; p = p->next) {
+               if (find_pack_entry_one(sha1, p))
+                       return p;
+       }
+       return NULL;
+
+}
+
+static int fill_pack_entry(const unsigned char *sha1,
+                          struct pack_entry *e,
+                          struct packed_git *p)
+{
+       off_t offset;
+
+       if (p->num_bad_objects) {
+               unsigned i;
+               for (i = 0; i < p->num_bad_objects; i++)
+                       if (!hashcmp(sha1, p->bad_object_sha1 + 20 * i))
+                               return 0;
+       }
+
+       offset = find_pack_entry_one(sha1, p);
+       if (!offset)
+               return 0;
+
+       /*
+        * We are about to tell the caller where they can locate the
+        * requested object.  We better make sure the packfile is
+        * still here and can be accessed before supplying that
+        * answer, as it may have been deleted since the index was
+        * loaded!
+        */
+       if (!is_pack_valid(p))
+               return 0;
+       e->offset = offset;
+       e->p = p;
+       hashcpy(e->sha1, sha1);
+       return 1;
+}
+
+/*
+ * Iff a pack file contains the object named by sha1, return true and
+ * store its location to e.
+ */
+int find_pack_entry(const unsigned char *sha1, struct pack_entry *e)
+{
+       struct mru_entry *p;
+
+       prepare_packed_git();
+       if (!packed_git)
+               return 0;
+
+       for (p = packed_git_mru.head; p; p = p->next) {
+               if (fill_pack_entry(sha1, e, p->item)) {
+                       mru_mark(&packed_git_mru, p);
+                       return 1;
+               }
+       }
+       return 0;
+}
+
+int has_sha1_pack(const unsigned char *sha1)
+{
+       struct pack_entry e;
+       return find_pack_entry(sha1, &e);
+}
+
+int has_pack_index(const unsigned char *sha1)
+{
+       struct stat st;
+       if (stat(sha1_pack_index_name(sha1), &st))
+               return 0;
+       return 1;
+}
+
+static int for_each_object_in_pack(struct packed_git *p, each_packed_object_fn cb, void *data)
+{
+       uint32_t i;
+       int r = 0;
+
+       for (i = 0; i < p->num_objects; i++) {
+               struct object_id oid;
+
+               if (!nth_packed_object_oid(&oid, p, i))
+                       return error("unable to get sha1 of object %u in %s",
+                                    i, p->pack_name);
+
+               r = cb(&oid, p, i, data);
+               if (r)
+                       break;
+       }
+       return r;
+}
+
+int for_each_packed_object(each_packed_object_fn cb, void *data, unsigned flags)
+{
+       struct packed_git *p;
+       int r = 0;
+       int pack_errors = 0;
+
+       prepare_packed_git();
+       for (p = packed_git; p; p = p->next) {
+               if ((flags & FOR_EACH_OBJECT_LOCAL_ONLY) && !p->pack_local)
+                       continue;
+               if (open_pack_index(p)) {
+                       pack_errors = 1;
+                       continue;
+               }
+               r = for_each_object_in_pack(p, cb, data);
+               if (r)
+                       break;
+       }
+       return r ? r : pack_errors;
+}