From ace1534d6f3a710ed82e7e27ba1fe77c0c278695 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 7 May 2005 00:38:04 -0700 Subject: [PATCH] Introduce SHA1_FILE_DIRECTORIES to support multiple object databases. SHA1_FILE_DIRECTORIES environment variable is a colon separated paths used when looking for SHA1 files not found in the usual place for reading. Creating a new SHA1 file does not use this alternate object database location mechanism. This is useful to archive older, rarely used objects into separate directories. Signed-off-by: Junio C Hamano --- cache.h | 1 + fsck-cache.c | 2 +- sha1_file.c | 110 ++++++++++++++++++++++++++++++++++++++++++++++++----------- 3 files changed, 93 insertions(+), 20 deletions(-) diff --git a/cache.h b/cache.h index 1dba405703..314ee0dd0f 100644 --- a/cache.h +++ b/cache.h @@ -101,6 +101,7 @@ unsigned int active_nr, active_alloc, active_cache_changed; #define DB_ENVIRONMENT "SHA1_FILE_DIRECTORY" #define DEFAULT_DB_ENVIRONMENT ".git/objects" +#define ALTERNATE_DB_ENVIRONMENT "SHA1_FILE_DIRECTORIES" #define get_object_directory() (getenv(DB_ENVIRONMENT) ? : DEFAULT_DB_ENVIRONMENT) diff --git a/fsck-cache.c b/fsck-cache.c index abdec92ffc..d59d57ea76 100644 --- a/fsck-cache.c +++ b/fsck-cache.c @@ -306,7 +306,7 @@ int main(int argc, char **argv) usage("fsck-cache [--tags] [[--unreachable] [--cache] *]"); } - sha1_dir = getenv(DB_ENVIRONMENT) ? : DEFAULT_DB_ENVIRONMENT; + sha1_dir = get_object_directory(); for (i = 0; i < 256; i++) { static char dir[4096]; sprintf(dir, "%s/%02x", sha1_dir, i); diff --git a/sha1_file.c b/sha1_file.c index f1c1c70d78..bd68783a40 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -100,18 +100,34 @@ char * sha1_to_hex(const unsigned char *sha1) return buffer; } +static void fill_sha1_path(char *pathbuf, const unsigned char *sha1) +{ + int i; + for (i = 0; i < 20; i++) { + static char hex[] = "0123456789abcdef"; + unsigned int val = sha1[i]; + char *pos = pathbuf + i*2 + (i > 0); + *pos++ = hex[val >> 4]; + *pos = hex[val & 0xf]; + } +} + /* * NOTE! This returns a statically allocated buffer, so you have to be * careful about using it. Do a "strdup()" if you need to save the * filename. + * + * Also note that this returns the location for creating. Reading + * SHA1 file can happen from any alternate directory listed in the + * SHA1_FILE_DIRECTORIES environment variable if it is not found in + * the primary object database. */ char *sha1_file_name(const unsigned char *sha1) { - int i; static char *name, *base; if (!base) { - char *sha1_file_directory = getenv(DB_ENVIRONMENT) ? : DEFAULT_DB_ENVIRONMENT; + char *sha1_file_directory = get_object_directory(); int len = strlen(sha1_file_directory); base = xmalloc(len + 60); memcpy(base, sha1_file_directory, len); @@ -120,16 +136,74 @@ char *sha1_file_name(const unsigned char *sha1) base[len+3] = '/'; name = base + len + 1; } - for (i = 0; i < 20; i++) { - static char hex[] = "0123456789abcdef"; - unsigned int val = sha1[i]; - char *pos = name + i*2 + (i > 0); - *pos++ = hex[val >> 4]; - *pos = hex[val & 0xf]; - } + fill_sha1_path(name, sha1); return base; } +static struct alternate_object_database +{ + char *base; + char *name; +} *alt_odb; + +static void prepare_alt_odb(void) +{ + int pass, totlen, i; + void *buf; + const char *cp, *last; + char *op = 0; + const char *alt = getenv(ALTERNATE_DB_ENVIRONMENT) ? : ""; + + for (totlen = pass = 0; pass < 2; pass++) { + last = alt; + i = 0; + do { + cp = strchr(last, ':') ? : last + strlen(last); + if (last != cp) { + /* 43 = 40-byte + 2 '/' + terminating NUL */ + int pfxlen = cp - last; + int entlen = pfxlen + 43; + if (pass == 0) + totlen += entlen; + else { + alt_odb[i].base = op; + alt_odb[i].name = op + pfxlen + 1; + memcpy(op, last, pfxlen); + op[pfxlen] = op[pfxlen + 3] = '/'; + op[entlen-1] = 0; + op += entlen; + } + i++; + } + while (*cp && *cp == ':') + cp++; + last = cp; + } while (*cp); + if (pass) + break; + alt_odb = buf = xmalloc(sizeof(*alt_odb) * (i + 1) + totlen); + alt_odb[i].base = alt_odb[i].name = 0; + op = (char*)(&alt_odb[i+1]); + } +} + +static char *find_sha1_file(const unsigned char *sha1, struct stat *st) +{ + int i; + char *name = sha1_file_name(sha1); + + if (!stat(name, st)) + return name; + if (!alt_odb) + prepare_alt_odb(); + for (i = 0; (name = alt_odb[i].name) != NULL; i++) { + fill_sha1_path(name, sha1); + if (!stat(alt_odb[i].base, st)) + return alt_odb[i].base; + } + return NULL; +} + int check_sha1_signature(unsigned char *sha1, void *map, unsigned long size, const char *type) { char header[100]; @@ -145,10 +219,15 @@ int check_sha1_signature(unsigned char *sha1, void *map, unsigned long size, con void *map_sha1_file(const unsigned char *sha1, unsigned long *size) { - char *filename = sha1_file_name(sha1); struct stat st; void *map; int fd; + char *filename = find_sha1_file(sha1, &st); + + if (!filename) { + error("cannot map sha1 file %s", sha1_to_hex(sha1)); + return NULL; + } fd = open(filename, O_RDONLY | sha1_file_open_flag); if (fd < 0) { @@ -167,10 +246,6 @@ void *map_sha1_file(const unsigned char *sha1, unsigned long *size) /* If it failed once, it will probably fail again. Stop using O_NOATIME */ sha1_file_open_flag = 0; } - if (fstat(fd, &st) < 0) { - close(fd); - return NULL; - } map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); close(fd); if (-1 == (int)(long)map) @@ -315,6 +390,7 @@ int write_sha1_file(char *buf, unsigned long len, const char *type, unsigned cha } snprintf(tmpfile, sizeof(tmpfile), "%s/obj_XXXXXX", get_object_directory()); + fd = mkstemp(tmpfile); if (fd < 0) { fprintf(stderr, "unable to create temporary sha1 filename %s: %s", tmpfile, strerror(errno)); @@ -442,12 +518,8 @@ int write_sha1_from_fd(const unsigned char *sha1, int fd) int has_sha1_file(const unsigned char *sha1) { - char *filename = sha1_file_name(sha1); struct stat st; - - if (!stat(filename, &st)) - return 1; - return 0; + return !!find_sha1_file(sha1, &st); } int index_fd(unsigned char *sha1, int fd, struct stat *st) -- 2.11.4.GIT