verify_path: drop clever fallthrough
[gitweb.git] / sha1_file.c
index d77b915db699f6f58731f645d6bcc188843d9eb8..dd90a785419b851adf97643db9b291ab23b01224 100644 (file)
@@ -129,8 +129,10 @@ enum scld_error safe_create_leading_directories(char *path)
                *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))
@@ -158,13 +160,85 @@ enum scld_error safe_create_leading_directories(char *path)
 
 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;
@@ -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;
@@ -1532,7 +1606,7 @@ static void mark_bad_packed_object(struct packed_git *p,
                if (!hashcmp(sha1, p->bad_object_sha1 + GIT_SHA1_RAWSZ * i))
                        return;
        p->bad_object_sha1 = xrealloc(p->bad_object_sha1,
-                                     st_mult(GIT_SHA1_RAWSZ,
+                                     st_mult(GIT_MAX_RAWSZ,
                                              st_add(p->num_bad_objects, 1)));
        hashcpy(p->bad_object_sha1 + GIT_SHA1_RAWSZ * p->num_bad_objects, sha1);
        p->num_bad_objects++;
@@ -1610,14 +1684,14 @@ int git_open_cloexec(const char *name, int flags)
                fd = open(name, flags | o_cloexec);
        }
 
-#if defined(F_GETFL) && defined(F_SETFL) && defined(FD_CLOEXEC)
+#if defined(F_GETFD) && defined(F_SETFD) && defined(FD_CLOEXEC)
        {
                static int fd_cloexec = FD_CLOEXEC;
 
                if (!o_cloexec && 0 <= fd && fd_cloexec) {
                        /* Opened w/o O_CLOEXEC?  try with fcntl(2) to add it */
-                       int flags = fcntl(fd, F_GETFL);
-                       if (fcntl(fd, F_SETFL, flags | fd_cloexec))
+                       int flags = fcntl(fd, F_GETFD);
+                       if (fcntl(fd, F_SETFD, flags | fd_cloexec))
                                fd_cloexec = 0;
                }
        }
@@ -2627,6 +2701,17 @@ const unsigned char *nth_packed_object_sha1(struct packed_git *p,
        }
 }
 
+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;
@@ -2867,7 +2952,7 @@ static int sha1_loose_object_info(const unsigned char *sha1,
        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)
@@ -3396,6 +3481,8 @@ int has_sha1_file_with_flags(const unsigned char *sha1, int flags)
 {
        struct pack_entry e;
 
+       if (!startup_info->have_repository)
+               return 0;
        if (find_pack_entry(sha1, &e))
                return 1;
        if (has_loose_object(sha1))
@@ -3673,15 +3760,15 @@ static int for_each_file_in_obj_subdir(int subdir_nr,
                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_MAX_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;
                                }
@@ -3787,13 +3874,13 @@ static int for_each_object_in_pack(struct packed_git *p, each_packed_object_fn c
        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;
        }
@@ -3828,7 +3915,7 @@ static int check_stream_sha1(git_zstream *stream,
                             const unsigned char *expected_sha1)
 {
        git_SHA_CTX c;
-       unsigned char real_sha1[GIT_SHA1_RAWSZ];
+       unsigned char real_sha1[GIT_MAX_RAWSZ];
        unsigned char buf[4096];
        unsigned long total_read;
        int status = Z_OK;
@@ -3885,7 +3972,6 @@ int read_loose_object(const char *path,
                      void **contents)
 {
        int ret = -1;
-       int fd = -1;
        void *map = NULL;
        unsigned long mapsize;
        git_zstream stream;
@@ -3935,7 +4021,5 @@ int read_loose_object(const char *path,
 out:
        if (map)
                munmap(map, mapsize);
-       if (fd >= 0)
-               close(fd);
        return ret;
 }