X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=sha1_file.c;h=f1c1c70d784aa0587cd4c7143c3d464fd8e5ddc6;hb=c899350e9dbe8700ad3c945b9743e9472732684b;hp=6f7228e106d4e24b18f8416cc6adc2a6fd303eb7;hpb=860edf7a710245ac748ada2ae3c0581a79f51f55;p=git.git diff --git a/sha1_file.c b/sha1_file.c index 6f7228e1..f1c1c70d 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -9,7 +9,15 @@ #include #include "cache.h" -const char *sha1_file_directory = NULL; +#ifndef O_NOATIME +#if defined(__linux__) && (defined(__i386__) || defined(__PPC__)) +#define O_NOATIME 01000000 +#else +#define O_NOATIME 0 +#endif +#endif + +static unsigned int sha1_file_open_flag = O_NOATIME; static unsigned hexval(char c) { @@ -35,6 +43,48 @@ int get_sha1_hex(const char *hex, unsigned char *sha1) return 0; } +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); +} + +int get_sha1(const char *str, unsigned char *sha1) +{ + static char pathname[PATH_MAX]; + static const char *prefix[] = { + "", + "refs", + "refs/tags", + "refs/heads", + "refs/snap", + NULL + }; + const char *gitdir; + const char **p; + + if (!get_sha1_hex(str, sha1)) + return 0; + + gitdir = ".git"; + for (p = prefix; *p; p++) { + snprintf(pathname, sizeof(pathname), "%s/%s/%s", gitdir, *p, str); + if (!get_sha1_file(pathname, sha1)) + return 0; + } + + return -1; +} + char * sha1_to_hex(const unsigned char *sha1) { static char buffer[50]; @@ -63,7 +113,7 @@ char *sha1_file_name(const unsigned char *sha1) if (!base) { char *sha1_file_directory = getenv(DB_ENVIRONMENT) ? : DEFAULT_DB_ENVIRONMENT; int len = strlen(sha1_file_directory); - base = malloc(len + 60); + base = xmalloc(len + 60); memcpy(base, sha1_file_directory, len); memset(base+len, 0, 60); base[len] = '/'; @@ -96,13 +146,26 @@ int check_sha1_signature(unsigned char *sha1, void *map, unsigned long size, con void *map_sha1_file(const unsigned char *sha1, unsigned long *size) { char *filename = sha1_file_name(sha1); - int fd = open(filename, O_RDONLY); struct stat st; void *map; + int fd; + fd = open(filename, O_RDONLY | sha1_file_open_flag); if (fd < 0) { - perror(filename); - return NULL; + /* See if it works without O_NOATIME */ + switch (sha1_file_open_flag) { + default: + fd = open(filename, O_RDONLY); + if (fd >= 0) + break; + /* Fallthrough */ + case 0: + perror(filename); + return NULL; + } + + /* If it failed once, it will probably fail again. Stop using O_NOATIME */ + sha1_file_open_flag = 0; } if (fstat(fd, &st) < 0) { close(fd); @@ -132,13 +195,13 @@ void * unpack_sha1_file(void *map, unsigned long mapsize, char *type, unsigned l inflateInit(&stream); ret = inflate(&stream, 0); + if (ret < Z_OK) + return NULL; if (sscanf(buffer, "%10s %lu", type, size) != 2) return NULL; bytes = strlen(buffer) + 1; - buf = malloc(*size); - if (!buf) - return NULL; + buf = xmalloc(*size); memcpy(buf, buffer + bytes, stream.total_out - bytes); bytes = stream.total_out - bytes; @@ -166,47 +229,52 @@ void * read_sha1_file(const unsigned char *sha1, char *type, unsigned long *size return NULL; } -void *read_tree_with_tree_or_commit_sha1(const unsigned char *sha1, - unsigned long *size, - unsigned char *tree_sha1_return) +void *read_object_with_reference(const unsigned char *sha1, + const unsigned char *required_type, + unsigned long *size, + unsigned char *actual_sha1_return) { char type[20]; void *buffer; unsigned long isize; - int was_commit = 0; - unsigned char tree_sha1[20]; - - buffer = read_sha1_file(sha1, type, &isize); - - /* - * We might have read a commit instead of a tree, in which case - * we parse out the tree_sha1 and attempt to read from there. - * (buffer + 5) is because the tree sha1 is always at offset 5 - * in a commit record ("tree "). - */ - if (buffer && - !strcmp(type, "commit") && - !get_sha1_hex(buffer + 5, tree_sha1)) { - free(buffer); - buffer = read_sha1_file(tree_sha1, type, &isize); - was_commit = 1; - } - - /* - * Now do we have something and if so is it a tree? - */ - if (!buffer || strcmp(type, "tree")) { - free(buffer); - return NULL; + unsigned char actual_sha1[20]; + + memcpy(actual_sha1, sha1, 20); + while (1) { + int ref_length = -1; + const char *ref_type = NULL; + + buffer = read_sha1_file(actual_sha1, type, &isize); + if (!buffer) + return NULL; + if (!strcmp(type, required_type)) { + *size = isize; + if (actual_sha1_return) + memcpy(actual_sha1_return, actual_sha1, 20); + return buffer; + } + /* Handle references */ + else if (!strcmp(type, "commit")) + ref_type = "tree "; + else if (!strcmp(type, "tag")) + ref_type = "object "; + else { + free(buffer); + return NULL; + } + ref_length = strlen(ref_type); + + if (memcmp(buffer, ref_type, ref_length) || + get_sha1_hex(buffer + ref_length, actual_sha1)) { + free(buffer); + return NULL; + } + /* Now we have the ID of the referred-to object in + * actual_sha1. Check again. */ } - - *size = isize; - if (tree_sha1_return) - memcpy(tree_sha1_return, was_commit ? tree_sha1 : sha1, 20); - return buffer; } -int write_sha1_file(char *buf, unsigned len, unsigned char *returnsha1) +int write_sha1_file(char *buf, unsigned long len, const char *type, unsigned char *returnsha1) { int size; char *compressed; @@ -214,10 +282,16 @@ int write_sha1_file(char *buf, unsigned len, unsigned char *returnsha1) unsigned char sha1[20]; SHA_CTX c; char *filename; - int fd; + static char tmpfile[PATH_MAX]; + char hdr[50]; + int fd, hdrlen, ret; + + /* Generate the header */ + hdrlen = sprintf(hdr, "%s %lu", type, len)+1; /* Sha1.. */ SHA1_Init(&c); + SHA1_Update(&c, hdr, hdrlen); SHA1_Update(&c, buf, len); SHA1_Final(sha1, &c); @@ -225,29 +299,47 @@ int write_sha1_file(char *buf, unsigned len, unsigned char *returnsha1) memcpy(returnsha1, sha1, 20); filename = sha1_file_name(sha1); - fd = open(filename, O_WRONLY | O_CREAT | O_EXCL, 0666); - if (fd < 0) { - if (errno != EEXIST) - return -1; - + fd = open(filename, O_RDONLY); + if (fd >= 0) { /* - * We might do collision checking here, but we'd need to - * uncompress the old file and check it. Later. + * FIXME!!! We might do collision checking here, but we'd + * need to uncompress the old file and check it. Later. */ + close(fd); return 0; } + if (errno != ENOENT) { + fprintf(stderr, "sha1 file %s: %s", filename, strerror(errno)); + return -1; + } + + snprintf(tmpfile, sizeof(tmpfile), "%s/obj_XXXXXX", get_object_directory()); + fd = mkstemp(tmpfile); + if (fd < 0) { + fprintf(stderr, "unable to create temporary sha1 filename %s: %s", tmpfile, strerror(errno)); + return -1; + } + /* Set it up */ memset(&stream, 0, sizeof(stream)); deflateInit(&stream, Z_BEST_COMPRESSION); - size = deflateBound(&stream, len); - compressed = malloc(size); + size = deflateBound(&stream, len+hdrlen); + compressed = xmalloc(size); /* Compress it */ - stream.next_in = buf; - stream.avail_in = len; stream.next_out = compressed; stream.avail_out = size; + + /* First header.. */ + stream.next_in = hdr; + stream.avail_in = hdrlen; + while (deflate(&stream, 0) == Z_OK) + /* nothing */ + + /* Then the data itself.. */ + stream.next_in = buf; + stream.avail_in = len; while (deflate(&stream, Z_FINISH) == Z_OK) /* nothing */; deflateEnd(&stream); @@ -255,53 +347,124 @@ int write_sha1_file(char *buf, unsigned len, unsigned char *returnsha1) if (write(fd, compressed, size) != size) die("unable to write file"); + fchmod(fd, 0444); close(fd); - + + ret = link(tmpfile, filename); + if (ret < 0) { + ret = errno; + + /* + * Coda hack - coda doesn't like cross-directory links, + * so we fall back to a rename, which will mean that it + * won't be able to check collisions, but that's not a + * big deal. + * + * When this succeeds, we just return 0. We have nothing + * left to unlink. + */ + if (ret == EXDEV && !rename(tmpfile, filename)) + return 0; + } + unlink(tmpfile); + if (ret) { + if (ret != EEXIST) { + fprintf(stderr, "unable to write sha1 filename %s: %s", filename, strerror(ret)); + return -1; + } + /* FIXME!!! Collision check here ? */ + } + return 0; } -static inline int collision_check(char *filename, void *buf, unsigned int size) +int write_sha1_from_fd(const unsigned char *sha1, int fd) { -#ifdef COLLISION_CHECK - void *map; - int fd = open(filename, O_RDONLY); - struct stat st; - int cmp; + char *filename = sha1_file_name(sha1); - /* Unreadable object, or object went away? Strange. */ - if (fd < 0) - return -1; + int local; + z_stream stream; + unsigned char real_sha1[20]; + char buf[4096]; + char discard[4096]; + int ret; + SHA_CTX c; - if (fstat(fd, &st) < 0 || size != st.st_size) - return -1; + local = open(filename, O_WRONLY | O_CREAT | O_EXCL, 0666); - map = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); - close(fd); - if (map == MAP_FAILED) - return -1; - cmp = memcmp(buf, map, size); - munmap(map, size); - if (cmp) - return -1; -#endif + if (local < 0) + return error("Couldn't open %s\n", filename); + + memset(&stream, 0, sizeof(stream)); + + inflateInit(&stream); + + SHA1_Init(&c); + + do { + ssize_t size; + size = read(fd, buf, 4096); + if (size <= 0) { + close(local); + unlink(filename); + if (!size) + return error("Connection closed?"); + 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); + inflateEnd(&stream); + + close(local); + SHA1_Final(real_sha1, &c); + if (ret != Z_STREAM_END) { + unlink(filename); + return error("File %s corrupted", sha1_to_hex(sha1)); + } + if (memcmp(sha1, real_sha1, 20)) { + unlink(filename); + return error("File %s has bad hash\n", sha1_to_hex(sha1)); + } + return 0; } -int write_sha1_buffer(const unsigned char *sha1, void *buf, unsigned int size) +int has_sha1_file(const unsigned char *sha1) { char *filename = sha1_file_name(sha1); - int fd; + struct stat st; - fd = open(filename, O_WRONLY | O_CREAT | O_EXCL, 0666); - if (fd < 0) { - if (errno != EEXIST) - return -1; - if (collision_check(filename, buf, size)) - return error("SHA1 collision detected!" - " This is bad, bad, BAD!\a\n"); - return 0; - } - write(fd, buf, size); - close(fd); + if (!stat(filename, &st)) + return 1; return 0; } + +int index_fd(unsigned char *sha1, int fd, struct stat *st) +{ + unsigned long size = st->st_size; + void *buf; + int ret; + + buf = ""; + if (size) + buf = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); + close(fd); + if ((int)(long)buf == -1) + return -1; + + ret = write_sha1_file(buf, size, "blob", sha1); + if (size) + munmap(buf, size); + return ret; +}