Merge branch 'jk/report-fail-to-read-objects-better'
authorJunio C Hamano <gitster@pobox.com>
Mon, 16 Jun 2014 17:06:15 +0000 (10:06 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 16 Jun 2014 17:06:15 +0000 (10:06 -0700)
* jk/report-fail-to-read-objects-better:
open_sha1_file: report "most interesting" errno

1  2 
sha1_file.c
diff --combined sha1_file.c
index 3e9f55f1bb19a33908be174b2fa463f8142d4cae,1e1edf4b25d74633d3605b42c5a552c284922a6c..34d527f6708fe242f75b7fa0fa98e36a5c96a3be
@@@ -60,12 -60,6 +60,12 @@@ static struct cached_object empty_tree 
        0
  };
  
 +/*
 + * A pointer to the last packed_git in which an object was found.
 + * When an object is sought, we look in this packfile first, because
 + * objects that are looked up at similar times are often in the same
 + * packfile as one another.
 + */
  static struct packed_git *last_found_pack;
  
  static struct cached_object *find_cached_object(const unsigned char *sha1)
@@@ -111,63 -105,50 +111,63 @@@ int mkdir_in_gitdir(const char *path
        return adjust_shared_perm(path);
  }
  
 -int safe_create_leading_directories(char *path)
 +enum scld_error safe_create_leading_directories(char *path)
  {
 -      char *pos = path + offset_1st_component(path);
 -      struct stat st;
 +      char *next_component = path + offset_1st_component(path);
 +      enum scld_error ret = SCLD_OK;
 +
 +      while (ret == SCLD_OK && next_component) {
 +              struct stat st;
 +              char *slash = next_component, slash_character;
 +
 +              while (*slash && !is_dir_sep(*slash))
 +                      slash++;
  
 -      while (pos) {
 -              pos = strchr(pos, '/');
 -              if (!pos)
 +              if (!*slash)
                        break;
 -              while (*++pos == '/')
 -                      ;
 -              if (!*pos)
 +
 +              next_component = slash + 1;
 +              while (is_dir_sep(*next_component))
 +                      next_component++;
 +              if (!*next_component)
                        break;
 -              *--pos = '\0';
 +
 +              slash_character = *slash;
 +              *slash = '\0';
                if (!stat(path, &st)) {
                        /* path exists */
 -                      if (!S_ISDIR(st.st_mode)) {
 -                              *pos = '/';
 -                              return -3;
 -                      }
 -              }
 -              else if (mkdir(path, 0777)) {
 +                      if (!S_ISDIR(st.st_mode))
 +                              ret = SCLD_EXISTS;
 +              } else if (mkdir(path, 0777)) {
                        if (errno == EEXIST &&
 -                          !stat(path, &st) && S_ISDIR(st.st_mode)) {
 +                          !stat(path, &st) && S_ISDIR(st.st_mode))
                                ; /* somebody created it since we checked */
 -                      } else {
 -                              *pos = '/';
 -                              return -1;
 -                      }
 -              }
 -              else if (adjust_shared_perm(path)) {
 -                      *pos = '/';
 -                      return -2;
 +                      else if (errno == ENOENT)
 +                              /*
 +                               * Either mkdir() failed because
 +                               * somebody just pruned the containing
 +                               * directory, or stat() failed because
 +                               * the file that was in our way was
 +                               * just removed.  Either way, inform
 +                               * the caller that it might be worth
 +                               * trying again:
 +                               */
 +                              ret = SCLD_VANISHED;
 +                      else
 +                              ret = SCLD_FAILED;
 +              } else if (adjust_shared_perm(path)) {
 +                      ret = SCLD_PERMS;
                }
 -              *pos++ = '/';
 +              *slash = slash_character;
        }
 -      return 0;
 +      return ret;
  }
  
 -int safe_create_leading_directories_const(const char *path)
 +enum scld_error safe_create_leading_directories_const(const char *path)
  {
        /* path points to cache entries, so xstrdup before messing with it */
        char *buf = xstrdup(path);
 -      int result = safe_create_leading_directories(buf);
 +      enum scld_error result = safe_create_leading_directories(buf);
        free(buf);
        return result;
  }
