X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=sha1_file.c;h=43bc2ea0cf039bb9fd02c8313981e85bd7398d33;hb=cefda27f741a948a78ce27180f5332c2d19199c4;hp=223001033c30eed20d7c5ee59719bffd25036ded;hpb=5c87a8c5602eb09810679dd3e0bcfc7ae1fed111;p=git.git diff --git a/sha1_file.c b/sha1_file.c index 223001033..43bc2ea0c 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -50,29 +50,6 @@ int get_sha1_hex(const char *hex, unsigned char *sha1) return 0; } -int adjust_shared_perm(const char *path) -{ - struct stat st; - int mode; - - if (!shared_repository) - return 0; - if (lstat(path, &st) < 0) - return -1; - mode = st.st_mode; - if (mode & S_IRUSR) - mode |= S_IRGRP; - if (mode & S_IWUSR) - mode |= S_IWGRP; - if (mode & S_IXUSR) - mode |= S_IXGRP; - if (S_ISDIR(mode)) - mode |= S_ISGID; - if (chmod(path, mode) < 0) - return -2; - return 0; -} - int safe_create_leading_directories(char *path) { char *pos = path; @@ -366,7 +343,7 @@ static void read_info_alternates(const char * relative_base, int depth) void prepare_alt_odb(void) { - char *alt; + const char *alt; alt = getenv(ALTERNATE_DB_ENVIRONMENT); if (!alt) alt = ""; @@ -476,7 +453,7 @@ int use_packed_git(struct packed_git *p) { if (!p->pack_size) { struct stat st; - // We created the struct before we had the pack + /* 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); @@ -509,8 +486,9 @@ 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)) { - + (char *) p->pack_base + p->pack_size - 20, + 20)) { + die("packfile %s does not match index.", p->pack_name); } } @@ -617,6 +595,12 @@ static void prepare_packed_git_one(char *objdir, int local) /* we have .idx. Is it a file we can map? */ strcpy(path + len, de->d_name); + for (p = packed_git; p; p = p->next) { + if (!memcmp(path, p->pack_name, len + namelen - 4)) + break; + } + if (p) + continue; p = add_packed_git(path, len + namelen, local); if (!p) continue; @@ -626,12 +610,12 @@ static void prepare_packed_git_one(char *objdir, int local) closedir(dir); } +static int prepare_packed_git_run_once = 0; void prepare_packed_git(void) { - static int run_once = 0; struct alternate_object_database *alt; - if (run_once) + if (prepare_packed_git_run_once) return; prepare_packed_git_one(get_object_directory(), 1); prepare_alt_odb(); @@ -640,7 +624,13 @@ void prepare_packed_git(void) prepare_packed_git_one(alt->base, 0); alt->name[-1] = '/'; } - run_once = 1; + prepare_packed_git_run_once = 1; +} + +static void reprepare_packed_git(void) +{ + prepare_packed_git_run_once = 0; + prepare_packed_git(); } int check_sha1_signature(const unsigned char *sha1, void *map, unsigned long size, const char *type) @@ -694,26 +684,74 @@ static void *map_sha1_file_internal(const unsigned char *sha1, return map; } -int unpack_sha1_header(z_stream *stream, void *map, unsigned long mapsize, void *buffer, unsigned long size) +static int unpack_sha1_header(z_stream *stream, unsigned char *map, unsigned long mapsize, void *buffer, unsigned long bufsiz) { + 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 + }; + const char *type; + /* Get the data stream */ memset(stream, 0, sizeof(*stream)); stream->next_in = map; stream->avail_in = mapsize; stream->next_out = buffer; - stream->avail_out = size; + 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)) { + inflateInit(stream); + return inflate(stream, 0); + } + + c = *map++; + mapsize--; + type = typename[(c >> 4) & 7]; + if (!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--; + } + + /* Set up the stream for the rest.. */ + stream->next_in = map; + stream->avail_in = mapsize; inflateInit(stream); - return inflate(stream, 0); + + /* And generate the fake traditional header */ + stream->total_out = 1 + snprintf(buffer, bufsiz, "%s %lu", type, size); + return 0; } static void *unpack_sha1_rest(z_stream *stream, void *buffer, unsigned long size) { int bytes = strlen(buffer) + 1; unsigned char *buf = xmalloc(1+size); + unsigned long n; - memcpy(buf, buffer + bytes, stream->total_out - bytes); - bytes = stream->total_out - bytes; + n = stream->total_out - bytes; + if (n > size) + n = size; + memcpy(buf, (char *) buffer + bytes, n); + bytes = n; if (bytes < size) { stream->next_out = buf + bytes; stream->avail_out = size - bytes; @@ -730,7 +768,7 @@ static void *unpack_sha1_rest(z_stream *stream, void *buffer, unsigned long size * too permissive for what we want to check. So do an anal * object header parse by hand. */ -int parse_sha1_header(char *hdr, char *type, unsigned long *sizep) +static int parse_sha1_header(char *hdr, char *type, unsigned long *sizep) { int i; unsigned long size; @@ -864,7 +902,7 @@ static unsigned long unpack_object_header(struct packed_git *p, unsigned long of if (offset >= p->pack_size) die("object offset outside of pack file"); - pack = p->pack_base + offset; + pack = (unsigned char *) p->pack_base + offset; c = *pack++; offset++; *type = (c >> 4) & 7; @@ -894,7 +932,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, p->pack_base + ptr, 20); + memcpy(base, (char *) p->pack_base + ptr, 20); status = 0; done: unuse_packed_git(p); @@ -914,7 +952,7 @@ void packed_object_info_detail(struct pack_entry *e, enum object_type kind; offset = unpack_object_header(p, e->offset, &kind, size); - pack = p->pack_base + offset; + pack = (unsigned char *) p->pack_base + offset; if (kind != OBJ_DELTA) *delta_chain_length = 0; else { @@ -930,7 +968,7 @@ void packed_object_info_detail(struct pack_entry *e, find_pack_entry_one(pack, &base_ent, p); offset = unpack_object_header(p, base_ent.offset, &kind, &junk); - pack = p->pack_base + offset; + pack = (unsigned char *) p->pack_base + offset; chain_length++; } while (kind == OBJ_DELTA); *delta_chain_length = chain_length; @@ -968,7 +1006,7 @@ static int packed_object_info(struct pack_entry *entry, die("cannot map packed file"); offset = unpack_object_header(p, entry->offset, &kind, &size); - pack = p->pack_base + offset; + pack = (unsigned char *) p->pack_base + offset; left = p->pack_size - offset; switch (kind) { @@ -1107,7 +1145,7 @@ void *unpack_entry_gently(struct pack_entry *entry, void *retval; offset = unpack_object_header(p, entry->offset, &kind, &size); - pack = p->pack_base + offset; + pack = (unsigned char *) p->pack_base + offset; left = p->pack_size - offset; switch (kind) { case OBJ_DELTA: @@ -1145,7 +1183,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, (index + 24 * n + 4), 20); + memcpy(sha1, (char *) index + (24 * n) + 4, 20); return 0; } @@ -1159,9 +1197,9 @@ int find_pack_entry_one(const unsigned char *sha1, do { int mi = (lo + hi) / 2; - int cmp = memcmp(index + 24 * mi + 4, sha1, 20); + int cmp = memcmp((char *) index + (24 * mi) + 4, sha1, 20); if (!cmp) { - e->offset = ntohl(*((unsigned int *)(index + 24 * mi))); + e->offset = ntohl(*((unsigned int *) ((char *) index + (24 * mi)))); memcpy(e->sha1, sha1, 20); e->p = p; return 1; @@ -1212,9 +1250,12 @@ int sha1_object_info(const unsigned char *sha1, char *type, unsigned long *sizep if (!map) { struct pack_entry e; - if (!find_pack_entry(sha1, &e)) - return error("unable to find %s", sha1_to_hex(sha1)); - return packed_object_info(&e, type, sizep); + if (find_pack_entry(sha1, &e)) + return packed_object_info(&e, type, sizep); + reprepare_packed_git(); + if (find_pack_entry(sha1, &e)) + return packed_object_info(&e, type, sizep); + return error("unable to find %s", sha1_to_hex(sha1)); } if (unpack_sha1_header(&stream, map, mapsize, hdr, sizeof(hdr)) < 0) status = error("unable to unpack %s header", @@ -1256,6 +1297,9 @@ void * read_sha1_file(const unsigned char *sha1, char *type, unsigned long *size munmap(map, mapsize); return buf; } + reprepare_packed_git(); + if (find_pack_entry(sha1, &e)) + return read_packed_sha1(sha1, type, size); return NULL; } @@ -1295,7 +1339,7 @@ void *read_object_with_reference(const unsigned char *sha1, ref_length = strlen(ref_type); if (memcmp(buffer, ref_type, ref_length) || - get_sha1_hex(buffer + ref_length, actual_sha1)) { + get_sha1_hex((char *) buffer + ref_length, actual_sha1)) { free(buffer); return NULL; } @@ -1335,31 +1379,29 @@ char *write_sha1_file_prepare(void *buf, static int link_temp_to_file(const char *tmpfile, char *filename) { int ret; + char *dir; if (!link(tmpfile, filename)) return 0; /* - * Try to mkdir the last path component if that failed - * with an ENOENT. + * Try to mkdir the last path component if that failed. * * Re-try the "link()" regardless of whether the mkdir * succeeds, since a race might mean that somebody * else succeeded. */ ret = errno; - if (ret == ENOENT) { - char *dir = strrchr(filename, '/'); - if (dir) { - *dir = 0; - mkdir(filename, 0777); - if (adjust_shared_perm(filename)) - return -2; - *dir = '/'; - if (!link(tmpfile, filename)) - return 0; - ret = errno; - } + dir = strrchr(filename, '/'); + if (dir) { + *dir = 0; + mkdir(filename, 0777); + if (adjust_shared_perm(filename)) + return -2; + *dir = '/'; + if (!link(tmpfile, filename)) + return 0; + ret = errno; } return ret; } @@ -1399,6 +1441,68 @@ int move_temp_to_file(const char *tmpfile, char *filename) return 0; } +static int write_buffer(int fd, const void *buf, size_t len) +{ + while (len) { + ssize_t size; + + size = write(fd, buf, len); + if (!size) + return error("file write: disk full"); + if (size < 0) { + if (errno == EINTR || errno == EAGAIN) + continue; + return error("file write error (%s)", strerror(errno)); + } + len -= size; + buf = (char *) buf + size; + } + return 0; +} + +static int write_binary_header(unsigned char *hdr, enum object_type type, unsigned long len) +{ + int hdr_len; + unsigned char c; + + c = (type << 4) | (len & 15); + len >>= 4; + hdr_len = 1; + while (len) { + *hdr++ = c | 0x80; + hdr_len++; + c = (len & 0x7f); + len >>= 7; + } + *hdr = c; + return hdr_len; +} + +static void setup_object_header(z_stream *stream, const char *type, unsigned long len) +{ + int obj_type, hdr; + + if (use_legacy_headers) { + while (deflate(stream, 0) == Z_OK) + /* nothing */; + return; + } + if (!strcmp(type, blob_type)) + obj_type = OBJ_BLOB; + else if (!strcmp(type, tree_type)) + obj_type = OBJ_TREE; + else if (!strcmp(type, commit_type)) + obj_type = OBJ_COMMIT; + else if (!strcmp(type, tag_type)) + obj_type = OBJ_TAG; + else + die("trying to generate bogus object of type '%s'", type); + hdr = write_binary_header(stream->next_out, obj_type, len); + stream->total_out = hdr; + stream->next_out += hdr; + stream->avail_out -= hdr; +} + int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *returnsha1) { int size; @@ -1443,8 +1547,8 @@ int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned cha /* Set it up */ memset(&stream, 0, sizeof(stream)); - deflateInit(&stream, Z_BEST_COMPRESSION); - size = deflateBound(&stream, len+hdrlen); + deflateInit(&stream, zlib_compression_level); + size = 8 + deflateBound(&stream, len+hdrlen); compressed = xmalloc(size); /* Compress it */ @@ -1454,8 +1558,7 @@ int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned cha /* First header.. */ stream.next_in = hdr; stream.avail_in = hdrlen; - while (deflate(&stream, 0) == Z_OK) - /* nothing */; + setup_object_header(&stream, type, len); /* Then the data itself.. */ stream.next_in = buf; @@ -1465,8 +1568,8 @@ int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned cha deflateEnd(&stream); size = stream.total_out; - if (write(fd, compressed, size) != size) - die("unable to write file"); + if (write_buffer(fd, compressed, size) < 0) + die("unable to write sha1 file"); fchmod(fd, 0444); close(fd); free(compressed); @@ -1474,73 +1577,70 @@ int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned cha return move_temp_to_file(tmpfile, filename); } -int write_sha1_to_fd(int fd, const unsigned char *sha1) +/* + * We need to unpack and recompress the object for writing + * it out to a different file. + */ +static void *repack_object(const unsigned char *sha1, unsigned long *objsize) { - ssize_t size; - unsigned long objsize; - int posn = 0; - void *map = map_sha1_file_internal(sha1, &objsize); - void *buf = map; - void *temp_obj = NULL; + size_t size; z_stream stream; + unsigned char *unpacked; + unsigned long len; + char type[20]; + char hdr[50]; + int hdrlen; + void *buf; - 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); + /* need to unpack and recompress it by itself */ + unpacked = read_packed_sha1(sha1, type, &len); - hdrlen = sprintf(hdr, "%s %lu", type, len) + 1; + 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 */; + /* Set it up */ + memset(&stream, 0, sizeof(stream)); + deflateInit(&stream, zlib_compression_level); + size = deflateBound(&stream, len + hdrlen); + buf = xmalloc(size); - /* 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; - } + /* Compress it */ + stream.next_out = buf; + stream.avail_out = size; - do { - size = write(fd, buf + posn, objsize - posn); - if (size <= 0) { - if (!size) { - fprintf(stderr, "write closed\n"); - } else { - perror("write "); - } - return -1; - } - posn += size; - } while (posn < objsize); + /* First header.. */ + stream.next_in = (void *)hdr; + stream.avail_in = hdrlen; + while (deflate(&stream, 0) == Z_OK) + /* nothing */; - if (map) - munmap(map, objsize); - if (temp_obj) - free(temp_obj); + /* Then the data itself.. */ + stream.next_in = unpacked; + stream.avail_in = len; + while (deflate(&stream, Z_FINISH) == Z_OK) + /* nothing */; + deflateEnd(&stream); + free(unpacked); - return 0; + *objsize = stream.total_out; + return buf; +} + +int write_sha1_to_fd(int fd, const unsigned char *sha1) +{ + int retval; + unsigned long objsize; + void *buf = map_sha1_file_internal(sha1, &objsize); + + if (buf) { + retval = write_buffer(fd, buf, objsize); + munmap(buf, objsize); + return retval; + } + + buf = repack_object(sha1, &objsize); + retval = write_buffer(fd, buf, objsize); + free(buf); + return retval; } int write_sha1_from_fd(const unsigned char *sha1, int fd, char *buffer, @@ -1579,7 +1679,8 @@ int write_sha1_from_fd(const unsigned char *sha1, int fd, char *buffer, SHA1_Update(&c, discard, sizeof(discard) - stream.avail_out); } while (stream.avail_in && ret == Z_OK); - write(local, buffer, *bufposn - stream.avail_in); + if (write_buffer(local, buffer, *bufposn - stream.avail_in) < 0) + die("unable to write sha1 file"); memmove(buffer, buffer + *bufposn - stream.avail_in, stream.avail_in); *bufposn = stream.avail_in; @@ -1645,16 +1746,24 @@ int has_sha1_file(const unsigned char *sha1) return find_sha1_file(sha1, &st) ? 1 : 0; } -int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object) +/* + * reads from fd as long as possible into a supplied buffer of size bytes. + * If necessary the buffer's size is increased using realloc() + * + * returns 0 if anything went fine and -1 otherwise + * + * NOTE: both buf and size may change, but even when -1 is returned + * you still have to free() it yourself. + */ +int read_pipe(int fd, char** return_buf, unsigned long* return_size) { - unsigned long size = 4096; - char *buf = malloc(size); - int iret, ret; + char* buf = *return_buf; + unsigned long size = *return_size; + int iret; unsigned long off = 0; - unsigned char hdr[50]; - int hdrlen; + do { - iret = read(fd, buf + off, size - off); + iret = xread(fd, buf + off, size - off); if (iret > 0) { off += iret; if (off == size) { @@ -1663,16 +1772,34 @@ int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object) } } } while (iret > 0); - if (iret < 0) { + + *return_buf = buf; + *return_size = off; + + if (iret < 0) + return -1; + return 0; +} + +int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object) +{ + unsigned long size = 4096; + char *buf = malloc(size); + int ret; + unsigned char hdr[50]; + int hdrlen; + + if (read_pipe(fd, &buf, &size)) { free(buf); return -1; } + if (!type) type = blob_type; if (write_object) - ret = write_sha1_file(buf, off, type, sha1); + ret = write_sha1_file(buf, size, type, sha1); else { - write_sha1_file_prepare(buf, off, type, sha1, hdr, &hdrlen); + write_sha1_file_prepare(buf, size, type, sha1, hdr, &hdrlen); ret = 0; } free(buf);