setup: expose enumerated repo info
[gitweb.git] / packfile.c
index 20562304b7035725b44b7b002b45e94838264827..4a5fe7ab1883843a389ce74bf1c7bd89890d8e51 100644 (file)
@@ -40,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; }
@@ -444,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);
@@ -485,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);
@@ -502,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))
@@ -861,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;
@@ -1738,7 +1743,7 @@ off_t find_pack_entry_one(const unsigned char *sha1,
                       sha1[0], sha1[1], sha1[2], lo, hi, p->num_objects);
 
        while (lo < hi) {
-               unsigned mi = (lo + hi) / 2;
+               unsigned mi = lo + (hi - lo) / 2;
                int cmp = hashcmp(index + mi * stride, sha1);
 
                if (debug_lookup)
@@ -1774,3 +1779,123 @@ int is_pack_valid(struct packed_git *p)
        /* 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;
+}