X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=sha1_file.c;h=e6ce455ae90bd430f2128f454bdb6e0575412486;hb=a31c6d022e2435a514fcc8ca57f9995c4376a986;hp=d2f38f0cdc78d81ffb5a601eb603a81775586368;hpb=a44c9a5e2e6d4108452f2c64dbd11f74a83745af;p=git.git diff --git a/sha1_file.c b/sha1_file.c index d2f38f0cd..e6ce455ae 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -45,6 +45,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]; @@ -73,7 +115,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] = '/'; @@ -161,9 +203,7 @@ void * unpack_sha1_file(void *map, unsigned long mapsize, char *type, unsigned l 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; @@ -191,47 +231,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; - } + unsigned char actual_sha1[20]; - /* - * Now do we have something and if so is it a tree? - */ - if (!buffer || strcmp(type, "tree")) { - free(buffer); - return NULL; - } + memcpy(actual_sha1, sha1, 20); + while (1) { + int ref_length = -1; + const char *ref_type = NULL; - *size = isize; - if (tree_sha1_return) - memcpy(tree_sha1_return, was_commit ? tree_sha1 : sha1, 20); - return buffer; + 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. */ + } } -int write_sha1_file(char *buf, unsigned len, const char *type, unsigned char *returnsha1) +int write_sha1_file(char *buf, unsigned long len, const char *type, unsigned char *returnsha1) { int size; char *compressed; @@ -239,11 +284,12 @@ int write_sha1_file(char *buf, unsigned len, const char *type, unsigned char *re unsigned char sha1[20]; SHA_CTX c; char *filename; + static char tmpfile[PATH_MAX]; char hdr[50]; - int fd, hdrlen; + int fd, hdrlen, ret; /* Generate the header */ - hdrlen = sprintf(hdr, "%s %d", type, len)+1; + hdrlen = sprintf(hdr, "%s %lu", type, len)+1; /* Sha1.. */ SHA1_Init(&c); @@ -255,23 +301,33 @@ int write_sha1_file(char *buf, unsigned len, const char *type, unsigned char *re 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+hdrlen); - compressed = malloc(size); + compressed = xmalloc(size); /* Compress it */ stream.next_out = compressed; @@ -293,54 +349,34 @@ int write_sha1_file(char *buf, unsigned len, const char *type, unsigned char *re if (write(fd, compressed, size) != size) die("unable to write file"); + fchmod(fd, 0444); close(fd); - - return 0; -} - -static inline int collision_check(char *filename, void *buf, unsigned int size) -{ -#ifdef COLLISION_CHECK - void *map; - int fd = open(filename, O_RDONLY); - struct stat st; - int cmp; - - /* Unreadable object, or object went away? Strange. */ - if (fd < 0) - return -1; - - if (fstat(fd, &st) < 0 || size != st.st_size) - return -1; - - 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 - return 0; -} -int write_sha1_buffer(const unsigned char *sha1, void *buf, unsigned int size) -{ - char *filename = sha1_file_name(sha1); - int fd; + ret = link(tmpfile, filename); + if (ret < 0) { + ret = errno; - fd = open(filename, O_WRONLY | O_CREAT | O_EXCL, 0666); - if (fd < 0) { - if (errno != EEXIST) + /* + * 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; - if (collision_check(filename, buf, size)) - return error("SHA1 collision detected!" - " This is bad, bad, BAD!\a\n"); - return 0; + } + /* FIXME!!! Collision check here ? */ } - write(fd, buf, size); - close(fd); + return 0; } @@ -415,3 +451,22 @@ int has_sha1_file(const unsigned char *sha1) 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; +}