X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=sha1_file.c;h=e648c068b558fbdbbdc866c8829af3641e2c5b60;hb=17ebe977d72290dcdc848b78ae2e65b59d4e1b4c;hp=942b673dc3c7fa9f057c5c452e3a1b73eaeb8707;hpb=f1a7eb36b017c62d9a007b6b8660bdeec3f94f97;p=git.git diff --git a/sha1_file.c b/sha1_file.c index 942b673d..e648c068 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -6,9 +6,8 @@ * This handles basic git sha1 object files - packing, unpacking, * creation etc. */ -#include -#include #include "cache.h" +#include "delta.h" #ifndef O_NOATIME #if defined(__linux__) && (defined(__i386__) || defined(__PPC__)) @@ -44,7 +43,7 @@ int get_sha1_hex(const char *hex, unsigned char *sha1) return 0; } -int get_sha1_file(const char *path, unsigned char *result) +static int get_sha1_file(const char *path, unsigned char *result) { char buffer[60]; int fd = open(path, O_RDONLY); @@ -59,7 +58,7 @@ int get_sha1_file(const char *path, unsigned char *result) return get_sha1_hex(buffer, result); } -static char *git_dir, *git_object_dir, *git_index_file; +static char *git_dir, *git_object_dir, *git_index_file, *git_refs_dir; static void setup_git_env(void) { git_dir = gitenv(GIT_DIR_ENVIRONMENT); @@ -70,6 +69,8 @@ static void setup_git_env(void) 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); if (!git_index_file) { git_index_file = xmalloc(strlen(git_dir) + 7); @@ -84,6 +85,13 @@ char *get_object_directory(void) return git_object_dir; } +char *get_refs_directory(void) +{ + if (!git_refs_dir) + setup_git_env(); + return git_refs_dir; +} + char *get_index_file(void) { if (!git_index_file) @@ -199,7 +207,7 @@ static void prepare_alt_odb(void) { int pass, totlen, i; const char *cp, *last; - char *op = 0; + char *op = NULL; const char *alt = gitenv(ALTERNATE_DB_ENVIRONMENT) ? : ""; /* The first pass counts how large an area to allocate to @@ -236,7 +244,7 @@ static void prepare_alt_odb(void) if (pass) break; alt_odb = xmalloc(sizeof(*alt_odb) * (i + 1) + totlen); - alt_odb[i].base = alt_odb[i].name = 0; + alt_odb[i].base = alt_odb[i].name = NULL; op = (char*)(&alt_odb[i+1]); } } @@ -258,7 +266,7 @@ static char *find_sha1_file(const unsigned char *sha1, struct stat *st) return NULL; } -int check_sha1_signature(unsigned char *sha1, void *map, unsigned long size, const char *type) +int check_sha1_signature(const unsigned char *sha1, void *map, unsigned long size, const char *type) { char header[100]; unsigned char real_sha1[20]; @@ -308,40 +316,189 @@ void *map_sha1_file(const unsigned char *sha1, unsigned long *size) return map; } +int unpack_sha1_header(z_stream *stream, void *map, unsigned long mapsize, void *buffer, unsigned long size) +{ + /* 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; + + inflateInit(stream); + return inflate(stream, 0); +} + +void *unpack_sha1_rest(z_stream *stream, void *buffer, unsigned long size) +{ + int bytes = strlen(buffer) + 1; + char *buf = xmalloc(1+size); + + memcpy(buf, buffer + bytes, stream->total_out - bytes); + bytes = stream->total_out - bytes; + if (bytes < size) { + stream->next_out = buf + bytes; + stream->avail_out = size - bytes; + while (inflate(stream, Z_FINISH) == Z_OK) + /* nothing */; + } + buf[size] = 0; + inflateEnd(stream); + return buf; +} + +/* + * We used to just use "sscanf()", but that's actually way + * 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) +{ + int i; + unsigned long size; + + /* + * The type can be at most ten bytes (including the + * terminating '\0' that we add), and is followed by + * a space. + */ + i = 10; + for (;;) { + char c = *hdr++; + if (c == ' ') + break; + if (!--i) + return -1; + *type++ = c; + } + *type = 0; + + /* + * The length must follow immediately, and be in canonical + * decimal format (ie "010" is not valid). + */ + size = *hdr++ - '0'; + if (size > 9) + return -1; + if (size) { + for (;;) { + unsigned long c = *hdr - '0'; + if (c > 9) + break; + hdr++; + size = size * 10 + c; + } + } + *sizep = size; + + /* + * The length must be followed by a zero byte + */ + return *hdr ? -1 : 0; +} + void * unpack_sha1_file(void *map, unsigned long mapsize, char *type, unsigned long *size) { - int ret, bytes; + int ret; z_stream stream; - char buffer[8192]; - char *buf; - - /* Get the data stream */ - memset(&stream, 0, sizeof(stream)); - stream.next_in = map; - stream.avail_in = mapsize; - stream.next_out = buffer; - stream.avail_out = sizeof(buffer); + char hdr[8192]; - inflateInit(&stream); - ret = inflate(&stream, 0); - if (ret < Z_OK) - return NULL; - if (sscanf(buffer, "%10s %lu", type, size) != 2) + ret = unpack_sha1_header(&stream, map, mapsize, hdr, sizeof(hdr)); + if (ret < Z_OK || parse_sha1_header(hdr, type, size) < 0) return NULL; - bytes = strlen(buffer) + 1; - buf = xmalloc(*size); + return unpack_sha1_rest(&stream, hdr, *size); +} - memcpy(buf, buffer + bytes, stream.total_out - bytes); - bytes = stream.total_out - bytes; - if (bytes < *size && ret == Z_OK) { - stream.next_out = buf + bytes; - stream.avail_out = *size - bytes; - while (inflate(&stream, Z_FINISH) == Z_OK) - /* nothing */; +int sha1_delta_base(const unsigned char *sha1, unsigned char *base_sha1) +{ + int ret; + unsigned long mapsize, size; + void *map; + z_stream stream; + char hdr[64], type[20]; + void *delta_data_head; + + map = map_sha1_file(sha1, &mapsize); + if (!map) + return -1; + ret = unpack_sha1_header(&stream, map, mapsize, hdr, sizeof(hdr)); + if (ret < Z_OK || parse_sha1_header(hdr, type, &size) < 0) { + ret = -1; + goto out; + } + if (strcmp(type, "delta")) { + ret = 0; + goto out; } + + delta_data_head = hdr + strlen(hdr) + 1; + ret = 1; + memcpy(base_sha1, delta_data_head, 20); + out: inflateEnd(&stream); - return buf; + munmap(map, mapsize); + return ret; +} + +int sha1_file_size(const unsigned char *sha1, unsigned long *sizep) +{ + int ret, status; + unsigned long mapsize, size; + void *map; + z_stream stream; + char hdr[64], type[20]; + const unsigned char *data; + unsigned char cmd; + int i; + + map = map_sha1_file(sha1, &mapsize); + if (!map) + return -1; + ret = unpack_sha1_header(&stream, map, mapsize, hdr, sizeof(hdr)); + status = -1; + if (ret < Z_OK || parse_sha1_header(hdr, type, &size) < 0) + goto out; + if (strcmp(type, "delta")) { + *sizep = size; + status = 0; + goto out; + } + + /* We are dealing with a delta object. Inflated, the first + * 20 bytes hold the base object SHA1, and delta data follows + * immediately after it. + * + * The initial part of the delta starts at delta_data_head + + * 20. Borrow code from patch-delta to read the result size. + */ + data = hdr + strlen(hdr) + 1 + 20; + + /* Skip over the source size; we are not interested in + * it and we cannot verify it because we do not want + * to read the base object. + */ + cmd = *data++; + while (cmd) { + if (cmd & 1) + data++; + cmd >>= 1; + } + /* Read the result size */ + size = i = 0; + cmd = *data++; + while (cmd) { + if (cmd & 1) + size |= *data++ << i; + i += 8; + cmd >>= 1; + } + *sizep = size; + status = 0; + out: + inflateEnd(&stream); + munmap(map, mapsize); + return status; } void * read_sha1_file(const unsigned char *sha1, char *type, unsigned long *size) @@ -353,13 +510,26 @@ void * read_sha1_file(const unsigned char *sha1, char *type, unsigned long *size if (map) { buf = unpack_sha1_file(map, mapsize, type, size); munmap(map, mapsize); + if (buf && !strcmp(type, "delta")) { + void *ref = NULL, *delta = buf; + unsigned long ref_size, delta_size = *size; + buf = NULL; + if (delta_size > 20) + ref = read_sha1_file(delta, type, &ref_size); + if (ref) + buf = patch_delta(ref, ref_size, + delta+20, delta_size-20, + size); + free(delta); + free(ref); + } return buf; } return NULL; } void *read_object_with_reference(const unsigned char *sha1, - const unsigned char *required_type, + const char *required_type, unsigned long *size, unsigned char *actual_sha1_return) { @@ -403,20 +573,20 @@ void *read_object_with_reference(const unsigned char *sha1, } } -int write_sha1_file(char *buf, unsigned long len, const char *type, unsigned char *returnsha1) +int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *returnsha1) { int size; - char *compressed; + unsigned char *compressed; z_stream stream; unsigned char sha1[20]; SHA_CTX c; char *filename; static char tmpfile[PATH_MAX]; - char hdr[50]; + unsigned char hdr[50]; int fd, hdrlen, ret; /* Generate the header */ - hdrlen = sprintf(hdr, "%s %lu", type, len)+1; + hdrlen = sprintf((char *)hdr, "%s %lu", type, len)+1; /* Sha1.. */ SHA1_Init(&c); @@ -516,8 +686,8 @@ int write_sha1_from_fd(const unsigned char *sha1, int fd) int local; z_stream stream; unsigned char real_sha1[20]; - char buf[4096]; - char discard[4096]; + unsigned char buf[4096]; + unsigned char discard[4096]; int ret; SHA_CTX c;