Merge branch 'jk/prune-mtime'
authorJunio C Hamano <gitster@pobox.com>
Tue, 15 Dec 2015 16:02:13 +0000 (08:02 -0800)
committerJunio C Hamano <gitster@pobox.com>
Tue, 15 Dec 2015 16:02:16 +0000 (08:02 -0800)
The helper used to iterate over loose object directories to prune
stale objects did not closedir() immediately when it is done with a
directory--a callback such as the one used for "git prune" may want
to do rmdir(), but it would fail on open directory on platforms
such as WinXP.

* jk/prune-mtime:
prune: close directory earlier during loose-object directory traversal

1  2 
sha1_file.c
diff --combined sha1_file.c
index 27ce7b705e12ca0343a4a232788494e5dee518d5,7ea7a93359312c376d6f091d4965eacedab39606..2c68766ebf0cab5f13f684ac3e26061b93bda44a
@@@ -8,7 -8,6 +8,7 @@@
   */
  #include "cache.h"
  #include "string-list.h"
 +#include "lockfile.h"
  #include "delta.h"
  #include "pack.h"
  #include "blob.h"
@@@ -208,25 -207,44 +208,25 @@@ const char *sha1_file_name(const unsign
   * provided by the caller.  which should be "pack" or "idx".
   */
  static char *sha1_get_pack_name(const unsigned char *sha1,
 -                              char **name, char **base, const char *which)
 +                              struct strbuf *buf,
 +                              const char *which)
  {
 -      static const char hex[] = "0123456789abcdef";
 -      char *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.%s",
 -                      sha1_file_directory, which);
 -              *name = *base + len + 11;
 -      }
 -
 -      buf = *name;
 -
 -      for (i = 0; i < 20; i++) {
 -              unsigned int val = *sha1++;
 -              *buf++ = hex[val >> 4];
 -              *buf++ = hex[val & 0xf];
 -      }
 -
 -      return *base;
 +      strbuf_reset(buf);
 +      strbuf_addf(buf, "%s/pack/pack-%s.%s", get_object_directory(),
 +                  sha1_to_hex(sha1), which);
 +      return buf->buf;
  }
  
  char *sha1_pack_name(const unsigned char *sha1)
  {
 -      static char *name, *base;
 -
 -      return sha1_get_pack_name(sha1, &name, &base, "pack");
 +      static struct strbuf buf = STRBUF_INIT;
 +      return sha1_get_pack_name(sha1, &buf, "pack");
  }
  
  char *sha1_pack_index_name(const unsigned char *sha1)
  {
 -      static char *name, *base;
 -
 -      return sha1_get_pack_name(sha1, &name, &base, "idx");
 +      static struct strbuf buf = STRBUF_INIT;
 +      return sha1_get_pack_name(sha1, &buf, "idx");
  }
  
  struct alternate_object_database *alt_odb_list;
