X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=sha1_file.c;h=64cf2454182dbf4cbf1a2ee70dea4823cf8cfaa0;hb=abb7c7b31c0896bd838bbb6437b310db5a42227a;hp=f0590049098b5371bc72855acc5736f9d5f61e2b;hpb=d06b689a933f6d2130f8afdf1ac0ddb83eeb59ab;p=git.git diff --git a/sha1_file.c b/sha1_file.c index f0590049..64cf2454 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -6,8 +6,6 @@ * This handles basic git sha1 object files - packing, unpacking, * creation etc. */ -#include -#include #include "cache.h" #include "delta.h" #include "pack.h" @@ -48,20 +46,57 @@ 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; + 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; @@ -79,6 +114,8 @@ char * sha1_to_hex(const unsigned char *sha1) *buf++ = hex[val >> 4]; *buf++ = hex[val & 0xf]; } + *buf = '\0'; + return buffer; } @@ -182,8 +219,8 @@ 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 @@ -195,6 +232,7 @@ static void link_alt_odb_entries(const char *alt, const char *ep, int sep, { const char *cp, *last; struct alternate_object_database *ent; + const char *objdir = get_object_directory(); int base_len = -1; last = alt; @@ -209,6 +247,7 @@ static void link_alt_odb_entries(const char *alt, const char *ep, int sep, for ( ; cp < ep && *cp != sep; cp++) ; if (last != cp) { + struct alternate_object_database *alt; /* 43 = 40-byte + 2 '/' + terminating NUL */ int pfxlen = cp - last; int entlen = pfxlen + 43; @@ -221,9 +260,7 @@ static void link_alt_odb_entries(const char *alt, const char *ep, int sep, 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] = '/'; @@ -235,6 +272,22 @@ static void link_alt_odb_entries(const char *alt, const char *ep, int sep, ent->name = ent->base + pfxlen + 1; ent->base[pfxlen] = ent->base[pfxlen + 3] = '/'; ent->base[entlen-1] = 0; + + /* 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)) + goto bad; + if (!memcmp(ent->base, objdir, pfxlen)) { + bad: + free(ent); + } + else { + *alt_odb_tail = ent; + alt_odb_tail = &(ent->next); + ent->next = NULL; + } } while (cp < ep && *cp == sep) cp++; @@ -416,12 +469,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; @@ -444,6 +498,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; } @@ -484,7 +541,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; @@ -506,7 +563,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; @@ -522,11 +579,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; } @@ -772,7 +830,7 @@ 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; @@ -786,7 +844,7 @@ void packed_object_info_detail(struct pack_entry *e, if (kind != OBJ_DELTA) *delta_chain_length = 0; else { - int chain_length = 0; + unsigned int chain_length = 0; memcpy(base_sha1, pack, 20); do { struct pack_entry base_ent; @@ -1216,6 +1274,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; @@ -1231,23 +1291,27 @@ static int link_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 ? */ @@ -1286,7 +1350,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; } @@ -1294,7 +1358,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; } @@ -1383,7 +1447,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 "); } @@ -1501,6 +1565,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"; + 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;