Git fork

refs/ref-cache: fix SEGFAULT when seeking in empty directories

The 'cache_ref_iterator_seek()' function is used to seek the
`ref_iterator` to the desired reference in the ref-cache mechanism. We
use the seeking functionality to implement the '--start-after' flag in
'git-for-each-ref(1)'.

When using the files-backend with packed-refs, it is possible that some
of the refs directories are empty. For e.g. just after repacking, the
'refs/heads' directory would be empty. The ref-cache seek mechanism,
doesn't take this into consideration when descending into a
subdirectory, and makes an out of bounds access, causing SEGFAULT as we
try to access entries within the directory. Fix this by breaking out of
the loop when we enter an empty directory.

Since we start with the base directory of 'refs/' which is never empty,
it is okay to perform this check after the first iteration in the
`do..while` clause.

Add tests which simulate this behavior and also provide coverage over
using the feature over packed-refs.

Helped-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Karthik Nayak <karthik.188@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>

authored by

Karthik Nayak and committed by
Junio C Hamano
351c6e71 821f583d

+66 -1
+1 -1
refs/ref-cache.c
··· 539 */ 540 break; 541 } 542 - } while (slash); 543 } 544 545 return 0;
··· 539 */ 540 break; 541 } 542 + } while (slash && dir->nr); 543 } 544 545 return 0;
+65
t/t6302-for-each-ref-filter.sh
··· 754 test_cmp expect actual 755 ' 756 757 test_done
··· 754 test_cmp expect actual 755 ' 756 757 + test_expect_success 'start after with packed refs' ' 758 + test_when_finished "rm -rf repo" && 759 + git init repo && 760 + ( 761 + cd repo && 762 + test_commit default && 763 + 764 + git update-ref --stdin <<-\EOF && 765 + create refs/heads/branch @ 766 + create refs/heads/side @ 767 + create refs/odd/spot @ 768 + create refs/tags/one @ 769 + create refs/tags/two @ 770 + commit 771 + EOF 772 + 773 + cat >expect <<-\EOF && 774 + refs/tags/default 775 + refs/tags/one 776 + refs/tags/two 777 + EOF 778 + 779 + git pack-refs --all && 780 + git for-each-ref --format="%(refname)" --start-after=refs/odd/spot >actual && 781 + test_cmp expect actual 782 + ) 783 + ' 784 + 785 + test_expect_success 'start after with packed refs and some loose refs' ' 786 + test_when_finished "rm -rf repo" && 787 + git init repo && 788 + ( 789 + cd repo && 790 + test_commit default && 791 + 792 + git update-ref --stdin <<-\EOF && 793 + create refs/heads/branch @ 794 + create refs/heads/side @ 795 + create refs/odd/spot @ 796 + create refs/tags/one @ 797 + create refs/tags/two @ 798 + commit 799 + EOF 800 + 801 + git pack-refs --all && 802 + 803 + git update-ref --stdin <<-\EOF && 804 + create refs/heads/foo @ 805 + create refs/odd/tee @ 806 + commit 807 + EOF 808 + 809 + cat >expect <<-\EOF && 810 + refs/odd/tee 811 + refs/tags/default 812 + refs/tags/one 813 + refs/tags/two 814 + EOF 815 + 816 + 817 + git for-each-ref --format="%(refname)" --start-after=refs/odd/spot >actual && 818 + test_cmp expect actual 819 + ) 820 + ' 821 + 822 test_done