diff options
Diffstat (limited to 'sha1_file.c')
-rw-r--r-- | sha1_file.c | 502 |
1 files changed, 216 insertions, 286 deletions
diff --git a/sha1_file.c b/sha1_file.c index 445a871db3..d1c406081e 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -15,6 +15,7 @@ #include "tree.h" #include "refs.h" #include "pack-revindex.h" +#include "sha1-lookup.h" #ifndef O_NOATIME #if defined(__linux__) && (defined(__i386__) || defined(__PPC__)) @@ -34,8 +35,6 @@ static size_t sz_fmt(size_t s) { return s; } const unsigned char null_sha1[20]; -static unsigned int sha1_file_open_flag = O_NOATIME; - const signed char hexval_table[256] = { -1, -1, -1, -1, -1, -1, -1, -1, /* 00-07 */ -1, -1, -1, -1, -1, -1, -1, -1, /* 08-0f */ @@ -117,7 +116,16 @@ int safe_create_leading_directories(char *path) return 0; } -char * sha1_to_hex(const unsigned char *sha1) +int safe_create_leading_directories_const(const char *path) +{ + /* path points to cache entries, so xstrdup before messing with it */ + char *buf = xstrdup(path); + int result = safe_create_leading_directories(buf); + free(buf); + return result; +} + +char *sha1_to_hex(const unsigned char *sha1) { static int bufno; static char hexbuffer[4][50]; @@ -175,21 +183,23 @@ char *sha1_file_name(const unsigned char *sha1) return base; } -char *sha1_pack_name(const unsigned char *sha1) +static char *sha1_get_pack_name(const unsigned char *sha1, + char **name, char **base, const char *which) { static const char hex[] = "0123456789abcdef"; - static char *name, *base, *buf; + char *buf; int i; - if (!base) { + if (!*base) { const char *sha1_file_directory = get_object_directory(); int len = strlen(sha1_file_directory); - base = xmalloc(len + 60); - sprintf(base, "%s/pack/pack-1234567890123456789012345678901234567890.pack", sha1_file_directory); - name = base + len + 11; + *base = xmalloc(len + 60); + sprintf(*base, "%s/pack/pack-1234567890123456789012345678901234567890.%s", + sha1_file_directory, which); + *name = *base + len + 11; } - buf = name; + buf = *name; for (i = 0; i < 20; i++) { unsigned int val = *sha1++; @@ -197,32 +207,21 @@ char *sha1_pack_name(const unsigned char *sha1) *buf++ = hex[val & 0xf]; } - return base; + return *base; } -char *sha1_pack_index_name(const unsigned char *sha1) +char *sha1_pack_name(const unsigned char *sha1) { - static const char hex[] = "0123456789abcdef"; - static char *name, *base, *buf; - int i; - - if (!base) { - const char *sha1_file_directory = get_object_directory(); - int len = strlen(sha1_file_directory); - base = xmalloc(len + 60); - sprintf(base, "%s/pack/pack-1234567890123456789012345678901234567890.idx", sha1_file_directory); - name = base + len + 11; - } + static char *name, *base; - buf = name; + return sha1_get_pack_name(sha1, &name, &base, "pack"); +} - for (i = 0; i < 20; i++) { - unsigned int val = *sha1++; - *buf++ = hex[val >> 4]; - *buf++ = hex[val & 0xf]; - } +char *sha1_pack_index_name(const unsigned char *sha1) +{ + static char *name, *base; - return base; + return sha1_get_pack_name(sha1, &name, &base, "idx"); } struct alternate_object_database *alt_odb_list; @@ -379,6 +378,18 @@ static void read_info_alternates(const char * relative_base, int depth) munmap(map, mapsz); } +void add_to_alternates_file(const char *reference) +{ + struct lock_file *lock = xcalloc(1, sizeof(struct lock_file)); + int fd = hold_lock_file_for_append(lock, git_path("objects/info/alternates"), 1); + char *alt = mkpath("%s/objects\n", reference); + write_or_die(fd, alt, strlen(alt)); + if (commit_lock_file(lock)) + die("could not close alternates file"); + if (alt_odb_tail) + link_alt_odb_entries(alt, alt + strlen(alt), '\n', NULL, 0); +} + void prepare_alt_odb(void) { const char *alt; @@ -395,21 +406,21 @@ void prepare_alt_odb(void) read_info_alternates(get_object_directory(), 0); } -static char *find_sha1_file(const unsigned char *sha1, struct stat *st) +static int has_loose_object(const unsigned char *sha1) { char *name = sha1_file_name(sha1); struct alternate_object_database *alt; - if (!stat(name, st)) - return name; + if (!access(name, F_OK)) + return 1; prepare_alt_odb(); for (alt = alt_odb_list; alt; alt = alt->next) { name = alt->name; fill_sha1_path(name, sha1); - if (!stat(alt->base, st)) - return alt->base; + if (!access(alt->base, F_OK)) + return 1; } - return NULL; + return 0; } static unsigned int pack_used_ctr; @@ -829,13 +840,7 @@ struct packed_git *add_packed_git(const char *path, int path_len, int local) struct packed_git *parse_pack_index(unsigned char *sha1) { - char *path = sha1_pack_index_name(sha1); - return parse_pack_index_file(sha1, path); -} - -struct packed_git *parse_pack_index_file(const unsigned char *sha1, - const char *idx_path) -{ + const char *idx_path = sha1_pack_index_name(sha1); const char *path = sha1_pack_name(sha1); struct packed_git *p = xmalloc(sizeof(*p) + strlen(path) + 2); @@ -993,38 +998,58 @@ int check_sha1_signature(const unsigned char *sha1, void *map, unsigned long siz return hashcmp(sha1, real_sha1) ? -1 : 0; } +static int git_open_noatime(const char *name) +{ + static int sha1_file_open_flag = O_NOATIME; + int fd = open(name, O_RDONLY | sha1_file_open_flag); + + /* Might the failure be due to O_NOATIME? */ + if (fd < 0 && errno != ENOENT && sha1_file_open_flag) { + fd = open(name, O_RDONLY); + if (fd >= 0) + sha1_file_open_flag = 0; + } + return fd; +} + +static int open_sha1_file(const unsigned char *sha1) +{ + int fd; + char *name = sha1_file_name(sha1); + struct alternate_object_database *alt; + + fd = git_open_noatime(name); + if (fd >= 0) + return fd; + + prepare_alt_odb(); + errno = ENOENT; + for (alt = alt_odb_list; alt; alt = alt->next) { + name = alt->name; + fill_sha1_path(name, sha1); + fd = git_open_noatime(alt->base); + if (fd >= 0) + return fd; + } + return -1; +} + static void *map_sha1_file(const unsigned char *sha1, unsigned long *size) { - struct stat st; void *map; int fd; - char *filename = find_sha1_file(sha1, &st); - if (!filename) { - return NULL; - } + fd = open_sha1_file(sha1); + map = NULL; + if (fd >= 0) { + struct stat st; - fd = open(filename, O_RDONLY | sha1_file_open_flag); - if (fd < 0) { - /* 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: - return NULL; + if (!fstat(fd, &st)) { + *size = xsize_t(st.st_size); + map = xmmap(NULL, *size, PROT_READ, MAP_PRIVATE, fd, 0); } - - /* If it failed once, it will probably fail again. - * Stop using O_NOATIME - */ - sha1_file_open_flag = 0; + close(fd); } - *size = xsize_t(st.st_size); - map = xmmap(NULL, *size, PROT_READ, MAP_PRIVATE, fd, 0); - close(fd); return map; } @@ -1675,7 +1700,12 @@ off_t find_pack_entry_one(const unsigned char *sha1, { const uint32_t *level1_ofs = p->index_data; const unsigned char *index = p->index_data; - unsigned hi, lo; + unsigned hi, lo, stride; + static int use_lookup = -1; + static int debug_lookup = -1; + + if (debug_lookup < 0) + debug_lookup = !!getenv("GIT_DEBUG_LOOKUP"); if (!index) { if (open_pack_index(p)) @@ -1690,11 +1720,34 @@ off_t find_pack_entry_one(const unsigned char *sha1, index += 4 * 256; hi = ntohl(level1_ofs[*sha1]); lo = ((*sha1 == 0x0) ? 0 : ntohl(level1_ofs[*sha1 - 1])); + if (p->index_version > 1) { + stride = 20; + } else { + stride = 24; + index += 4; + } + + if (debug_lookup) + printf("%02x%02x%02x... lo %u hi %u nr %u\n", + sha1[0], sha1[1], sha1[2], lo, hi, p->num_objects); + + if (use_lookup < 0) + use_lookup = !!getenv("GIT_USE_LOOKUP"); + if (use_lookup) { + int pos = sha1_entry_pos(index, stride, 0, + lo, hi, p->num_objects, sha1); + if (pos < 0) + return 0; + return nth_packed_object_offset(p, pos); + } do { unsigned mi = (lo + hi) / 2; - unsigned x = (p->index_version > 1) ? (mi * 20) : (mi * 24 + 4); - int cmp = hashcmp(index + x, sha1); + int cmp = hashcmp(index + mi * stride, sha1); + + if (debug_lookup) + printf("lo %u hi %u rg %u mi %u\n", + lo, hi, hi - lo, mi); if (!cmp) return nth_packed_object_offset(p, mi); if (cmp > 0) @@ -1987,48 +2040,11 @@ static void write_sha1_file_prepare(const void *buf, unsigned long len, } /* - * Link the tempfile to the final place, possibly creating the - * last directory level as you do so. - * - * Returns the errno on failure, 0 on success. - */ -static int link_temp_to_file(const char *tmpfile, const char *filename) -{ - int ret; - char *dir; - - if (!link(tmpfile, filename)) - return 0; - - /* - * Try to mkdir the last path component if that failed. - * - * Re-try the "link()" regardless of whether the mkdir - * succeeds, since a race might mean that somebody - * else succeeded. - */ - ret = errno; - dir = strrchr(filename, '/'); - if (dir) { - *dir = 0; - if (!mkdir(filename, 0777) && adjust_shared_perm(filename)) { - *dir = '/'; - return -2; - } - *dir = '/'; - if (!link(tmpfile, filename)) - return 0; - ret = errno; - } - return ret; -} - -/* * Move the just written object into its final resting place */ int move_temp_to_file(const char *tmpfile, const char *filename) { - int ret = link_temp_to_file(tmpfile, filename); + int ret = link(tmpfile, filename); /* * Coda hack - coda doesn't like cross-directory links, @@ -2073,43 +2089,67 @@ int hash_sha1_file(const void *buf, unsigned long len, const char *type, return 0; } -int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *returnsha1) +/* Finalize a file on disk, and close it. */ +static void close_sha1_file(int fd) { - int size, ret; - unsigned char *compressed; - z_stream stream; - unsigned char sha1[20]; - char *filename; - static char tmpfile[PATH_MAX]; - char hdr[32]; - int fd, hdrlen; + /* For safe-mode, we could fsync_or_die(fd, "sha1 file") here */ + fchmod(fd, 0444); + if (close(fd) != 0) + die("unable to write sha1 file"); +} - /* Normally if we have it in the pack then we do not bother writing - * it out into .git/objects/??/?{38} file. - */ - write_sha1_file_prepare(buf, len, type, sha1, hdr, &hdrlen); - filename = sha1_file_name(sha1); - if (returnsha1) - hashcpy(returnsha1, sha1); - if (has_sha1_file(sha1)) - return 0; - fd = open(filename, O_RDONLY); - if (fd >= 0) { - /* - * FIXME!!! We might do collision checking here, but we'd - * need to uncompress the old file and check it. Later. - */ - close(fd); +/* Size of directory component, including the ending '/' */ +static inline int directory_size(const char *filename) +{ + const char *s = strrchr(filename, '/'); + if (!s) return 0; + return s - filename + 1; +} + +/* + * This creates a temporary file in the same directory as the final + * 'filename' + * + * We want to avoid cross-directory filename renames, because those + * can have problems on various filesystems (FAT, NFS, Coda). + */ +static int create_tmpfile(char *buffer, size_t bufsiz, const char *filename) +{ + int fd, dirlen = directory_size(filename); + + if (dirlen + 20 > bufsiz) { + errno = ENAMETOOLONG; + return -1; } + memcpy(buffer, filename, dirlen); + strcpy(buffer + dirlen, "tmp_obj_XXXXXX"); + fd = mkstemp(buffer); + if (fd < 0 && dirlen) { + /* Make sure the directory exists */ + memcpy(buffer, filename, dirlen); + buffer[dirlen-1] = 0; + if (mkdir(buffer, 0777) || adjust_shared_perm(buffer)) + return -1; - if (errno != ENOENT) { - return error("sha1 file %s: %s\n", filename, strerror(errno)); + /* Try again */ + strcpy(buffer + dirlen - 1, "/tmp_obj_XXXXXX"); + fd = mkstemp(buffer); } + return fd; +} - snprintf(tmpfile, sizeof(tmpfile), "%s/tmp_obj_XXXXXX", get_object_directory()); +static int write_loose_object(const unsigned char *sha1, char *hdr, int hdrlen, + void *buf, unsigned long len, time_t mtime) +{ + int fd, size, ret; + unsigned char *compressed; + z_stream stream; + char *filename; + static char tmpfile[PATH_MAX]; - fd = mkstemp(tmpfile); + filename = sha1_file_name(sha1); + fd = create_tmpfile(tmpfile, sizeof(tmpfile), filename); if (fd < 0) { if (errno == EPERM) return error("insufficient permission for adding an object to repository database %s\n", get_object_directory()); @@ -2148,156 +2188,53 @@ int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned cha if (write_buffer(fd, compressed, size) < 0) die("unable to write sha1 file"); - fchmod(fd, 0444); - if (close(fd)) - die("unable to write sha1 file"); + close_sha1_file(fd); free(compressed); + if (mtime) { + struct utimbuf utb; + utb.actime = mtime; + utb.modtime = mtime; + if (utime(tmpfile, &utb) < 0) + warning("failed utime() on %s: %s", + tmpfile, strerror(errno)); + } + return move_temp_to_file(tmpfile, filename); } -/* - * We need to unpack and recompress the object for writing - * it out to a different file. - */ -static void *repack_object(const unsigned char *sha1, unsigned long *objsize) +int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *returnsha1) { - size_t size; - z_stream stream; - unsigned char *unpacked; - unsigned long len; - enum object_type type; + unsigned char sha1[20]; char hdr[32]; int hdrlen; - void *buf; - - /* need to unpack and recompress it by itself */ - unpacked = read_packed_sha1(sha1, &type, &len); - if (!unpacked) - error("cannot read sha1_file for %s", sha1_to_hex(sha1)); - - hdrlen = sprintf(hdr, "%s %lu", typename(type), len) + 1; - - /* Set it up */ - memset(&stream, 0, sizeof(stream)); - deflateInit(&stream, zlib_compression_level); - size = deflateBound(&stream, len + hdrlen); - buf = xmalloc(size); - - /* Compress it */ - stream.next_out = buf; - stream.avail_out = size; - - /* First header.. */ - stream.next_in = (void *)hdr; - stream.avail_in = hdrlen; - while (deflate(&stream, 0) == Z_OK) - /* nothing */; - - /* Then the data itself.. */ - stream.next_in = unpacked; - stream.avail_in = len; - while (deflate(&stream, Z_FINISH) == Z_OK) - /* nothing */; - deflateEnd(&stream); - free(unpacked); - - *objsize = stream.total_out; - return buf; -} - -int write_sha1_to_fd(int fd, const unsigned char *sha1) -{ - int retval; - unsigned long objsize; - void *buf = map_sha1_file(sha1, &objsize); - if (buf) { - retval = write_buffer(fd, buf, objsize); - munmap(buf, objsize); - return retval; - } - - buf = repack_object(sha1, &objsize); - retval = write_buffer(fd, buf, objsize); - free(buf); - return retval; + /* Normally if we have it in the pack then we do not bother writing + * it out into .git/objects/??/?{38} file. + */ + write_sha1_file_prepare(buf, len, type, sha1, hdr, &hdrlen); + if (returnsha1) + hashcpy(returnsha1, sha1); + if (has_sha1_file(sha1)) + return 0; + return write_loose_object(sha1, hdr, hdrlen, buf, len, 0); } -int write_sha1_from_fd(const unsigned char *sha1, int fd, char *buffer, - size_t bufsize, size_t *bufposn) +int force_object_loose(const unsigned char *sha1, time_t mtime) { - char tmpfile[PATH_MAX]; - int local; - z_stream stream; - unsigned char real_sha1[20]; - unsigned char discard[4096]; - int ret; - SHA_CTX c; - - snprintf(tmpfile, sizeof(tmpfile), "%s/tmp_obj_XXXXXX", get_object_directory()); - - local = mkstemp(tmpfile); - if (local < 0) { - if (errno == EPERM) - return error("insufficient permission for adding an object to repository database %s\n", get_object_directory()); - else - return error("unable to create temporary sha1 filename %s: %s\n", tmpfile, strerror(errno)); - } - - memset(&stream, 0, sizeof(stream)); - - inflateInit(&stream); - - SHA1_Init(&c); - - do { - ssize_t size; - if (*bufposn) { - stream.avail_in = *bufposn; - stream.next_in = (unsigned char *) buffer; - 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); - if (write_buffer(local, buffer, *bufposn - stream.avail_in) < 0) - die("unable to write sha1 file"); - memmove(buffer, buffer + *bufposn - stream.avail_in, - stream.avail_in); - *bufposn = stream.avail_in; - if (ret != Z_OK) - break; - } - size = xread(fd, buffer + *bufposn, bufsize - *bufposn); - if (size <= 0) { - close(local); - unlink(tmpfile); - if (!size) - return error("Connection closed?"); - perror("Reading from connection"); - return -1; - } - *bufposn += size; - } while (1); - inflateEnd(&stream); - - fchmod(local, 0444); - if (close(local) != 0) - die("unable to write sha1 file"); - SHA1_Final(real_sha1, &c); - if (ret != Z_STREAM_END) { - unlink(tmpfile); - return error("File %s corrupted", sha1_to_hex(sha1)); - } - if (hashcmp(sha1, real_sha1)) { - unlink(tmpfile); - return error("File %s has bad hash", sha1_to_hex(sha1)); - } + void *buf; + unsigned long len; + enum object_type type; + char hdr[32]; + int hdrlen; - return move_temp_to_file(tmpfile, sha1_file_name(sha1)); + if (has_loose_object(sha1)) + return 0; + buf = read_packed_sha1(sha1, &type, &len); + if (!buf) + return error("cannot read sha1_file for %s", sha1_to_hex(sha1)); + hdrlen = sprintf(hdr, "%s %lu", typename(type), len) + 1; + return write_loose_object(sha1, hdr, hdrlen, buf, len, mtime); } int has_pack_index(const unsigned char *sha1) @@ -2324,12 +2261,11 @@ int has_sha1_pack(const unsigned char *sha1, const char **ignore_packed) int has_sha1_file(const unsigned char *sha1) { - struct stat st; struct pack_entry e; if (find_pack_entry(sha1, &e, NULL)) return 1; - return find_sha1_file(sha1, &st) ? 1 : 0; + return has_loose_object(sha1); } int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object) @@ -2437,16 +2373,10 @@ int index_path(unsigned char *sha1, const char *path, struct stat *st, int write int read_pack_header(int fd, struct pack_header *header) { - char *c = (char*)header; - ssize_t remaining = sizeof(struct pack_header); - do { - ssize_t r = xread(fd, c, remaining); - if (r <= 0) - /* "eof before pack header was fully read" */ - return PH_ERROR_EOF; - remaining -= r; - c += r; - } while (remaining > 0); + if (read_in_full(fd, header, sizeof(*header)) < sizeof(*header)) + /* "eof before pack header was fully read" */ + return PH_ERROR_EOF; + if (header->hdr_signature != htonl(PACK_SIGNATURE)) /* "protocol error (pack signature mismatch detected)" */ return PH_ERROR_PACK_SIGNATURE; |