Merge branch 'jk/fast-import-fixes' into maint
authorJunio C Hamano <gitster@pobox.com>
Fri, 19 Sep 2014 21:05:12 +0000 (14:05 -0700)
committerJunio C Hamano <gitster@pobox.com>
Fri, 19 Sep 2014 21:05:12 +0000 (14:05 -0700)
* jk/fast-import-fixes:
fast-import: fix buffer overflow in dump_tags
fast-import: clean up pack_data pointer in end_packfile

1  2 
fast-import.c
diff --combined fast-import.c
index d73f58cbe3fe4522fa24d47f9086d927b3296c8b,6475febe99a094736cf138b4542bc736702dc384..a1479e980da154f9179d71aeeda7746770686baa
@@@ -248,7 -248,6 +248,7 @@@ struct branch 
        uintmax_t last_commit;
        uintmax_t num_notes;
        unsigned active : 1;
 +      unsigned delete : 1;
        unsigned pack_id : PACK_ID_BITS;
        unsigned char sha1[20];
  };
@@@ -371,8 -370,8 +371,8 @@@ static volatile sig_atomic_t checkpoint
  static int cat_blob_fd = STDOUT_FILENO;
  
  static void parse_argv(void);
 -static void parse_cat_blob(void);
 -static void parse_ls(struct branch *b);
 +static void parse_cat_blob(const char *p);
 +static void parse_ls(const char *p, struct branch *b);
  
  static void write_branch_report(FILE *rpt, struct branch *b)
  {
@@@ -946,10 -945,12 +946,12 @@@ static void unkeep_all_packs(void
  
  static void end_packfile(void)
  {
-       struct packed_git *old_p = pack_data, *new_p;
+       if (!pack_data)
+               return;
  
        clear_delta_base_cache();
        if (object_count) {
+               struct packed_git *new_p;
                unsigned char cur_pack_sha1[20];
                char *idx_name;
                int i;
                pack_id++;
        }
        else {
-               close(old_p->pack_fd);
-               unlink_or_warn(old_p->pack_name);
+               close(pack_data->pack_fd);
+               unlink_or_warn(pack_data->pack_name);
        }
-       free(old_p);
+       free(pack_data);
+       pack_data = NULL;
  
        /* We can't carry a delta across packfiles. */
        strbuf_release(&last_blob.data);
@@@ -1682,13 -1684,10 +1685,13 @@@ static int update_branch(struct branch 
        struct ref_lock *lock;
        unsigned char old_sha1[20];
  
 -      if (is_null_sha1(b->sha1))
 -              return 0;
        if (read_ref(b->name, old_sha1))
                hashclr(old_sha1);
 +      if (is_null_sha1(b->sha1)) {
 +              if (b->delete)
 +                      delete_ref(b->name, old_sha1, 0);
 +              return 0;
 +      }
        lock = lock_any_ref_for_update(b->name, old_sha1, 0, NULL);
        if (!lock)
                return error("Unable to lock %s", b->name);
@@@ -1731,14 -1730,16 +1734,16 @@@ static void dump_tags(void
        static const char *msg = "fast-import";
        struct tag *t;
        struct ref_lock *lock;
-       char ref_name[PATH_MAX];
+       struct strbuf ref_name = STRBUF_INIT;
  
        for (t = first_tag; t; t = t->next_tag) {
-               sprintf(ref_name, "tags/%s", t->name);
-               lock = lock_ref_sha1(ref_name, NULL);
+               strbuf_reset(&ref_name);
+               strbuf_addf(&ref_name, "tags/%s", t->name);
+               lock = lock_ref_sha1(ref_name.buf, NULL);
                if (!lock || write_ref_sha1(lock, t->sha1, msg) < 0)
-                       failure |= error("Unable to update %s", ref_name);
+                       failure |= error("Unable to update %s", ref_name.buf);
        }
+       strbuf_release(&ref_name);
  }
  
  static void dump_marks_helper(FILE *f,
@@@ -1861,8 -1862,6 +1866,8 @@@ static int read_next_command(void
        }
  
        for (;;) {
 +              const char *p;
 +
                if (unread_command_buf) {
                        unread_command_buf = 0;
                } else {
                        rc->prev->next = rc;
                        cmd_tail = rc;
                }
 -              if (starts_with(command_buf.buf, "cat-blob ")) {
 -                      parse_cat_blob();
 +              if (skip_prefix(command_buf.buf, "cat-blob ", &p)) {
 +                      parse_cat_blob(p);
                        continue;
                }
                if (command_buf.buf[0] == '#')
@@@ -1914,9 -1913,8 +1919,9 @@@ static void skip_optional_lf(void
  
  static void parse_mark(void)
  {
 -      if (starts_with(command_buf.buf, "mark :")) {
 -              next_mark = strtoumax(command_buf.buf + 6, NULL, 10);
 +      const char *v;
 +      if (skip_prefix(command_buf.buf, "mark :", &v)) {
 +              next_mark = strtoumax(v, NULL, 10);
                read_next_command();
        }
        else
  
  static int parse_data(struct strbuf *sb, uintmax_t limit, uintmax_t *len_res)
  {
 +      const char *data;
        strbuf_reset(sb);
  
 -      if (!starts_with(command_buf.buf, "data "))
 +      if (!skip_prefix(command_buf.buf, "data ", &data))
                die("Expected 'data n' command, found: %s", command_buf.buf);
  
 -      if (starts_with(command_buf.buf + 5, "<<")) {
 -              char *term = xstrdup(command_buf.buf + 5 + 2);
 -              size_t term_len = command_buf.len - 5 - 2;
 +      if (skip_prefix(data, "<<", &data)) {
 +              char *term = xstrdup(data);
 +              size_t term_len = command_buf.len - (data - command_buf.buf);
  
                strbuf_detach(&command_buf, NULL);
                for (;;) {
                free(term);
        }
        else {
 -              uintmax_t len = strtoumax(command_buf.buf + 5, NULL, 10);
 +              uintmax_t len = strtoumax(data, NULL, 10);
                size_t n = 0, length = (size_t)len;
  
                if (limit && limit < len) {
@@@ -2269,14 -2266,15 +2274,14 @@@ static uintmax_t parse_mark_ref_space(c
        char *end;
  
        mark = parse_mark_ref(*p, &end);
 -      if (*end != ' ')
 +      if (*end++ != ' ')
                die("Missing space after mark: %s", command_buf.buf);
        *p = end;
        return mark;
  }
  
 -static void file_change_m(struct branch *b)
 +static void file_change_m(const char *p, struct branch *b)
  {
 -      const char *p = command_buf.buf + 2;
        static struct strbuf uq = STRBUF_INIT;
        const char *endp;
        struct object_entry *oe;
        if (*p == ':') {
                oe = find_mark(parse_mark_ref_space(&p));
                hashcpy(sha1, oe->idx.sha1);
 -      } else if (starts_with(p, "inline ")) {
 +      } else if (skip_prefix(p, "inline ", &p)) {
                inline_data = 1;
                oe = NULL; /* not used with inline_data, but makes gcc happy */
 -              p += strlen("inline");  /* advance to space */
        } else {
                if (get_sha1_hex(p, sha1))
                        die("Invalid dataref: %s", command_buf.buf);
                oe = find_object(sha1);
                p += 40;
 -              if (*p != ' ')
 +              if (*p++ != ' ')
                        die("Missing space after SHA1: %s", command_buf.buf);
        }
 -      assert(*p == ' ');
 -      p++;  /* skip space */
  
        strbuf_reset(&uq);
        if (!unquote_c_style(&uq, p, &endp)) {
        }
  
        /* Git does not track empty, non-toplevel directories. */
 -      if (S_ISDIR(mode) && !memcmp(sha1, EMPTY_TREE_SHA1_BIN, 20) && *p) {
 +      if (S_ISDIR(mode) && !hashcmp(sha1, EMPTY_TREE_SHA1_BIN) && *p) {
                tree_content_remove(&b->branch_tree, p, NULL, 0);
                return;
        }
        tree_content_set(&b->branch_tree, p, sha1, mode, NULL);
  }
  
 -static void file_change_d(struct branch *b)
 +static void file_change_d(const char *p, struct branch *b)
  {
 -      const char *p = command_buf.buf + 2;
        static struct strbuf uq = STRBUF_INIT;
        const char *endp;
  
        tree_content_remove(&b->branch_tree, p, NULL, 1);
  }
  
 -static void file_change_cr(struct branch *b, int rename)
 +static void file_change_cr(const char *s, struct branch *b, int rename)
  {
 -      const char *s, *d;
 +      const char *d;
        static struct strbuf s_uq = STRBUF_INIT;
        static struct strbuf d_uq = STRBUF_INIT;
        const char *endp;
        struct tree_entry leaf;
  
 -      s = command_buf.buf + 2;
        strbuf_reset(&s_uq);
        if (!unquote_c_style(&s_uq, s, &endp)) {
                if (*endp != ' ')
                leaf.tree);
  }
  
 -static void note_change_n(struct branch *b, unsigned char *old_fanout)
 +static void note_change_n(const char *p, struct branch *b, unsigned char *old_fanout)
  {
 -      const char *p = command_buf.buf + 2;
        static struct strbuf uq = STRBUF_INIT;
        struct object_entry *oe;
        struct branch *s;
        if (*p == ':') {
                oe = find_mark(parse_mark_ref_space(&p));
                hashcpy(sha1, oe->idx.sha1);
 -      } else if (starts_with(p, "inline ")) {
 +      } else if (skip_prefix(p, "inline ", &p)) {
                inline_data = 1;
                oe = NULL; /* not used with inline_data, but makes gcc happy */
 -              p += strlen("inline");  /* advance to space */
        } else {
                if (get_sha1_hex(p, sha1))
                        die("Invalid dataref: %s", command_buf.buf);
                oe = find_object(sha1);
                p += 40;
 -              if (*p != ' ')
 +              if (*p++ != ' ')
                        die("Missing space after SHA1: %s", command_buf.buf);
        }
 -      assert(*p == ' ');
 -      p++;  /* skip space */
  
        /* <commit-ish> */
        s = lookup_branch(p);
@@@ -2579,7 -2586,7 +2584,7 @@@ static int parse_from(struct branch *b
        const char *from;
        struct branch *s;
  
 -      if (!starts_with(command_buf.buf, "from "))
 +      if (!skip_prefix(command_buf.buf, "from ", &from))
                return 0;
  
        if (b->branch_tree.tree) {
                b->branch_tree.tree = NULL;
        }
  
 -      from = strchr(command_buf.buf, ' ') + 1;
        s = lookup_branch(from);
        if (b == s)
                die("Can't create a branch from itself: %s", b->name);
                        free(buf);
                } else
                        parse_from_existing(b);
 -      } else if (!get_sha1(from, b->sha1))
 +      } else if (!get_sha1(from, b->sha1)) {
                parse_from_existing(b);
 +              if (is_null_sha1(b->sha1))
 +                      b->delete = 1;
 +      }
        else
                die("Invalid ref name or SHA1 expression: %s", from);
  
@@@ -2627,7 -2632,8 +2632,7 @@@ static struct hash_list *parse_merge(un
        struct branch *s;
  
        *count = 0;
 -      while (starts_with(command_buf.buf, "merge ")) {
 -              from = strchr(command_buf.buf, ' ') + 1;
 +      while (skip_prefix(command_buf.buf, "merge ", &from)) {
                n = xmalloc(sizeof(*n));
                s = lookup_branch(from);
                if (s)
        return list;
  }
  
 -static void parse_new_commit(void)
 +static void parse_new_commit(const char *arg)
  {
        static struct strbuf msg = STRBUF_INIT;
        struct branch *b;
 -      char *sp;
        char *author = NULL;
        char *committer = NULL;
        struct hash_list *merge_list = NULL;
        unsigned int merge_count;
        unsigned char prev_fanout, new_fanout;
 +      const char *v;
  
 -      /* Obtain the branch name from the rest of our command */
 -      sp = strchr(command_buf.buf, ' ') + 1;
 -      b = lookup_branch(sp);
 +      b = lookup_branch(arg);
        if (!b)
 -              b = new_branch(sp);
 +              b = new_branch(arg);
  
        read_next_command();
        parse_mark();
 -      if (starts_with(command_buf.buf, "author ")) {
 -              author = parse_ident(command_buf.buf + 7);
 +      if (skip_prefix(command_buf.buf, "author ", &v)) {
 +              author = parse_ident(v);
                read_next_command();
        }
 -      if (starts_with(command_buf.buf, "committer ")) {
 -              committer = parse_ident(command_buf.buf + 10);
 +      if (skip_prefix(command_buf.buf, "committer ", &v)) {
 +              committer = parse_ident(v);
                read_next_command();
        }
        if (!committer)
  
        /* file_change* */
        while (command_buf.len > 0) {
 -              if (starts_with(command_buf.buf, "M "))
 -                      file_change_m(b);
 -              else if (starts_with(command_buf.buf, "D "))
 -                      file_change_d(b);
 -              else if (starts_with(command_buf.buf, "R "))
 -                      file_change_cr(b, 1);
 -              else if (starts_with(command_buf.buf, "C "))
 -                      file_change_cr(b, 0);
 -              else if (starts_with(command_buf.buf, "N "))
 -                      note_change_n(b, &prev_fanout);
 +              if (skip_prefix(command_buf.buf, "M ", &v))
 +                      file_change_m(v, b);
 +              else if (skip_prefix(command_buf.buf, "D ", &v))
 +                      file_change_d(v, b);
 +              else if (skip_prefix(command_buf.buf, "R ", &v))
 +                      file_change_cr(v, b, 1);
 +              else if (skip_prefix(command_buf.buf, "C ", &v))
 +                      file_change_cr(v, b, 0);
 +              else if (skip_prefix(command_buf.buf, "N ", &v))
 +                      note_change_n(v, b, &prev_fanout);
                else if (!strcmp("deleteall", command_buf.buf))
                        file_change_deleteall(b);
 -              else if (starts_with(command_buf.buf, "ls "))
 -                      parse_ls(b);
 +              else if (skip_prefix(command_buf.buf, "ls ", &v))
 +                      parse_ls(v, b);
                else {
                        unread_command_buf = 1;
                        break;
        b->last_commit = object_count_by_type[OBJ_COMMIT];
  }
  
 -static void parse_new_tag(void)
 +static void parse_new_tag(const char *arg)
  {
        static struct strbuf msg = STRBUF_INIT;
 -      char *sp;
        const char *from;
        char *tagger;
        struct branch *s;
        uintmax_t from_mark = 0;
        unsigned char sha1[20];
        enum object_type type;
 +      const char *v;
  
 -      /* Obtain the new tag name from the rest of our command */
 -      sp = strchr(command_buf.buf, ' ') + 1;
        t = pool_alloc(sizeof(struct tag));
        memset(t, 0, sizeof(struct tag));
 -      t->name = pool_strdup(sp);
 +      t->name = pool_strdup(arg);
        if (last_tag)
                last_tag->next_tag = t;
        else
        read_next_command();
  
        /* from ... */
 -      if (!starts_with(command_buf.buf, "from "))
 +      if (!skip_prefix(command_buf.buf, "from ", &from))
                die("Expected from command, got %s", command_buf.buf);
 -      from = strchr(command_buf.buf, ' ') + 1;
        s = lookup_branch(from);
        if (s) {
                if (is_null_sha1(s->sha1))
        read_next_command();
  
        /* tagger ... */
 -      if (starts_with(command_buf.buf, "tagger ")) {
 -              tagger = parse_ident(command_buf.buf + 7);
 +      if (skip_prefix(command_buf.buf, "tagger ", &v)) {
 +              tagger = parse_ident(v);
                read_next_command();
        } else
                tagger = NULL;
                t->pack_id = pack_id;
  }
  
 -static void parse_reset_branch(void)
 +static void parse_reset_branch(const char *arg)
  {
        struct branch *b;
 -      char *sp;
  
 -      /* Obtain the branch name from the rest of our command */
 -      sp = strchr(command_buf.buf, ' ') + 1;
 -      b = lookup_branch(sp);
 +      b = lookup_branch(arg);
        if (b) {
                hashclr(b->sha1);
                hashclr(b->branch_tree.versions[0].sha1);
                }
        }
        else
 -              b = new_branch(sp);
 +              b = new_branch(arg);
        read_next_command();
        parse_from(b);
        if (command_buf.len > 0)
@@@ -2909,12 -2923,14 +2914,12 @@@ static void cat_blob(struct object_entr
                free(buf);
  }
  
 -static void parse_cat_blob(void)
 +static void parse_cat_blob(const char *p)
  {
 -      const char *p;
        struct object_entry *oe = oe;
        unsigned char sha1[20];
  
        /* cat-blob SP <object> LF */
 -      p = command_buf.buf + strlen("cat-blob ");
        if (*p == ':') {
                oe = find_mark(parse_mark_ref_eol(p));
                if (!oe)
@@@ -2997,8 -3013,6 +3002,8 @@@ static struct object_entry *parse_treei
                        die("Invalid dataref: %s", command_buf.buf);
                e = find_object(sha1);
                *p += 40;
 +              if (*(*p)++ != ' ')
 +                      die("Missing space after tree-ish: %s", command_buf.buf);
        }
  
        while (!e || e->type != OBJ_TREE)
@@@ -3033,12 -3047,14 +3038,12 @@@ static void print_ls(int mode, const un
        cat_blob_write(line.buf, line.len);
  }
  
 -static void parse_ls(struct branch *b)
 +static void parse_ls(const char *p, struct branch *b)
  {
 -      const char *p;
        struct tree_entry *root = NULL;
        struct tree_entry leaf = {NULL};
  
        /* ls SP (<tree-ish> SP)? <path> */
 -      p = command_buf.buf + strlen("ls ");
        if (*p == '"') {
                if (!b)
                        die("Not in a commit: %s", command_buf.buf);
                if (!is_null_sha1(root->versions[1].sha1))
                        root->versions[1].mode = S_IFDIR;
                load_tree(root);
 -              if (*p++ != ' ')
 -                      die("Missing space after tree-ish: %s", command_buf.buf);
        }
        if (*p == '"') {
                static struct strbuf uq = STRBUF_INIT;
@@@ -3187,9 -3205,9 +3192,9 @@@ static void option_export_pack_edges(co
  
  static int parse_one_option(const char *option)
  {
 -      if (starts_with(option, "max-pack-size=")) {
 +      if (skip_prefix(option, "max-pack-size=", &option)) {
                unsigned long v;
 -              if (!git_parse_ulong(option + 14, &v))
 +              if (!git_parse_ulong(option, &v))
                        return 0;
                if (v < 8192) {
                        warning("max-pack-size is now in bytes, assuming --max-pack-size=%lum", v);
                        v = 1024 * 1024;
                }
                max_packsize = v;
 -      } else if (starts_with(option, "big-file-threshold=")) {
 +      } else if (skip_prefix(option, "big-file-threshold=", &option)) {
                unsigned long v;
 -              if (!git_parse_ulong(option + 19, &v))
 +              if (!git_parse_ulong(option, &v))
                        return 0;
                big_file_threshold = v;
 -      } else if (starts_with(option, "depth=")) {
 -              option_depth(option + 6);
 -      } else if (starts_with(option, "active-branches=")) {
 -              option_active_branches(option + 16);
 -      } else if (starts_with(option, "export-pack-edges=")) {
 -              option_export_pack_edges(option + 18);
 +      } else if (skip_prefix(option, "depth=", &option)) {
 +              option_depth(option);
 +      } else if (skip_prefix(option, "active-branches=", &option)) {
 +              option_active_branches(option);
 +      } else if (skip_prefix(option, "export-pack-edges=", &option)) {
 +              option_export_pack_edges(option);
        } else if (starts_with(option, "quiet")) {
                show_stats = 0;
        } else if (starts_with(option, "stats")) {
  
  static int parse_one_feature(const char *feature, int from_stream)
  {
 -      if (starts_with(feature, "date-format=")) {
 -              option_date_format(feature + 12);
 -      } else if (starts_with(feature, "import-marks=")) {
 -              option_import_marks(feature + 13, from_stream, 0);
 -      } else if (starts_with(feature, "import-marks-if-exists=")) {
 -              option_import_marks(feature + strlen("import-marks-if-exists="),
 -                                      from_stream, 1);
 -      } else if (starts_with(feature, "export-marks=")) {
 -              option_export_marks(feature + 13);
 +      const char *arg;
 +
 +      if (skip_prefix(feature, "date-format=", &arg)) {
 +              option_date_format(arg);
 +      } else if (skip_prefix(feature, "import-marks=", &arg)) {
 +              option_import_marks(arg, from_stream, 0);
 +      } else if (skip_prefix(feature, "import-marks-if-exists=", &arg)) {
 +              option_import_marks(arg, from_stream, 1);
 +      } else if (skip_prefix(feature, "export-marks=", &arg)) {
 +              option_export_marks(arg);
        } else if (!strcmp(feature, "cat-blob")) {
                ; /* Don't die - this feature is supported */
        } else if (!strcmp(feature, "relative-marks")) {
        return 1;
  }
  
 -static void parse_feature(void)
 +static void parse_feature(const char *feature)
  {
 -      char *feature = command_buf.buf + 8;
 -
        if (seen_data_command)
                die("Got feature command '%s' after data command", feature);
  
        die("This version of fast-import does not support feature %s.", feature);
  }
  
 -static void parse_option(void)
 +static void parse_option(const char *option)
  {
 -      char *option = command_buf.buf + 11;
 -
        if (seen_data_command)
                die("Got option command '%s' after data command", option);
  
@@@ -3319,21 -3340,18 +3324,21 @@@ static void parse_argv(void
                if (*a != '-' || !strcmp(a, "--"))
                        break;
  
 -              if (parse_one_option(a + 2))
 +              if (!skip_prefix(a, "--", &a))
 +                      die("unknown option %s", a);
 +
 +              if (parse_one_option(a))
                        continue;
  
 -              if (parse_one_feature(a + 2, 0))
 +              if (parse_one_feature(a, 0))
                        continue;
  
 -              if (starts_with(a + 2, "cat-blob-fd=")) {
 -                      option_cat_blob_fd(a + 2 + strlen("cat-blob-fd="));
 +              if (skip_prefix(a, "cat-blob-fd=", &a)) {
 +                      option_cat_blob_fd(a);
                        continue;
                }
  
 -              die("unknown option %s", a);
 +              die("unknown option --%s", a);
        }
        if (i != global_argc)
                usage(fast_import_usage);
@@@ -3380,27 -3398,26 +3385,27 @@@ int main(int argc, char **argv
        set_die_routine(die_nicely);
        set_checkpoint_signal();
        while (read_next_command() != EOF) {
 +              const char *v;
                if (!strcmp("blob", command_buf.buf))
                        parse_new_blob();
 -              else if (starts_with(command_buf.buf, "ls "))
 -                      parse_ls(NULL);
 -              else if (starts_with(command_buf.buf, "commit "))
 -                      parse_new_commit();
 -              else if (starts_with(command_buf.buf, "tag "))
 -                      parse_new_tag();
 -              else if (starts_with(command_buf.buf, "reset "))
 -                      parse_reset_branch();
 +              else if (skip_prefix(command_buf.buf, "ls ", &v))
 +                      parse_ls(v, NULL);
 +              else if (skip_prefix(command_buf.buf, "commit ", &v))
 +                      parse_new_commit(v);
 +              else if (skip_prefix(command_buf.buf, "tag ", &v))
 +                      parse_new_tag(v);
 +              else if (skip_prefix(command_buf.buf, "reset ", &v))
 +                      parse_reset_branch(v);
                else if (!strcmp("checkpoint", command_buf.buf))
                        parse_checkpoint();
                else if (!strcmp("done", command_buf.buf))
                        break;
                else if (starts_with(command_buf.buf, "progress "))
                        parse_progress();
 -              else if (starts_with(command_buf.buf, "feature "))
 -                      parse_feature();
 -              else if (starts_with(command_buf.buf, "option git "))
 -                      parse_option();
 +              else if (skip_prefix(command_buf.buf, "feature ", &v))
 +                      parse_feature(v);
 +              else if (skip_prefix(command_buf.buf, "option git ", &v))
 +                      parse_option(v);
                else if (starts_with(command_buf.buf, "option "))
                        /* ignore non-git options*/;
                else