Code

git-cvsserver: detect/diagnose write failure, etc.
[git.git] / index-pack.c
index 3c768fbc631387b59bbbae3423b65b6a311a702b..8403c36b63c041fe0c81276391a0bbea906e833d 100644 (file)
@@ -6,23 +6,23 @@
 #include "commit.h"
 #include "tag.h"
 #include "tree.h"
+#include "progress.h"
 
 static const char index_pack_usage[] =
 "git-index-pack [-v] [-o <index-file>] [{ ---keep | --keep=<msg> }] { <pack-file> | --stdin [--fix-thin] [<pack-file>] }";
 
 struct object_entry
 {
-       unsigned long offset;
+       struct pack_idx_entry idx;
        unsigned long size;
        unsigned int hdr_size;
        enum object_type type;
        enum object_type real_type;
-       unsigned char sha1[20];
 };
 
 union delta_base {
        unsigned char sha1[20];
-       unsigned long offset;
+       off_t offset;
 };
 
 /*
@@ -46,45 +46,14 @@ static int nr_resolved_deltas;
 static int from_stdin;
 static int verbose;
 
-static volatile sig_atomic_t progress_update;
-
-static void progress_interval(int signum)
-{
-       progress_update = 1;
-}
-
-static void setup_progress_signal(void)
-{
-       struct sigaction sa;
-       struct itimerval v;
-
-       memset(&sa, 0, sizeof(sa));
-       sa.sa_handler = progress_interval;
-       sigemptyset(&sa.sa_mask);
-       sa.sa_flags = SA_RESTART;
-       sigaction(SIGALRM, &sa, NULL);
-
-       v.it_interval.tv_sec = 1;
-       v.it_interval.tv_usec = 0;
-       v.it_value = v.it_interval;
-       setitimer(ITIMER_REAL, &v, NULL);
-
-}
-
-static unsigned display_progress(unsigned n, unsigned total, unsigned last_pc)
-{
-       unsigned percent = n * 100 / total;
-       if (percent != last_pc || progress_update) {
-               fprintf(stderr, "%4u%% (%u/%u) done\r", percent, n, total);
-               progress_update = 0;
-       }
-       return percent;
-}
+static struct progress progress;
 
 /* We always read in 4kB chunks. */
 static unsigned char input_buffer[4096];
-static unsigned long input_offset, input_len, consumed_bytes;
+static unsigned int input_offset, input_len;
+static off_t consumed_bytes;
 static SHA_CTX input_ctx;
+static uint32_t input_crc32;
 static int input_fd, output_fd, pack_fd;
 
 /* Discard current buffer used content. */
