Find pack files in $GIT_DIR/objects/pack directory on git_odb_open Currently we only catalog the available pack files into a table, storing their path names relative to the pack directory. Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
diff --git a/src/odb.c b/src/odb.c
index 8a6be55..0e40ca2 100644
--- a/src/odb.c
+++ b/src/odb.c
@@ -30,14 +30,26 @@
#include "hash.h"
#include <stdio.h>
+#define GIT_PACK_NAME_MAX (5 + 40 + 1)
+typedef struct {
+ git_refcnt ref;
+
+ char pack_name[GIT_PACK_NAME_MAX];
+} git_pack;
+
struct git_odb {
git_lck lock;
/** Path to the "objects" directory. */
char *objects_dir;
+ /** Known pack files from ${objects_dir}/packs. */
+ git_pack **packs;
+ size_t n_packs;
+
/** Alternate databases to search. */
git_odb **alternates;
+ size_t n_alternates;
};
typedef struct { /* object header data */
@@ -432,13 +444,111 @@ static int open_alternates(git_odb *db)
}
db->alternates[n] = NULL;
+ db->n_alternates = n;
+ gitlck_unlock(&db->lock);
+ return 0;
+}
+
+static void free_pack(git_pack *p)
+{
+ gitrc_free(&p->ref);
+ free(p);
+}
+
+static git_pack *alloc_pack(const char *pack_name)
+{
+ git_pack *p = git__calloc(1, sizeof(*p));
+ if (!p)
+ return NULL;
+
+ gitrc_init(&p->ref);
+ strcpy(p->pack_name, pack_name);
+ gitrc_inc(&p->ref);
+ return p;
+}
+
+struct scanned_pack {
+ struct scanned_pack *next;
+ git_pack *pack;
+};
+
+static int scan_one_pack(void *state, char *name)
+{
+ struct scanned_pack **ret = state, *r;
+ char *s = strrchr(name, '/'), *d;
+
+ if (git__prefixcmp(s + 1, "pack-")
+ || git__suffixcmp(s, ".pack")
+ || strlen(s + 1) != GIT_PACK_NAME_MAX + 4)
+ return 0;
+
+ d = strrchr(s + 1, '.');
+ strcpy(d + 1, "idx"); /* "pack-abc.pack" -> "pack-abc.idx" */
+ if (gitfo_exists(name))
+ return 0;
+
+ if (!(r = git__malloc(sizeof(*r))))
+ return GIT_ERROR;
+
+ *d = '\0'; /* "pack-abc.pack" -_> "pack-abc" */
+ if (!(r->pack = alloc_pack(s + 1))) {
+ free(r);
+ return GIT_ERROR;
+ }
+
+ r->next = *ret;
+ *ret = r;
+ return 0;
+}
+
+static int scan_packs(git_odb *db)
+{
+ char pb[GIT_PATH_MAX];
+ struct scanned_pack *state = NULL, *c;
+ size_t cnt;
+
+ if (git__fmt(pb, sizeof(pb), "%s/pack", db->objects_dir) < 0)
+ return GIT_ERROR;
+ gitfo_dirent(pb, sizeof(pb), scan_one_pack, &state);
+ gitlck_lock(&db->lock);
+
+ if (!db->packs) {
+ for (cnt = 0, c = state; c; c = c->next)
+ cnt++;
+
+ db->packs = git__malloc(sizeof(*db->packs) * (cnt + 1));
+ if (!db->packs)
+ goto unlock_fail;
+ for (cnt = 0, c = state; c; ) {
+ struct scanned_pack *n = c->next;
+ db->packs[cnt++] = c->pack;
+ free(c);
+ c = n;
+ }
+ db->packs[cnt] = NULL;
+ db->n_packs = cnt;
+ } else {
+ /* TODO - merge new entries into the existing array */
+ goto unlock_fail;
+ }
+
gitlck_unlock(&db->lock);
return 0;
+
+unlock_fail:
+ gitlck_unlock(&db->lock);
+ while (state) {
+ struct scanned_pack *n = state->next;
+ free_pack(state->pack);
+ free(state);
+ state = n;
+ }
+ return GIT_ERROR;
}
int git_odb_open(git_odb **out, const char *objects_dir)
{
- git_odb *db = git__malloc(sizeof(*db));
+ git_odb *db = git__calloc(1, sizeof(*db));
if (!db)
return GIT_ERROR;
@@ -448,8 +558,11 @@ int git_odb_open(git_odb **out, const char *objects_dir)
return GIT_ERROR;
}
- db->alternates = NULL;
gitlck_init(&db->lock);
+ if (scan_packs(db)) {
+ git_odb_close(db);
+ return GIT_ERROR;
+ }
*out = db;
return GIT_SUCCESS;
@@ -462,6 +575,14 @@ void git_odb_close(git_odb *db)
gitlck_lock(&db->lock);
+ if (db->packs) {
+ git_pack **p;
+ for (p = db->packs; *p; p++)
+ if (gitrc_dec(&(*p)->ref))
+ free_pack(*p);
+ free(db->packs);
+ }
+
if (db->alternates) {
git_odb **alt;
for (alt = db->alternates; *alt; alt++)