@@@ -184,7 -165,17 +184,7 @@@ static void fill_sha1_path(char *pathbu
        }
  }
  
 -/*
 - * NOTE! This returns a statically allocated buffer, so you have to be
 - * careful about using it. Do an "xstrdup()" if you need to save the
 - * filename.
 - *
 - * Also note that this returns the location for creating.  Reading
 - * SHA1 file can happen from any alternate directory listed in the
 - * DB_ENVIRONMENT environment variable if it is not found in
 - * the primary object database.
 - */
 -char *sha1_file_name(const unsigned char *sha1)
 +const char *sha1_file_name(const unsigned char *sha1)
  {
        static char buf[PATH_MAX];
        const char *objdir;
        return buf;
  }
  
 +/*
 + * Return the name of the pack or index file with the specified sha1
 + * in its filename.  *base and *name are scratch space that must be
 + * 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)
  {
@@@ -253,6 -239,8 +253,6 @@@ char *sha1_pack_index_name(const unsign
  struct alternate_object_database *alt_odb_list;
  static struct alternate_object_database **alt_odb_tail;
  
 -static int git_open_noatime(const char *name);
 -
  /*
   * Prepare alternate object database registry.
   *
@@@ -437,7 -425,8 +437,7 @@@ void prepare_alt_odb(void
  
  static int has_loose_object_local(const unsigned char *sha1)
  {
 -      char *name = sha1_file_name(sha1);
 -      return !access(name, F_OK);
 +      return !access(sha1_file_name(sha1), F_OK);
  }
  
  int has_loose_object_nonlocal(const unsigned char *sha1)
@@@ -489,12 -478,7 +489,12 @@@ void pack_report(void
                sz_fmt(pack_mapped), sz_fmt(peak_pack_mapped));
  }
  
 -static int check_packed_git_idx(const char *path,  struct packed_git *p)
 +/*
 + * Open and mmap the index file at path, perform a couple of
 + * consistency checks, then record its information to p.  Return 0 on
 + * success.
 + */
 +static int check_packed_git_idx(const char *path, struct packed_git *p)
  {
        void *idx_map;
        struct pack_idx_header *hdr;
@@@ -790,7 -774,7 +790,7 @@@ void close_pack_index(struct packed_gi
   * contain the same set of objects as an existing one.  In that case
   * the resulting file might be different even if its name would be the
   * same.  It is best to close any reference to the old pack before it is
 - * replaced on disk.  Of course no index pointers nor windows for given pack
 + * replaced on disk.  Of course no index pointers or windows for given pack
   * must subsist at this point.  If ever objects from this pack are requested
   * again, the new version of the pack will be reinitialized through
   * reprepare_packed_git().
@@@ -1126,7 -1110,7 +1126,7 @@@ static void report_helper(const struct 
        const char *msg;
        switch (seen_bits) {
        case 0:
 -              msg = "no corresponding .idx nor .pack";
 +              msg = "no corresponding .idx or .pack";
                break;
        case 1:
                msg = "no corresponding .idx";
@@@ -1235,7 -1219,6 +1235,7 @@@ static void prepare_packed_git_one(cha
  
                if (has_extension(de->d_name, ".idx") ||
                    has_extension(de->d_name, ".pack") ||
 +                  has_extension(de->d_name, ".bitmap") ||
                    has_extension(de->d_name, ".keep"))
                        string_list_append(&garbage, path);
                else
@@@ -1320,6 -1303,7 +1320,6 @@@ void prepare_packed_git(void
  
  void reprepare_packed_git(void)
  {
 -      discard_revindex();
        prepare_packed_git_run_once = 0;
        prepare_packed_git();
  }
@@@ -1396,7 -1380,7 +1396,7 @@@ int check_sha1_signature(const unsigne
        return hashcmp(sha1, real_sha1) ? -1 : 0;
  }
  
 -static int git_open_noatime(const char *name)
 +int git_open_noatime(const char *name)
  {
        static int sha1_file_open_flag = O_NOATIME;
  
  
  static int stat_sha1_file(const unsigned char *sha1, struct stat *st)
  {
 -      char *name = sha1_file_name(sha1);
        struct alternate_object_database *alt;
  
 -      if (!lstat(name, st))
 +      if (!lstat(sha1_file_name(sha1), st))
                return 0;
  
        prepare_alt_odb();
        errno = ENOENT;
        for (alt = alt_odb_list; alt; alt = alt->next) {
 -              name = alt->name;
 -              fill_sha1_path(name, sha1);
 +              fill_sha1_path(alt->name, sha1);
                if (!lstat(alt->base, st))
                        return 0;
        }
  static int open_sha1_file(const unsigned char *sha1)
  {
        int fd;
 -      char *name = sha1_file_name(sha1);
        struct alternate_object_database *alt;
+       int most_interesting_errno;
  
 -      fd = git_open_noatime(name);
 +      fd = git_open_noatime(sha1_file_name(sha1));
        if (fd >= 0)
                return fd;
+       most_interesting_errno = errno;
  
        prepare_alt_odb();
-       errno = ENOENT;
        for (alt = alt_odb_list; alt; alt = alt->next) {
 -              name = alt->name;
 -              fill_sha1_path(name, sha1);
 +              fill_sha1_path(alt->name, sha1);
                fd = git_open_noatime(alt->base);
                if (fd >= 0)
                        return fd;
+               if (most_interesting_errno == ENOENT)
+                       most_interesting_errno = errno;
        }
+       errno = most_interesting_errno;
        return -1;
  }
  
@@@ -1477,6 -1469,51 +1481,6 @@@ void *map_sha1_file(const unsigned cha
        return map;
  }
  
 -/*
 - * There used to be a second loose object header format which
 - * was meant to mimic the in-pack format, allowing for direct
 - * copy of the object data.  This format turned up not to be
 - * really worth it and we no longer write loose objects in that
 - * format.
 - */
 -static int experimental_loose_object(unsigned char *map)
 -{
 -      unsigned int word;
 -
 -      /*
 -       * We must determine if the buffer contains the standard
 -       * zlib-deflated stream or the experimental format based
 -       * on the in-pack object format. Compare the header byte
 -       * for each format:
 -       *
 -       * RFC1950 zlib w/ deflate : 0www1000 : 0 <= www <= 7
 -       * Experimental pack-based : Stttssss : ttt = 1,2,3,4
 -       *
 -       * If bit 7 is clear and bits 0-3 equal 8, the buffer MUST be
 -       * in standard loose-object format, UNLESS it is a Git-pack
 -       * format object *exactly* 8 bytes in size when inflated.
 -       *
 -       * However, RFC1950 also specifies that the 1st 16-bit word
 -       * must be divisible by 31 - this checksum tells us our buffer
 -       * is in the standard format, giving a false positive only if
 -       * the 1st word of the Git-pack format object happens to be
 -       * divisible by 31, ie:
 -       *      ((byte0 * 256) + byte1) % 31 = 0
 -       *   =>        0ttt10000www1000 % 31 = 0
 -       *
 -       * As it happens, this case can only arise for www=3 & ttt=1
 -       * - ie, a Commit object, which would have to be 8 bytes in
 -       * size. As no Commit can be that small, we find that the
 -       * combination of these two criteria (bitmask & checksum)
 -       * can always correctly determine the buffer format.
 -       */
 -      word = (map[0] << 8) + map[1];
 -      if ((map[0] & 0x8F) == 0x08 && !(word % 31))
 -              return 0;
 -      else
 -              return 1;
 -}
 -
  unsigned long unpack_object_header_buffer(const unsigned char *buf,
                unsigned long len, enum object_type *type, unsigned long *sizep)
  {
  
  int unpack_sha1_header(git_zstream *stream, unsigned char *map, unsigned long mapsize, void *buffer, unsigned long bufsiz)
  {
 -      unsigned long size, used;
 -      static const char valid_loose_object_type[8] = {
 -              0, /* OBJ_EXT */
 -              1, 1, 1, 1, /* "commit", "tree", "blob", "tag" */
 -              0, /* "delta" and others are invalid in a loose object */
 -      };
 -      enum object_type type;
 -
        /* Get the data stream */
        memset(stream, 0, sizeof(*stream));
        stream->next_in = map;
        stream->next_out = buffer;
        stream->avail_out = bufsiz;
  
 -      if (experimental_loose_object(map)) {
 -              /*
 -               * The old experimental format we no longer produce;
 -               * we can still read it.
 -               */
 -              used = unpack_object_header_buffer(map, mapsize, &type, &size);
 -              if (!used || !valid_loose_object_type[type])
 -                      return -1;
 -              map += used;
 -              mapsize -= used;
 -
 -              /* Set up the stream for the rest.. */
 -              stream->next_in = map;
 -              stream->avail_in = mapsize;
 -              git_inflate_init(stream);
 -
 -              /* And generate the fake traditional header */
 -              stream->total_out = 1 + snprintf(buffer, bufsiz, "%s %lu",
 -                                               typename(type), size);
 -              return 0;
 -      }
        git_inflate_init(stream);
        return git_inflate(stream, 0);
  }
@@@ -1702,38 -1768,6 +1706,38 @@@ static off_t get_delta_base(struct pack
        return base_offset;
  }
  
 +/*
 + * Like get_delta_base above, but we return the sha1 instead of the pack
 + * offset. This means it is cheaper for REF deltas (we do not have to do
 + * the final object lookup), but more expensive for OFS deltas (we
 + * have to load the revidx to convert the offset back into a sha1).
 + */
 +static const unsigned char *get_delta_base_sha1(struct packed_git *p,
 +                                              struct pack_window **w_curs,
 +                                              off_t curpos,
 +                                              enum object_type type,
 +                                              off_t delta_obj_offset)
 +{
 +      if (type == OBJ_REF_DELTA) {
 +              unsigned char *base = use_pack(p, w_curs, curpos, NULL);
 +              return base;
 +      } else if (type == OBJ_OFS_DELTA) {
 +              struct revindex_entry *revidx;
 +              off_t base_offset = get_delta_base(p, w_curs, &curpos,
 +                                                 type, delta_obj_offset);
 +
 +              if (!base_offset)
 +                      return NULL;
 +
 +              revidx = find_pack_revindex(p, base_offset);
 +              if (!revidx)
 +                      return NULL;
 +
 +              return nth_packed_object_sha1(p, revidx->nr);
 +      } else
 +              return NULL;
 +}
 +
  int unpack_object_header(struct packed_git *p,
                         struct pack_window **w_curs,
                         off_t *curpos,
@@@ -1891,22 -1925,6 +1895,22 @@@ static int packed_object_info(struct pa
                }
        }
  
 +      if (oi->delta_base_sha1) {
 +              if (type == OBJ_OFS_DELTA || type == OBJ_REF_DELTA) {
 +                      const unsigned char *base;
 +
 +                      base = get_delta_base_sha1(p, &w_curs, curpos,
 +                                                 type, obj_offset);
 +                      if (!base) {
 +                              type = OBJ_BAD;
 +                              goto out;
 +                      }
 +
 +                      hashcpy(oi->delta_base_sha1, base);
 +              } else
 +                      hashclr(oi->delta_base_sha1);
 +      }
 +
  out:
        unuse_pack(&w_curs);
        return type;
@@@ -2288,10 -2306,6 +2292,10 @@@ void *unpack_entry(struct packed_git *p
        *final_size = size;
  
        unuse_pack(&w_curs);
 +
 +      if (delta_stack != small_delta_stack)
 +              free(delta_stack);
 +
        return data;
  }
  
@@@ -2451,10 -2465,6 +2455,10 @@@ static int fill_pack_entry(const unsign
        return 1;
  }
  
 +/*
 + * Iff a pack file contains the object named by sha1, return true and
 + * store its location to e.
 + */
  static int find_pack_entry(const unsigned char *sha1, struct pack_entry *e)
  {
        struct packed_git *p;
                return 1;
  
        for (p = packed_git; p; p = p->next) {
 -              if (p == last_found_pack || !fill_pack_entry(sha1, e, p))
 -                      continue;
 +              if (p == last_found_pack)
 +                      continue; /* we already checked this one */
  
 -              last_found_pack = p;
 -              return 1;
 +              if (fill_pack_entry(sha1, e, p)) {
 +                      last_found_pack = p;
 +                      return 1;
 +              }
        }
        return 0;
  }
@@@ -2500,9 -2508,6 +2504,9 @@@ static int sha1_loose_object_info(cons
        git_zstream stream;
        char hdr[32];
  
 +      if (oi->delta_base_sha1)
 +              hashclr(oi->delta_base_sha1);
 +
        /*
         * If we don't care about type or size, then we don't
         * need to look inside the object at all. Note that we
        return 0;
  }
  
 -int sha1_object_info_extended(const unsigned char *sha1, struct object_info *oi)
 +int sha1_object_info_extended(const unsigned char *sha1, struct object_info *oi, unsigned flags)
  {
        struct cached_object *co;
        struct pack_entry e;
        int rtype;
 +      const unsigned char *real = lookup_replace_object_extended(sha1, flags);
  
 -      co = find_cached_object(sha1);
 +      co = find_cached_object(real);
        if (co) {
                if (oi->typep)
                        *(oi->typep) = co->type;
                        *(oi->sizep) = co->size;
                if (oi->disk_sizep)
                        *(oi->disk_sizep) = 0;
 +              if (oi->delta_base_sha1)
 +                      hashclr(oi->delta_base_sha1);
                oi->whence = OI_CACHED;
                return 0;
        }
  
 -      if (!find_pack_entry(sha1, &e)) {
 +      if (!find_pack_entry(real, &e)) {
                /* Most likely it's a loose object. */
 -              if (!sha1_loose_object_info(sha1, oi)) {
 +              if (!sha1_loose_object_info(real, oi)) {
                        oi->whence = OI_LOOSE;
                        return 0;
                }
  
                /* Not a loose object; someone else may have just packed it. */
                reprepare_packed_git();
 -              if (!find_pack_entry(sha1, &e))
 +              if (!find_pack_entry(real, &e))
                        return -1;
        }
  
        rtype = packed_object_info(e.p, e.offset, oi);
        if (rtype < 0) {
 -              mark_bad_packed_object(e.p, sha1);
 -              return sha1_object_info_extended(sha1, oi);
 +              mark_bad_packed_object(e.p, real);
 +              return sha1_object_info_extended(real, oi, 0);
        } else if (in_delta_base_cache(e.p, e.offset)) {
                oi->whence = OI_DBCACHED;
        } else {
@@@ -2598,7 -2600,7 +2602,7 @@@ int sha1_object_info(const unsigned cha
  
        oi.typep = &type;
        oi.sizep = sizep;
 -      if (sha1_object_info_extended(sha1, &oi) < 0)
 +      if (sha1_object_info_extended(sha1, &oi, LOOKUP_REPLACE_OBJECT) < 0)
                return -1;
        return type;
  }
@@@ -2635,7 -2637,12 +2639,7 @@@ int pretend_sha1_file(void *buf, unsign
        hash_sha1_file(buf, len, typename(type), sha1);
        if (has_sha1_file(sha1) || find_cached_object(sha1))
                return 0;
 -      if (cached_object_alloc <= cached_object_nr) {
 -              cached_object_alloc = alloc_nr(cached_object_alloc);
 -              cached_objects = xrealloc(cached_objects,
 -                                        sizeof(*cached_objects) *
 -                                        cached_object_alloc);
 -      }
 +      ALLOC_GROW(cached_objects, cached_object_nr + 1, cached_object_alloc);
        co = &cached_objects[cached_object_nr++];
        co->size = len;
        co->type = type;
@@@ -2683,8 -2690,10 +2687,8 @@@ void *read_sha1_file_extended(const uns
                              unsigned flag)
  {
        void *data;
 -      char *path;
        const struct packed_git *p;
 -      const unsigned char *repl = (flag & READ_SHA1_FILE_REPLACE)
 -              ? lookup_replace_object(sha1) : sha1;
 +      const unsigned char *repl = lookup_replace_object_extended(sha1, flag);
  
        errno = 0;
        data = read_object(repl, type, size);
                    sha1_to_hex(repl), sha1_to_hex(sha1));
  
        if (has_loose_object(repl)) {
 -              path = sha1_file_name(sha1);
 +              const char *path = sha1_file_name(sha1);
 +
                die("loose object %s (stored in %s) is corrupt",
                    sha1_to_hex(repl), path);
        }
@@@ -2899,9 -2907,10 +2903,9 @@@ static int write_loose_object(const uns
        git_zstream stream;
        git_SHA_CTX c;
        unsigned char parano_sha1[20];
 -      char *filename;
        static char tmp_file[PATH_MAX];
 +      const char *filename = sha1_file_name(sha1);
  
 -      filename = sha1_file_name(sha1);
        fd = create_tmpfile(tmp_file, sizeof(tmp_file), filename);
        if (fd < 0) {
                if (errno == EACCES)