Merge branch 'jk/loose-object-info-report-error'
authorJunio C Hamano <gitster@pobox.com>
Mon, 17 Apr 2017 06:29:30 +0000 (23:29 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 17 Apr 2017 06:29:30 +0000 (23:29 -0700)
Update error handling for codepath that deals with corrupt loose
objects.

* jk/loose-object-info-report-error:
index-pack: detect local corruption in collision check
sha1_loose_object_info: return error for corrupted objects

1  2 
builtin/index-pack.c
sha1_file.c
diff --combined builtin/index-pack.c
index 197c51912d96bd0d5cbe9f34bbc8da3f2d2623c6,9f17adb37ba05d1d7e2ca2d00283091c02499053..4ff567db475573f8f45830983b7d3716d6832b4b
@@@ -307,15 -307,14 +307,15 @@@ static const char *open_pack_file(cons
        if (from_stdin) {
                input_fd = 0;
                if (!pack_name) {
 -                      static char tmp_file[PATH_MAX];
 -                      output_fd = odb_mkstemp(tmp_file, sizeof(tmp_file),
 +                      struct strbuf tmp_file = STRBUF_INIT;
 +                      output_fd = odb_mkstemp(&tmp_file,
                                                "pack/tmp_pack_XXXXXX");
 -                      pack_name = xstrdup(tmp_file);
 -              } else
 +                      pack_name = strbuf_detach(&tmp_file, NULL);
 +              } else {
                        output_fd = open(pack_name, O_CREAT|O_EXCL|O_RDWR, 0600);
 -              if (output_fd < 0)
 -                      die_errno(_("unable to create '%s'"), pack_name);
 +                      if (output_fd < 0)
 +                              die_errno(_("unable to create '%s'"), pack_name);
 +              }
                nothread_data.pack_fd = output_fd;
        } else {
                input_fd = open(pack_name, O_RDONLY);
@@@ -810,6 -809,8 +810,8 @@@ static void sha1_object(const void *dat
                unsigned long has_size;
                read_lock();
                has_type = sha1_object_info(sha1, &has_size);
+               if (has_type < 0)
+                       die(_("cannot read existing object info %s"), sha1_to_hex(sha1));
                if (has_type != type || has_size != size)
                        die(_("SHA1 COLLISION FOUND WITH %s !"), sha1_to_hex(sha1));
                has_data = read_sha1_file(sha1, &has_type, &has_size);
@@@ -1443,11 -1444,10 +1445,11 @@@ static void final(const char *final_pac
        if (!from_stdin) {
                printf("%s\n", sha1_to_hex(sha1));
        } else {
 -              char buf[48];
 -              int len = snprintf(buf, sizeof(buf), "%s\t%s\n",
 -                                 report, sha1_to_hex(sha1));
 -              write_or_die(1, buf, len);
 +              struct strbuf buf = STRBUF_INIT;
 +
 +              strbuf_addf(&buf, "%s\t%s\n", report, sha1_to_hex(sha1));
 +              write_or_die(1, buf.buf, buf.len);
 +              strbuf_release(&buf);
  
                /*
                 * Let's just mimic git-unpack-objects here and write
diff --combined sha1_file.c
index 43990dec735099fd2e94cf73e41f318fbaf5dd38,b8fbcc54c5a43dc7d7368426e06573e512078fe1..7369f7495a06e40eb885deb2101736c9105d950e
@@@ -129,10 -129,8 +129,10 @@@ enum scld_error safe_create_leading_dir
                *slash = '\0';
                if (!stat(path, &st)) {
                        /* path exists */
 -                      if (!S_ISDIR(st.st_mode))
 +                      if (!S_ISDIR(st.st_mode)) {
 +                              errno = ENOTDIR;
                                ret = SCLD_EXISTS;
 +                      }
                } else if (mkdir(path, 0777)) {
                        if (errno == EEXIST &&
                            !stat(path, &st) && S_ISDIR(st.st_mode))
  
  enum scld_error safe_create_leading_directories_const(const char *path)
  {
 +      int save_errno;
        /* path points to cache entries, so xstrdup before messing with it */
        char *buf = xstrdup(path);
        enum scld_error result = safe_create_leading_directories(buf);
 +
 +      save_errno = errno;
        free(buf);
 +      errno = save_errno;
        return result;
  }
  
 +int raceproof_create_file(const char *path, create_file_fn fn, void *cb)
 +{
 +      /*
 +       * The number of times we will try to remove empty directories
 +       * in the way of path. This is only 1 because if another
 +       * process is racily creating directories that conflict with
 +       * us, we don't want to fight against them.
 +       */
 +      int remove_directories_remaining = 1;
 +
 +      /*
 +       * The number of times that we will try to create the
 +       * directories containing path. We are willing to attempt this
 +       * more than once, because another process could be trying to
 +       * clean up empty directories at the same time as we are
 +       * trying to create them.
 +       */
 +      int create_directories_remaining = 3;
 +
 +      /* A scratch copy of path, filled lazily if we need it: */
 +      struct strbuf path_copy = STRBUF_INIT;
 +
 +      int ret, save_errno;
 +
 +      /* Sanity check: */
 +      assert(*path);
 +
 +retry_fn:
 +      ret = fn(path, cb);
 +      save_errno = errno;
 +      if (!ret)
 +              goto out;
 +
 +      if (errno == EISDIR && remove_directories_remaining-- > 0) {
 +              /*
 +               * A directory is in the way. Maybe it is empty; try
 +               * to remove it:
 +               */
 +              if (!path_copy.len)
 +                      strbuf_addstr(&path_copy, path);
 +
 +              if (!remove_dir_recursively(&path_copy, REMOVE_DIR_EMPTY_ONLY))
 +                      goto retry_fn;
 +      } else if (errno == ENOENT && create_directories_remaining-- > 0) {
 +              /*
 +               * Maybe the containing directory didn't exist, or
 +               * maybe it was just deleted by a process that is
 +               * racing with us to clean up empty directories. Try
 +               * to create it:
 +               */
 +              enum scld_error scld_result;
 +
 +              if (!path_copy.len)
 +                      strbuf_addstr(&path_copy, path);
 +
 +              do {
 +                      scld_result = safe_create_leading_directories(path_copy.buf);
 +                      if (scld_result == SCLD_OK)
 +                              goto retry_fn;
 +              } while (scld_result == SCLD_VANISHED && create_directories_remaining-- > 0);
 +      }
 +
 +out:
 +      strbuf_release(&path_copy);
 +      errno = save_errno;
 +      return ret;
 +}
 +
  static void fill_sha1_path(struct strbuf *buf, const unsigned char *sha1)
  {
        int i;
@@@ -662,7 -588,7 +662,7 @@@ static int freshen_file(const char *fn
   * 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)
 +int check_and_freshen_file(const char *fn, int freshen)
  {
        if (access(fn, F_OK))
                return 0;
@@@ -2701,17 -2627,6 +2701,17 @@@ const unsigned char *nth_packed_object_
        }
  }
  
 +const struct object_id *nth_packed_object_oid(struct object_id *oid,
 +                                            struct packed_git *p,
 +                                            uint32_t n)
 +{
 +      const unsigned char *hash = nth_packed_object_sha1(p, n);
 +      if (!hash)
 +              return NULL;
 +      hashcpy(oid->hash, hash);
 +      return oid;
 +}
 +
  void check_pack_index_ptr(const struct packed_git *p, const void *vptr)
  {
        const unsigned char *ptr = vptr;
@@@ -2952,7 -2867,7 +2952,7 @@@ static int sha1_loose_object_info(cons
        if (status && oi->typep)
                *oi->typep = status;
        strbuf_release(&hdrbuf);
-       return 0;
+       return (status < 0) ? status : 0;
  }
  
  int sha1_object_info_extended(const unsigned char *sha1, struct object_info *oi, unsigned flags)
@@@ -3758,15 -3673,15 +3758,15 @@@ static int for_each_file_in_obj_subdir(
                strbuf_setlen(path, baselen);
                strbuf_addf(path, "/%s", de->d_name);
  
 -              if (strlen(de->d_name) == 38)  {
 -                      char hex[41];
 -                      unsigned char sha1[20];
 +              if (strlen(de->d_name) == GIT_SHA1_HEXSZ - 2)  {
 +                      char hex[GIT_SHA1_HEXSZ+1];
 +                      struct object_id oid;
  
 -                      snprintf(hex, sizeof(hex), "%02x%s",
 -                               subdir_nr, de->d_name);
 -                      if (!get_sha1_hex(hex, sha1)) {
 +                      xsnprintf(hex, sizeof(hex), "%02x%s",
 +                                subdir_nr, de->d_name);
 +                      if (!get_oid_hex(hex, &oid)) {
                                if (obj_cb) {
 -                                      r = obj_cb(sha1, path->buf, data);
 +                                      r = obj_cb(&oid, path->buf, data);
                                        if (r)
                                                break;
                                }
@@@ -3872,13 -3787,13 +3872,13 @@@ static int for_each_object_in_pack(stru
        int r = 0;
  
        for (i = 0; i < p->num_objects; i++) {
 -              const unsigned char *sha1 = nth_packed_object_sha1(p, i);
 +              struct object_id oid;
  
 -              if (!sha1)
 +              if (!nth_packed_object_oid(&oid, p, i))
                        return error("unable to get sha1 of object %u in %s",
                                     i, p->pack_name);
  
 -              r = cb(sha1, p, i, data);
 +              r = cb(&oid, p, i, data);
                if (r)
                        break;
        }