Instead of coding the retry loop inline, use raceproof_create_file() to
make lock acquisition safe against directory creation/deletion races.

Signed-off-by: Michael Haggerty <mhag...@alum.mit.edu>
---
 refs/files-backend.c | 47 +++++++++++++++++++----------------------------
 1 file changed, 19 insertions(+), 28 deletions(-)

diff --git a/refs/files-backend.c b/refs/files-backend.c
index b569762..a549942 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -1889,6 +1889,19 @@ static int remove_empty_directories(struct strbuf *path)
        return remove_dir_recursively(path, REMOVE_DIR_EMPTY_ONLY);
 }
 
+struct create_reflock_data {
+       struct lock_file *lk;
+       int lflags;
+};
+
+static int create_reflock(const char *path, void *cb)
+{
+       struct create_reflock_data *data = cb;
+
+       return hold_lock_file_for_update(data->lk, path, data->lflags) < 0
+               ? -1 : 0;
+}
+
 /*
  * Locks a ref returning the lock on success and NULL on failure.
  * On failure errno is set to something meaningful.
@@ -1906,10 +1919,9 @@ static struct ref_lock *lock_ref_sha1_basic(const char 
*refname,
        struct ref_lock *lock;
        int last_errno = 0;
        int type;
-       int lflags = 0;
        int mustexist = (old_sha1 && !is_null_sha1(old_sha1));
        int resolve_flags = 0;
-       int attempts_remaining = 3;
+       struct create_reflock_data create_reflock_data = {NULL, 0};
 
        assert(err);
 
@@ -1921,7 +1933,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char 
*refname,
                resolve_flags |= RESOLVE_REF_ALLOW_BAD_NAME;
        if (flags & REF_NODEREF) {
                resolve_flags |= RESOLVE_REF_NO_RECURSE;
-               lflags |= LOCK_NO_DEREF;
+               create_reflock_data.lflags |= LOCK_NO_DEREF;
        }
 
        refname = resolve_ref_unsafe(refname, resolve_flags,
@@ -1980,35 +1992,14 @@ static struct ref_lock *lock_ref_sha1_basic(const char 
*refname,
        lock->orig_ref_name = xstrdup(orig_refname);
        strbuf_git_path(&ref_file, "%s", refname);
 
- retry:
-       switch (safe_create_leading_directories_const(ref_file.buf)) {
-       case SCLD_OK:
-               break; /* success */
-       case SCLD_VANISHED:
-               if (--attempts_remaining > 0)
-                       goto retry;
-               /* fall through */
-       default:
+       create_reflock_data.lk = lock->lk;
+
+       if (raceproof_create_file(ref_file.buf, create_reflock, 
&create_reflock_data)) {
                last_errno = errno;
-               strbuf_addf(err, "unable to create directory for %s",
-                           ref_file.buf);
+               unable_to_lock_message(ref_file.buf, errno, err);
                goto error_return;
        }
 
-       if (hold_lock_file_for_update(lock->lk, ref_file.buf, lflags) < 0) {
-               last_errno = errno;
-               if (errno == ENOENT && --attempts_remaining > 0)
-                       /*
-                        * Maybe somebody just deleted one of the
-                        * directories leading to ref_file.  Try
-                        * again:
-                        */
-                       goto retry;
-               else {
-                       unable_to_lock_message(ref_file.buf, errno, err);
-                       goto error_return;
-               }
-       }
        if (verify_lock(lock, old_sha1, mustexist, err)) {
                last_errno = errno;
                goto error_return;
-- 
2.7.0

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to