X-Git-Url: https://git.verplant.org/?a=blobdiff_plain;f=sha1_file.c;h=74dc2aab26c0e9f87e9238686ad0a077b1570d4a;hb=bbca20accefe8329f976371d0d8c111c965cdf1a;hp=737ecb48002c873d43bafc99810feb00194b0d8c;hpb=dcde55bc58ae845307efbdce3a1071f75ccd758e;p=git.git diff --git a/sha1_file.c b/sha1_file.c index 737ecb48..74dc2aab 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -102,9 +102,30 @@ char *get_index_file(void) return git_index_file; } -int get_sha1(const char *str, unsigned char *sha1) +char *git_path(const char *fmt, ...) { static char pathname[PATH_MAX]; + va_list args; + int len; + + if (!git_dir) + setup_git_env(); + len = strlen(git_dir); + if (len == 1 && *git_dir == '.') + len = 0; + if (len > PATH_MAX-100) + return "pad-path"; + memcpy(pathname, git_dir, len); + if (len && git_dir[len-1] != '/') + pathname[len++] = '/'; + va_start(args, fmt); + vsnprintf(pathname + len, sizeof(pathname) - len, fmt, args); + va_end(args); + return pathname; +} + +int get_sha1(const char *str, unsigned char *sha1) +{ static const char *prefix[] = { "", "refs", @@ -118,11 +139,8 @@ int get_sha1(const char *str, unsigned char *sha1) if (!get_sha1_hex(str, sha1)) return 0; - if (!git_dir) - setup_git_env(); for (p = prefix; *p; p++) { - snprintf(pathname, sizeof(pathname), "%s/%s/%s", - git_dir, *p, str); + char * pathname = git_path("%s/%s", *p, str); if (!get_sha1_file(pathname, sha1)) return 0; } @@ -272,12 +290,6 @@ static int pack_used_ctr; static unsigned long pack_mapped; struct packed_git *packed_git; -struct pack_entry { - unsigned int offset; - unsigned char sha1[20]; - struct packed_git *p; -}; - static int check_packed_git_idx(const char *path, unsigned long *idx_size_, void **idx_map_) { @@ -300,9 +312,11 @@ static int check_packed_git_idx(const char *path, unsigned long *idx_size_, return -1; index = idx_map; + *idx_map_ = idx_map; + *idx_size_ = idx_size; /* check index map */ - if (idx_size < 4*256 + 20) + if (idx_size < 4*256 + 20 + 20) return error("index file too small"); nr = 0; for (i = 0; i < 256; i++) { @@ -322,17 +336,32 @@ static int check_packed_git_idx(const char *path, unsigned long *idx_size_, if (idx_size != 4*256 + nr * 24 + 20 + 20) return error("wrong index file size"); - *idx_map_ = idx_map; - *idx_size_ = idx_size; return 0; } -static void unuse_one_packed_git(void) +static int unuse_one_packed_git(void) { - /* NOTYET */ + struct packed_git *p, *lru = NULL; + + for (p = packed_git; p; p = p->next) { + if (p->pack_use_cnt || !p->pack_base) + continue; + if (!lru || p->pack_last_used < lru->pack_last_used) + lru = p; + } + if (!lru) + return 0; + munmap(lru->pack_base, lru->pack_size); + lru->pack_base = NULL; + return 1; +} + +void unuse_packed_git(struct packed_git *p) +{ + p->pack_use_cnt--; } -static int use_packed_git(struct packed_git *p) +int use_packed_git(struct packed_git *p) { if (!p->pack_base) { int fd; @@ -340,28 +369,36 @@ static int use_packed_git(struct packed_git *p) void *map; pack_mapped += p->pack_size; - while (PACK_MAX_SZ < pack_mapped) - unuse_one_packed_git(); + while (PACK_MAX_SZ < pack_mapped && unuse_one_packed_git()) + ; /* nothing */ fd = open(p->pack_name, O_RDONLY); if (fd < 0) - return -1; + die("packfile %s cannot be opened", p->pack_name); if (fstat(fd, &st)) { close(fd); - return -1; + die("packfile %s cannot be opened", p->pack_name); } if (st.st_size != p->pack_size) - return -1; + die("packfile %s size mismatch.", p->pack_name); map = mmap(NULL, p->pack_size, PROT_READ, MAP_PRIVATE, fd, 0); close(fd); if (map == MAP_FAILED) - return -1; + die("packfile %s cannot be mapped.", p->pack_name); p->pack_base = map; + + /* Check if the pack file matches with the index file. + * this is cheap. + */ + if (memcmp((char*)(p->index_base) + p->index_size - 40, + 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++; return 0; } -static struct packed_git *add_packed_git(char *path, int path_len) +struct packed_git *add_packed_git(char *path, int path_len) { struct stat st; struct packed_git *p; @@ -388,6 +425,7 @@ static struct packed_git *add_packed_git(char *path, int path_len) p->next = NULL; p->pack_base = NULL; p->pack_last_used = 0; + p->pack_use_cnt = 0; return p; } @@ -511,7 +549,7 @@ int unpack_sha1_header(z_stream *stream, void *map, unsigned long mapsize, void return inflate(stream, 0); } -void *unpack_sha1_rest(z_stream *stream, void *buffer, unsigned long size) +static void *unpack_sha1_rest(z_stream *stream, void *buffer, unsigned long size) { int bytes = strlen(buffer) + 1; unsigned char *buf = xmalloc(1+size); @@ -592,50 +630,67 @@ void * unpack_sha1_file(void *map, unsigned long mapsize, char *type, unsigned l return unpack_sha1_rest(&stream, hdr, *size); } +/* forward declaration for a mutually recursive function */ +static int packed_object_info(struct pack_entry *entry, + char *type, unsigned long *sizep); + static int packed_delta_info(unsigned char *base_sha1, unsigned long delta_size, unsigned long left, char *type, - unsigned long *sizep) + unsigned long *sizep, + struct packed_git *p) { - unsigned char *data; - unsigned char delta_head[64]; - unsigned long data_size, result_size, base_size, verify_base_size; - z_stream stream; - int st; + struct pack_entry base_ent; if (left < 20) die("truncated pack file"); - if (sha1_object_info(base_sha1, type, &base_size)) - die("cannot get info for delta-pack base"); - - data = base_sha1 + 20; - data_size = left - 20; - memset(&stream, 0, sizeof(stream)); + /* The base entry _must_ be in the same pack */ + if (!find_pack_entry_one(base_sha1, &base_ent, p)) + die("failed to find delta-pack base object %s", + sha1_to_hex(base_sha1)); - stream.next_in = data; - stream.avail_in = data_size; - stream.next_out = delta_head; - stream.avail_out = sizeof(delta_head); + /* We choose to only get the type of the base object and + * ignore potentially corrupt pack file that expects the delta + * based on a base with a wrong size. This saves tons of + * inflate() calls. + */ - inflateInit(&stream); - st = inflate(&stream, Z_FINISH); - inflateEnd(&stream); - if ((st != Z_STREAM_END) && stream.total_out != sizeof(delta_head)) - die("delta data unpack-initial failed"); + if (packed_object_info(&base_ent, type, NULL)) + die("cannot get info for delta-pack base"); - /* Examine the initial part of the delta to figure out - * the result size. Verify the base size while we are at it. - */ - data = delta_head; - verify_base_size = get_delta_hdr_size(&data); - if (verify_base_size != base_size) - die("delta base size mismatch"); + if (sizep) { + const unsigned char *data; + unsigned char delta_head[64]; + unsigned long result_size; + z_stream stream; + int st; + + memset(&stream, 0, sizeof(stream)); + + data = stream.next_in = base_sha1 + 20; + stream.avail_in = left - 20; + stream.next_out = delta_head; + stream.avail_out = sizeof(delta_head); + + inflateInit(&stream); + st = inflate(&stream, Z_FINISH); + inflateEnd(&stream); + if ((st != Z_STREAM_END) && + stream.total_out != sizeof(delta_head)) + die("delta data unpack-initial failed"); + + /* Examine the initial part of the delta to figure out + * the result size. + */ + data = delta_head; + get_delta_hdr_size(&data); /* ignore base size */ - /* Read the result size */ - result_size = get_delta_hdr_size(&data); - *sizep = result_size; + /* Read the result size */ + result_size = get_delta_hdr_size(&data); + *sizep = result_size; + } return 0; } @@ -667,6 +722,57 @@ static unsigned long unpack_object_header(struct packed_git *p, unsigned long of return offset; } +void packed_object_info_detail(struct pack_entry *e, + char *type, + unsigned long *size, + unsigned long *store_size, + int *delta_chain_length, + unsigned char *base_sha1) +{ + struct packed_git *p = e->p; + unsigned long offset, left; + unsigned char *pack; + enum object_type kind; + + offset = unpack_object_header(p, e->offset, &kind, size); + pack = p->pack_base + offset; + left = p->pack_size - offset; + if (kind != OBJ_DELTA) + *delta_chain_length = 0; + else { + int chain_length = 0; + memcpy(base_sha1, pack, 20); + do { + struct pack_entry base_ent; + unsigned long junk; + + find_pack_entry_one(pack, &base_ent, p); + offset = unpack_object_header(p, base_ent.offset, + &kind, &junk); + pack = p->pack_base + offset; + chain_length++; + } while (kind == OBJ_DELTA); + *delta_chain_length = chain_length; + } + switch (kind) { + case OBJ_COMMIT: + strcpy(type, "commit"); + break; + case OBJ_TREE: + strcpy(type, "tree"); + break; + case OBJ_BLOB: + strcpy(type, "blob"); + break; + case OBJ_TAG: + strcpy(type, "tag"); + break; + default: + die("corrupted pack file"); + } + *store_size = 0; /* notyet */ +} + static int packed_object_info(struct pack_entry *entry, char *type, unsigned long *sizep) { @@ -674,6 +780,7 @@ static int packed_object_info(struct pack_entry *entry, unsigned long offset, size, left; unsigned char *pack; enum object_type kind; + int retval; if (use_packed_git(p)) die("cannot map packed file"); @@ -684,8 +791,9 @@ static int packed_object_info(struct pack_entry *entry, switch (kind) { case OBJ_DELTA: - return packed_delta_info(pack, size, left, type, sizep); - break; + retval = packed_delta_info(pack, size, left, type, sizep, p); + unuse_packed_git(p); + return retval; case OBJ_COMMIT: strcpy(type, "commit"); break; @@ -701,7 +809,9 @@ static int packed_object_info(struct pack_entry *entry, default: die("corrupted pack file"); } - *sizep = size; + if (sizep) + *sizep = size; + unuse_packed_git(p); return 0; } @@ -712,8 +822,10 @@ static void *unpack_delta_entry(unsigned char *base_sha1, unsigned long delta_size, unsigned long left, char *type, - unsigned long *sizep) + unsigned long *sizep, + struct packed_git *p) { + struct pack_entry base_ent; void *data, *delta_data, *result, *base; unsigned long data_size, result_size, base_size; z_stream stream; @@ -738,8 +850,11 @@ static void *unpack_delta_entry(unsigned char *base_sha1, if ((st != Z_STREAM_END) || stream.total_out != delta_size) die("delta data unpack failed"); - /* This may recursively unpack the base, which is what we want */ - base = read_sha1_file(base_sha1, type, &base_size); + /* The base entry _must_ be in the same pack */ + if (!find_pack_entry_one(base_sha1, &base_ent, p)) + die("failed to find delta-pack base object %s", + sha1_to_hex(base_sha1)); + base = unpack_entry_gently(&base_ent, type, &base_size); if (!base) die("failed to read delta-pack base object %s", sha1_to_hex(base_sha1)); @@ -760,7 +875,7 @@ static void *unpack_non_delta_entry(unsigned char *data, { int st; z_stream stream; - char *buffer; + unsigned char *buffer; buffer = xmalloc(size + 1); buffer[size] = 0; @@ -785,19 +900,34 @@ static void *unpack_entry(struct pack_entry *entry, char *type, unsigned long *sizep) { struct packed_git *p = entry->p; - unsigned long offset, size, left; - unsigned char *pack; - enum object_type kind; + void *retval; if (use_packed_git(p)) die("cannot map packed file"); + retval = unpack_entry_gently(entry, type, sizep); + unuse_packed_git(p); + if (!retval) + die("corrupted pack file"); + return retval; +} + +/* The caller is responsible for use_packed_git()/unuse_packed_git() pair */ +void *unpack_entry_gently(struct pack_entry *entry, + char *type, unsigned long *sizep) +{ + struct packed_git *p = entry->p; + unsigned long offset, size, left; + unsigned char *pack; + enum object_type kind; + void *retval; offset = unpack_object_header(p, entry->offset, &kind, &size); pack = p->pack_base + offset; left = p->pack_size - offset; switch (kind) { case OBJ_DELTA: - return unpack_delta_entry(pack, size, left, type, sizep); + retval = unpack_delta_entry(pack, size, left, type, sizep, p); + return retval; case OBJ_COMMIT: strcpy(type, "commit"); break; @@ -811,15 +941,16 @@ static void *unpack_entry(struct pack_entry *entry, strcpy(type, "tag"); break; default: - die("corrupted pack file"); + return NULL; } *sizep = size; - return unpack_non_delta_entry(pack, size, left); + retval = unpack_non_delta_entry(pack, size, left); + return retval; } int num_packed_objects(const struct packed_git *p) { - /* See check_packed_git_idx and pack-objects.c */ + /* See check_packed_git_idx() */ return (p->index_size - 20 - 20 - 4*256) / 24; } @@ -833,10 +964,10 @@ int nth_packed_object_sha1(const struct packed_git *p, int n, return 0; } -static int find_pack_entry_1(const unsigned char *sha1, - struct pack_entry *e, struct packed_git *p) +int find_pack_entry_one(const unsigned char *sha1, + struct pack_entry *e, struct packed_git *p) { - int *level1_ofs = p->index_base; + unsigned int *level1_ofs = p->index_base; int hi = ntohl(level1_ofs[*sha1]); int lo = ((*sha1 == 0x0) ? 0 : ntohl(level1_ofs[*sha1 - 1])); void *index = p->index_base + 256; @@ -864,7 +995,7 @@ static int find_pack_entry(const unsigned char *sha1, struct pack_entry *e) prepare_packed_git(); for (p = packed_git; p; p = p->next) { - if (find_pack_entry_1(sha1, e, p)) + if (find_pack_entry_one(sha1, e, p)) return 1; } return 0; @@ -884,12 +1015,7 @@ int sha1_object_info(const unsigned char *sha1, char *type, unsigned long *sizep if (!find_pack_entry(sha1, &e)) return error("unable to find %s", sha1_to_hex(sha1)); - if (!packed_object_info(&e, type, sizep)) - return 0; - /* sheesh */ - map = unpack_entry(&e, type, sizep); - free(map); - return (map == NULL) ? 0 : -1; + return packed_object_info(&e, type, sizep); } if (unpack_sha1_header(&stream, map, mapsize, hdr, sizeof(hdr)) < 0) status = error("unable to unpack %s header", @@ -898,7 +1024,8 @@ int sha1_object_info(const unsigned char *sha1, char *type, unsigned long *sizep status = error("unable to parse %s header", sha1_to_hex(sha1)); else { status = 0; - *sizep = size; + if (sizep) + *sizep = size; } inflateEnd(&stream); munmap(map, mapsize); @@ -1158,6 +1285,12 @@ int write_sha1_from_fd(const unsigned char *sha1, int fd) return 0; } +int has_sha1_pack(const unsigned char *sha1) +{ + struct pack_entry e; + return find_pack_entry(sha1, &e); +} + int has_sha1_file(const unsigned char *sha1) { struct stat st;