@@ -111,7 +80,7 @@ static void *fill(int min)
                die("cannot fill %d bytes", min);
        flush();
        do {
-               int ret = xread(input_fd, input_buffer + input_len,
+               ssize_t ret = xread(input_fd, input_buffer + input_len,
                                sizeof(input_buffer) - input_len);
                if (ret <= 0) {
                        if (!ret)
@@ -127,8 +96,13 @@ static void use(int bytes)
 {
        if (bytes > input_len)
                die("used more bytes than were available");
+       input_crc32 = crc32(input_crc32, input_buffer + input_offset, bytes);
        input_len -= bytes;
        input_offset += bytes;
+
+       /* make sure off_t is sufficiently large not to wrap */
+       if (consumed_bytes > consumed_bytes + bytes)
+               die("pack too large for current definition of off_t");
        consumed_bytes += bytes;
 }
 
@@ -216,10 +190,13 @@ static void *unpack_entry_data(unsigned long offset, unsigned long size)
 static void *unpack_raw_entry(struct object_entry *obj, union delta_base *delta_base)
 {
        unsigned char *p, c;
-       unsigned long size, base_offset;
+       unsigned long size;
+       off_t base_offset;
        unsigned shift;
+       void *data;
 
-       obj->offset = consumed_bytes;
+       obj->idx.offset = consumed_bytes;
+       input_crc32 = crc32(0, Z_NULL, 0);
 
        p = fill(1);
        c = *p;
@@ -249,16 +226,16 @@ static void *unpack_raw_entry(struct object_entry *obj, union delta_base *delta_
                base_offset = c & 127;
                while (c & 128) {
                        base_offset += 1;
-                       if (!base_offset || base_offset & ~(~0UL >> 7))
-                               bad_object(obj->offset, "offset value overflow for delta base object");
+                       if (!base_offset || MSB(base_offset, 7))
+                               bad_object(obj->idx.offset, "offset value overflow for delta base object");
                        p = fill(1);
                        c = *p;
                        use(1);
                        base_offset = (base_offset << 7) + (c & 127);
                }
-               delta_base->offset = obj->offset - base_offset;
-               if (delta_base->offset >= obj->offset)
-                       bad_object(obj->offset, "delta base offset is out of bound");
+               delta_base->offset = obj->idx.offset - base_offset;
+               if (delta_base->offset >= obj->idx.offset)
+                       bad_object(obj->idx.offset, "delta base offset is out of bound");
                break;
        case OBJ_COMMIT:
        case OBJ_TREE:
@@ -266,17 +243,19 @@ static void *unpack_raw_entry(struct object_entry *obj, union delta_base *delta_
        case OBJ_TAG:
                break;
        default:
-               bad_object(obj->offset, "unknown object type %d", obj->type);
+               bad_object(obj->idx.offset, "unknown object type %d", obj->type);
        }
-       obj->hdr_size = consumed_bytes - obj->offset;
+       obj->hdr_size = consumed_bytes - obj->idx.offset;
 
-       return unpack_entry_data(obj->offset, obj->size);
+       data = unpack_entry_data(obj->idx.offset, obj->size);
+       obj->idx.crc32 = input_crc32;
+       return data;
 }
 
 static void *get_data_from_pack(struct object_entry *obj)
 {
-       unsigned long from = obj[0].offset + obj[0].hdr_size;
-       unsigned long len = obj[1].offset - from;
+       unsigned long from = obj[0].idx.offset + obj[0].hdr_size;
+       unsigned long len = obj[1].idx.offset - from;
        unsigned long rdy = 0;
        unsigned char *src, *data;
        z_stream stream;
@@ -379,11 +358,11 @@ static void resolve_delta(struct object_entry *delta_obj, void *base_data,
                             &result_size);
        free(delta_data);
        if (!result)
-               bad_object(delta_obj->offset, "failed to apply delta");
-       sha1_object(result, result_size, type, delta_obj->sha1);
+               bad_object(delta_obj->idx.offset, "failed to apply delta");
+       sha1_object(result, result_size, type, delta_obj->idx.sha1);
        nr_resolved_deltas++;
 
-       hashcpy(delta_base.sha1, delta_obj->sha1);
+       hashcpy(delta_base.sha1, delta_obj->idx.sha1);
        if (!find_delta_children(&delta_base, &first, &last)) {
                for (j = first; j <= last; j++) {
                        struct object_entry *child = objects + deltas[j].obj_no;
@@ -393,7 +372,7 @@ static void resolve_delta(struct object_entry *delta_obj, void *base_data,
        }
 
        memset(&delta_base, 0, sizeof(delta_base));
-       delta_base.offset = delta_obj->offset;
+       delta_base.offset = delta_obj->idx.offset;
        if (!find_delta_children(&delta_base, &first, &last)) {
                for (j = first; j <= last; j++) {
                        struct object_entry *child = objects + deltas[j].obj_no;
@@ -415,7 +394,7 @@ static int compare_delta_entry(const void *a, const void *b)
 /* Parse all objects and return the pack content SHA1 hash */
 static void parse_pack_objects(unsigned char *sha1)
 {
-       int i, percent = -1;
+       int i;
        struct delta_entry *delta = deltas;
        void *data;
        struct stat st;
@@ -427,7 +406,7 @@ static void parse_pack_objects(unsigned char *sha1)
         * - remember base (SHA1 or offset) for all deltas.
         */
        if (verbose)
-               fprintf(stderr, "Indexing %d objects.\n", nr_objects);
+               start_progress(&progress, "Indexing %u objects...", "", nr_objects);
        for (i = 0; i < nr_objects; i++) {
                struct object_entry *obj = &objects[i];
                data = unpack_raw_entry(obj, &delta->base);
@@ -437,14 +416,14 @@ static void parse_pack_objects(unsigned char *sha1)
                        delta->obj_no = i;
                        delta++;
                } else
-                       sha1_object(data, obj->size, obj->type, obj->sha1);
+                       sha1_object(data, obj->size, obj->type, obj->idx.sha1);
                free(data);
                if (verbose)
-                       percent = display_progress(i+1, nr_objects, percent);
+                       display_progress(&progress, i+1);
        }
-       objects[i].offset = consumed_bytes;
+       objects[i].idx.offset = consumed_bytes;
        if (verbose)
-               fputc('\n', stderr);
+               stop_progress(&progress);
 
        /* Check pack integrity */
        flush();
@@ -476,7 +455,7 @@ static void parse_pack_objects(unsigned char *sha1)
         *   for some more deltas.
         */
        if (verbose)
-               fprintf(stderr, "Resolving %d deltas.\n", nr_deltas);
+               start_progress(&progress, "Resolving %u deltas...", "", nr_deltas);
        for (i = 0; i < nr_objects; i++) {
                struct object_entry *obj = &objects[i];
                union delta_base base;
@@ -484,10 +463,10 @@ static void parse_pack_objects(unsigned char *sha1)
 
                if (obj->type == OBJ_REF_DELTA || obj->type == OBJ_OFS_DELTA)
                        continue;
-               hashcpy(base.sha1, obj->sha1);
+               hashcpy(base.sha1, obj->idx.sha1);
                ref = !find_delta_children(&base, &ref_first, &ref_last);
                memset(&base, 0, sizeof(base));
-               base.offset = obj->offset;
+               base.offset = obj->idx.offset;
                ofs = !find_delta_children(&base, &ofs_first, &ofs_last);
                if (!ref && !ofs)
                        continue;
@@ -508,14 +487,11 @@ static void parse_pack_objects(unsigned char *sha1)
                        }
                free(data);
                if (verbose)
-                       percent = display_progress(nr_resolved_deltas,
-                                                  nr_deltas, percent);
+                       display_progress(&progress, nr_resolved_deltas);
        }
-       if (verbose && nr_resolved_deltas == nr_deltas)
-               fputc('\n', stderr);
 }
 
-static int write_compressed(int fd, void *in, unsigned int size)
+static int write_compressed(int fd, void *in, unsigned int size, uint32_t *obj_crc)
 {
        z_stream stream;
        unsigned long maxsize;
@@ -536,6 +512,7 @@ static int write_compressed(int fd, void *in, unsigned int size)
 
        size = stream.total_out;
        write_or_die(fd, out, size);
+       *obj_crc = crc32(*obj_crc, out, size);
        free(out);
        return size;
 }
@@ -556,9 +533,11 @@ static void append_obj_to_pack(const unsigned char *sha1, void *buf,
        }
        header[n++] = c;
        write_or_die(output_fd, header, n);
-       obj[1].offset = obj[0].offset + n;
-       obj[1].offset += write_compressed(output_fd, buf, size);
-       hashcpy(obj->sha1, sha1);
+       obj[0].idx.crc32 = crc32(0, Z_NULL, 0);
+       obj[0].idx.crc32 = crc32(obj[0].idx.crc32, header, n);
+       obj[1].idx.offset = obj[0].idx.offset + n;
+       obj[1].idx.offset += write_compressed(output_fd, buf, size, &obj[0].idx.crc32);
+       hashcpy(obj->idx.sha1, sha1);
 }
 
 static int delta_pos_compare(const void *_a, const void *_b)
@@ -571,7 +550,7 @@ static int delta_pos_compare(const void *_a, const void *_b)
 static void fix_unresolved_deltas(int nr_unresolved)
 {
        struct delta_entry **sorted_by_pos;
-       int i, n = 0, percent = -1;
+       int i, n = 0;
 
        /*
         * Since many unresolved deltas may well be themselves base objects
@@ -616,131 +595,9 @@ static void fix_unresolved_deltas(int nr_unresolved)
                append_obj_to_pack(d->base.sha1, data, size, type);
                free(data);
                if (verbose)
-                       percent = display_progress(nr_resolved_deltas,
-                                                  nr_deltas, percent);
+                       display_progress(&progress, nr_resolved_deltas);
        }
        free(sorted_by_pos);
-       if (verbose)
-               fputc('\n', stderr);
-}
-
-static void readjust_pack_header_and_sha1(unsigned char *sha1)
-{
-       struct pack_header hdr;
-       SHA_CTX ctx;
-       int size;
-
-       /* Rewrite pack header with updated object number */
-       if (lseek(output_fd, 0, SEEK_SET) != 0)
-               die("cannot seek back: %s", strerror(errno));
-       if (read_in_full(output_fd, &hdr, sizeof(hdr)) != sizeof(hdr))
-               die("cannot read pack header back: %s", strerror(errno));
-       hdr.hdr_entries = htonl(nr_objects);
-       if (lseek(output_fd, 0, SEEK_SET) != 0)
-               die("cannot seek back: %s", strerror(errno));
-       write_or_die(output_fd, &hdr, sizeof(hdr));
-       if (lseek(output_fd, 0, SEEK_SET) != 0)
-               die("cannot seek back: %s", strerror(errno));
-
-       /* Recompute and store the new pack's SHA1 */
-       SHA1_Init(&ctx);
-       do {
-               unsigned char *buf[4096];
-               size = xread(output_fd, buf, sizeof(buf));
-               if (size < 0)
-                       die("cannot read pack data back: %s", strerror(errno));
-               SHA1_Update(&ctx, buf, size);
-       } while (size > 0);
-       SHA1_Final(sha1, &ctx);
-       write_or_die(output_fd, sha1, 20);
-}
-
-static int sha1_compare(const void *_a, const void *_b)
-{
-       struct object_entry *a = *(struct object_entry **)_a;
-       struct object_entry *b = *(struct object_entry **)_b;
-       return hashcmp(a->sha1, b->sha1);
-}
-
-/*
- * On entry *sha1 contains the pack content SHA1 hash, on exit it is
- * the SHA1 hash of sorted object names.
- */
-static const char *write_index_file(const char *index_name, unsigned char *sha1)
-{
-       struct sha1file *f;
-       struct object_entry **sorted_by_sha, **list, **last;
-       unsigned int array[256];
-       int i, fd;
-       SHA_CTX ctx;
-
-       if (nr_objects) {
-               sorted_by_sha =
-                       xcalloc(nr_objects, sizeof(struct object_entry *));
-               list = sorted_by_sha;
-               last = sorted_by_sha + nr_objects;
-               for (i = 0; i < nr_objects; ++i)
-                       sorted_by_sha[i] = &objects[i];
-               qsort(sorted_by_sha, nr_objects, sizeof(sorted_by_sha[0]),
-                     sha1_compare);
-
-       }
-       else
-               sorted_by_sha = list = last = NULL;
-
-       if (!index_name) {
-               static char tmpfile[PATH_MAX];
-               snprintf(tmpfile, sizeof(tmpfile),
-                        "%s/tmp_idx_XXXXXX", get_object_directory());
-               fd = mkstemp(tmpfile);
-               index_name = xstrdup(tmpfile);
-       } else {
-               unlink(index_name);
-               fd = open(index_name, O_CREAT|O_EXCL|O_WRONLY, 0600);
-       }
-       if (fd < 0)
-               die("unable to create %s: %s", index_name, strerror(errno));
-       f = sha1fd(fd, index_name);
-
-       /*
-        * Write the first-level table (the list is sorted,
-        * but we use a 256-entry lookup to be able to avoid
-        * having to do eight extra binary search iterations).
-        */
-       for (i = 0; i < 256; i++) {
-               struct object_entry **next = list;
-               while (next < last) {
-                       struct object_entry *obj = *next;
-                       if (obj->sha1[0] != i)
-                               break;
-                       next++;
-               }
-               array[i] = htonl(next - sorted_by_sha);
-               list = next;
-       }
-       sha1write(f, array, 256 * sizeof(int));
-
-       /* recompute the SHA1 hash of sorted object names.
-        * currently pack-objects does not do this, but that
-        * can be fixed.
-        */
-       SHA1_Init(&ctx);
-       /*
-        * Write the actual SHA1 entries..
-        */
-       list = sorted_by_sha;
-       for (i = 0; i < nr_objects; i++) {
-               struct object_entry *obj = *list++;
-               unsigned int offset = htonl(obj->offset);
-               sha1write(f, &offset, 4);
-               sha1write(f, obj->sha1, 20);
-               SHA1_Update(&ctx, obj->sha1, 20);
-       }
-       sha1write(f, sha1, 20);
-       sha1close(f, NULL, 1);
-       free(sorted_by_sha);
-       SHA1_Final(sha1, &ctx);
-       return index_name;
 }
 
 static void final(const char *final_pack_name, const char *curr_pack_name,
@@ -777,7 +634,8 @@ static void final(const char *final_pack_name, const char *curr_pack_name,
                                write_or_die(keep_fd, keep_msg, keep_msg_len);
                                write_or_die(keep_fd, "\n", 1);
                        }
-                       close(keep_fd);
+                       if (close(keep_fd) != 0)
+                               die("cannot write keep file");
                        report = "keep";
                }
        }
@@ -832,6 +690,7 @@ int main(int argc, char **argv)
        const char *curr_index, *index_name = NULL;
        const char *keep_name = NULL, *keep_msg = NULL;
        char *index_name_buf = NULL, *keep_name_buf = NULL;
+       struct pack_idx_entry **idx_objects;
        unsigned char sha1[20];
 
        for (i = 1; i < argc; i++) {
@@ -865,6 +724,15 @@ int main(int argc, char **argv)
                                if (index_name || (i+1) >= argc)
                                        usage(index_pack_usage);
                                index_name = argv[++i];
+                       } else if (!prefixcmp(arg, "--index-version=")) {
+                               char *c;
+                               pack_idx_default_version = strtoul(arg + 16, &c, 10);
+                               if (pack_idx_default_version > 2)
+                                       die("bad %s", arg);
+                               if (*c == ',')
+                                       pack_idx_off32_limit = strtoul(c+1, &c, 0);
+                               if (*c || pack_idx_off32_limit & 0x80000000)
+                                       die("bad %s", arg);
                        } else
                                usage(index_pack_usage);
                        continue;
@@ -904,10 +772,13 @@ int main(int argc, char **argv)
        parse_pack_header();
        objects = xmalloc((nr_objects + 1) * sizeof(struct object_entry));
        deltas = xmalloc(nr_objects * sizeof(struct delta_entry));
-       if (verbose)
-               setup_progress_signal();
        parse_pack_objects(sha1);
-       if (nr_deltas != nr_resolved_deltas) {
+       if (nr_deltas == nr_resolved_deltas) {
+               if (verbose)
+                       stop_progress(&progress);
+               /* Flush remaining pack final 20-byte SHA1. */
+               flush();
+       } else {
                if (fix_thin_pack) {
                        int nr_unresolved = nr_deltas - nr_resolved_deltas;
                        int nr_objects_initial = nr_objects;
@@ -917,20 +788,26 @@ int main(int argc, char **argv)
                                           (nr_objects + nr_unresolved + 1)
                                           * sizeof(*objects));
                        fix_unresolved_deltas(nr_unresolved);
-                       if (verbose)
+                       if (verbose) {
+                               stop_progress(&progress);
                                fprintf(stderr, "%d objects were added to complete this thin pack.\n",
                                        nr_objects - nr_objects_initial);
-                       readjust_pack_header_and_sha1(sha1);
+                       }
+                       fixup_pack_header_footer(output_fd, sha1,
+                               curr_pack, nr_objects);
                }
                if (nr_deltas != nr_resolved_deltas)
                        die("pack has %d unresolved deltas",
                            nr_deltas - nr_resolved_deltas);
-       } else {
-               /* Flush remaining pack final 20-byte SHA1. */
-               flush();
        }
        free(deltas);
-       curr_index = write_index_file(index_name, sha1);
+
+       idx_objects = xmalloc((nr_objects) * sizeof(struct pack_idx_entry *));
+       for (i = 0; i < nr_objects; i++)
+               idx_objects[i] = &objects[i].idx;
+       curr_index = write_idx_file(index_name, idx_objects, nr_objects, sha1);
+       free(idx_objects);
+
        final(pack_name, curr_pack,
                index_name, curr_index,
                keep_name, keep_msg,