Merge branch 'jk/write-broken-index-with-nul-sha1'
authorJunio C Hamano <gitster@pobox.com>
Tue, 17 Sep 2013 18:40:27 +0000 (11:40 -0700)
committerJunio C Hamano <gitster@pobox.com>
Tue, 17 Sep 2013 18:40:27 +0000 (11:40 -0700)
Earlier we started rejecting an attempt to add 0{40} object name to
the index and to tree objects, but it sometimes is necessary to
allow so to be able to use tools like filter-branch to correct such
broken tree objects.

* jk/write-broken-index-with-nul-sha1:
write_index: optionally allow broken null sha1s

1  2 
read-cache.c
diff --combined read-cache.c
index 885943a6df77da646481c34d2876d7fdaa5c12b8,835070e24c45fd277ca75e2c4caeee84d71deb03..6bbe1b1fb3435247e1114cbf496cd9d7640f3def
@@@ -67,61 -67,6 +67,61 @@@ void rename_index_entry_at(struct index
        add_index_entry(istate, new, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);
  }
  
 +void fill_stat_data(struct stat_data *sd, struct stat *st)
 +{
 +      sd->sd_ctime.sec = (unsigned int)st->st_ctime;
 +      sd->sd_mtime.sec = (unsigned int)st->st_mtime;
 +      sd->sd_ctime.nsec = ST_CTIME_NSEC(*st);
 +      sd->sd_mtime.nsec = ST_MTIME_NSEC(*st);
 +      sd->sd_dev = st->st_dev;
 +      sd->sd_ino = st->st_ino;
 +      sd->sd_uid = st->st_uid;
 +      sd->sd_gid = st->st_gid;
 +      sd->sd_size = st->st_size;
 +}
 +
 +int match_stat_data(const struct stat_data *sd, struct stat *st)
 +{
 +      int changed = 0;
 +
 +      if (sd->sd_mtime.sec != (unsigned int)st->st_mtime)
 +              changed |= MTIME_CHANGED;
 +      if (trust_ctime && check_stat &&
 +          sd->sd_ctime.sec != (unsigned int)st->st_ctime)
 +              changed |= CTIME_CHANGED;
 +
 +#ifdef USE_NSEC
 +      if (check_stat && sd->sd_mtime.nsec != ST_MTIME_NSEC(*st))
 +              changed |= MTIME_CHANGED;
 +      if (trust_ctime && check_stat &&
 +          sd->sd_ctime.nsec != ST_CTIME_NSEC(*st))
 +              changed |= CTIME_CHANGED;
 +#endif
 +
 +      if (check_stat) {
 +              if (sd->sd_uid != (unsigned int) st->st_uid ||
 +                      sd->sd_gid != (unsigned int) st->st_gid)
 +                      changed |= OWNER_CHANGED;
 +              if (sd->sd_ino != (unsigned int) st->st_ino)
 +                      changed |= INODE_CHANGED;
 +      }
 +
 +#ifdef USE_STDEV
 +      /*
 +       * st_dev breaks on network filesystems where different
 +       * clients will have different views of what "device"
 +       * the filesystem is on
 +       */
 +      if (check_stat && sd->sd_dev != (unsigned int) st->st_dev)
 +                      changed |= INODE_CHANGED;
 +#endif
 +
 +      if (sd->sd_size != (unsigned int) st->st_size)
 +              changed |= DATA_CHANGED;
 +
 +      return changed;
 +}
 +
  /*
   * This only updates the "non-critical" parts of the directory
   * cache, ie the parts that aren't tracked by GIT, and only used
   */
  void fill_stat_cache_info(struct cache_entry *ce, struct stat *st)
  {
 -      ce->ce_ctime.sec = (unsigned int)st->st_ctime;
 -      ce->ce_mtime.sec = (unsigned int)st->st_mtime;
 -      ce->ce_ctime.nsec = ST_CTIME_NSEC(*st);
 -      ce->ce_mtime.nsec = ST_MTIME_NSEC(*st);
 -      ce->ce_dev = st->st_dev;
 -      ce->ce_ino = st->st_ino;
 -      ce->ce_uid = st->st_uid;
 -      ce->ce_gid = st->st_gid;
 -      ce->ce_size = st->st_size;
 +      fill_stat_data(&ce->ce_stat_data, st);
  
        if (assume_unchanged)
                ce->ce_flags |= CE_VALID;
                ce_mark_uptodate(ce);
  }
  
 -static int ce_compare_data(struct cache_entry *ce, struct stat *st)
 +static int ce_compare_data(const struct cache_entry *ce, struct stat *st)
  {
        int match = -1;
        int fd = open(ce->name, O_RDONLY);
        return match;
  }
  
 -static int ce_compare_link(struct cache_entry *ce, size_t expected_size)
 +static int ce_compare_link(const struct cache_entry *ce, size_t expected_size)
  {
        int match = -1;
        void *buffer;
        return match;
  }
  
 -static int ce_compare_gitlink(struct cache_entry *ce)
 +static int ce_compare_gitlink(const struct cache_entry *ce)
  {
        unsigned char sha1[20];
  
        return hashcmp(sha1, ce->sha1);
  }
  
 -static int ce_modified_check_fs(struct cache_entry *ce, struct stat *st)
 +static int ce_modified_check_fs(const struct cache_entry *ce, struct stat *st)
  {
        switch (st->st_mode & S_IFMT) {
        case S_IFREG:
        return 0;
  }
  
 -static int ce_match_stat_basic(struct cache_entry *ce, struct stat *st)
 +static int ce_match_stat_basic(const struct cache_entry *ce, struct stat *st)
  {
        unsigned int changed = 0;
  
        default:
                die("internal error: ce_mode is %o", ce->ce_mode);
        }
 -      if (ce->ce_mtime.sec != (unsigned int)st->st_mtime)
 -              changed |= MTIME_CHANGED;
 -      if (trust_ctime && check_stat &&
 -          ce->ce_ctime.sec != (unsigned int)st->st_ctime)
 -              changed |= CTIME_CHANGED;
 -
 -#ifdef USE_NSEC
 -      if (check_stat && ce->ce_mtime.nsec != ST_MTIME_NSEC(*st))
 -              changed |= MTIME_CHANGED;
 -      if (trust_ctime && check_stat &&
 -          ce->ce_ctime.nsec != ST_CTIME_NSEC(*st))
 -              changed |= CTIME_CHANGED;
 -#endif
 -
 -      if (check_stat) {
 -              if (ce->ce_uid != (unsigned int) st->st_uid ||
 -                      ce->ce_gid != (unsigned int) st->st_gid)
 -                      changed |= OWNER_CHANGED;
 -              if (ce->ce_ino != (unsigned int) st->st_ino)
 -                      changed |= INODE_CHANGED;
 -      }
  
 -#ifdef USE_STDEV
 -      /*
 -       * st_dev breaks on network filesystems where different
 -       * clients will have different views of what "device"
 -       * the filesystem is on
 -       */
 -      if (check_stat && ce->ce_dev != (unsigned int) st->st_dev)
 -                      changed |= INODE_CHANGED;
 -#endif
 -
 -      if (ce->ce_size != (unsigned int) st->st_size)
 -              changed |= DATA_CHANGED;
 +      changed |= match_stat_data(&ce->ce_stat_data, st);
  
        /* Racily smudged entry? */
 -      if (!ce->ce_size) {
 +      if (!ce->ce_stat_data.sd_size) {
                if (!is_empty_blob_sha1(ce->sha1))
                        changed |= DATA_CHANGED;
        }
        return changed;
  }
  
 -static int is_racy_timestamp(const struct index_state *istate, struct cache_entry *ce)
 +static int is_racy_timestamp(const struct index_state *istate,
 +                           const struct cache_entry *ce)
  {
        return (!S_ISGITLINK(ce->ce_mode) &&
                istate->timestamp.sec &&
  #ifdef USE_NSEC
                 /* nanosecond timestamped files can also be racy! */
 -              (istate->timestamp.sec < ce->ce_mtime.sec ||
 -               (istate->timestamp.sec == ce->ce_mtime.sec &&
 -                istate->timestamp.nsec <= ce->ce_mtime.nsec))
 +              (istate->timestamp.sec < ce->ce_stat_data.sd_mtime.sec ||
 +               (istate->timestamp.sec == ce->ce_stat_data.sd_mtime.sec &&
 +                istate->timestamp.nsec <= ce->ce_stat_data.sd_mtime.nsec))
  #else
 -              istate->timestamp.sec <= ce->ce_mtime.sec
 +              istate->timestamp.sec <= ce->ce_stat_data.sd_mtime.sec
  #endif
                 );
  }
  
  int ie_match_stat(const struct index_state *istate,
 -                struct cache_entry *ce, struct stat *st,
 +                const struct cache_entry *ce, struct stat *st,
                  unsigned int options)
  {
        unsigned int changed;
  }
  
  int ie_modified(const struct index_state *istate,
 -              struct cache_entry *ce, struct stat *st, unsigned int options)
 +              const struct cache_entry *ce,
 +              struct stat *st, unsigned int options)
  {
        int changed, changed_fs;
  
         * then we know it is.
         */
        if ((changed & DATA_CHANGED) &&
 -          (S_ISGITLINK(ce->ce_mode) || ce->ce_size != 0))
 +          (S_ISGITLINK(ce->ce_mode) || ce->ce_stat_data.sd_size != 0))
                return changed;
  
        changed_fs = ce_modified_check_fs(ce, st);
