X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=sha1_file.c;h=3372ebcdcac0d36b81663f958784e8a1ec84b900;hb=a62be77f5ebdbbd46d6956dc4d07571835588389;hp=baaa4c00da50342a7db8064dadf7f963b0e2e679;hpb=b12bbd5986e1fae4120d7a415c1271c0273cad7e;p=git.git diff --git a/sha1_file.c b/sha1_file.c index baaa4c00..3372ebcd 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -6,11 +6,14 @@ * This handles basic git sha1 object files - packing, unpacking, * creation etc. */ -#include -#include #include "cache.h" #include "delta.h" #include "pack.h" +#include "blob.h" +#include "commit.h" +#include "tag.h" +#include "tree.h" +#include #ifndef O_NOATIME #if defined(__linux__) && (defined(__i386__) || defined(__PPC__)) @@ -48,79 +51,57 @@ int get_sha1_hex(const char *hex, unsigned char *sha1) return 0; } -static char *git_dir, *git_object_dir, *git_index_file, *git_refs_dir, - *git_graft_file; -static void setup_git_env(void) +int adjust_shared_perm(const char *path) { - git_dir = getenv(GIT_DIR_ENVIRONMENT); - if (!git_dir) - git_dir = DEFAULT_GIT_DIR_ENVIRONMENT; - git_object_dir = getenv(DB_ENVIRONMENT); - if (!git_object_dir) { - 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 = getenv(INDEX_ENVIRONMENT); - if (!git_index_file) { - git_index_file = xmalloc(strlen(git_dir) + 7); - sprintf(git_index_file, "%s/index", git_dir); - } - git_graft_file = getenv(GRAFT_ENVIRONMENT); - if (!git_graft_file) - git_graft_file = strdup(git_path("info/grafts")); -} - -char *get_git_dir(void) -{ - if (!git_dir) - setup_git_env(); - return git_dir; -} - -char *get_object_directory(void) -{ - if (!git_object_dir) - setup_git_env(); - 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) - setup_git_env(); - return git_index_file; -} + struct stat st; + int mode; -char *get_graft_file(void) -{ - if (!git_graft_file) - setup_git_env(); - return git_graft_file; + 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; + struct stat st; + + if (*pos == '/') + pos++; while (pos) { pos = strchr(pos, '/'); if (!pos) break; *pos = 0; - if (mkdir(path, 0777) < 0) - if (errno != EEXIST) { + if (!stat(path, &st)) { + /* path exists */ + if (!S_ISDIR(st.st_mode)) { *pos = '/'; - return -1; + return -3; } + } + else if (mkdir(path, 0777)) { + *pos = '/'; + return -1; + } + else if (adjust_shared_perm(path)) { + *pos = '/'; + return -2; + } *pos++ = '/'; } return 0; @@ -128,9 +109,10 @@ int safe_create_leading_directories(char *path) char * sha1_to_hex(const unsigned char *sha1) { - static char buffer[50]; + static int bufno; + static char hexbuffer[4][50]; static const char hex[] = "0123456789abcdef"; - char *buf = buffer; + char *buffer = hexbuffer[3 & ++bufno], *buf = buffer; int i; for (i = 0; i < 20; i++) { @@ -138,6 +120,8 @@ char * sha1_to_hex(const unsigned char *sha1) *buf++ = hex[val >> 4]; *buf++ = hex[val & 0xf]; } + *buf = '\0'; + return buffer; } @@ -234,6 +218,8 @@ char *sha1_pack_index_name(const unsigned char *sha1) struct alternate_object_database *alt_odb_list; static struct alternate_object_database **alt_odb_tail; +static void read_info_alternates(const char * alternates, int depth); + /* * Prepare alternate object database registry. * @@ -241,21 +227,93 @@ static struct alternate_object_database **alt_odb_tail; * alternate_object_database. The elements on this list come from * non-empty elements from colon separated ALTERNATE_DB_ENVIRONMENT * environment variable, and $GIT_OBJECT_DIRECTORY/info/alternates, - * whose contents is exactly in the same format as that environment - * variable. Its base points at a statically allocated buffer that + * whose contents is similar to that environment variable but can be + * LF separated. Its base points at a statically allocated buffer that * contains "/the/directory/corresponding/to/.git/objects/...", while * its name points just after the slash at the end of ".git/objects/" * in the example above, and has enough space to hold 40-byte hex * SHA1, an extra slash for the first level indirection, and the * terminating NUL. */ -static void link_alt_odb_entries(const char *alt, const char *ep, int sep, - const char *relative_base) +static int link_alt_odb_entry(const char * entry, int len, const char * relative_base, int depth) { - const char *cp, *last; + struct stat st; + const char *objdir = get_object_directory(); struct alternate_object_database *ent; + struct alternate_object_database *alt; + /* 43 = 40-byte + 2 '/' + terminating NUL */ + int pfxlen = len; + int entlen = pfxlen + 43; int base_len = -1; + if (*entry != '/' && relative_base) { + /* Relative alt-odb */ + if (base_len < 0) + base_len = strlen(relative_base) + 1; + entlen += base_len; + pfxlen += base_len; + } + ent = xmalloc(sizeof(*ent) + entlen); + + if (*entry != '/' && relative_base) { + memcpy(ent->base, relative_base, base_len - 1); + ent->base[base_len - 1] = '/'; + memcpy(ent->base + base_len, entry, len); + } + else + memcpy(ent->base, entry, pfxlen); + + ent->name = ent->base + pfxlen + 1; + ent->base[pfxlen + 3] = '/'; + ent->base[pfxlen] = ent->base[entlen-1] = 0; + + /* Detect cases where alternate disappeared */ + if (stat(ent->base, &st) || !S_ISDIR(st.st_mode)) { + error("object directory %s does not exist; " + "check .git/objects/info/alternates.", + ent->base); + free(ent); + return -1; + } + + /* Prevent the common mistake of listing the same + * thing twice, or object directory itself. + */ + for (alt = alt_odb_list; alt; alt = alt->next) { + if (!memcmp(ent->base, alt->base, pfxlen)) { + free(ent); + return -1; + } + } + if (!memcmp(ent->base, objdir, pfxlen)) { + free(ent); + return -1; + } + + /* add the alternate entry */ + *alt_odb_tail = ent; + alt_odb_tail = &(ent->next); + ent->next = NULL; + + /* recursively add alternates */ + read_info_alternates(ent->base, depth + 1); + + ent->base[pfxlen] = '/'; + + return 0; +} + +static void link_alt_odb_entries(const char *alt, const char *ep, int sep, + const char *relative_base, int depth) +{ + const char *cp, *last; + + if (depth > 5) { + error("%s: ignoring alternate object stores, nesting too deep.", + relative_base); + return; + } + last = alt; while (last < ep) { cp = last; @@ -265,35 +323,16 @@ static void link_alt_odb_entries(const char *alt, const char *ep, int sep, last = cp + 1; continue; } - for ( ; cp < ep && *cp != sep; cp++) - ; + while (cp < ep && *cp != sep) + cp++; if (last != cp) { - /* 43 = 40-byte + 2 '/' + terminating NUL */ - int pfxlen = cp - last; - int entlen = pfxlen + 43; - - if (*last != '/' && relative_base) { - /* Relative alt-odb */ - if (base_len < 0) - base_len = strlen(relative_base) + 1; - entlen += base_len; - pfxlen += base_len; - } - ent = xmalloc(sizeof(*ent) + entlen); - *alt_odb_tail = ent; - alt_odb_tail = &(ent->next); - ent->next = NULL; - if (*last != '/' && relative_base) { - memcpy(ent->base, relative_base, base_len - 1); - ent->base[base_len - 1] = '/'; - memcpy(ent->base + base_len, - last, cp - last); + if ((*last != '/') && depth) { + error("%s: ignoring relative alternate object store %s", + relative_base, last); + } else { + link_alt_odb_entry(last, cp - last, + relative_base, depth); } - else - memcpy(ent->base, last, pfxlen); - ent->name = ent->base + pfxlen + 1; - ent->base[pfxlen] = ent->base[pfxlen + 3] = '/'; - ent->base[entlen-1] = 0; } while (cp < ep && *cp == sep) cp++; @@ -301,23 +340,14 @@ static void link_alt_odb_entries(const char *alt, const char *ep, int sep, } } -void prepare_alt_odb(void) +static void read_info_alternates(const char * relative_base, int depth) { - char path[PATH_MAX]; char *map; - int fd; struct stat st; - char *alt; - - alt = getenv(ALTERNATE_DB_ENVIRONMENT); - if (!alt) alt = ""; - - if (alt_odb_tail) - return; - alt_odb_tail = &alt_odb_list; - link_alt_odb_entries(alt, alt + strlen(alt), ':', NULL); + char path[PATH_MAX]; + int fd; - sprintf(path, "%s/info/alternates", get_object_directory()); + sprintf(path, "%s/info/alternates", relative_base); fd = open(path, O_RDONLY); if (fd < 0) return; @@ -330,11 +360,26 @@ void prepare_alt_odb(void) if (map == MAP_FAILED) return; - link_alt_odb_entries(map, map + st.st_size, '\n', - get_object_directory()); + link_alt_odb_entries(map, map + st.st_size, '\n', relative_base, depth); + munmap(map, st.st_size); } +void prepare_alt_odb(void) +{ + char *alt; + + alt = getenv(ALTERNATE_DB_ENVIRONMENT); + if (!alt) alt = ""; + + if (alt_odb_tail) + return; + alt_odb_tail = &alt_odb_list; + link_alt_odb_entries(alt, alt + strlen(alt), ':', NULL, 0); + + read_info_alternates(get_object_directory(), 0); +} + static char *find_sha1_file(const unsigned char *sha1, struct stat *st) { char *name = sha1_file_name(sha1); @@ -475,12 +520,13 @@ int use_packed_git(struct packed_git *p) return 0; } -struct packed_git *add_packed_git(char *path, int path_len) +struct packed_git *add_packed_git(char *path, int path_len, int local) { struct stat st; struct packed_git *p; unsigned long idx_size; void *idx_map; + unsigned char sha1[20]; if (check_packed_git_idx(path, &idx_size, &idx_map)) return NULL; @@ -503,6 +549,9 @@ struct packed_git *add_packed_git(char *path, int path_len) p->pack_base = NULL; p->pack_last_used = 0; 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); return p; } @@ -543,7 +592,7 @@ void install_packed_git(struct packed_git *pack) packed_git = pack; } -static void prepare_packed_git_one(char *objdir) +static void prepare_packed_git_one(char *objdir, int local) { char path[PATH_MAX]; int len; @@ -553,8 +602,12 @@ static void prepare_packed_git_one(char *objdir) sprintf(path, "%s/pack", objdir); len = strlen(path); dir = opendir(path); - if (!dir) + if (!dir) { + if (errno != ENOENT) + error("unable to open object pack directory: %s: %s", + path, strerror(errno)); return; + } path[len++] = '/'; while ((de = readdir(dir)) != NULL) { int namelen = strlen(de->d_name); @@ -565,7 +618,7 @@ static void prepare_packed_git_one(char *objdir) /* we have .idx. Is it a file we can map? */ strcpy(path + len, de->d_name); - p = add_packed_git(path, len + namelen); + p = add_packed_git(path, len + namelen, local); if (!p) continue; p->next = packed_git; @@ -581,11 +634,12 @@ void prepare_packed_git(void) if (run_once) return; - prepare_packed_git_one(get_object_directory()); + prepare_packed_git_one(get_object_directory(), 1); prepare_alt_odb(); for (alt = alt_odb_list; alt; alt = alt->next) { - alt->name[0] = 0; - prepare_packed_git_one(alt->base); + alt->name[-1] = 0; + prepare_packed_git_one(alt->base, 0); + alt->name[-1] = '/'; } run_once = 1; } @@ -790,10 +844,12 @@ static int packed_delta_info(unsigned char *base_sha1, * the result size. */ data = delta_head; - get_delta_hdr_size(&data); /* ignore base size */ + + /* ignore base size */ + get_delta_hdr_size(&data, delta_head+sizeof(delta_head)); /* Read the result size */ - result_size = get_delta_hdr_size(&data); + result_size = get_delta_hdr_size(&data, delta_head+sizeof(delta_head)); *sizep = result_size; } return 0; @@ -827,25 +883,46 @@ static unsigned long unpack_object_header(struct packed_git *p, unsigned long of return offset; } +int check_reuse_pack_delta(struct packed_git *p, unsigned long offset, + unsigned char *base, unsigned long *sizep, + enum object_type *kindp) +{ + unsigned long ptr; + int status = -1; + + use_packed_git(p); + ptr = offset; + ptr = unpack_object_header(p, ptr, kindp, sizep); + if (*kindp != OBJ_DELTA) + goto done; + memcpy(base, p->pack_base + ptr, 20); + status = 0; + done: + unuse_packed_git(p); + return status; +} + void packed_object_info_detail(struct pack_entry *e, char *type, unsigned long *size, unsigned long *store_size, - int *delta_chain_length, + unsigned int *delta_chain_length, unsigned char *base_sha1) { struct packed_git *p = e->p; - unsigned long offset, left; + unsigned long offset; unsigned char *pack; enum object_type kind; offset = unpack_object_header(p, e->offset, &kind, size); pack = p->pack_base + offset; - left = p->pack_size - offset; if (kind != OBJ_DELTA) *delta_chain_length = 0; else { - int chain_length = 0; + unsigned int chain_length = 0; + if (p->pack_size <= offset + 20) + die("pack file %s records an incomplete delta base", + p->pack_name); memcpy(base_sha1, pack, 20); do { struct pack_entry base_ent; @@ -861,16 +938,16 @@ void packed_object_info_detail(struct pack_entry *e, } switch (kind) { case OBJ_COMMIT: - strcpy(type, "commit"); + strcpy(type, commit_type); break; case OBJ_TREE: - strcpy(type, "tree"); + strcpy(type, tree_type); break; case OBJ_BLOB: - strcpy(type, "blob"); + strcpy(type, blob_type); break; case OBJ_TAG: - strcpy(type, "tag"); + strcpy(type, tag_type); break; default: die("corrupted pack file %s containing object of kind %d", @@ -901,16 +978,16 @@ static int packed_object_info(struct pack_entry *entry, unuse_packed_git(p); return retval; case OBJ_COMMIT: - strcpy(type, "commit"); + strcpy(type, commit_type); break; case OBJ_TREE: - strcpy(type, "tree"); + strcpy(type, tree_type); break; case OBJ_BLOB: - strcpy(type, "blob"); + strcpy(type, blob_type); break; case OBJ_TAG: - strcpy(type, "tag"); + strcpy(type, tag_type); break; default: die("corrupted pack file %s containing object of kind %d", @@ -940,6 +1017,16 @@ static void *unpack_delta_entry(unsigned char *base_sha1, if (left < 20) die("truncated pack file"); + + /* The base entry _must_ be in the same pack */ + if (!find_pack_entry_one(base_sha1, &base_ent, p)) + die("failed to find delta-pack base object %s", + sha1_to_hex(base_sha1)); + base = unpack_entry_gently(&base_ent, type, &base_size); + if (!base) + 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); @@ -957,14 +1044,6 @@ static void *unpack_delta_entry(unsigned char *base_sha1, if ((st != Z_STREAM_END) || stream.total_out != delta_size) die("delta data unpack failed"); - /* The base entry _must_ be in the same pack */ - if (!find_pack_entry_one(base_sha1, &base_ent, p)) - die("failed to find delta-pack base object %s", - sha1_to_hex(base_sha1)); - base = unpack_entry_gently(&base_ent, type, &base_size); - if (!base) - die("failed to read delta-pack base object %s", - sha1_to_hex(base_sha1)); result = patch_delta(base, base_size, delta_data, delta_size, &result_size); @@ -1036,16 +1115,16 @@ void *unpack_entry_gently(struct pack_entry *entry, retval = unpack_delta_entry(pack, size, left, type, sizep, p); return retval; case OBJ_COMMIT: - strcpy(type, "commit"); + strcpy(type, commit_type); break; case OBJ_TREE: - strcpy(type, "tree"); + strcpy(type, tree_type); break; case OBJ_BLOB: - strcpy(type, "blob"); + strcpy(type, blob_type); break; case OBJ_TAG: - strcpy(type, "tag"); + strcpy(type, tag_type); break; default: return NULL; @@ -1083,7 +1162,7 @@ int find_pack_entry_one(const unsigned char *sha1, int mi = (lo + hi) / 2; int cmp = memcmp(index + 24 * mi + 4, sha1, 20); if (!cmp) { - e->offset = ntohl(*((int*)(index + 24 * mi))); + e->offset = ntohl(*((uint32_t *)(index + 24 * mi))); memcpy(e->sha1, sha1, 20); e->p = p; return 1; @@ -1206,9 +1285,9 @@ void *read_object_with_reference(const unsigned char *sha1, return buffer; } /* Handle references */ - else if (!strcmp(type, "commit")) + else if (!strcmp(type, commit_type)) ref_type = "tree "; - else if (!strcmp(type, "tag")) + else if (!strcmp(type, tag_type)) ref_type = "object "; else { free(buffer); @@ -1275,6 +1354,8 @@ static int link_temp_to_file(const char *tmpfile, char *filename) if (dir) { *dir = 0; mkdir(filename, 0777); + if (adjust_shared_perm(filename)) + return -2; *dir = '/'; if (!link(tmpfile, filename)) return 0; @@ -1287,26 +1368,30 @@ static int link_temp_to_file(const char *tmpfile, char *filename) /* * Move the just written object into its final resting place */ -static int move_temp_to_file(const char *tmpfile, char *filename) +int move_temp_to_file(const char *tmpfile, char *filename) { int ret = link_temp_to_file(tmpfile, filename); - if (ret) { - /* - * 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)) + + /* + * 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. + * + * The same holds for FAT formatted media. + * + * When this succeeds, we just return 0. We have nothing + * left to unlink. + */ + if (ret && ret != EEXIST) { + if (!rename(tmpfile, filename)) return 0; + ret = errno; } unlink(tmpfile); if (ret) { if (ret != EEXIST) { - fprintf(stderr, "unable to write sha1 filename %s: %s", filename, strerror(ret)); + fprintf(stderr, "unable to write sha1 filename %s: %s\n", filename, strerror(ret)); return -1; } /* FIXME!!! Collision check here ? */ @@ -1345,7 +1430,7 @@ int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned cha } if (errno != ENOENT) { - fprintf(stderr, "sha1 file %s: %s", filename, strerror(errno)); + fprintf(stderr, "sha1 file %s: %s\n", filename, strerror(errno)); return -1; } @@ -1353,7 +1438,7 @@ int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned cha fd = mkstemp(tmpfile); if (fd < 0) { - fprintf(stderr, "unable to create temporary sha1 filename %s: %s", tmpfile, strerror(errno)); + fprintf(stderr, "unable to create temporary sha1 filename %s: %s\n", tmpfile, strerror(errno)); return -1; } @@ -1442,7 +1527,7 @@ int write_sha1_to_fd(int fd, const unsigned char *sha1) size = write(fd, buf + posn, objsize - posn); if (size <= 0) { if (!size) { - fprintf(stderr, "write closed"); + fprintf(stderr, "write closed\n"); } else { perror("write "); } @@ -1474,7 +1559,8 @@ int write_sha1_from_fd(const unsigned char *sha1, int fd, char *buffer, local = mkstemp(tmpfile); if (local < 0) - return error("Couldn't open %s for %s\n", tmpfile, sha1_to_hex(sha1)); + return error("Couldn't open %s for %s", + tmpfile, sha1_to_hex(sha1)); memset(&stream, 0, sizeof(stream)); @@ -1522,7 +1608,7 @@ int write_sha1_from_fd(const unsigned char *sha1, int fd, char *buffer, } if (memcmp(sha1, real_sha1, 20)) { unlink(tmpfile); - return error("File %s has bad hash\n", sha1_to_hex(sha1)); + return error("File %s has bad hash", sha1_to_hex(sha1)); } return move_temp_to_file(tmpfile, sha1_file_name(sha1)); @@ -1560,6 +1646,40 @@ 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) +{ + unsigned long size = 4096; + char *buf = malloc(size); + int iret, ret; + unsigned long off = 0; + unsigned char hdr[50]; + int hdrlen; + do { + iret = read(fd, buf + off, size - off); + if (iret > 0) { + off += iret; + if (off == size) { + size *= 2; + buf = realloc(buf, size); + } + } + } while (iret > 0); + if (iret < 0) { + free(buf); + return -1; + } + if (!type) + type = blob_type; + if (write_object) + ret = write_sha1_file(buf, off, type, sha1); + else { + write_sha1_file_prepare(buf, off, type, sha1, hdr, &hdrlen); + ret = 0; + } + free(buf); + return ret; +} + int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, const char *type) { unsigned long size = st->st_size; @@ -1576,7 +1696,7 @@ int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, con return -1; if (!type) - type = "blob"; + type = blob_type; if (write_object) ret = write_sha1_file(buf, size, type, sha1); else { @@ -1614,9 +1734,9 @@ int index_path(unsigned char *sha1, const char *path, struct stat *st, int write if (!write_object) { unsigned char hdr[50]; int hdrlen; - write_sha1_file_prepare(target, st->st_size, "blob", + write_sha1_file_prepare(target, st->st_size, blob_type, sha1, hdr, &hdrlen); - } else if (write_sha1_file(target, st->st_size, "blob", sha1)) + } else if (write_sha1_file(target, st->st_size, blob_type, sha1)) return error("%s: failed to insert into database", path); free(target);