@@@ -358,12 -376,15 +358,12 @@@ void read_info_alternates(const char * 
        char *map;
        size_t mapsz;
        struct stat st;
 -      const char alt_file_name[] = "info/alternates";
 -      /* Given that relative_base is no longer than PATH_MAX,
 -         ensure that "path" has enough space to append "/", the
 -         file name, "info/alternates", and a trailing NUL.  */
 -      char path[PATH_MAX + 1 + sizeof alt_file_name];
 +      char *path;
        int fd;
  
 -      sprintf(path, "%s/%s", relative_base, alt_file_name);
 +      path = xstrfmt("%s/info/alternates", relative_base);
        fd = git_open_noatime(path);
 +      free(path);
        if (fd < 0)
                return;
        if (fstat(fd, &st) || (st.st_size == 0)) {
  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"), LOCK_DIE_ON_ERROR);
 -      char *alt = mkpath("%s\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, strlen(alt), '\n', NULL, 0);
 +      char *alts = git_pathdup("objects/info/alternates");
 +      FILE *in, *out;
 +
 +      hold_lock_file_for_update(lock, alts, LOCK_DIE_ON_ERROR);
 +      out = fdopen_lock_file(lock, "w");
 +      if (!out)
 +              die_errno("unable to fdopen alternates lockfile");
 +
 +      in = fopen(alts, "r");
 +      if (in) {
 +              struct strbuf line = STRBUF_INIT;
 +              int found = 0;
 +
 +              while (strbuf_getline(&line, in, '\n') != EOF) {
 +                      if (!strcmp(reference, line.buf)) {
 +                              found = 1;
 +                              break;
 +                      }
 +                      fprintf_or_die(out, "%s\n", line.buf);
 +              }
 +
 +              strbuf_release(&line);
 +              fclose(in);
 +
 +              if (found) {
 +                      rollback_lock_file(lock);
 +                      lock = NULL;
 +              }
 +      }
 +      else if (errno != ENOENT)
 +              die_errno("unable to read alternates file");
 +
 +      if (lock) {
 +              fprintf_or_die(out, "%s\n", reference);
 +              if (commit_lock_file(lock))
 +                      die_errno("unable to move new alternates file into place");
 +              if (alt_odb_tail)
 +                      link_alt_odb_entries(reference, strlen(reference), '\n', NULL, 0);
 +      }
 +      free(alts);
  }
  
  int foreach_alt_odb(alt_odb_fn fn, void *cb)
@@@ -454,7 -442,6 +454,7 @@@ void prepare_alt_odb(void
        read_info_alternates(get_object_directory(), 0);
  }
  
 +/* Returns 1 if we have successfully freshened the file, 0 otherwise. */
  static int freshen_file(const char *fn)
  {
        struct utimbuf t;
        return !utime(fn, &t);
  }
  
 +/*
 + * All of the check_and_freshen functions return 1 if the file exists and was
 + * freshened (if freshening was requested), 0 otherwise. If they return
 + * 0, you should not assume that it is safe to skip a write of the object (it
 + * either does not exist on disk, or has a stale mtime and may be subject to
 + * pruning).
 + */
  static int check_and_freshen_file(const char *fn, int freshen)
  {
        if (access(fn, F_OK))
                return 0;
 -      if (freshen && freshen_file(fn))
 +      if (freshen && !freshen_file(fn))
                return 0;
        return 1;
  }
@@@ -652,15 -632,13 +652,15 @@@ static int check_packed_git_idx(const c
  int open_pack_index(struct packed_git *p)
  {
        char *idx_name;
 +      size_t len;
        int ret;
  
        if (p->index_data)
                return 0;
  
 -      idx_name = xstrdup(p->pack_name);
 -      strcpy(idx_name + strlen(idx_name) - strlen(".pack"), ".idx");
 +      if (!strip_suffix(p->pack_name, ".pack", &len))
 +              die("BUG: pack_name does not end in .pack");
 +      idx_name = xstrfmt("%.*s.idx", (int)len, p->pack_name);
        ret = check_packed_git_idx(idx_name, p);
        free(idx_name);
        return ret;
@@@ -728,8 -706,8 +728,8 @@@ static void mmap_limit_check(size_t len
                    (uintmax_t)length, (uintmax_t)limit);
  }
  
 -void *xmmap(void *start, size_t length,
 -      int prot, int flags, int fd, off_t offset)
 +void *xmmap_gently(void *start, size_t length,
 +                int prot, int flags, int fd, off_t offset)
  {
        void *ret;
  
                        return NULL;
                release_pack_memory(length);
                ret = mmap(start, length, prot, flags, fd, offset);
 -              if (ret == MAP_FAILED)
 -                      die_errno("Out of memory? mmap failed");
        }
        return ret;
  }
  
 +void *xmmap(void *start, size_t length,
 +      int prot, int flags, int fd, off_t offset)
 +{
 +      void *ret = xmmap_gently(start, length, prot, flags, fd, offset);
 +      if (ret == MAP_FAILED)
 +              die_errno("mmap failed");
 +      return ret;
 +}
 +
  void close_pack_windows(struct packed_git *p)
  {
        while (p->windows) {
        }
  }
  
 +static int close_pack_fd(struct packed_git *p)
 +{
 +      if (p->pack_fd < 0)
 +              return 0;
 +
 +      close(p->pack_fd);
 +      pack_open_fds--;
 +      p->pack_fd = -1;
 +
 +      return 1;
 +}
 +
 +static void close_pack(struct packed_git *p)
 +{
 +      close_pack_windows(p);
 +      close_pack_fd(p);
 +      close_pack_index(p);
 +}
 +
 +void close_all_packs(void)
 +{
 +      struct packed_git *p;
 +
 +      for (p = packed_git; p; p = p->next)
 +              if (p->do_not_close)
 +                      die("BUG! Want to close pack marked 'do-not-close'");
 +              else
 +                      close_pack(p);
 +}
 +
 +
  /*
   * The LRU pack is the one with the oldest MRU window, preferring packs
   * with no used windows, or the oldest mtime if it has no windows allocated.
@@@ -867,8 -807,12 +867,8 @@@ static int close_one_pack(void
                find_lru_pack(p, &lru_p, &mru_w, &accept_windows_inuse);
        }
  
 -      if (lru_p) {
 -              close(lru_p->pack_fd);
 -              pack_open_fds--;
 -              lru_p->pack_fd = -1;
 -              return 1;
 -      }
 +      if (lru_p)
 +              return close_pack_fd(lru_p);
  
        return 0;
  }
@@@ -908,7 -852,12 +908,7 @@@ void free_pack_by_name(const char *pack
                p = *pp;
                if (strcmp(pack_name, p->pack_name) == 0) {
                        clear_delta_base_cache();
 -                      close_pack_windows(p);
 -                      if (p->pack_fd != -1) {
 -                              close(p->pack_fd);
 -                              pack_open_fds--;
 -                      }
 -                      close_pack_index(p);
 +                      close_pack(p);
                        free(p->bad_object_sha1);
                        *pp = p->next;
                        if (last_found_pack == p)
@@@ -1042,7 -991,11 +1042,7 @@@ static int open_packed_git(struct packe
  {
        if (!open_packed_git_1(p))
                return 0;
 -      if (p->pack_fd != -1) {
 -              close(p->pack_fd);
 -              pack_open_fds--;
 -              p->pack_fd = -1;
 -      }
 +      close_pack_fd(p);
        return -1;
  }
  
@@@ -1108,8 -1061,11 +1108,8 @@@ unsigned char *use_pack(struct packed_g
                                        p->pack_name,
                                        strerror(errno));
                        if (!win->offset && win->len == p->pack_size
 -                              && !p->do_not_close) {
 -                              close(p->pack_fd);
 -                              pack_open_fds--;
 -                              p->pack_fd = -1;
 -                      }
 +                              && !p->do_not_close)
 +                              close_pack_fd(p);
                        pack_mmap_calls++;
                        pack_open_windows++;
                        if (pack_mapped > peak_pack_mapped)
@@@ -1144,12 -1100,11 +1144,12 @@@ static void try_to_free_pack_memory(siz
        release_pack_memory(size);
  }
  
 -struct packed_git *add_packed_git(const char *path, int path_len, int local)
 +struct packed_git *add_packed_git(const char *path, size_t path_len, int local)
  {
        static int have_set_try_to_free_routine;
        struct stat st;
 -      struct packed_git *p = alloc_packed_git(path_len + 2);
 +      size_t alloc;
 +      struct packed_git *p;
  
        if (!have_set_try_to_free_routine) {
                have_set_try_to_free_routine = 1;
         * Make sure a corresponding .pack file exists and that
         * the index looks sane.
         */
 -      path_len -= strlen(".idx");
 -      if (path_len < 1) {
 -              free(p);
 +      if (!strip_suffix_mem(path, &path_len, ".idx"))
                return NULL;
 -      }
 +
 +      /*
 +       * ".pack" is long enough to hold any suffix we're adding (and
 +       * the use xsnprintf double-checks that)
 +       */
 +      alloc = path_len + strlen(".pack") + 1;
 +      p = alloc_packed_git(alloc);
        memcpy(p->pack_name, path, path_len);
  
 -      strcpy(p->pack_name + path_len, ".keep");
 +      xsnprintf(p->pack_name + path_len, alloc - path_len, ".keep");
        if (!access(p->pack_name, F_OK))
                p->pack_keep = 1;
  
 -      strcpy(p->pack_name + path_len, ".pack");
 +      xsnprintf(p->pack_name + path_len, alloc - path_len, ".pack");
        if (stat(p->pack_name, &st) || !S_ISREG(st.st_mode)) {
                free(p);
                return NULL;
  struct packed_git *parse_pack_index(unsigned char *sha1, const char *idx_path)
  {
        const char *path = sha1_pack_name(sha1);
 -      struct packed_git *p = alloc_packed_git(strlen(path) + 1);
 +      int alloc = strlen(path) + 1;
 +      struct packed_git *p = alloc_packed_git(alloc);
  
 -      strcpy(p->pack_name, path);
 +      memcpy(p->pack_name, path, alloc); /* includes NUL */
        hashcpy(p->sha1, sha1);
        if (check_packed_git_idx(idx_path, p)) {
                free(p);
@@@ -1217,16 -1167,27 +1217,16 @@@ void install_packed_git(struct packed_g
        packed_git = pack;
  }
  
 -void (*report_garbage)(const char *desc, const char *path);
 +void (*report_garbage)(unsigned seen_bits, const char *path);
  
  static void report_helper(const struct string_list *list,
                          int seen_bits, int first, int last)
  {
 -      const char *msg;
 -      switch (seen_bits) {
 -      case 0:
 -              msg = "no corresponding .idx or .pack";
 -              break;
 -      case 1:
 -              msg = "no corresponding .idx";
 -              break;
 -      case 2:
 -              msg = "no corresponding .pack";
 -              break;
 -      default:
 +      if (seen_bits == (PACKDIR_FILE_PACK|PACKDIR_FILE_IDX))
                return;
 -      }
 +
        for (; first < last; first++)
 -              report_garbage(msg, list->items[first].string);
 +              report_garbage(seen_bits, list->items[first].string);
  }
  
  static void report_pack_garbage(struct string_list *list)
        if (!report_garbage)
                return;
  
 -      sort_string_list(list);
 +      string_list_sort(list);
  
        for (i = 0; i < list->nr; i++) {
                const char *path = list->items[i].string;
                if (baselen == -1) {
                        const char *dot = strrchr(path, '.');
                        if (!dot) {
 -                              report_garbage("garbage found", path);
 +                              report_garbage(PACKDIR_FILE_GARBAGE, path);
                                continue;
                        }
                        baselen = dot - path + 1;
@@@ -1321,7 -1282,7 +1321,7 @@@ static void prepare_packed_git_one(cha
                    ends_with(de->d_name, ".keep"))
                        string_list_append(&garbage, path.buf);
                else
 -                      report_garbage("garbage found", path.buf);
 +                      report_garbage(PACKDIR_FILE_GARBAGE, path.buf);
        }
        closedir(dir);
        report_pack_garbage(&garbage);
@@@ -1457,7 -1418,7 +1457,7 @@@ int check_sha1_signature(const unsigne
                return -1;
  
        /* Generate the header */
 -      hdrlen = sprintf(hdr, "%s %lu", typename(obj_type), size) + 1;
 +      hdrlen = xsnprintf(hdr, sizeof(hdr), "%s %lu", typename(obj_type), size) + 1;
  
        /* Sha1.. */
        git_SHA1_Init(&c);
@@@ -1484,10 -1445,7 +1484,10 @@@ int git_open_noatime(const char *name
        static int sha1_file_open_flag = O_NOATIME;
  
        for (;;) {
 -              int fd = open(name, O_RDONLY | sha1_file_open_flag);
 +              int fd;
 +
 +              errno = 0;
 +              fd = open(name, O_RDONLY | sha1_file_open_flag);
                if (fd >= 0)
                        return fd;
  
@@@ -1605,40 -1563,6 +1605,40 @@@ int unpack_sha1_header(git_zstream *str
        return git_inflate(stream, 0);
  }
  
 +static int unpack_sha1_header_to_strbuf(git_zstream *stream, unsigned char *map,
 +                                      unsigned long mapsize, void *buffer,
 +                                      unsigned long bufsiz, struct strbuf *header)
 +{
 +      int status;
 +
 +      status = unpack_sha1_header(stream, map, mapsize, buffer, bufsiz);
 +
 +      /*
 +       * Check if entire header is unpacked in the first iteration.
 +       */
 +      if (memchr(buffer, '\0', stream->next_out - (unsigned char *)buffer))
 +              return 0;
 +
 +      /*
 +       * buffer[0..bufsiz] was not large enough.  Copy the partial
 +       * result out to header, and then append the result of further
 +       * reading the stream.
 +       */
 +      strbuf_add(header, buffer, stream->next_out - (unsigned char *)buffer);
 +      stream->next_out = buffer;
 +      stream->avail_out = bufsiz;
 +
 +      do {
 +              status = git_inflate(stream, 0);
 +              strbuf_add(header, buffer, stream->next_out - (unsigned char *)buffer);
 +              if (memchr(buffer, '\0', stream->next_out - (unsigned char *)buffer))
 +                      return 0;
 +              stream->next_out = buffer;
 +              stream->avail_out = bufsiz;
 +      } while (status != Z_STREAM_END);
 +      return -1;
 +}
 +
  static void *unpack_sha1_rest(git_zstream *stream, void *buffer, unsigned long size, const unsigned char *sha1)
  {
        int bytes = strlen(buffer) + 1;
   * too permissive for what we want to check. So do an anal
   * object header parse by hand.
   */
 -int parse_sha1_header(const char *hdr, unsigned long *sizep)
 +static int parse_sha1_header_extended(const char *hdr, struct object_info *oi,
 +                             unsigned int flags)
  {
 -      char type[10];
 -      int i;
 +      const char *type_buf = hdr;
        unsigned long size;
 +      int type, type_len = 0;
  
        /*
 -       * The type can be at most ten bytes (including the
 -       * terminating '\0' that we add), and is followed by
 +       * The type can be of any size but is followed by
         * a space.
         */
 -      i = 0;
        for (;;) {
                char c = *hdr++;
                if (c == ' ')
                        break;
 -              type[i++] = c;
 -              if (i >= sizeof(type))
 -                      return -1;
 +              type_len++;
        }
 -      type[i] = 0;
 +
 +      type = type_from_string_gently(type_buf, type_len, 1);
 +      if (oi->typename)
 +              strbuf_add(oi->typename, type_buf, type_len);
 +      /*
 +       * Set type to 0 if its an unknown object and
 +       * we're obtaining the type using '--allow-unkown-type'
 +       * option.
 +       */
 +      if ((flags & LOOKUP_UNKNOWN_OBJECT) && (type < 0))
 +              type = 0;
 +      else if (type < 0)
 +              die("invalid object type");
 +      if (oi->typep)
 +              *oi->typep = type;
  
        /*
         * The length must follow immediately, and be in canonical
                        size = size * 10 + c;
                }
        }
 -      *sizep = size;
 +
 +      if (oi->sizep)
 +              *oi->sizep = size;
  
        /*
         * The length must be followed by a zero byte
         */
 -      return *hdr ? -1 : type_from_string(type);
 +      return *hdr ? -1 : type;
 +}
 +
 +int parse_sha1_header(const char *hdr, unsigned long *sizep)
 +{
 +      struct object_info oi;
 +
 +      oi.sizep = sizep;
 +      oi.typename = NULL;
 +      oi.typep = NULL;
 +      return parse_sha1_header_extended(hdr, &oi, LOOKUP_REPLACE_OBJECT);
  }
  
  static void *unpack_sha1_file(void *map, unsigned long mapsize, enum object_type *type, unsigned long *size, const unsigned char *sha1)
@@@ -2115,7 -2016,7 +2115,7 @@@ static unsigned long pack_entry_hash(st
  {
        unsigned long hash;
  
 -      hash = (unsigned long)p + (unsigned long)base_offset;
 +      hash = (unsigned long)(intptr_t)p + (unsigned long)base_offset;
        hash += (hash >> 8) + (hash >> 16);
        return hash % MAX_DELTA_CACHE;
  }
@@@ -2571,8 -2472,10 +2571,8 @@@ static int fill_pack_entry(const unsign
         * answer, as it may have been deleted since the index was
         * loaded!
         */
 -      if (!is_pack_valid(p)) {
 -              warning("packfile %s cannot be accessed", p->pack_name);
 +      if (!is_pack_valid(p))
                return 0;
 -      }
        e->offset = offset;
        e->p = p;
        hashcpy(e->sha1, sha1);
@@@ -2620,15 -2523,13 +2620,15 @@@ struct packed_git *find_sha1_pack(cons
  }
  
  static int sha1_loose_object_info(const unsigned char *sha1,
 -                                struct object_info *oi)
 +                                struct object_info *oi,
 +                                int flags)
  {
 -      int status;
 -      unsigned long mapsize, size;
 +      int status = 0;
 +      unsigned long mapsize;
        void *map;
        git_zstream stream;
        char hdr[32];
 +      struct strbuf hdrbuf = STRBUF_INIT;
  
        if (oi->delta_base_sha1)
                hashclr(oi->delta_base_sha1);
         * return value implicitly indicates whether the
         * object even exists.
         */
 -      if (!oi->typep && !oi->sizep) {
 +      if (!oi->typep && !oi->typename && !oi->sizep) {
                struct stat st;
                if (stat_sha1_file(sha1, &st) < 0)
                        return -1;
                return -1;
        if (oi->disk_sizep)
                *oi->disk_sizep = mapsize;
 -      if (unpack_sha1_header(&stream, map, mapsize, hdr, sizeof(hdr)) < 0)
 +      if ((flags & LOOKUP_UNKNOWN_OBJECT)) {
 +              if (unpack_sha1_header_to_strbuf(&stream, map, mapsize, hdr, sizeof(hdr), &hdrbuf) < 0)
 +                      status = error("unable to unpack %s header with --allow-unknown-type",
 +                                     sha1_to_hex(sha1));
 +      } else if (unpack_sha1_header(&stream, map, mapsize, hdr, sizeof(hdr)) < 0)
                status = error("unable to unpack %s header",
                               sha1_to_hex(sha1));
 -      else if ((status = parse_sha1_header(hdr, &size)) < 0)
 +      if (status < 0)
 +              ; /* Do nothing */
 +      else if (hdrbuf.len) {
 +              if ((status = parse_sha1_header_extended(hdrbuf.buf, oi, flags)) < 0)
 +                      status = error("unable to parse %s header with --allow-unknown-type",
 +                                     sha1_to_hex(sha1));
 +      } else if ((status = parse_sha1_header_extended(hdr, oi, flags)) < 0)
                status = error("unable to parse %s header", sha1_to_hex(sha1));
 -      else if (oi->sizep)
 -              *oi->sizep = size;
        git_inflate_end(&stream);
        munmap(map, mapsize);
 -      if (oi->typep)
 +      if (status && oi->typep)
                *oi->typep = status;
 +      strbuf_release(&hdrbuf);
        return 0;
  }
  
@@@ -2683,7 -2575,6 +2683,7 @@@ int sha1_object_info_extended(const uns
        struct cached_object *co;
        struct pack_entry e;
        int rtype;
 +      enum object_type real_type;
        const unsigned char *real = lookup_replace_object_extended(sha1, flags);
  
        co = find_cached_object(real);
                        *(oi->disk_sizep) = 0;
                if (oi->delta_base_sha1)
                        hashclr(oi->delta_base_sha1);
 +              if (oi->typename)
 +                      strbuf_addstr(oi->typename, typename(co->type));
                oi->whence = OI_CACHED;
                return 0;
        }
  
        if (!find_pack_entry(real, &e)) {
                /* Most likely it's a loose object. */
 -              if (!sha1_loose_object_info(real, oi)) {
 +              if (!sha1_loose_object_info(real, oi, flags)) {
                        oi->whence = OI_LOOSE;
                        return 0;
                }
                        return -1;
        }
  
 +      /*
 +       * packed_object_info() does not follow the delta chain to
 +       * find out the real type, unless it is given oi->typep.
 +       */
 +      if (oi->typename && !oi->typep)
 +              oi->typep = &real_type;
 +
        rtype = packed_object_info(e.p, e.offset, oi);
        if (rtype < 0) {
                mark_bad_packed_object(e.p, real);
 +              if (oi->typep == &real_type)
 +                      oi->typep = NULL;
                return sha1_object_info_extended(real, oi, 0);
        } else if (in_delta_base_cache(e.p, e.offset)) {
                oi->whence = OI_DBCACHED;
                oi->u.packed.is_delta = (rtype == OBJ_REF_DELTA ||
                                         rtype == OBJ_OFS_DELTA);
        }
 +      if (oi->typename)
 +              strbuf_addstr(oi->typename, typename(*oi->typep));
 +      if (oi->typep == &real_type)
 +              oi->typep = NULL;
  
        return 0;
  }
@@@ -2923,7 -2799,7 +2923,7 @@@ static void write_sha1_file_prepare(con
        git_SHA_CTX c;
  
        /* Generate the header */
 -      *hdrlen = sprintf(hdr, "%s %lu", type, len)+1;
 +      *hdrlen = xsnprintf(hdr, *hdrlen, "%s %lu", type, len)+1;
  
        /* Sha1.. */
        git_SHA1_Init(&c);
  
  /*
   * Move the just written object into its final resting place.
 - * NEEDSWORK: this should be renamed to finalize_temp_file() as
 - * "moving" is only a part of what it does, when no patch between
 - * master to pu changes the call sites of this function.
   */
 -int move_temp_to_file(const char *tmpfile, const char *filename)
 +int finalize_object_file(const char *tmpfile, const char *filename)
  {
        int ret = 0;
  
@@@ -2986,7 -2865,7 +2986,7 @@@ int hash_sha1_file(const void *buf, uns
                     unsigned char *sha1)
  {
        char hdr[32];
 -      int hdrlen;
 +      int hdrlen = sizeof(hdr);
        write_sha1_file_prepare(buf, len, type, sha1, hdr, &hdrlen);
        return 0;
  }
@@@ -3016,31 -2895,29 +3016,31 @@@ static inline int directory_size(const 
   * 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)
 +static int create_tmpfile(struct strbuf *tmp, 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 = git_mkstemp_mode(buffer, 0444);
 +      strbuf_reset(tmp);
 +      strbuf_add(tmp, filename, dirlen);
 +      strbuf_addstr(tmp, "tmp_obj_XXXXXX");
 +      fd = git_mkstemp_mode(tmp->buf, 0444);
        if (fd < 0 && dirlen && errno == ENOENT) {
 -              /* Make sure the directory exists */
 -              memcpy(buffer, filename, dirlen);
 -              buffer[dirlen-1] = 0;
 -              if (mkdir(buffer, 0777) && errno != EEXIST)
 +              /*
 +               * Make sure the directory exists; note that the contents
 +               * of the buffer are undefined after mkstemp returns an
 +               * error, so we have to rewrite the whole buffer from
 +               * scratch.
 +               */
 +              strbuf_reset(tmp);
 +              strbuf_add(tmp, filename, dirlen - 1);
 +              if (mkdir(tmp->buf, 0777) && errno != EEXIST)
                        return -1;
 -              if (adjust_shared_perm(buffer))
 +              if (adjust_shared_perm(tmp->buf))
                        return -1;
  
                /* Try again */
 -              strcpy(buffer + dirlen - 1, "/tmp_obj_XXXXXX");
 -              fd = git_mkstemp_mode(buffer, 0444);
 +              strbuf_addstr(tmp, "/tmp_obj_XXXXXX");
 +              fd = git_mkstemp_mode(tmp->buf, 0444);
        }
        return fd;
  }
@@@ -3053,10 -2930,10 +3053,10 @@@ static int write_loose_object(const uns
        git_zstream stream;
        git_SHA_CTX c;
        unsigned char parano_sha1[20];
 -      static char tmp_file[PATH_MAX];
 +      static struct strbuf tmp_file = STRBUF_INIT;
        const char *filename = sha1_file_name(sha1);
  
 -      fd = create_tmpfile(tmp_file, sizeof(tmp_file), filename);
 +      fd = create_tmpfile(&tmp_file, filename);
        if (fd < 0) {
                if (errno == EACCES)
                        return error("insufficient permission for adding an object to repository database %s", get_object_directory());
        }
  
        /* Set it up */
 -      memset(&stream, 0, sizeof(stream));
        git_deflate_init(&stream, zlib_compression_level);
        stream.next_out = compressed;
        stream.avail_out = sizeof(compressed);
                struct utimbuf utb;
                utb.actime = mtime;
                utb.modtime = mtime;
 -              if (utime(tmp_file, &utb) < 0)
 +              if (utime(tmp_file.buf, &utb) < 0)
                        warning("failed utime() on %s: %s",
 -                              tmp_file, strerror(errno));
 +                              tmp_file.buf, strerror(errno));
        }
  
 -      return move_temp_to_file(tmp_file, filename);
 +      return finalize_object_file(tmp_file.buf, filename);
  }
  
  static int freshen_loose_object(const unsigned char *sha1)
@@@ -3131,42 -3009,23 +3131,42 @@@ static int freshen_packed_object(const 
        return 1;
  }
  
 -int write_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *returnsha1)
 +int write_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *sha1)
  {
 -      unsigned char sha1[20];
        char hdr[32];
 -      int hdrlen;
 +      int hdrlen = sizeof(hdr);
  
        /* 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 (freshen_packed_object(sha1) || freshen_loose_object(sha1))
                return 0;
        return write_loose_object(sha1, hdr, hdrlen, buf, len, 0);
  }
  
 +int hash_sha1_file_literally(const void *buf, unsigned long len, const char *type,
 +                           unsigned char *sha1, unsigned flags)
 +{
 +      char *header;
 +      int hdrlen, status = 0;
 +
 +      /* type string, SP, %lu of the length plus NUL must fit this */
 +      hdrlen = strlen(type) + 32;
 +      header = xmalloc(hdrlen);
 +      write_sha1_file_prepare(buf, len, type, sha1, header, &hdrlen);
 +
 +      if (!(flags & HASH_WRITE_OBJECT))
 +              goto cleanup;
 +      if (freshen_packed_object(sha1) || freshen_loose_object(sha1))
 +              goto cleanup;
 +      status = write_loose_object(sha1, header, hdrlen, buf, len, 0);
 +
 +cleanup:
 +      free(header);
 +      return status;
 +}
 +
  int force_object_loose(const unsigned char *sha1, time_t mtime)
  {
        void *buf;
        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;
 +      hdrlen = xsnprintf(hdr, sizeof(hdr), "%s %lu", typename(type), len) + 1;
        ret = write_loose_object(sha1, hdr, hdrlen, buf, len, mtime);
        free(buf);
  
@@@ -3202,7 -3061,7 +3202,7 @@@ int has_sha1_pack(const unsigned char *
        return find_pack_entry(sha1, &e);
  }
  
 -int has_sha1_file(const unsigned char *sha1)
 +int has_sha1_file_with_flags(const unsigned char *sha1, int flags)
  {
        struct pack_entry e;
  
                return 1;
        if (has_loose_object(sha1))
                return 1;
 +      if (flags & HAS_SHA1_QUICK)
 +              return 0;
        reprepare_packed_git();
        return find_pack_entry(sha1, &e);
  }
  
 +int has_object_file(const struct object_id *oid)
 +{
 +      return has_sha1_file(oid->hash);
 +}
 +
  static void check_tree(const void *buf, size_t size)
  {
        struct tree_desc desc;
@@@ -3334,7 -3186,7 +3334,7 @@@ static int index_core(unsigned char *sh
        int ret;
  
        if (!size) {
 -              ret = index_mem(sha1, NULL, size, type, path, flags);
 +              ret = index_mem(sha1, "", size, type, path, flags);
        } else if (size <= SMALL_FILE_SIZE) {
                char *buf = xmalloc(size);
                if (size == read_in_full(fd, buf, size))
@@@ -3504,12 -3356,12 +3504,12 @@@ static int for_each_file_in_obj_subdir(
                                break;
                }
        }
-       strbuf_setlen(path, baselen);
+       closedir(dir);
  
+       strbuf_setlen(path, baselen);
        if (!r && subdir_cb)
                r = subdir_cb(subdir_nr, path->buf, data);
  
-       closedir(dir);
        return r;
  }
  
@@@ -3614,19 -3466,14 +3614,19 @@@ int for_each_packed_object(each_packed_
  {
        struct packed_git *p;
        int r = 0;
 +      int pack_errors = 0;
  
        prepare_packed_git();
        for (p = packed_git; p; p = p->next) {
                if ((flags & FOR_EACH_OBJECT_LOCAL_ONLY) && !p->pack_local)
                        continue;
 +              if (open_pack_index(p)) {
 +                      pack_errors = 1;
 +                      continue;
 +              }
                r = for_each_object_in_pack(p, cb, data);
                if (r)
                        break;
        }
 -      return r;
 +      return r ? r : pack_errors;
  }