@@@ -489,7 -472,7 +489,7 @@@ int remove_index_entry_at(struct index_
  }
  
  /*
 - * Remove all cache ententries marked for removal, that is where
 + * Remove all cache entries marked for removal, that is where
   * CE_REMOVE is set in ce_flags.  This is much more effective than
   * calling remove_index_entry_at() for each entry to be removed.
   */
@@@ -722,7 -705,7 +722,7 @@@ struct cache_entry *make_cache_entry(un
        return ce;
  }
  
 -int ce_same_name(struct cache_entry *a, struct cache_entry *b)
 +int ce_same_name(const struct cache_entry *a, const struct cache_entry *b)
  {
        int len = ce_namelen(a);
        return ce_namelen(b) == len && !memcmp(a->name, b->name, len);
@@@ -1114,8 -1097,7 +1114,8 @@@ static void show_file(const char * fmt
        printf(fmt, name);
  }
  
 -int refresh_index(struct index_state *istate, unsigned int flags, const char **pathspec,
 +int refresh_index(struct index_state *istate, unsigned int flags,
 +                const struct pathspec *pathspec,
                  char *seen, const char *header_msg)
  {
        int i;
                        continue;
  
                if (pathspec &&
 -                  !match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, seen))
 +                  !match_pathspec_depth(pathspec, ce->name, ce_namelen(ce), 0, seen))
                        filtered = 1;
  
                if (ce_stage(ce)) {
@@@ -1230,14 -1212,14 +1230,14 @@@ static struct cache_entry *refresh_cach
  struct ondisk_cache_entry {
        struct cache_time ctime;
        struct cache_time mtime;
 -      unsigned int dev;
 -      unsigned int ino;
 -      unsigned int mode;
 -      unsigned int uid;
 -      unsigned int gid;
 -      unsigned int size;
 +      uint32_t dev;
 +      uint32_t ino;
 +      uint32_t mode;
 +      uint32_t uid;
 +      uint32_t gid;
 +      uint32_t size;
        unsigned char sha1[20];
 -      unsigned short flags;
 +      uint16_t flags;
        char name[FLEX_ARRAY]; /* more */
  };
  
  struct ondisk_cache_entry_extended {
        struct cache_time ctime;
        struct cache_time mtime;
 -      unsigned int dev;
 -      unsigned int ino;
 -      unsigned int mode;
 -      unsigned int uid;
 -      unsigned int gid;
 -      unsigned int size;
 +      uint32_t dev;
 +      uint32_t ino;
 +      uint32_t mode;
 +      uint32_t uid;
 +      uint32_t gid;
 +      uint32_t size;
        unsigned char sha1[20];
 -      unsigned short flags;
 -      unsigned short flags2;
 +      uint16_t flags;
 +      uint16_t flags2;
        char name[FLEX_ARRAY]; /* more */
  };
  
@@@ -1340,16 -1322,16 +1340,16 @@@ static struct cache_entry *cache_entry_
  {
        struct cache_entry *ce = xmalloc(cache_entry_size(len));
  
 -      ce->ce_ctime.sec = ntoh_l(ondisk->ctime.sec);
 -      ce->ce_mtime.sec = ntoh_l(ondisk->mtime.sec);
 -      ce->ce_ctime.nsec = ntoh_l(ondisk->ctime.nsec);
 -      ce->ce_mtime.nsec = ntoh_l(ondisk->mtime.nsec);
 -      ce->ce_dev   = ntoh_l(ondisk->dev);
 -      ce->ce_ino   = ntoh_l(ondisk->ino);
 +      ce->ce_stat_data.sd_ctime.sec = ntoh_l(ondisk->ctime.sec);
 +      ce->ce_stat_data.sd_mtime.sec = ntoh_l(ondisk->mtime.sec);
 +      ce->ce_stat_data.sd_ctime.nsec = ntoh_l(ondisk->ctime.nsec);
 +      ce->ce_stat_data.sd_mtime.nsec = ntoh_l(ondisk->mtime.nsec);
 +      ce->ce_stat_data.sd_dev   = ntoh_l(ondisk->dev);
 +      ce->ce_stat_data.sd_ino   = ntoh_l(ondisk->ino);
        ce->ce_mode  = ntoh_l(ondisk->mode);
 -      ce->ce_uid   = ntoh_l(ondisk->uid);
 -      ce->ce_gid   = ntoh_l(ondisk->gid);
 -      ce->ce_size  = ntoh_l(ondisk->size);
 +      ce->ce_stat_data.sd_uid   = ntoh_l(ondisk->uid);
 +      ce->ce_stat_data.sd_gid   = ntoh_l(ondisk->gid);
 +      ce->ce_stat_data.sd_size  = ntoh_l(ondisk->size);
        ce->ce_flags = flags & ~CE_NAMEMASK;
        ce->ce_namelen = len;
        hashcpy(ce->sha1, ondisk->sha1);
@@@ -1627,7 -1609,7 +1627,7 @@@ static void ce_smudge_racily_clean_entr
         * The only thing we care about in this function is to smudge the
         * falsely clean entry due to touch-update-touch race, so we leave
         * everything else as they are.  We are called for entries whose
 -       * ce_mtime match the index file mtime.
 +       * ce_stat_data.sd_mtime match the index file mtime.
         *
         * Note that this actually does not do much for gitlinks, for
         * which ce_match_stat_basic() always goes to the actual
                 * file, and never calls us, so the cached size information
                 * for "frotz" stays 6 which does not match the filesystem.
                 */
 -              ce->ce_size = 0;
 +              ce->ce_stat_data.sd_size = 0;
        }
  }
  
@@@ -1676,16 -1658,16 +1676,16 @@@ static char *copy_cache_entry_to_ondisk
  {
        short flags;
  
 -      ondisk->ctime.sec = htonl(ce->ce_ctime.sec);
 -      ondisk->mtime.sec = htonl(ce->ce_mtime.sec);
 -      ondisk->ctime.nsec = htonl(ce->ce_ctime.nsec);
 -      ondisk->mtime.nsec = htonl(ce->ce_mtime.nsec);
 -      ondisk->dev  = htonl(ce->ce_dev);
 -      ondisk->ino  = htonl(ce->ce_ino);
 +      ondisk->ctime.sec = htonl(ce->ce_stat_data.sd_ctime.sec);
 +      ondisk->mtime.sec = htonl(ce->ce_stat_data.sd_mtime.sec);
 +      ondisk->ctime.nsec = htonl(ce->ce_stat_data.sd_ctime.nsec);
 +      ondisk->mtime.nsec = htonl(ce->ce_stat_data.sd_mtime.nsec);
 +      ondisk->dev  = htonl(ce->ce_stat_data.sd_dev);
 +      ondisk->ino  = htonl(ce->ce_stat_data.sd_ino);
        ondisk->mode = htonl(ce->ce_mode);
 -      ondisk->uid  = htonl(ce->ce_uid);
 -      ondisk->gid  = htonl(ce->ce_gid);
 -      ondisk->size = htonl(ce->ce_size);
 +      ondisk->uid  = htonl(ce->ce_stat_data.sd_uid);
 +      ondisk->gid  = htonl(ce->ce_stat_data.sd_gid);
 +      ondisk->size = htonl(ce->ce_stat_data.sd_size);
        hashcpy(ondisk->sha1, ce->sha1);
  
        flags = ce->ce_flags;
@@@ -1761,7 -1743,7 +1761,7 @@@ static int has_racy_timestamp(struct in
  }
  
  /*
 - * Opportunisticly update the index but do not complain if we can't
 + * Opportunistically update the index but do not complain if we can't
   */
  void update_index_if_able(struct index_state *istate, struct lock_file *lockfile)
  {
@@@ -1818,8 -1800,17 +1818,17 @@@ int write_index(struct index_state *ist
                        continue;
                if (!ce_uptodate(ce) && is_racy_timestamp(istate, ce))
                        ce_smudge_racily_clean_entry(ce);
-               if (is_null_sha1(ce->sha1))
-                       return error("cache entry has null sha1: %s", ce->name);
+               if (is_null_sha1(ce->sha1)) {
+                       static const char msg[] = "cache entry has null sha1: %s";
+                       static int allow = -1;
+                       if (allow < 0)
+                               allow = git_env_bool("GIT_ALLOW_NULL_SHA1", 0);
+                       if (allow)
+                               warning(msg, ce->name);
+                       else
+                               return error(msg, ce->name);
+               }
                if (ce_write_entry(&c, newfd, ce, previous_name) < 0)
                        return -1;
        }
@@@ -1952,33 -1943,3 +1961,33 @@@ void *read_blob_data_from_index(struct 
                *size = sz;
        return data;
  }
 +
 +void stat_validity_clear(struct stat_validity *sv)
 +{
 +      free(sv->sd);
 +      sv->sd = NULL;
 +}
 +
 +int stat_validity_check(struct stat_validity *sv, const char *path)
 +{
 +      struct stat st;
 +
 +      if (stat(path, &st) < 0)
 +              return sv->sd == NULL;
 +      if (!sv->sd)
 +              return 0;
 +      return S_ISREG(st.st_mode) && !match_stat_data(sv->sd, &st);
 +}
 +
 +void stat_validity_update(struct stat_validity *sv, int fd)
 +{
 +      struct stat st;
 +
 +      if (fstat(fd, &st) < 0 || !S_ISREG(st.st_mode))
 +              stat_validity_clear(sv);
 +      else {
 +              if (!sv->sd)
 +                      sv->sd = xcalloc(1, sizeof(struct stat_data));
 +              fill_stat_data(sv->sd, &st);
 +      }
 +}