struct alternate_object_database *alt_odb_list;
static struct alternate_object_database **alt_odb_tail;
+static void read_info_alternates(const char * alternates, int depth);
+
/*
* Prepare alternate object database registry.
*
* SHA1, an extra slash for the first level indirection, and the
* terminating NUL.
*/
-static void link_alt_odb_entries(const char *alt, const char *ep, int sep,
- const char *relative_base)
+static int link_alt_odb_entry(const char * entry, int len, const char * relative_base, int depth)
{
- const char *cp, *last;
- struct alternate_object_database *ent;
+ struct stat st;
const char *objdir = get_object_directory();
+ struct alternate_object_database *ent;
+ struct alternate_object_database *alt;
+ /* 43 = 40-byte + 2 '/' + terminating NUL */
+ int pfxlen = len;
+ int entlen = pfxlen + 43;
int base_len = -1;
+ if (*entry != '/' && relative_base) {
+ /* Relative alt-odb */
+ if (base_len < 0)
+ base_len = strlen(relative_base) + 1;
+ entlen += base_len;
+ pfxlen += base_len;
+ }
+ ent = xmalloc(sizeof(*ent) + entlen);
+
+ if (*entry != '/' && relative_base) {
+ memcpy(ent->base, relative_base, base_len - 1);
+ ent->base[base_len - 1] = '/';
+ memcpy(ent->base + base_len, entry, len);
+ }
+ else
+ memcpy(ent->base, entry, pfxlen);
+
+ ent->name = ent->base + pfxlen + 1;
+ ent->base[pfxlen + 3] = '/';
+ ent->base[pfxlen] = ent->base[entlen-1] = 0;
+
+ /* Detect cases where alternate disappeared */
+ if (stat(ent->base, &st) || !S_ISDIR(st.st_mode)) {
+ error("object directory %s does not exist; "
+ "check .git/objects/info/alternates.",
+ ent->base);
+ free(ent);
+ return -1;
+ }
+
+ /* Prevent the common mistake of listing the same
+ * thing twice, or object directory itself.
+ */
+ for (alt = alt_odb_list; alt; alt = alt->next) {
+ if (!memcmp(ent->base, alt->base, pfxlen)) {
+ free(ent);
+ return -1;
+ }
+ }
+ if (!memcmp(ent->base, objdir, pfxlen)) {
+ free(ent);
+ return -1;
+ }
+
+ /* add the alternate entry */
+ *alt_odb_tail = ent;
+ alt_odb_tail = &(ent->next);
+ ent->next = NULL;
+
+ /* recursively add alternates */
+ read_info_alternates(ent->base, depth + 1);
+
+ ent->base[pfxlen] = '/';
+
+ return 0;
+}
+
+static void link_alt_odb_entries(const char *alt, const char *ep, int sep,
+ const char *relative_base, int depth)
+{
+ const char *cp, *last;
+
+ if (depth > 5) {
+ error("%s: ignoring alternate object stores, nesting too deep.",
+ relative_base);
+ return;
+ }
+
last = alt;
while (last < ep) {
cp = last;
last = cp + 1;
continue;
}
- for ( ; cp < ep && *cp != sep; cp++)
- ;
+ while (cp < ep && *cp != sep)
+ cp++;
if (last != cp) {
- struct stat st;
- struct alternate_object_database *alt;
- /* 43 = 40-byte + 2 '/' + terminating NUL */
- int pfxlen = cp - last;
- int entlen = pfxlen + 43;
-
- if (*last != '/' && relative_base) {
- /* Relative alt-odb */
- if (base_len < 0)
- base_len = strlen(relative_base) + 1;
- entlen += base_len;
- pfxlen += base_len;
- }
- ent = xmalloc(sizeof(*ent) + entlen);
-
- if (*last != '/' && relative_base) {
- memcpy(ent->base, relative_base, base_len - 1);
- ent->base[base_len - 1] = '/';
- memcpy(ent->base + base_len,
- last, cp - last);
- }
- else
- memcpy(ent->base, last, pfxlen);
-
- ent->name = ent->base + pfxlen + 1;
- ent->base[pfxlen + 3] = '/';
- ent->base[pfxlen] = ent->base[entlen-1] = 0;
-
- /* Detect cases where alternate disappeared */
- if (stat(ent->base, &st) || !S_ISDIR(st.st_mode)) {
- error("object directory %s does not exist; "
- "check .git/objects/info/alternates.",
- ent->base);
- goto bad;
- }
- ent->base[pfxlen] = '/';
-
- /* Prevent the common mistake of listing the same
- * thing twice, or object directory itself.
- */
- for (alt = alt_odb_list; alt; alt = alt->next)
- if (!memcmp(ent->base, alt->base, pfxlen))
- goto bad;
- if (!memcmp(ent->base, objdir, pfxlen)) {
- bad:
- free(ent);
- }
- else {
- *alt_odb_tail = ent;
- alt_odb_tail = &(ent->next);
- ent->next = NULL;
+ if ((*last != '/') && depth) {
+ error("%s: ignoring relative alternate object store %s",
+ relative_base, last);
+ } else {
+ link_alt_odb_entry(last, cp - last,
+ relative_base, depth);
}
}
while (cp < ep && *cp == sep)
}
}
-void prepare_alt_odb(void)
+static void read_info_alternates(const char * relative_base, int depth)
{
- char path[PATH_MAX];
char *map;
- int fd;
struct stat st;
- char *alt;
-
- alt = getenv(ALTERNATE_DB_ENVIRONMENT);
- if (!alt) alt = "";
-
- if (alt_odb_tail)
- return;
- alt_odb_tail = &alt_odb_list;
- link_alt_odb_entries(alt, alt + strlen(alt), ':', NULL);
+ char path[PATH_MAX];
+ int fd;
- sprintf(path, "%s/info/alternates", get_object_directory());
+ sprintf(path, "%s/info/alternates", relative_base);
fd = open(path, O_RDONLY);
if (fd < 0)
return;
if (map == MAP_FAILED)
return;
- link_alt_odb_entries(map, map + st.st_size, '\n',
- get_object_directory());
+ link_alt_odb_entries(map, map + st.st_size, '\n', relative_base, depth);
+
munmap(map, st.st_size);
}
+void prepare_alt_odb(void)
+{
+ char *alt;
+
+ alt = getenv(ALTERNATE_DB_ENVIRONMENT);
+ if (!alt) alt = "";
+
+ if (alt_odb_tail)
+ return;
+ alt_odb_tail = &alt_odb_list;
+ link_alt_odb_entries(alt, alt + strlen(alt), ':', NULL, 0);
+
+ read_info_alternates(get_object_directory(), 0);
+}
+
static char *find_sha1_file(const unsigned char *sha1, struct stat *st)
{
char *name = sha1_file_name(sha1);