Merge branch 'sw/safe-create-leading-dir-race'
authorJunio C Hamano <gitster@pobox.com>
Tue, 2 Apr 2013 22:09:48 +0000 (15:09 -0700)
committerJunio C Hamano <gitster@pobox.com>
Tue, 2 Apr 2013 22:09:48 +0000 (15:09 -0700)
* sw/safe-create-leading-dir-race:
safe_create_leading_directories: fix race that could give a false negative

1  2 
sha1_file.c
diff --combined sha1_file.c
index 16967d3b9a86dc481a5161f0a98220e05790ca01,964c4d44ab0bca796d8366b52867555965d58b62..5f573d9b8107720b1a4f5c1da63a2d8e5b4369cf
@@@ -7,7 -7,6 +7,7 @@@
   * creation etc.
   */
  #include "cache.h"
 +#include "string-list.h"
  #include "delta.h"
  #include "pack.h"
  #include "blob.h"
@@@ -21,7 -20,6 +21,7 @@@
  #include "sha1-lookup.h"
  #include "bulk-checkin.h"
  #include "streaming.h"
 +#include "dir.h"
  
  #ifndef O_NOATIME
  #if defined(__linux__) && (defined(__i386__) || defined(__PPC__))
@@@ -124,8 -122,13 +124,13 @@@ int safe_create_leading_directories(cha
                        }
                }
                else if (mkdir(path, 0777)) {
-                       *pos = '/';
-                       return -1;
+                       if (errno == EEXIST &&
+                           !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 = '/';
@@@ -248,7 -251,7 +253,7 @@@ static int git_open_noatime(const char 
   * SHA1, an extra slash for the first level indirection, and the
   * terminating NUL.
   */
 -static int link_alt_odb_entry(const char * entry, int len, const char * relative_base, int depth)
 +static int link_alt_odb_entry(const char *entry, const char *relative_base, int depth)
  {
        const char *objdir = get_object_directory();
        struct alternate_object_database *ent;
                strbuf_addstr(&pathbuf, real_path(relative_base));
                strbuf_addch(&pathbuf, '/');
        }
 -      strbuf_add(&pathbuf, entry, len);
 +      strbuf_addstr(&pathbuf, entry);
  
        normalize_path_copy(pathbuf.buf, pathbuf.buf);
  
        return 0;
  }
  
 -static void link_alt_odb_entries(const char *alt, const char *ep, int sep,
 +static void link_alt_odb_entries(const char *alt, int len, int sep,
                                 const char *relative_base, int depth)
  {
 -      const char *cp, *last;
 +      struct string_list entries = STRING_LIST_INIT_NODUP;
 +      char *alt_copy;
 +      int i;
  
        if (depth > 5) {
                error("%s: ignoring alternate object stores, nesting too deep.",
                return;
        }
  
 -      last = alt;
 -      while (last < ep) {
 -              cp = last;
 -              if (cp < ep && *cp == '#') {
 -                      while (cp < ep && *cp != sep)
 -                              cp++;
 -                      last = cp + 1;
 +      alt_copy = xmemdupz(alt, len);
 +      string_list_split_in_place(&entries, alt_copy, sep, -1);
 +      for (i = 0; i < entries.nr; i++) {
 +              const char *entry = entries.items[i].string;
 +              if (entry[0] == '\0' || entry[0] == '#')
                        continue;
 +              if (!is_absolute_path(entry) && depth) {
 +                      error("%s: ignoring relative alternate object store %s",
 +                                      relative_base, entry);
 +              } else {
 +                      link_alt_odb_entry(entry, relative_base, depth);
                }
 -              while (cp < ep && *cp != sep)
 -                      cp++;
 -              if (last != cp) {
 -                      if (!is_absolute_path(last) && depth) {
 -                              error("%s: ignoring relative alternate object store %s",
 -                                              relative_base, last);
 -                      } else {
 -                              link_alt_odb_entry(last, cp - last,
 -                                              relative_base, depth);
 -                      }
 -              }
 -              while (cp < ep && *cp == sep)
 -                      cp++;
 -              last = cp;
        }
 +      string_list_clear(&entries, 0);
 +      free(alt_copy);
  }
  
  void read_info_alternates(const char * relative_base, int depth)
        map = xmmap(NULL, mapsz, PROT_READ, MAP_PRIVATE, fd, 0);
        close(fd);
  
 -      link_alt_odb_entries(map, map + mapsz, '\n', relative_base, depth);
 +      link_alt_odb_entries(map, mapsz, '\n', relative_base, depth);
  
        munmap(map, mapsz);
  }
@@@ -386,7 -396,7 +391,7 @@@ void add_to_alternates_file(const char 
        if (commit_lock_file(lock))
                die("could not close alternates file");
        if (alt_odb_tail)
 -              link_alt_odb_entries(alt, alt + strlen(alt), '\n', NULL, 0);
 +              link_alt_odb_entries(alt, strlen(alt), '\n', NULL, 0);
  }
  
  void foreach_alt_odb(alt_odb_fn fn, void *cb)
@@@ -410,7 -420,7 +415,7 @@@ void prepare_alt_odb(void
        if (!alt) alt = "";
  
        alt_odb_tail = &alt_odb_list;
 -      link_alt_odb_entries(alt, alt + strlen(alt), PATH_SEP, NULL, 0);
 +      link_alt_odb_entries(alt, strlen(alt), PATH_SEP, NULL, 0);
  
        read_info_alternates(get_object_directory(), 0);
  }
@@@ -1001,63 -1011,6 +1006,63 @@@ void install_packed_git(struct packed_g
        packed_git = pack;
  }
  
 +void (*report_garbage)(const char *desc, 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 nor .pack";
 +              break;
 +      case 1:
 +              msg = "no corresponding .idx";
 +              break;
 +      case 2:
 +              msg = "no corresponding .pack";
 +              break;
 +      default:
 +              return;
 +      }
 +      for (; first < last; first++)
 +              report_garbage(msg, list->items[first].string);
 +}
 +
 +static void report_pack_garbage(struct string_list *list)
 +{
 +      int i, baselen = -1, first = 0, seen_bits = 0;
 +
 +      if (!report_garbage)
 +              return;
 +
 +      sort_string_list(list);
 +
 +      for (i = 0; i < list->nr; i++) {
 +              const char *path = list->items[i].string;
 +              if (baselen != -1 &&
 +                  strncmp(path, list->items[first].string, baselen)) {
 +                      report_helper(list, seen_bits, first, i);
 +                      baselen = -1;
 +                      seen_bits = 0;
 +              }
 +              if (baselen == -1) {
 +                      const char *dot = strrchr(path, '.');
 +                      if (!dot) {
 +                              report_garbage("garbage found", path);
 +                              continue;
 +                      }
 +                      baselen = dot - path + 1;
 +                      first = i;
 +              }
 +              if (!strcmp(path + baselen, "pack"))
 +                      seen_bits |= 1;
 +              else if (!strcmp(path + baselen, "idx"))
 +                      seen_bits |= 2;
 +      }
 +      report_helper(list, seen_bits, first, list->nr);
 +}
 +
  static void prepare_packed_git_one(char *objdir, int local)
  {
        /* Ensure that this buffer is large enough so that we can
        int len;
        DIR *dir;
        struct dirent *de;
 +      struct string_list garbage = STRING_LIST_INIT_DUP;
  
        sprintf(path, "%s/pack", objdir);
        len = strlen(path);
                int namelen = strlen(de->d_name);
                struct packed_git *p;
  
 -              if (!has_extension(de->d_name, ".idx"))
 +              if (len + namelen + 1 > sizeof(path)) {
 +                      if (report_garbage) {
 +                              struct strbuf sb = STRBUF_INIT;
 +                              strbuf_addf(&sb, "%.*s/%s", len - 1, path, de->d_name);
 +                              report_garbage("path too long", sb.buf);
 +                              strbuf_release(&sb);
 +                      }
                        continue;
 +              }
  
 -              if (len + namelen + 1 > sizeof(path))
 +              if (is_dot_or_dotdot(de->d_name))
                        continue;
  
 -              /* Don't reopen a pack we already have. */
                strcpy(path + len, de->d_name);
 -              for (p = packed_git; p; p = p->next) {
 -                      if (!memcmp(path, p->pack_name, len + namelen - 4))
 -                              break;
 +
 +              if (has_extension(de->d_name, ".idx")) {
 +                      /* Don't reopen a pack we already have. */
 +                      for (p = packed_git; p; p = p->next) {
 +                              if (!memcmp(path, p->pack_name, len + namelen - 4))
 +                                      break;
 +                      }
 +                      if (p == NULL &&
 +                          /*
 +                           * See if it really is a valid .idx file with
 +                           * corresponding .pack file that we can map.
 +                           */
 +                          (p = add_packed_git(path, len + namelen, local)) != NULL)
 +                              install_packed_git(p);
                }
 -              if (p)
 -                      continue;
 -              /* See if it really is a valid .idx file with corresponding
 -               * .pack file that we can map.
 -               */
 -              p = add_packed_git(path, len + namelen, local);
 -              if (!p)
 +
 +              if (!report_garbage)
                        continue;
 -              install_packed_git(p);
 +
 +              if (has_extension(de->d_name, ".idx") ||
 +                  has_extension(de->d_name, ".pack") ||
 +                  has_extension(de->d_name, ".keep"))
 +                      string_list_append(&garbage, path);
 +              else
 +                      report_garbage("garbage found", path);
        }
        closedir(dir);
 +      report_pack_garbage(&garbage);
 +      string_list_clear(&garbage, 0);
  }
  
  static int sort_pack(const void *a_, const void *b_)