Merge branch 'maint-1.6.0' into maint-1.6.1
authorJunio C Hamano <gitster@pobox.com>
Tue, 24 Mar 2009 22:31:15 +0000 (15:31 -0700)
committerJunio C Hamano <gitster@pobox.com>
Tue, 24 Mar 2009 22:31:15 +0000 (15:31 -0700)
* maint-1.6.0:
close_sha1_file(): make it easier to diagnose errors
avoid possible overflow in delta size filtering computation

1  2 
builtin-pack-objects.c
sha1_file.c
diff --combined builtin-pack-objects.c
index 7079fab7db808438b8b2bdf79ff693127610dfd3,84a13c7ccd4c04df2d45b7130ef7005643e42cb6..7234d76f6e7c0ca59964ec06bf3ced7196716749
@@@ -195,16 -195,16 +195,16 @@@ static int check_pack_inflate(struct pa
        int st;
  
        memset(&stream, 0, sizeof(stream));
 -      inflateInit(&stream);
 +      git_inflate_init(&stream);
        do {
                in = use_pack(p, w_curs, offset, &stream.avail_in);
                stream.next_in = in;
                stream.next_out = fakebuf;
                stream.avail_out = sizeof(fakebuf);
 -              st = inflate(&stream, Z_FINISH);
 +              st = git_inflate(&stream, Z_FINISH);
                offset += stream.next_in - in;
        } while (st == Z_OK || st == Z_BUF_ERROR);
 -      inflateEnd(&stream);
 +      git_inflate_end(&stream);
        return (st == Z_STREAM_END &&
                stream.total_out == expect &&
                stream.total_in == len) ? 0 : -1;
@@@ -286,7 -286,6 +286,7 @@@ static unsigned long write_object(struc
                                 */
  
        if (!to_reuse) {
 +              no_reuse:
                if (!usable_delta) {
                        buf = read_sha1_file(entry->idx.sha1, &type, &size);
                        if (!buf)
                struct revindex_entry *revidx;
                off_t offset;
  
 -              if (entry->delta) {
 +              if (entry->delta)
                        type = (allow_ofs_delta && entry->delta->idx.offset) ?
                                OBJ_OFS_DELTA : OBJ_REF_DELTA;
 -                      reused_delta++;
 -              }
                hdrlen = encode_header(type, entry->size, header);
 +
                offset = entry->in_pack_offset;
                revidx = find_pack_revindex(p, offset);
                datalen = revidx[1].offset - offset;
                if (!pack_to_stdout && p->index_version > 1 &&
 -                  check_pack_crc(p, &w_curs, offset, datalen, revidx->nr))
 -                      die("bad packed object CRC for %s", sha1_to_hex(entry->idx.sha1));
 +                  check_pack_crc(p, &w_curs, offset, datalen, revidx->nr)) {
 +                      error("bad packed object CRC for %s", sha1_to_hex(entry->idx.sha1));
 +                      unuse_pack(&w_curs);
 +                      goto no_reuse;
 +              }
 +
                offset += entry->in_pack_header_size;
                datalen -= entry->in_pack_header_size;
 +              if (!pack_to_stdout && p->index_version == 1 &&
 +                  check_pack_inflate(p, &w_curs, offset, datalen, entry->size)) {
 +                      error("corrupt packed object for %s", sha1_to_hex(entry->idx.sha1));
 +                      unuse_pack(&w_curs);
 +                      goto no_reuse;
 +              }
 +
                if (type == OBJ_OFS_DELTA) {
                        off_t ofs = entry->idx.offset - entry->delta->idx.offset;
                        unsigned pos = sizeof(dheader) - 1;
                        dheader[pos] = ofs & 127;
                        while (ofs >>= 7)
                                dheader[--pos] = 128 | (--ofs & 127);
 -                      if (limit && hdrlen + sizeof(dheader) - pos + datalen + 20 >= limit)
 +                      if (limit && hdrlen + sizeof(dheader) - pos + datalen + 20 >= limit) {
 +                              unuse_pack(&w_curs);
                                return 0;
 +                      }
                        sha1write(f, header, hdrlen);
                        sha1write(f, dheader + pos, sizeof(dheader) - pos);
                        hdrlen += sizeof(dheader) - pos;
 +                      reused_delta++;
                } else if (type == OBJ_REF_DELTA) {
 -                      if (limit && hdrlen + 20 + datalen + 20 >= limit)
 +                      if (limit && hdrlen + 20 + datalen + 20 >= limit) {
 +                              unuse_pack(&w_curs);
                                return 0;
 +                      }
                        sha1write(f, header, hdrlen);
                        sha1write(f, entry->delta->idx.sha1, 20);
                        hdrlen += 20;
 +                      reused_delta++;
                } else {
 -                      if (limit && hdrlen + datalen + 20 >= limit)
 +                      if (limit && hdrlen + datalen + 20 >= limit) {
 +                              unuse_pack(&w_curs);
                                return 0;
 +                      }
                        sha1write(f, header, hdrlen);
                }
 -
 -              if (!pack_to_stdout && p->index_version == 1 &&
 -                  check_pack_inflate(p, &w_curs, offset, datalen, entry->size))
 -                      die("corrupt packed object for %s", sha1_to_hex(entry->idx.sha1));
                copy_pack_data(f, p, &w_curs, offset, datalen);
                unuse_pack(&w_curs);
                reused++;
@@@ -488,8 -473,9 +488,8 @@@ static void write_pack_file(void
                } else {
                        char tmpname[PATH_MAX];
                        int fd;
 -                      snprintf(tmpname, sizeof(tmpname),
 -                               "%s/pack/tmp_pack_XXXXXX", get_object_directory());
 -                      fd = xmkstemp(tmpname);
 +                      fd = odb_mkstemp(tmpname, sizeof(tmpname),
 +                                       "pack/tmp_pack_XXXXXX");
                        pack_tmp_name = xstrdup(tmpname);
                        f = sha1fd(fd, pack_tmp_name);
                }
@@@ -1031,11 -1017,9 +1031,11 @@@ static void check_object(struct object_
                 * We want in_pack_type even if we do not reuse delta
                 * since non-delta representations could still be reused.
                 */
 -              used = unpack_object_header_gently(buf, avail,
 +              used = unpack_object_header_buffer(buf, avail,
                                                   &entry->in_pack_type,
                                                   &entry->size);
 +              if (used == 0)
 +                      goto give_up;
  
                /*
                 * Determine if this is a delta and if so whether we can
                        /* Not a delta hence we've already got all we need. */
                        entry->type = entry->in_pack_type;
                        entry->in_pack_header_size = used;
 +                      if (entry->type < OBJ_COMMIT || entry->type > OBJ_BLOB)
 +                              goto give_up;
                        unuse_pack(&w_curs);
                        return;
                case OBJ_REF_DELTA:
                        ofs = c & 127;
                        while (c & 128) {
                                ofs += 1;
 -                              if (!ofs || MSB(ofs, 7))
 -                                      die("delta base offset overflow in pack for %s",
 -                                          sha1_to_hex(entry->idx.sha1));
 +                              if (!ofs || MSB(ofs, 7)) {
 +                                      error("delta base offset overflow in pack for %s",
 +                                            sha1_to_hex(entry->idx.sha1));
 +                                      goto give_up;
 +                              }
                                c = buf[used_0++];
                                ofs = (ofs << 7) + (c & 127);
                        }
 -                      if (ofs >= entry->in_pack_offset)
 -                              die("delta base offset out of bound for %s",
 -                                  sha1_to_hex(entry->idx.sha1));
                        ofs = entry->in_pack_offset - ofs;
 +                      if (ofs <= 0 || ofs >= entry->in_pack_offset) {
 +                              error("delta base offset out of bound for %s",
 +                                    sha1_to_hex(entry->idx.sha1));
 +                              goto give_up;
 +                      }
                        if (reuse_delta && !entry->preferred_base) {
                                struct revindex_entry *revidx;
                                revidx = find_pack_revindex(p, ofs);
 +                              if (!revidx)
 +                                      goto give_up;
                                base_ref = nth_packed_object_sha1(p, revidx->nr);
                        }
                        entry->in_pack_header_size = used + used_0;
                         */
                        entry->type = entry->in_pack_type;
                        entry->delta = base_entry;
 +                      entry->delta_size = entry->size;
                        entry->delta_sibling = base_entry->delta_child;
                        base_entry->delta_child = entry;
                        unuse_pack(&w_curs);
                         */
                        entry->size = get_size_from_delta(p, &w_curs,
                                        entry->in_pack_offset + entry->in_pack_header_size);
 +                      if (entry->size == 0)
 +                              goto give_up;
                        unuse_pack(&w_curs);
                        return;
                }
                 * with sha1_object_info() to find about the object type
                 * at this point...
                 */
 +              give_up:
                unuse_pack(&w_curs);
        }
  
@@@ -1293,7 -1265,7 +1293,7 @@@ static int try_delta(struct unpacked *t
                max_size = trg_entry->delta_size;
                ref_depth = trg->depth;
        }
-       max_size = max_size * (max_depth - src->depth) /
+       max_size = (uint64_t)max_size * (max_depth - src->depth) /
                                                (max_depth - ref_depth + 1);
        if (max_size == 0)
                return 0;
@@@ -1412,10 -1384,12 +1412,10 @@@ static void find_deltas(struct object_e
                        int window, int depth, unsigned *processed)
  {
        uint32_t i, idx = 0, count = 0;
 -      unsigned int array_size = window * sizeof(struct unpacked);
        struct unpacked *array;
        unsigned long mem_usage = 0;
  
 -      array = xmalloc(array_size);
 -      memset(array, 0, array_size);
 +      array = xcalloc(window, sizeof(struct unpacked));
  
        for (;;) {
                struct object_entry *entry;
@@@ -1741,16 -1715,6 +1741,16 @@@ static void prepare_pack(int window, in
  
        get_object_details();
  
 +      /*
 +       * If we're locally repacking then we need to be doubly careful
 +       * from now on in order to make sure no stealth corruption gets
 +       * propagated to the new pack.  Clients receiving streamed packs
 +       * should validate everything they get anyway so no need to incur
 +       * the additional cost here in that case.
 +       */
 +      if (!pack_to_stdout)
 +              do_check_packed_object_crc = 1;
 +
        if (!nr_objects || !window || !depth)
                return;
  
                        if (entry->type < 0)
                                die("unable to get type of object %s",
                                    sha1_to_hex(entry->idx.sha1));
 +              } else {
 +                      if (entry->type < 0) {
 +                              /*
 +                               * This object is not found, but we
 +                               * don't have to include it anyway.
 +                               */
 +                              continue;
 +                      }
                }
  
                delta_list[n++] = entry;
diff --combined sha1_file.c
index b63f842cbdaf01e8a3b0ba35cbfc1ad0cbac6913,bcfcab351dd9f8d5b4e1cc0d86ecd2162f46cf94..3d93d936e4e3ddf24f46af2ef92e62e5f7db3877
@@@ -99,11 -99,7 +99,11 @@@ int safe_create_leading_directories(cha
                pos = strchr(pos, '/');
                if (!pos)
                        break;
 -              *pos = 0;
 +              while (*++pos == '/')
 +                      ;
 +              if (!*pos)
 +                      break;
 +              *--pos = '\0';
                if (!stat(path, &st)) {
                        /* path exists */
                        if (!S_ISDIR(st.st_mode)) {
@@@ -254,6 -250,7 +254,6 @@@ static void read_info_alternates(const 
   */
  static int link_alt_odb_entry(const char * entry, int len, const char * relative_base, int depth)
  {
 -      struct stat st;
        const char *objdir = get_object_directory();
        struct alternate_object_database *ent;
        struct alternate_object_database *alt;
        ent->base[pfxlen] = ent->base[entlen-1] = 0;
  
        /* Detect cases where alternate disappeared */
 -      if (stat(ent->base, &st) || !S_ISDIR(st.st_mode)) {
 +      if (!is_directory(ent->base)) {
                error("object directory %s does not exist; "
                      "check .git/objects/info/alternates.",
                      ent->base);
@@@ -397,16 -394,6 +397,16 @@@ void add_to_alternates_file(const char 
                link_alt_odb_entries(alt, alt + strlen(alt), '\n', NULL, 0);
  }
  
 +void foreach_alt_odb(alt_odb_fn fn, void *cb)
 +{
 +      struct alternate_object_database *ent;
 +
 +      prepare_alt_odb();
 +      for (ent = alt_odb_list; ent; ent = ent->next)
 +              if (fn(ent, cb))
 +                      return;
 +}
 +
  void prepare_alt_odb(void)
  {
        const char *alt;
@@@ -1154,8 -1141,7 +1154,8 @@@ static int legacy_loose_object(unsigne
                return 0;
  }
  
 -unsigned long unpack_object_header_gently(const unsigned char *buf, unsigned long len, enum object_type *type, unsigned long *sizep)
 +unsigned long unpack_object_header_buffer(const unsigned char *buf,
 +              unsigned long len, enum object_type *type, unsigned long *sizep)
  {
        unsigned shift;
        unsigned char c;
        size = c & 15;
        shift = 4;
        while (c & 0x80) {
 -              if (len <= used)
 -                      return 0;
 -              if (sizeof(long) * 8 <= shift)
 +              if (len <= used || sizeof(long) * 8 <= shift) {
 +                      error("bad object header");
                        return 0;
 +              }
                c = buf[used++];
                size += (c & 0x7f) << shift;
                shift += 7;
@@@ -1197,8 -1183,8 +1197,8 @@@ static int unpack_sha1_header(z_stream 
        stream->avail_out = bufsiz;
  
        if (legacy_loose_object(map)) {
 -              inflateInit(stream);
 -              return inflate(stream, 0);
 +              git_inflate_init(stream);
 +              return git_inflate(stream, 0);
        }
  
  
         * really worth it and we don't write it any longer.  But we
         * can still read it.
         */
 -      used = unpack_object_header_gently(map, mapsize, &type, &size);
 +      used = unpack_object_header_buffer(map, mapsize, &type, &size);
        if (!used || !valid_loose_object_type[type])
                return -1;
        map += used;
        /* Set up the stream for the rest.. */
        stream->next_in = map;
        stream->avail_in = mapsize;
 -      inflateInit(stream);
 +      git_inflate_init(stream);
  
        /* And generate the fake traditional header */
        stream->total_out = 1 + snprintf(buffer, bufsiz, "%s %lu",
@@@ -1255,11 -1241,11 +1255,11 @@@ static void *unpack_sha1_rest(z_stream 
                stream->next_out = buf + bytes;
                stream->avail_out = size - bytes;
                while (status == Z_OK)
 -                      status = inflate(stream, Z_FINISH);
 +                      status = git_inflate(stream, Z_FINISH);
        }
        buf[size] = 0;
        if (status == Z_STREAM_END && !stream->avail_in) {
 -              inflateEnd(stream);
 +              git_inflate_end(stream);
                return buf;
        }
  
@@@ -1349,19 -1335,17 +1349,19 @@@ unsigned long get_size_from_delta(struc
        stream.next_out = delta_head;
        stream.avail_out = sizeof(delta_head);
  
 -      inflateInit(&stream);
 +      git_inflate_init(&stream);
        do {
                in = use_pack(p, w_curs, curpos, &stream.avail_in);
                stream.next_in = in;
 -              st = inflate(&stream, Z_FINISH);
 +              st = git_inflate(&stream, Z_FINISH);
                curpos += stream.next_in - in;
        } while ((st == Z_OK || st == Z_BUF_ERROR) &&
                 stream.total_out < sizeof(delta_head));
 -      inflateEnd(&stream);
 -      if ((st != Z_STREAM_END) && stream.total_out != sizeof(delta_head))
 -              die("delta data unpack-initial failed");
 +      git_inflate_end(&stream);
 +      if ((st != Z_STREAM_END) && stream.total_out != sizeof(delta_head)) {
 +              error("delta data unpack-initial failed");
 +              return 0;
 +      }
  
        /* Examine the initial part of the delta to figure out
         * the result size.
@@@ -1402,7 -1386,7 +1402,7 @@@ static off_t get_delta_base(struct pack
                        base_offset = (base_offset << 7) + (c & 127);
                }
                base_offset = delta_obj_offset - base_offset;
 -              if (base_offset >= delta_obj_offset)
 +              if (base_offset <= 0 || base_offset >= delta_obj_offset)
                        return 0;  /* out of bound */
                *curpos += used;
        } else if (type == OBJ_REF_DELTA) {
@@@ -1428,32 -1412,15 +1428,32 @@@ static int packed_delta_info(struct pac
        off_t base_offset;
  
        base_offset = get_delta_base(p, w_curs, &curpos, type, obj_offset);
 +      if (!base_offset)
 +              return OBJ_BAD;
        type = packed_object_info(p, base_offset, NULL);
 +      if (type <= OBJ_NONE) {
 +              struct revindex_entry *revidx;
 +              const unsigned char *base_sha1;
 +              revidx = find_pack_revindex(p, base_offset);
 +              if (!revidx)
 +                      return OBJ_BAD;
 +              base_sha1 = nth_packed_object_sha1(p, revidx->nr);
 +              mark_bad_packed_object(p, base_sha1);
 +              type = sha1_object_info(base_sha1, NULL);
 +              if (type <= OBJ_NONE)
 +                      return OBJ_BAD;
 +      }
  
        /* We choose to only get the type of the base object and
         * ignore potentially corrupt pack file that expects the delta
         * based on a base with a wrong size.  This saves tons of
         * inflate() calls.
         */
 -      if (sizep)
 +      if (sizep) {
                *sizep = get_size_from_delta(p, w_curs, curpos);
 +              if (*sizep == 0)
 +                      type = OBJ_BAD;
 +      }
  
        return type;
  }
@@@ -1475,11 -1442,10 +1475,11 @@@ static int unpack_object_header(struct 
         * insane, so we know won't exceed what we have been given.
         */
        base = use_pack(p, w_curs, *curpos, &left);
 -      used = unpack_object_header_gently(base, left, &type, sizep);
 -      if (!used)
 -              die("object offset outside of pack file");
 -      *curpos += used;
 +      used = unpack_object_header_buffer(base, left, &type, sizep);
 +      if (!used) {
 +              type = OBJ_BAD;
 +      } else
 +              *curpos += used;
  
        return type;
  }
@@@ -1563,9 -1529,8 +1563,9 @@@ static int packed_object_info(struct pa
                        *sizep = size;
                break;
        default:
 -              die("pack %s contains unknown object type %d",
 -                  p->pack_name, type);
 +              error("unknown object type %i at offset %"PRIuMAX" in %s",
 +                    type, (uintmax_t)obj_offset, p->pack_name);
 +              type = OBJ_BAD;
        }
        unuse_pack(&w_curs);
        return type;
@@@ -1586,14 -1551,14 +1586,14 @@@ static void *unpack_compressed_entry(st
        stream.next_out = buffer;
        stream.avail_out = size;
  
 -      inflateInit(&stream);
 +      git_inflate_init(&stream);
        do {
                in = use_pack(p, w_curs, curpos, &stream.avail_in);
                stream.next_in = in;
 -              st = inflate(&stream, Z_FINISH);
 +              st = git_inflate(&stream, Z_FINISH);
                curpos += stream.next_in - in;
        } while (st == Z_OK || st == Z_BUF_ERROR);
 -      inflateEnd(&stream);
 +      git_inflate_end(&stream);
        if ((st != Z_STREAM_END) || stream.total_out != size) {
                free(buffer);
                return NULL;
@@@ -1637,9 -1602,11 +1637,9 @@@ static void *cache_or_unpack_entry(stru
        struct delta_base_cache_entry *ent = delta_base_cache + hash;
  
        ret = ent->data;
 -      if (ret && ent->p == p && ent->base_offset == base_offset)
 -              goto found_cache_entry;
 -      return unpack_entry(p, base_offset, type, base_size);
 +      if (!ret || ent->p != p || ent->base_offset != base_offset)
 +              return unpack_entry(p, base_offset, type, base_size);
  
 -found_cache_entry:
        if (!keep_cache) {
                ent->data = NULL;
                ent->lru.next->prev = ent->lru.prev;
@@@ -1736,12 -1703,9 +1736,12 @@@ static void *unpack_delta_entry(struct 
                 * This is costly but should happen only in the presence
                 * of a corrupted pack, and is better than failing outright.
                 */
 -              struct revindex_entry *revidx = find_pack_revindex(p, base_offset);
 -              const unsigned char *base_sha1 =
 -                                      nth_packed_object_sha1(p, revidx->nr);
 +              struct revindex_entry *revidx;
 +              const unsigned char *base_sha1;
 +              revidx = find_pack_revindex(p, base_offset);
 +              if (!revidx)
 +                      return NULL;
 +              base_sha1 = nth_packed_object_sha1(p, revidx->nr);
                error("failed to read delta base object %s"
                      " at offset %"PRIuMAX" from %s",
                      sha1_to_hex(base_sha1), (uintmax_t)base_offset,
        return result;
  }
  
 +int do_check_packed_object_crc;
 +
  void *unpack_entry(struct packed_git *p, off_t obj_offset,
                   enum object_type *type, unsigned long *sizep)
  {
        off_t curpos = obj_offset;
        void *data;
  
 +      if (do_check_packed_object_crc && p->index_version > 1) {
 +              struct revindex_entry *revidx = find_pack_revindex(p, obj_offset);
 +              unsigned long len = revidx[1].offset - obj_offset;
 +              if (check_pack_crc(p, &w_curs, obj_offset, len, revidx->nr)) {
 +                      const unsigned char *sha1 =
 +                              nth_packed_object_sha1(p, revidx->nr);
 +                      error("bad packed object CRC for %s",
 +                            sha1_to_hex(sha1));
 +                      mark_bad_packed_object(p, sha1);
 +                      unuse_pack(&w_curs);
 +                      return NULL;
 +              }
 +      }
 +
        *type = unpack_object_header(p, &w_curs, &curpos, sizep);
        switch (*type) {
        case OBJ_OFS_DELTA:
@@@ -2025,7 -1973,7 +2025,7 @@@ static int sha1_loose_object_info(cons
                status = error("unable to parse %s header", sha1_to_hex(sha1));
        else if (sizep)
                *sizep = size;
 -      inflateEnd(&stream);
 +      git_inflate_end(&stream);
        munmap(map, mapsize);
        return status;
  }
@@@ -2046,14 -1994,7 +2046,14 @@@ int sha1_object_info(const unsigned cha
                if (!find_pack_entry(sha1, &e, NULL))
                        return status;
        }
 -      return packed_object_info(e.p, e.offset, sizep);
 +
 +      status = packed_object_info(e.p, e.offset, sizep);
 +      if (status < 0) {
 +              mark_bad_packed_object(e.p, sha1);
 +              status = sha1_object_info(sha1, sizep);
 +      }
 +
 +      return status;
  }
  
  static void *read_packed_sha1(const unsigned char *sha1,
@@@ -2095,7 -2036,9 +2095,7 @@@ static struct cached_object 
  static int cached_object_nr, cached_object_alloc;
  
  static struct cached_object empty_tree = {
 -      /* empty tree sha1: 4b825dc642cb6eb9a060e54bf8d69288fbee4904 */
 -      "\x4b\x82\x5d\xc6\x42\xcb\x6e\xb9\xa0\x60"
 -      "\xe5\x4b\xf8\xd6\x92\x88\xfb\xee\x49\x04",
 +      EMPTY_TREE_SHA1_BIN,
        OBJ_TREE,
        "",
        0
@@@ -2227,16 -2170,16 +2227,16 @@@ static void write_sha1_file_prepare(con
                                      const char *type, unsigned char *sha1,
                                      char *hdr, int *hdrlen)
  {
 -      SHA_CTX c;
 +      git_SHA_CTX c;
  
        /* Generate the header */
        *hdrlen = sprintf(hdr, "%s %lu", type, len)+1;
  
        /* Sha1.. */
 -      SHA1_Init(&c);
 -      SHA1_Update(&c, hdr, *hdrlen);
 -      SHA1_Update(&c, buf, len);
 -      SHA1_Final(sha1, &c);
 +      git_SHA1_Init(&c);
 +      git_SHA1_Update(&c, hdr, *hdrlen);
 +      git_SHA1_Update(&c, buf, len);
 +      git_SHA1_Final(sha1, &c);
  }
  
  /*
@@@ -2298,7 -2241,7 +2298,7 @@@ static void close_sha1_file(int fd
                fsync_or_die(fd, "sha1 file");
        fchmod(fd, 0444);
        if (close(fd) != 0)
-               die("unable to write sha1 file");
+               die("error when closing sha1 file (%s)", strerror(errno));
  }
  
  /* Size of directory component, including the ending '/' */
@@@ -2476,21 -2419,51 +2476,21 @@@ int has_sha1_file(const unsigned char *
        return has_loose_object(sha1);
  }
  
 -int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object)
 +static int index_mem(unsigned char *sha1, void *buf, size_t size,
 +                   int write_object, enum object_type type, const char *path)
  {
 -      struct strbuf buf;
 -      int ret;
 -
 -      strbuf_init(&buf, 0);
 -      if (strbuf_read(&buf, fd, 4096) < 0) {
 -              strbuf_release(&buf);
 -              return -1;
 -      }
 -
 -      if (!type)
 -              type = blob_type;
 -      if (write_object)
 -              ret = write_sha1_file(buf.buf, buf.len, type, sha1);
 -      else
 -              ret = hash_sha1_file(buf.buf, buf.len, type, sha1);
 -      strbuf_release(&buf);
 -
 -      return ret;
 -}
 -
 -int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object,
 -           enum object_type type, const char *path)
 -{
 -      size_t size = xsize_t(st->st_size);
 -      void *buf = NULL;
        int ret, re_allocated = 0;
  
 -      if (size)
 -              buf = xmmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
 -      close(fd);
 -
        if (!type)
                type = OBJ_BLOB;
  
        /*
         * Convert blobs to git internal format
         */
 -      if ((type == OBJ_BLOB) && S_ISREG(st->st_mode)) {
 -              struct strbuf nbuf;
 -              strbuf_init(&nbuf, 0);
 +      if ((type == OBJ_BLOB) && path) {
 +              struct strbuf nbuf = STRBUF_INIT;
                if (convert_to_git(path, buf, size, &nbuf,
                                   write_object ? safe_crlf : 0)) {
 -                      munmap(buf, size);
                        buf = strbuf_detach(&nbuf, &size);
                        re_allocated = 1;
                }
                ret = write_sha1_file(buf, size, typename(type), sha1);
        else
                ret = hash_sha1_file(buf, size, typename(type), sha1);
 -      if (re_allocated) {
 +      if (re_allocated)
                free(buf);
 -              return ret;
 -      }
 -      if (size)
 +      return ret;
 +}
 +
 +int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object,
 +           enum object_type type, const char *path)
 +{
 +      int ret;
 +      size_t size = xsize_t(st->st_size);
 +
 +      if (!S_ISREG(st->st_mode)) {
 +              struct strbuf sbuf = STRBUF_INIT;
 +              if (strbuf_read(&sbuf, fd, 4096) >= 0)
 +                      ret = index_mem(sha1, sbuf.buf, sbuf.len, write_object,
 +                                      type, path);
 +              else
 +                      ret = -1;
 +              strbuf_release(&sbuf);
 +      } else if (size) {
 +              void *buf = xmmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
 +              ret = index_mem(sha1, buf, size, write_object, type, path);
                munmap(buf, size);
 +      } else
 +              ret = index_mem(sha1, NULL, size, write_object, type, path);
 +      close(fd);
        return ret;
  }
  
  int index_path(unsigned char *sha1, const char *path, struct stat *st, int write_object)
  {
        int fd;
 -      char *target;
 -      size_t len;
 +      struct strbuf sb = STRBUF_INIT;
  
        switch (st->st_mode & S_IFMT) {
        case S_IFREG:
                                     path);
                break;
        case S_IFLNK:
 -              len = xsize_t(st->st_size);
 -              target = xmalloc(len + 1);
 -              if (readlink(path, target, len + 1) != st->st_size) {
 +              if (strbuf_readlink(&sb, path, st->st_size)) {
                        char *errstr = strerror(errno);
 -                      free(target);
                        return error("readlink(\"%s\"): %s", path,
                                     errstr);
                }
                if (!write_object)
 -                      hash_sha1_file(target, len, blob_type, sha1);
 -              else if (write_sha1_file(target, len, blob_type, sha1))
 +                      hash_sha1_file(sb.buf, sb.len, blob_type, sha1);
 +              else if (write_sha1_file(sb.buf, sb.len, blob_type, sha1))
                        return error("%s: failed to insert into database",
                                     path);
 -              free(target);
 +              strbuf_release(&sb);
                break;
        case S_IFDIR:
                return resolve_gitlink_ref(path, "HEAD", sha1);