Merge branch 'jk/ref-symlink-loop'
authorJunio C Hamano <gitster@pobox.com>
Mon, 17 Oct 2016 20:25:20 +0000 (13:25 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 17 Oct 2016 20:25:20 +0000 (13:25 -0700)
A stray symbolic link in $GIT_DIR/refs/ directory could make name
resolution loop forever, which has been corrected.

* jk/ref-symlink-loop:
files_read_raw_ref: prevent infinite retry loops in general
files_read_raw_ref: avoid infinite loop on broken symlinks

refs/files-backend.c
t/t1503-rev-parse-verify.sh
index d16feb19c57b1b74594947f8730b984e94c43def..f9023939d5884e0f975d5997336313695e407ca1 100644 (file)
@@ -1353,6 +1353,7 @@ static int files_read_raw_ref(struct ref_store *ref_store,
        int fd;
        int ret = -1;
        int save_errno;
+       int remaining_retries = 3;
 
        *type = 0;
        strbuf_reset(&sb_path);
@@ -1373,8 +1374,14 @@ static int files_read_raw_ref(struct ref_store *ref_store,
         * <-> symlink) between the lstat() and reading, then
         * we don't want to report that as an error but rather
         * try again starting with the lstat().
+        *
+        * We'll keep a count of the retries, though, just to avoid
+        * any confusing situation sending us into an infinite loop.
         */
 
+       if (remaining_retries-- <= 0)
+               goto out;
+
        if (lstat(path, &st) < 0) {
                if (errno != ENOENT)
                        goto out;
@@ -1403,6 +1410,11 @@ static int files_read_raw_ref(struct ref_store *ref_store,
                        ret = 0;
                        goto out;
                }
+               /*
+                * It doesn't look like a refname; fall through to just
+                * treating it like a non-symlink, and reading whatever it
+                * points to.
+                */
        }
 
        /* Is it a directory? */
@@ -1426,7 +1438,7 @@ static int files_read_raw_ref(struct ref_store *ref_store,
         */
        fd = open(path, O_RDONLY);
        if (fd < 0) {
-               if (errno == ENOENT)
+               if (errno == ENOENT && !S_ISLNK(st.st_mode))
                        /* inconsistent with lstat; retry */
                        goto stat_ref;
                else
index ab27d0db5c5577a23e1053b58eefa63015526d0c..492edffa9cfb626b4747ead04251fc33b0502ac9 100755 (executable)
@@ -139,4 +139,9 @@ test_expect_success 'master@{n} for various n' '
        test_must_fail git rev-parse --verify master@{$Np1}
 '
 
+test_expect_success SYMLINKS 'ref resolution not confused by broken symlinks' '
+       ln -s does-not-exist .git/refs/heads/broken &&
+       test_must_fail git rev-parse --verify broken
+'
+
 test_done