X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=sha1_file.c;h=b89edb9515aa38c2c404367aa28ec5531fcc7de1;hb=b3dc864c6d5ffb96513328f976c076c3a90331b0;hp=43bc2ea0cf039bb9fd02c8313981e85bd7398d33;hpb=09f5dc406a9e6630af881108af815f6c3fb6d9df;p=git.git diff --git a/sha1_file.c b/sha1_file.c index 43bc2ea0c..b89edb951 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -22,19 +22,47 @@ #endif #endif -const unsigned char null_sha1[20] = { 0, }; +const unsigned char null_sha1[20]; static unsigned int sha1_file_open_flag = O_NOATIME; -static unsigned hexval(char c) -{ - if (c >= '0' && c <= '9') - return c - '0'; - if (c >= 'a' && c <= 'f') - return c - 'a' + 10; - if (c >= 'A' && c <= 'F') - return c - 'A' + 10; - return ~0; +static inline unsigned int hexval(unsigned int c) +{ + static signed char val[256] = { + -1, -1, -1, -1, -1, -1, -1, -1, /* 00-07 */ + -1, -1, -1, -1, -1, -1, -1, -1, /* 08-0f */ + -1, -1, -1, -1, -1, -1, -1, -1, /* 10-17 */ + -1, -1, -1, -1, -1, -1, -1, -1, /* 18-1f */ + -1, -1, -1, -1, -1, -1, -1, -1, /* 20-27 */ + -1, -1, -1, -1, -1, -1, -1, -1, /* 28-2f */ + 0, 1, 2, 3, 4, 5, 6, 7, /* 30-37 */ + 8, 9, -1, -1, -1, -1, -1, -1, /* 38-3f */ + -1, 10, 11, 12, 13, 14, 15, -1, /* 40-47 */ + -1, -1, -1, -1, -1, -1, -1, -1, /* 48-4f */ + -1, -1, -1, -1, -1, -1, -1, -1, /* 50-57 */ + -1, -1, -1, -1, -1, -1, -1, -1, /* 58-5f */ + -1, 10, 11, 12, 13, 14, 15, -1, /* 60-67 */ + -1, -1, -1, -1, -1, -1, -1, -1, /* 68-67 */ + -1, -1, -1, -1, -1, -1, -1, -1, /* 70-77 */ + -1, -1, -1, -1, -1, -1, -1, -1, /* 78-7f */ + -1, -1, -1, -1, -1, -1, -1, -1, /* 80-87 */ + -1, -1, -1, -1, -1, -1, -1, -1, /* 88-8f */ + -1, -1, -1, -1, -1, -1, -1, -1, /* 90-97 */ + -1, -1, -1, -1, -1, -1, -1, -1, /* 98-9f */ + -1, -1, -1, -1, -1, -1, -1, -1, /* a0-a7 */ + -1, -1, -1, -1, -1, -1, -1, -1, /* a8-af */ + -1, -1, -1, -1, -1, -1, -1, -1, /* b0-b7 */ + -1, -1, -1, -1, -1, -1, -1, -1, /* b8-bf */ + -1, -1, -1, -1, -1, -1, -1, -1, /* c0-c7 */ + -1, -1, -1, -1, -1, -1, -1, -1, /* c8-cf */ + -1, -1, -1, -1, -1, -1, -1, -1, /* d0-d7 */ + -1, -1, -1, -1, -1, -1, -1, -1, /* d8-df */ + -1, -1, -1, -1, -1, -1, -1, -1, /* e0-e7 */ + -1, -1, -1, -1, -1, -1, -1, -1, /* e8-ef */ + -1, -1, -1, -1, -1, -1, -1, -1, /* f0-f7 */ + -1, -1, -1, -1, -1, -1, -1, -1, /* f8-ff */ + }; + return val[c]; } int get_sha1_hex(const char *hex, unsigned char *sha1) @@ -115,7 +143,7 @@ static void fill_sha1_path(char *pathbuf, const unsigned char *sha1) /* * 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 + * careful about using it. Do a "xstrdup()" if you need to save the * filename. * * Also note that this returns the location for creating. Reading @@ -463,6 +491,7 @@ int use_packed_git(struct packed_git *p) int fd; struct stat st; void *map; + struct pack_header *hdr; pack_mapped += p->pack_size; while (PACK_MAX_SZ < pack_mapped && unuse_one_packed_git()) @@ -482,13 +511,24 @@ int use_packed_git(struct packed_git *p) die("packfile %s cannot be mapped.", p->pack_name); p->pack_base = map; + /* Check if we understand this pack file. If we don't we're + * likely too old to handle it. + */ + hdr = map; + if (hdr->hdr_signature != htonl(PACK_SIGNATURE)) + die("packfile %s isn't actually a pack.", p->pack_name); + if (!pack_version_ok(hdr->hdr_version)) + die("packfile %s is version %i and not supported" + " (try upgrading GIT to a newer version)", + p->pack_name, ntohl(hdr->hdr_version)); + /* Check if the pack file matches with the index file. * this is cheap. */ - if (memcmp((char*)(p->index_base) + p->index_size - 40, - (char *) p->pack_base + p->pack_size - 20, - 20)) { - + if (hashcmp((unsigned char *)(p->index_base) + + p->index_size - 40, + (unsigned char *)p->pack_base + + p->pack_size - 20)) { die("packfile %s does not match index.", p->pack_name); } } @@ -528,7 +568,7 @@ struct packed_git *add_packed_git(char *path, int path_len, int local) p->pack_use_cnt = 0; p->pack_local = local; if ((path_len > 44) && !get_sha1_hex(path + path_len - 44, sha1)) - memcpy(p->sha1, sha1, 20); + hashcpy(p->sha1, sha1); return p; } @@ -559,7 +599,7 @@ struct packed_git *parse_pack_index_file(const unsigned char *sha1, char *idx_pa p->pack_base = NULL; p->pack_last_used = 0; p->pack_use_cnt = 0; - memcpy(p->sha1, sha1, 20); + hashcpy(p->sha1, sha1); return p; } @@ -590,7 +630,7 @@ static void prepare_packed_git_one(char *objdir, int local) int namelen = strlen(de->d_name); struct packed_git *p; - if (strcmp(de->d_name + namelen - 4, ".idx")) + if (!has_extension(de->d_name, ".idx")) continue; /* we have .idx. Is it a file we can map? */ @@ -643,11 +683,10 @@ int check_sha1_signature(const unsigned char *sha1, void *map, unsigned long siz SHA1_Update(&c, header, 1+sprintf(header, "%s %lu", type, size)); SHA1_Update(&c, map, size); SHA1_Final(real_sha1, &c); - return memcmp(sha1, real_sha1, 20) ? -1 : 0; + return hashcmp(sha1, real_sha1) ? -1 : 0; } -static void *map_sha1_file_internal(const unsigned char *sha1, - unsigned long *size) +void *map_sha1_file(const unsigned char *sha1, unsigned long *size) { struct stat st; void *map; @@ -684,17 +723,55 @@ static void *map_sha1_file_internal(const unsigned char *sha1, return map; } -static int unpack_sha1_header(z_stream *stream, unsigned char *map, unsigned long mapsize, void *buffer, unsigned long bufsiz) +int legacy_loose_object(unsigned char *map) +{ + unsigned int word; + + /* + * Is it a zlib-compressed buffer? If so, the first byte + * must be 0x78 (15-bit window size, deflated), and the + * first 16-bit word is evenly divisible by 31 + */ + word = (map[0] << 8) + map[1]; + if (map[0] == 0x78 && !(word % 31)) + return 1; + else + return 0; +} + +unsigned long unpack_object_header_gently(const unsigned char *buf, unsigned long len, enum object_type *type, unsigned long *sizep) { + unsigned shift; unsigned char c; - unsigned int word, bits; unsigned long size; - static const char *typename[8] = { - NULL, /* OBJ_EXT */ - "commit", "tree", "blob", "tag", - NULL, NULL, NULL + unsigned long used = 0; + + c = buf[used++]; + *type = (c >> 4) & 7; + size = c & 15; + shift = 4; + while (c & 0x80) { + if (len <= used) + return 0; + if (sizeof(long) * 8 <= shift) + return 0; + c = buf[used++]; + size += (c & 0x7f) << shift; + shift += 7; + } + *sizep = size; + return used; +} + +static int unpack_sha1_header(z_stream *stream, unsigned char *map, unsigned long mapsize, void *buffer, unsigned long bufsiz) +{ + unsigned long size, used; + static const char valid_loose_object_type[8] = { + 0, /* OBJ_EXT */ + 1, 1, 1, 1, /* "commit", "tree", "blob", "tag" */ + 0, /* "delta" and others are invalid in a loose object */ }; - const char *type; + enum object_type type; /* Get the data stream */ memset(stream, 0, sizeof(*stream)); @@ -703,33 +780,16 @@ static int unpack_sha1_header(z_stream *stream, unsigned char *map, unsigned lon stream->next_out = buffer; stream->avail_out = bufsiz; - /* - * Is it a zlib-compressed buffer? If so, the first byte - * must be 0x78 (15-bit window size, deflated), and the - * first 16-bit word is evenly divisible by 31 - */ - word = (map[0] << 8) + map[1]; - if (map[0] == 0x78 && !(word % 31)) { + if (legacy_loose_object(map)) { inflateInit(stream); return inflate(stream, 0); } - c = *map++; - mapsize--; - type = typename[(c >> 4) & 7]; - if (!type) + used = unpack_object_header_gently(map, mapsize, &type, &size); + if (!used || !valid_loose_object_type[type]) return -1; - - bits = 4; - size = c & 0xf; - while ((c & 0x80)) { - if (bits >= 8*sizeof(long)) - return -1; - c = *map++; - size += (c & 0x7f) << bits; - bits += 7; - mapsize--; - } + map += used; + mapsize -= used; /* Set up the stream for the rest.. */ stream->next_in = map; @@ -737,7 +797,8 @@ static int unpack_sha1_header(z_stream *stream, unsigned char *map, unsigned lon inflateInit(stream); /* And generate the fake traditional header */ - stream->total_out = 1 + snprintf(buffer, bufsiz, "%s %lu", type, size); + stream->total_out = 1 + snprintf(buffer, bufsiz, "%s %lu", + type_names[type], size); return 0; } @@ -895,29 +956,18 @@ static int packed_delta_info(unsigned char *base_sha1, static unsigned long unpack_object_header(struct packed_git *p, unsigned long offset, enum object_type *type, unsigned long *sizep) { - unsigned shift; - unsigned char *pack, c; - unsigned long size; + unsigned long used; - if (offset >= p->pack_size) + if (p->pack_size <= offset) die("object offset outside of pack file"); - pack = (unsigned char *) p->pack_base + offset; - c = *pack++; - offset++; - *type = (c >> 4) & 7; - size = c & 15; - shift = 4; - while (c & 0x80) { - if (offset >= p->pack_size) - die("object offset outside of pack file"); - c = *pack++; - offset++; - size += (c & 0x7f) << shift; - shift += 7; - } - *sizep = size; - return offset; + used = unpack_object_header_gently((unsigned char *)p->pack_base + + offset, + p->pack_size - offset, type, sizep); + if (!used) + die("object offset outside of pack file"); + + return offset + used; } int check_reuse_pack_delta(struct packed_git *p, unsigned long offset, @@ -932,7 +982,7 @@ int check_reuse_pack_delta(struct packed_git *p, unsigned long offset, ptr = unpack_object_header(p, ptr, kindp, sizep); if (*kindp != OBJ_DELTA) goto done; - memcpy(base, (char *) p->pack_base + ptr, 20); + hashcpy(base, (unsigned char *) p->pack_base + ptr); status = 0; done: unuse_packed_git(p); @@ -960,7 +1010,7 @@ void packed_object_info_detail(struct pack_entry *e, if (p->pack_size <= offset + 20) die("pack file %s records an incomplete delta base", p->pack_name); - memcpy(base_sha1, pack, 20); + hashcpy(base_sha1, pack); do { struct pack_entry base_ent; unsigned long junk; @@ -975,16 +1025,10 @@ void packed_object_info_detail(struct pack_entry *e, } switch (kind) { case OBJ_COMMIT: - strcpy(type, commit_type); - break; case OBJ_TREE: - strcpy(type, tree_type); - break; case OBJ_BLOB: - strcpy(type, blob_type); - break; case OBJ_TAG: - strcpy(type, tag_type); + strcpy(type, type_names[kind]); break; default: die("corrupted pack file %s containing object of kind %d", @@ -1015,16 +1059,10 @@ static int packed_object_info(struct pack_entry *entry, unuse_packed_git(p); return retval; case OBJ_COMMIT: - strcpy(type, commit_type); - break; case OBJ_TREE: - strcpy(type, tree_type); - break; case OBJ_BLOB: - strcpy(type, blob_type); - break; case OBJ_TAG: - strcpy(type, tag_type); + strcpy(type, type_names[kind]); break; default: die("corrupted pack file %s containing object of kind %d", @@ -1036,26 +1074,49 @@ static int packed_object_info(struct pack_entry *entry, return 0; } -/* forward declaration for a mutually recursive function */ -static void *unpack_entry(struct pack_entry *, char *, unsigned long *); +static void *unpack_compressed_entry(struct packed_git *p, + unsigned long offset, + unsigned long size) +{ + int st; + z_stream stream; + unsigned char *buffer; -static void *unpack_delta_entry(unsigned char *base_sha1, + buffer = xmalloc(size + 1); + buffer[size] = 0; + memset(&stream, 0, sizeof(stream)); + stream.next_in = (unsigned char*)p->pack_base + offset; + stream.avail_in = p->pack_size - offset; + stream.next_out = buffer; + stream.avail_out = size; + + inflateInit(&stream); + st = inflate(&stream, Z_FINISH); + inflateEnd(&stream); + if ((st != Z_STREAM_END) || stream.total_out != size) { + free(buffer); + return NULL; + } + + return buffer; +} + +static void *unpack_delta_entry(struct packed_git *p, + unsigned long offset, unsigned long delta_size, - unsigned long left, char *type, - unsigned long *sizep, - struct packed_git *p) + unsigned long *sizep) { struct pack_entry base_ent; - void *data, *delta_data, *result, *base; - unsigned long data_size, result_size, base_size; - z_stream stream; - int st; + void *delta_data, *result, *base; + unsigned long result_size, base_size; + unsigned char* base_sha1; - if (left < 20) + if ((offset + 20) >= p->pack_size) die("truncated pack file"); /* The base entry _must_ be in the same pack */ + base_sha1 = (unsigned char*)p->pack_base + offset; if (!find_pack_entry_one(base_sha1, &base_ent, p)) die("failed to find delta-pack base object %s", sha1_to_hex(base_sha1)); @@ -1064,23 +1125,7 @@ static void *unpack_delta_entry(unsigned char *base_sha1, die("failed to read delta-pack base object %s", sha1_to_hex(base_sha1)); - data = base_sha1 + 20; - data_size = left - 20; - delta_data = xmalloc(delta_size); - - memset(&stream, 0, sizeof(stream)); - - stream.next_in = data; - stream.avail_in = data_size; - stream.next_out = delta_data; - stream.avail_out = delta_size; - - inflateInit(&stream); - st = inflate(&stream, Z_FINISH); - inflateEnd(&stream); - if ((st != Z_STREAM_END) || stream.total_out != delta_size) - die("delta data unpack failed"); - + delta_data = unpack_compressed_entry(p, offset + 20, delta_size); result = patch_delta(base, base_size, delta_data, delta_size, &result_size); @@ -1092,33 +1137,6 @@ static void *unpack_delta_entry(unsigned char *base_sha1, return result; } -static void *unpack_non_delta_entry(unsigned char *data, - unsigned long size, - unsigned long left) -{ - int st; - z_stream stream; - unsigned char *buffer; - - buffer = xmalloc(size + 1); - buffer[size] = 0; - memset(&stream, 0, sizeof(stream)); - stream.next_in = data; - stream.avail_in = left; - stream.next_out = buffer; - stream.avail_out = size; - - inflateInit(&stream); - st = inflate(&stream, Z_FINISH); - inflateEnd(&stream); - if ((st != Z_STREAM_END) || stream.total_out != size) { - free(buffer); - return NULL; - } - - return buffer; -} - static void *unpack_entry(struct pack_entry *entry, char *type, unsigned long *sizep) { @@ -1139,36 +1157,23 @@ 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; + unsigned long offset, size; enum object_type kind; - void *retval; offset = unpack_object_header(p, entry->offset, &kind, &size); - pack = (unsigned char *) p->pack_base + offset; - left = p->pack_size - offset; switch (kind) { case OBJ_DELTA: - retval = unpack_delta_entry(pack, size, left, type, sizep, p); - return retval; + return unpack_delta_entry(p, offset, size, type, sizep); case OBJ_COMMIT: - strcpy(type, commit_type); - break; case OBJ_TREE: - strcpy(type, tree_type); - break; case OBJ_BLOB: - strcpy(type, blob_type); - break; case OBJ_TAG: - strcpy(type, tag_type); - break; + strcpy(type, type_names[kind]); + *sizep = size; + return unpack_compressed_entry(p, offset, size); default: return NULL; } - *sizep = size; - retval = unpack_non_delta_entry(pack, size, left); - return retval; } int num_packed_objects(const struct packed_git *p) @@ -1183,7 +1188,7 @@ int nth_packed_object_sha1(const struct packed_git *p, int n, void *index = p->index_base + 256; if (n < 0 || num_packed_objects(p) <= n) return -1; - memcpy(sha1, (char *) index + (24 * n) + 4, 20); + hashcpy(sha1, (unsigned char *) index + (24 * n) + 4); return 0; } @@ -1197,10 +1202,10 @@ int find_pack_entry_one(const unsigned char *sha1, do { int mi = (lo + hi) / 2; - int cmp = memcmp((char *) index + (24 * mi) + 4, sha1, 20); + int cmp = hashcmp((unsigned char *)index + (24 * mi) + 4, sha1); if (!cmp) { e->offset = ntohl(*((unsigned int *) ((char *) index + (24 * mi)))); - memcpy(e->sha1, sha1, 20); + hashcpy(e->sha1, sha1); e->p = p; return 1; } @@ -1212,12 +1217,20 @@ int find_pack_entry_one(const unsigned char *sha1, return 0; } -static int find_pack_entry(const unsigned char *sha1, struct pack_entry *e) +static int find_pack_entry(const unsigned char *sha1, struct pack_entry *e, const char **ignore_packed) { struct packed_git *p; prepare_packed_git(); for (p = packed_git; p; p = p->next) { + if (ignore_packed) { + const char **ig; + for (ig = ignore_packed; *ig; ig++) + if (!strcmp(p->pack_name, *ig)) + break; + if (*ig) + continue; + } if (find_pack_entry_one(sha1, e, p)) return 1; } @@ -1246,14 +1259,14 @@ 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); + map = map_sha1_file(sha1, &mapsize); if (!map) { struct pack_entry e; - if (find_pack_entry(sha1, &e)) + if (find_pack_entry(sha1, &e, NULL)) return packed_object_info(&e, type, sizep); reprepare_packed_git(); - if (find_pack_entry(sha1, &e)) + if (find_pack_entry(sha1, &e, NULL)) return packed_object_info(&e, type, sizep); return error("unable to find %s", sha1_to_hex(sha1)); } @@ -1276,7 +1289,7 @@ static void *read_packed_sha1(const unsigned char *sha1, char *type, unsigned lo { struct pack_entry e; - if (!find_pack_entry(sha1, &e)) { + if (!find_pack_entry(sha1, &e, NULL)) { error("cannot read sha1_file for %s", sha1_to_hex(sha1)); return NULL; } @@ -1289,16 +1302,16 @@ void * read_sha1_file(const unsigned char *sha1, char *type, unsigned long *size void *map, *buf; struct pack_entry e; - if (find_pack_entry(sha1, &e)) + if (find_pack_entry(sha1, &e, NULL)) return read_packed_sha1(sha1, type, size); - map = map_sha1_file_internal(sha1, &mapsize); + map = map_sha1_file(sha1, &mapsize); if (map) { buf = unpack_sha1_file(map, mapsize, type, size); munmap(map, mapsize); return buf; } reprepare_packed_git(); - if (find_pack_entry(sha1, &e)) + if (find_pack_entry(sha1, &e, NULL)) return read_packed_sha1(sha1, type, size); return NULL; } @@ -1313,7 +1326,7 @@ void *read_object_with_reference(const unsigned char *sha1, unsigned long isize; unsigned char actual_sha1[20]; - memcpy(actual_sha1, sha1, 20); + hashcpy(actual_sha1, sha1); while (1) { int ref_length = -1; const char *ref_type = NULL; @@ -1324,7 +1337,7 @@ void *read_object_with_reference(const unsigned char *sha1, if (!strcmp(type, required_type)) { *size = isize; if (actual_sha1_return) - memcpy(actual_sha1_return, actual_sha1, 20); + hashcpy(actual_sha1_return, actual_sha1); return buffer; } /* Handle references */ @@ -1376,7 +1389,7 @@ char *write_sha1_file_prepare(void *buf, * * Returns the errno on failure, 0 on success. */ -static int link_temp_to_file(const char *tmpfile, char *filename) +static int link_temp_to_file(const char *tmpfile, const char *filename) { int ret; char *dir; @@ -1409,7 +1422,7 @@ static int link_temp_to_file(const char *tmpfile, char *filename) /* * Move the just written object into its final resting place */ -int move_temp_to_file(const char *tmpfile, char *filename) +int move_temp_to_file(const char *tmpfile, const char *filename) { int ret = link_temp_to_file(tmpfile, filename); @@ -1519,7 +1532,7 @@ int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned cha */ filename = write_sha1_file_prepare(buf, len, type, sha1, hdr, &hdrlen); if (returnsha1) - memcpy(returnsha1, sha1, 20); + hashcpy(returnsha1, sha1); if (has_sha1_file(sha1)) return 0; fd = open(filename, O_RDONLY); @@ -1629,7 +1642,7 @@ int write_sha1_to_fd(int fd, const unsigned char *sha1) { int retval; unsigned long objsize; - void *buf = map_sha1_file_internal(sha1, &objsize); + void *buf = map_sha1_file(sha1, &objsize); if (buf) { retval = write_buffer(fd, buf, objsize); @@ -1706,7 +1719,7 @@ int write_sha1_from_fd(const unsigned char *sha1, int fd, char *buffer, unlink(tmpfile); return error("File %s corrupted", sha1_to_hex(sha1)); } - if (memcmp(sha1, real_sha1, 20)) { + if (hashcmp(sha1, real_sha1)) { unlink(tmpfile); return error("File %s has bad hash", sha1_to_hex(sha1)); } @@ -1730,10 +1743,10 @@ int has_pack_file(const unsigned char *sha1) return 1; } -int has_sha1_pack(const unsigned char *sha1) +int has_sha1_pack(const unsigned char *sha1, const char **ignore_packed) { struct pack_entry e; - return find_pack_entry(sha1, &e); + return find_pack_entry(sha1, &e, ignore_packed); } int has_sha1_file(const unsigned char *sha1) @@ -1741,7 +1754,7 @@ int has_sha1_file(const unsigned char *sha1) struct stat st; struct pack_entry e; - if (find_pack_entry(sha1, &e)) + if (find_pack_entry(sha1, &e, NULL)) return 1; return find_sha1_file(sha1, &st) ? 1 : 0; } @@ -1768,7 +1781,7 @@ int read_pipe(int fd, char** return_buf, unsigned long* return_size) off += iret; if (off == size) { size *= 2; - buf = realloc(buf, size); + buf = xrealloc(buf, size); } } } while (iret > 0); @@ -1784,7 +1797,7 @@ int read_pipe(int fd, char** return_buf, unsigned long* return_size) int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object) { unsigned long size = 4096; - char *buf = malloc(size); + char *buf = xmalloc(size); int ret; unsigned char hdr[50]; int hdrlen;