read-cache: don't write index twice if we can't write shared index

In a0a967568e ("update-index --split-index: do not split if $GIT_DIR is read only", 2014-06-13), we tried to make sure we can still write an index, even if the shared index can not be written. We did so by just calling 'do_write_locked_index()' just before 'write_shared_index()'. 'do_write_locked_index()' always at least closes the tempfile nowadays, and used to close or commit the lockfile if COMMIT_LOCK or CLOSE_LOCK were given at the time this feature was introduced. COMMIT_LOCK or CLOSE_LOCK is passed in by most callers of 'write_locked_index()'. After calling 'write_shared_index()', we call 'write_split_index()', which calls 'do_write_locked_index()' again, which then tries to use the closed lockfile again, but in fact fails to do so as it's already closed. This eventually leads to a segfault. Make sure to write the main index only once. [nd: most of the commit message and investigation done by Thomas, I only tweaked the solution a bit] Helped-by: Thomas Gummerer <t.gummerer@gmail.com> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Nguyễn Thái Ngọc Duy committed Jan 24, 2018 at 16:38 UTC ef5b3a6c5e24c54ba4436e225b9431c63ab163f0
2 files changed +22 -2
read-cache.c
+3 -2
@@ -2561,8 +2561,9 @@ int write_locked_index(struct index_state *istate, struct lock_file *lock,
2561 if (!temp) {
2562 hashclr(si->base_sha1);
2563 ret = do_write_locked_index(istate, lock, flags);
2564 - } else
2565 - ret = write_shared_index(istate, &temp);
2564 + goto out;
2565 + }
2566 + ret = write_shared_index(istate, &temp);
2567
2568 saved_errno = errno;
2569 if (is_tempfile_active(temp))
t/t1700-split-index.sh
+19
@@ -401,4 +401,23 @@ done <<\EOF
401 0642 -rw-r---w-
402 EOF
403
404 +test_expect_success POSIXPERM,SANITY 'graceful handling when splitting index is not allowed' '
405 + test_create_repo ro &&
406 + (
407 + cd ro &&
408 + test_commit initial &&
409 + git update-index --split-index &&
410 + test -f .git/sharedindex.*
411 + ) &&
412 + cp ro/.git/index new-index &&
413 + test_when_finished "chmod u+w ro/.git" &&
414 + chmod u-w ro/.git &&
415 + GIT_INDEX_FILE="$(pwd)/new-index" git -C ro update-index --split-index &&
416 + chmod u+w ro/.git &&
417 + rm ro/.git/sharedindex.* &&
418 + GIT_INDEX_FILE=new-index git ls-files >actual &&
419 + echo initial.t >expected &&
420 + test_cmp expected actual
421 +'
422 +
423 test_done