X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=sha1_file.c;h=895c1fab6fc00b8131e44851f33b79b1d2310b12;hb=750a09a7de63c92868cff41a3151eac320c77fa0;hp=b2914dd2ea629ae974fd4b4c272e77cb04e5c0e0;hpb=7672db20c2060f20b01788e4a4289ebc5f818605;p=git.git diff --git a/sha1_file.c b/sha1_file.c index b2914dd2..895c1fab 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -20,6 +20,8 @@ #endif #endif +const unsigned char null_sha1[20] = { 0, }; + static unsigned int sha1_file_open_flag = O_NOATIME; static unsigned hexval(char c) @@ -46,39 +48,35 @@ int get_sha1_hex(const char *hex, unsigned char *sha1) return 0; } -static int get_sha1_file(const char *path, unsigned char *result) -{ - char buffer[60]; - int fd = open(path, O_RDONLY); - int len; - - if (fd < 0) - return -1; - len = read(fd, buffer, sizeof(buffer)); - close(fd); - if (len < 40) - return -1; - return get_sha1_hex(buffer, result); -} - -static char *git_dir, *git_object_dir, *git_index_file, *git_refs_dir; +static char *git_dir, *git_object_dir, *git_index_file, *git_refs_dir, + *git_graft_file; static void setup_git_env(void) { - git_dir = gitenv(GIT_DIR_ENVIRONMENT); + git_dir = getenv(GIT_DIR_ENVIRONMENT); if (!git_dir) git_dir = DEFAULT_GIT_DIR_ENVIRONMENT; - git_object_dir = gitenv(DB_ENVIRONMENT); + git_object_dir = getenv(DB_ENVIRONMENT); if (!git_object_dir) { git_object_dir = xmalloc(strlen(git_dir) + 9); sprintf(git_object_dir, "%s/objects", git_dir); } git_refs_dir = xmalloc(strlen(git_dir) + 6); sprintf(git_refs_dir, "%s/refs", git_dir); - git_index_file = gitenv(INDEX_ENVIRONMENT); + git_index_file = getenv(INDEX_ENVIRONMENT); if (!git_index_file) { git_index_file = xmalloc(strlen(git_dir) + 7); sprintf(git_index_file, "%s/index", git_dir); } + git_graft_file = getenv(GRAFT_ENVIRONMENT); + if (!git_graft_file) + git_graft_file = strdup(git_path("info/grafts")); +} + +char *get_git_dir(void) +{ + if (!git_dir) + setup_git_env(); + return git_dir; } char *get_object_directory(void) @@ -102,6 +100,13 @@ char *get_index_file(void) return git_index_file; } +char *get_graft_file(void) +{ + if (!git_graft_file) + setup_git_env(); + return git_graft_file; +} + int safe_create_leading_directories(char *path) { char *pos = path; @@ -121,30 +126,6 @@ int safe_create_leading_directories(char *path) return 0; } -int get_sha1(const char *str, unsigned char *sha1) -{ - static const char *prefix[] = { - "", - "refs", - "refs/tags", - "refs/heads", - "refs/snap", - NULL - }; - const char **p; - - if (!get_sha1_hex(str, sha1)) - return 0; - - for (p = prefix; *p; p++) { - char * pathname = git_path("%s/%s", *p, str); - if (!get_sha1_file(pathname, sha1)) - return 0; - } - - return -1; -} - char * sha1_to_hex(const unsigned char *sha1) { static char buffer[50]; @@ -200,84 +181,173 @@ char *sha1_file_name(const unsigned char *sha1) return base; } -struct alternate_object_database *alt_odb; +char *sha1_pack_name(const unsigned char *sha1) +{ + static const char hex[] = "0123456789abcdef"; + static char *name, *base, *buf; + int i; + + if (!base) { + const char *sha1_file_directory = get_object_directory(); + int len = strlen(sha1_file_directory); + base = xmalloc(len + 60); + sprintf(base, "%s/pack/pack-1234567890123456789012345678901234567890.pack", sha1_file_directory); + name = base + len + 11; + } + + buf = name; + + for (i = 0; i < 20; i++) { + unsigned int val = *sha1++; + *buf++ = hex[val >> 4]; + *buf++ = hex[val & 0xf]; + } + + return base; +} + +char *sha1_pack_index_name(const unsigned char *sha1) +{ + static const char hex[] = "0123456789abcdef"; + static char *name, *base, *buf; + int i; + + if (!base) { + const char *sha1_file_directory = get_object_directory(); + int len = strlen(sha1_file_directory); + base = xmalloc(len + 60); + sprintf(base, "%s/pack/pack-1234567890123456789012345678901234567890.idx", sha1_file_directory); + name = base + len + 11; + } + + buf = name; + + for (i = 0; i < 20; i++) { + unsigned int val = *sha1++; + *buf++ = hex[val >> 4]; + *buf++ = hex[val & 0xf]; + } + + return base; +} + +struct alternate_object_database *alt_odb_list; +static struct alternate_object_database **alt_odb_tail; /* * Prepare alternate object database registry. - * alt_odb points at an array of struct alternate_object_database. - * This array is terminated with an element that has both its base - * and name set to NULL. alt_odb[n] comes from n'th non-empty - * element from colon separated ALTERNATE_DB_ENVIRONMENT environment - * variable, and its base points at a statically allocated buffer - * that contains "/the/directory/corresponding/to/.git/objects/...", - * while its name points just after the slash at the end of - * ".git/objects/" in the example above, and has enough space to hold - * 40-byte hex SHA1, an extra slash for the first level indirection, - * and the terminating NUL. - * This function allocates the alt_odb array and all the strings - * pointed by base fields of the array elements with one xmalloc(); - * the string pool immediately follows the array. + * + * The variable alt_odb_list points at the list of struct + * alternate_object_database. The elements on this list come from + * non-empty elements from colon separated ALTERNATE_DB_ENVIRONMENT + * environment variable, and $GIT_OBJECT_DIRECTORY/info/alternates, + * whose contents is exactly in the same format as that environment + * variable. Its base points at a statically allocated buffer that + * contains "/the/directory/corresponding/to/.git/objects/...", while + * its name points just after the slash at the end of ".git/objects/" + * in the example above, and has enough space to hold 40-byte hex + * SHA1, an extra slash for the first level indirection, and the + * terminating NUL. */ -void prepare_alt_odb(void) +static void link_alt_odb_entries(const char *alt, const char *ep, int sep, + const char *relative_base) { - int pass, totlen, i; const char *cp, *last; - char *op = NULL; - const char *alt = gitenv(ALTERNATE_DB_ENVIRONMENT) ? : ""; + struct alternate_object_database *ent; + int base_len = -1; + + last = alt; + while (last < ep) { + cp = last; + if (cp < ep && *cp == '#') { + while (cp < ep && *cp != sep) + cp++; + last = cp + 1; + continue; + } + for ( ; cp < ep && *cp != sep; cp++) + ; + if (last != cp) { + /* 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); + *alt_odb_tail = ent; + alt_odb_tail = &(ent->next); + ent->next = NULL; + 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] = ent->base[pfxlen + 3] = '/'; + ent->base[entlen-1] = 0; + } + while (cp < ep && *cp == sep) + cp++; + last = cp; + } +} - if (alt_odb) +void prepare_alt_odb(void) +{ + 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); + + sprintf(path, "%s/info/alternates", get_object_directory()); + fd = open(path, O_RDONLY); + if (fd < 0) + return; + if (fstat(fd, &st) || (st.st_size == 0)) { + close(fd); return; - /* The first pass counts how large an area to allocate to - * hold the entire alt_odb structure, including array of - * structs and path buffers for them. The second pass fills - * the structure and prepares the path buffers for use by - * fill_sha1_path(). - */ - 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 = xmalloc(sizeof(*alt_odb) * (i + 1) + totlen); - alt_odb[i].base = alt_odb[i].name = NULL; - op = (char*)(&alt_odb[i+1]); } + map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + close(fd); + if (map == MAP_FAILED) + return; + + link_alt_odb_entries(map, map + st.st_size, '\n', + get_object_directory()); + munmap(map, st.st_size); } static char *find_sha1_file(const unsigned char *sha1, struct stat *st) { - int i; char *name = sha1_file_name(sha1); + struct alternate_object_database *alt; if (!stat(name, st)) return name; prepare_alt_odb(); - for (i = 0; (name = alt_odb[i].name) != NULL; i++) { + for (alt = alt_odb_list; alt; alt = alt->next) { + name = alt->name; fill_sha1_path(name, sha1); - if (!stat(alt_odb[i].base, st)) - return alt_odb[i].base; + if (!stat(alt->base, st)) + return alt->base; } return NULL; } @@ -360,6 +430,14 @@ void unuse_packed_git(struct packed_git *p) int use_packed_git(struct packed_git *p) { + if (!p->pack_size) { + struct stat st; + // We created the struct before we had the pack + stat(p->pack_name, &st); + if (!S_ISREG(st.st_mode)) + die("packfile %s not a regular file", p->pack_name); + p->pack_size = st.st_size; + } if (!p->pack_base) { int fd; struct stat st; @@ -387,8 +465,10 @@ int use_packed_git(struct packed_git *p) * this is cheap. */ if (memcmp((char*)(p->index_base) + p->index_size - 40, - p->pack_base + p->pack_size - 20, 20)) + p->pack_base + p->pack_size - 20, 20)) { + die("packfile %s does not match index.", p->pack_name); + } } p->pack_last_used = pack_used_ctr++; p->pack_use_cnt++; @@ -426,6 +506,43 @@ struct packed_git *add_packed_git(char *path, int path_len) return p; } +struct packed_git *parse_pack_index(unsigned char *sha1) +{ + char *path = sha1_pack_index_name(sha1); + return parse_pack_index_file(sha1, path); +} + +struct packed_git *parse_pack_index_file(const unsigned char *sha1, char *idx_path) +{ + struct packed_git *p; + unsigned long idx_size; + void *idx_map; + char *path; + + if (check_packed_git_idx(idx_path, &idx_size, &idx_map)) + return NULL; + + path = sha1_pack_name(sha1); + + p = xmalloc(sizeof(*p) + strlen(path) + 2); + strcpy(p->pack_name, path); + p->index_size = idx_size; + p->pack_size = 0; + p->index_base = idx_map; + p->next = NULL; + p->pack_base = NULL; + p->pack_last_used = 0; + p->pack_use_cnt = 0; + memcpy(p->sha1, sha1, 20); + return p; +} + +void install_packed_git(struct packed_git *pack) +{ + pack->next = packed_git; + packed_git = pack; +} + static void prepare_packed_git_one(char *objdir) { char path[PATH_MAX]; @@ -459,18 +576,18 @@ static void prepare_packed_git_one(char *objdir) void prepare_packed_git(void) { - int i; static int run_once = 0; + struct alternate_object_database *alt; - if (run_once++) + if (run_once) return; - prepare_packed_git_one(get_object_directory()); prepare_alt_odb(); - for (i = 0; alt_odb[i].base != NULL; i++) { - alt_odb[i].name[0] = 0; - prepare_packed_git_one(alt_odb[i].base); + for (alt = alt_odb_list; alt; alt = alt->next) { + alt->name[0] = 0; + prepare_packed_git_one(alt->base); } + run_once = 1; } int check_sha1_signature(const unsigned char *sha1, void *map, unsigned long size, const char *type) @@ -487,8 +604,7 @@ int check_sha1_signature(const unsigned char *sha1, void *map, unsigned long siz } static void *map_sha1_file_internal(const unsigned char *sha1, - unsigned long *size, - int say_error) + unsigned long *size) { struct stat st; void *map; @@ -496,8 +612,6 @@ static void *map_sha1_file_internal(const unsigned char *sha1, char *filename = find_sha1_file(sha1, &st); if (!filename) { - if (say_error) - error("cannot map sha1 file %s", sha1_to_hex(sha1)); return NULL; } @@ -511,8 +625,6 @@ static void *map_sha1_file_internal(const unsigned char *sha1, break; /* Fallthrough */ case 0: - if (say_error) - perror(filename); return NULL; } @@ -523,17 +635,12 @@ static void *map_sha1_file_internal(const unsigned char *sha1, } map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); close(fd); - if (-1 == (int)(long)map) + if (map == MAP_FAILED) return NULL; *size = st.st_size; return map; } -void *map_sha1_file(const unsigned char *sha1, unsigned long *size) -{ - return map_sha1_file_internal(sha1, size, 1); -} - int unpack_sha1_header(z_stream *stream, void *map, unsigned long mapsize, void *buffer, unsigned long size) { /* Get the data stream */ @@ -766,7 +873,8 @@ void packed_object_info_detail(struct pack_entry *e, strcpy(type, "tag"); break; default: - die("corrupted pack file"); + die("corrupted pack file %s containing object of kind %d", + p->pack_name, kind); } *store_size = 0; /* notyet */ } @@ -805,7 +913,8 @@ static int packed_object_info(struct pack_entry *entry, strcpy(type, "tag"); break; default: - die("corrupted pack file"); + die("corrupted pack file %s containing object of kind %d", + p->pack_name, kind); } if (sizep) *sizep = size; @@ -905,7 +1014,7 @@ static void *unpack_entry(struct pack_entry *entry, retval = unpack_entry_gently(entry, type, sizep); unuse_packed_git(p); if (!retval) - die("corrupted pack file"); + die("corrupted pack file %s", p->pack_name); return retval; } @@ -999,6 +1108,20 @@ static int find_pack_entry(const unsigned char *sha1, struct pack_entry *e) return 0; } +struct packed_git *find_sha1_pack(const unsigned char *sha1, + struct packed_git *packs) +{ + struct packed_git *p; + struct pack_entry e; + + for (p = packs; p; p = p->next) { + if (find_pack_entry_one(sha1, &e, p)) + return p; + } + return NULL; + +} + int sha1_object_info(const unsigned char *sha1, char *type, unsigned long *sizep) { int status; @@ -1007,7 +1130,7 @@ int sha1_object_info(const unsigned char *sha1, char *type, unsigned long *sizep z_stream stream; char hdr[128]; - map = map_sha1_file_internal(sha1, &mapsize, 0); + map = map_sha1_file_internal(sha1, &mapsize); if (!map) { struct pack_entry e; @@ -1045,14 +1168,17 @@ void * read_sha1_file(const unsigned char *sha1, char *type, unsigned long *size { unsigned long mapsize; void *map, *buf; + struct pack_entry e; - map = map_sha1_file_internal(sha1, &mapsize, 0); + if (find_pack_entry(sha1, &e)) + return read_packed_sha1(sha1, type, size); + map = map_sha1_file_internal(sha1, &mapsize); if (map) { buf = unpack_sha1_file(map, mapsize, type, size); munmap(map, mapsize); return buf; } - return read_packed_sha1(sha1, type, size); + return NULL; } void *read_object_with_reference(const unsigned char *sha1, @@ -1095,6 +1221,7 @@ void *read_object_with_reference(const unsigned char *sha1, free(buffer); return NULL; } + free(buffer); /* Now we have the ID of the referred-to object in * actual_sha1. Check again. */ } @@ -1221,14 +1348,83 @@ int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned cha return 0; } -int write_sha1_from_fd(const unsigned char *sha1, int fd) +int write_sha1_to_fd(int fd, const unsigned char *sha1) +{ + ssize_t size; + unsigned long objsize; + int posn = 0; + void *map = map_sha1_file_internal(sha1, &objsize); + void *buf = map; + void *temp_obj = NULL; + z_stream stream; + + if (!buf) { + unsigned char *unpacked; + unsigned long len; + char type[20]; + char hdr[50]; + int hdrlen; + // need to unpack and recompress it by itself + unpacked = read_packed_sha1(sha1, type, &len); + + hdrlen = sprintf(hdr, "%s %lu", type, len) + 1; + + /* Set it up */ + memset(&stream, 0, sizeof(stream)); + deflateInit(&stream, Z_BEST_COMPRESSION); + size = deflateBound(&stream, len + hdrlen); + temp_obj = buf = xmalloc(size); + + /* Compress it */ + stream.next_out = buf; + stream.avail_out = size; + + /* First header.. */ + stream.next_in = (void *)hdr; + stream.avail_in = hdrlen; + while (deflate(&stream, 0) == Z_OK) + /* nothing */; + + /* Then the data itself.. */ + stream.next_in = unpacked; + stream.avail_in = len; + while (deflate(&stream, Z_FINISH) == Z_OK) + /* nothing */; + deflateEnd(&stream); + free(unpacked); + + objsize = stream.total_out; + } + + do { + size = write(fd, buf + posn, objsize - posn); + if (size <= 0) { + if (!size) { + fprintf(stderr, "write closed"); + } else { + perror("write "); + } + return -1; + } + posn += size; + } while (posn < objsize); + + if (map) + munmap(map, objsize); + if (temp_obj) + free(temp_obj); + + return 0; +} + +int write_sha1_from_fd(const unsigned char *sha1, int fd, char *buffer, + size_t bufsize, size_t *bufposn) { char *filename = sha1_file_name(sha1); int local; z_stream stream; unsigned char real_sha1[20]; - unsigned char buf[4096]; unsigned char discard[4096]; int ret; SHA_CTX c; @@ -1246,7 +1442,24 @@ int write_sha1_from_fd(const unsigned char *sha1, int fd) do { ssize_t size; - size = read(fd, buf, 4096); + if (*bufposn) { + stream.avail_in = *bufposn; + stream.next_in = (unsigned char *) buffer; + do { + stream.next_out = discard; + stream.avail_out = sizeof(discard); + ret = inflate(&stream, Z_SYNC_FLUSH); + SHA1_Update(&c, discard, sizeof(discard) - + stream.avail_out); + } while (stream.avail_in && ret == Z_OK); + write(local, buffer, *bufposn - stream.avail_in); + memmove(buffer, buffer + *bufposn - stream.avail_in, + stream.avail_in); + *bufposn = stream.avail_in; + if (ret != Z_OK) + break; + } + size = read(fd, buffer + *bufposn, bufsize - *bufposn); if (size <= 0) { close(local); unlink(filename); @@ -1255,18 +1468,8 @@ int write_sha1_from_fd(const unsigned char *sha1, int fd) perror("Reading from connection"); return -1; } - write(local, buf, size); - stream.avail_in = size; - stream.next_in = buf; - do { - stream.next_out = discard; - stream.avail_out = sizeof(discard); - ret = inflate(&stream, Z_SYNC_FLUSH); - SHA1_Update(&c, discard, sizeof(discard) - - stream.avail_out); - } while (stream.avail_in && ret == Z_OK); - - } while (ret == Z_OK); + *bufposn += size; + } while (1); inflateEnd(&stream); close(local); @@ -1283,6 +1486,22 @@ int write_sha1_from_fd(const unsigned char *sha1, int fd) return 0; } +int has_pack_index(const unsigned char *sha1) +{ + struct stat st; + if (stat(sha1_pack_index_name(sha1), &st)) + return 0; + return 1; +} + +int has_pack_file(const unsigned char *sha1) +{ + struct stat st; + if (stat(sha1_pack_name(sha1), &st)) + return 0; + return 1; +} + int has_sha1_pack(const unsigned char *sha1) { struct pack_entry e; @@ -1294,9 +1513,9 @@ int has_sha1_file(const unsigned char *sha1) struct stat st; struct pack_entry e; - if (find_sha1_file(sha1, &st)) + if (find_pack_entry(sha1, &e)) return 1; - return find_pack_entry(sha1, &e); + return find_sha1_file(sha1, &st) ? 1 : 0; } int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, const char *type) @@ -1311,7 +1530,7 @@ int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, con if (size) buf = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); close(fd); - if ((int)(long)buf == -1) + if (buf == MAP_FAILED) return -1; if (!type)