ref: add symlink ref content check for files backend

Besides the textual symref, we also allow symbolic links as the symref. So, we should also provide the consistency check as what we have done for textual symref. And also we consider deprecating writing the symbolic links. We first need to access whether symbolic links still be used. So, add a new fsck message "symlinkRef(INFO)" to tell the user be aware of this information. We have already introduced "files_fsck_symref_target". We should reuse this function to handle the symrefs which use legacy symbolic links. We should not check the trailing garbage for symbolic refs. Add a new parameter "symbolic_link" to disable some checks which should only be executed for textual symrefs. And we need to also generate the "referent" parameter for reusing "files_fsck_symref_target" by the following steps: 1. Use "strbuf_add_real_path" to resolve the symlink and get the absolute path "ref_content" which the symlink ref points to. 2. Generate the absolute path "abs_gitdir" of "gitdir" and combine "ref_content" and "abs_gitdir" to extract the relative path "relative_referent_path". 3. If "ref_content" is outside of "gitdir", we just set "referent" with "ref_content". Instead, we set "referent" with "relative_referent_path". Mentored-by: Patrick Steinhardt <ps@pks.im> Mentored-by: Karthik Nayak <karthik.188@gmail.com> Signed-off-by: shejialuo <shejialuo@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

shejialuo committed Nov 20, 2024 at 19:52 UTC c9f03f38821ee4472a137a3272bf6ddd7037f5fd
4 files changed +182 -4
Documentation/fsck-msgids.txt
+6
@@ -183,6 +183,12 @@
183 git@vger.kernel.org mailing list if you see this error, as
184 we need to know what tools created such a file.
185
186 +`symlinkRef`::
187 + (INFO) A symbolic link is used as a symref. Report to the
188 + git@vger.kernel.org mailing list if you see this error, as we
189 + are assessing the feasibility of dropping the support to drop
190 + creating symbolic links as symrefs.
191 +
192 `symrefTargetIsNotARef`::
193 (INFO) The target of a symbolic reference points neither to
194 a root reference nor to a reference starting with "refs/".
fsck.h
+1
@@ -86,6 +86,7 @@ enum fsck_msg_type {
86 FUNC(MAILMAP_SYMLINK, INFO) \
87 FUNC(BAD_TAG_NAME, INFO) \
88 FUNC(MISSING_TAGGER_ENTRY, INFO) \
89 + FUNC(SYMLINK_REF, INFO) \
90 FUNC(REF_MISSING_NEWLINE, INFO) \
91 FUNC(SYMREF_TARGET_IS_NOT_A_REF, INFO) \
92 FUNC(TRAILING_REF_CONTENT, INFO) \
refs/files-backend.c
+34 -4
@@ -1,6 +1,7 @@
1 #define USE_THE_REPOSITORY_VARIABLE
2
3 #include "../git-compat-util.h"
4 +#include "../abspath.h"
5 #include "../config.h"
6 #include "../copy.h"
7 #include "../environment.h"
@@ -3511,7 +3512,8 @@ typedef int (*files_fsck_refs_fn)(struct ref_store *ref_store,
3512
3513 static int files_fsck_symref_target(struct fsck_options *o,
3514 struct fsck_ref_report *report,
3514 - struct strbuf *referent)
3515 + struct strbuf *referent,
3516 + unsigned int symbolic_link)
3517 {
3518 int is_referent_root;
3519 char orig_last_byte;
@@ -3520,7 +3522,8 @@ static int files_fsck_symref_target(struct fsck_options *o,
3522
3523 orig_len = referent->len;
3524 orig_last_byte = referent->buf[orig_len - 1];
3523 - strbuf_rtrim(referent);
3525 + if (!symbolic_link)
3526 + strbuf_rtrim(referent);
3527
3528 is_referent_root = is_root_ref(referent->buf);
3529 if (!is_referent_root &&
@@ -3539,6 +3542,9 @@ static int files_fsck_symref_target(struct fsck_options *o,
3542 goto out;
3543 }
3544
3545 + if (symbolic_link)
3546 + goto out;
3547 +
3548 if (referent->len == orig_len ||
3549 (referent->len < orig_len && orig_last_byte != '\n')) {
3550 ret = fsck_report_ref(o, report,
@@ -3562,6 +3568,7 @@ static int files_fsck_refs_content(struct ref_store *ref_store,
3568 struct dir_iterator *iter)
3569 {
3570 struct strbuf ref_content = STRBUF_INIT;
3571 + struct strbuf abs_gitdir = STRBUF_INIT;
3572 struct strbuf referent = STRBUF_INIT;
3573 struct fsck_ref_report report = { 0 };
3574 const char *trailing = NULL;
@@ -3572,8 +3579,30 @@ static int files_fsck_refs_content(struct ref_store *ref_store,
3579
3580 report.path = target_name;
3581
3575 - if (S_ISLNK(iter->st.st_mode))
3582 + if (S_ISLNK(iter->st.st_mode)) {
3583 + const char *relative_referent_path = NULL;
3584 +
3585 + ret = fsck_report_ref(o, &report,
3586 + FSCK_MSG_SYMLINK_REF,
3587 + "use deprecated symbolic link for symref");
3588 +
3589 + strbuf_add_absolute_path(&abs_gitdir, ref_store->repo->gitdir);
3590 + strbuf_normalize_path(&abs_gitdir);
3591 + if (!is_dir_sep(abs_gitdir.buf[abs_gitdir.len - 1]))
3592 + strbuf_addch(&abs_gitdir, '/');
3593 +
3594 + strbuf_add_real_path(&ref_content, iter->path.buf);
3595 + skip_prefix(ref_content.buf, abs_gitdir.buf,
3596 + &relative_referent_path);
3597 +
3598 + if (relative_referent_path)
3599 + strbuf_addstr(&referent, relative_referent_path);
3600 + else
3601 + strbuf_addbuf(&referent, &ref_content);
3602 +
3603 + ret |= files_fsck_symref_target(o, &report, &referent, 1);
3604 goto cleanup;
3605 + }
3606
3607 if (strbuf_read_file(&ref_content, iter->path.buf, 0) < 0) {
3608 /*
@@ -3611,13 +3640,14 @@ static int files_fsck_refs_content(struct ref_store *ref_store,
3640 goto cleanup;
3641 }
3642 } else {
3614 - ret = files_fsck_symref_target(o, &report, &referent);
3643 + ret = files_fsck_symref_target(o, &report, &referent, 0);
3644 goto cleanup;
3645 }
3646
3647 cleanup:
3648 strbuf_release(&ref_content);
3649 strbuf_release(&referent);
3650 + strbuf_release(&abs_gitdir);
3651 return ret;
3652 }
3653
t/t0602-reffiles-fsck.sh
+141
@@ -395,6 +395,147 @@ test_expect_success 'the target of the textual symref should be checked' '
395 done
396 '
397
398 +test_expect_success SYMLINKS 'symlink symref content should be checked' '
399 + test_when_finished "rm -rf repo" &&
400 + git init repo &&
401 + branch_dir_prefix=.git/refs/heads &&
402 + tag_dir_prefix=.git/refs/tags &&
403 + cd repo &&
404 + test_commit default &&
405 + mkdir -p "$branch_dir_prefix/a/b" &&
406 +
407 + ln -sf ./main $branch_dir_prefix/branch-symbolic-good &&
408 + git refs verify 2>err &&
409 + cat >expect <<-EOF &&
410 + warning: refs/heads/branch-symbolic-good: symlinkRef: use deprecated symbolic link for symref
411 + EOF
412 + rm $branch_dir_prefix/branch-symbolic-good &&
413 + test_cmp expect err &&
414 +
415 + ln -sf ../../logs/branch-escape $branch_dir_prefix/branch-symbolic &&
416 + git refs verify 2>err &&
417 + cat >expect <<-EOF &&
418 + warning: refs/heads/branch-symbolic: symlinkRef: use deprecated symbolic link for symref
419 + warning: refs/heads/branch-symbolic: symrefTargetIsNotARef: points to non-ref target '\''logs/branch-escape'\''
420 + EOF
421 + rm $branch_dir_prefix/branch-symbolic &&
422 + test_cmp expect err &&
423 +
424 + ln -sf ./"branch " $branch_dir_prefix/branch-symbolic-bad &&
425 + test_must_fail git refs verify 2>err &&
426 + cat >expect <<-EOF &&
427 + warning: refs/heads/branch-symbolic-bad: symlinkRef: use deprecated symbolic link for symref
428 + error: refs/heads/branch-symbolic-bad: badReferentName: points to invalid refname '\''refs/heads/branch '\''
429 + EOF
430 + rm $branch_dir_prefix/branch-symbolic-bad &&
431 + test_cmp expect err &&
432 +
433 + ln -sf ./".tag" $tag_dir_prefix/tag-symbolic-1 &&
434 + test_must_fail git refs verify 2>err &&
435 + cat >expect <<-EOF &&
436 + warning: refs/tags/tag-symbolic-1: symlinkRef: use deprecated symbolic link for symref
437 + error: refs/tags/tag-symbolic-1: badReferentName: points to invalid refname '\''refs/tags/.tag'\''
438 + EOF
439 + rm $tag_dir_prefix/tag-symbolic-1 &&
440 + test_cmp expect err
441 +'
442 +
443 +test_expect_success SYMLINKS 'symlink symref content should be checked (worktree)' '
444 + test_when_finished "rm -rf repo" &&
445 + git init repo &&
446 + cd repo &&
447 + test_commit default &&
448 + git branch branch-1 &&
449 + git branch branch-2 &&
450 + git branch branch-3 &&
451 + git worktree add ./worktree-1 branch-2 &&
452 + git worktree add ./worktree-2 branch-3 &&
453 + main_worktree_refdir_prefix=.git/refs/heads &&
454 + worktree1_refdir_prefix=.git/worktrees/worktree-1/refs/worktree &&
455 + worktree2_refdir_prefix=.git/worktrees/worktree-2/refs/worktree &&
456 +
457 + (
458 + cd worktree-1 &&
459 + git update-ref refs/worktree/branch-4 refs/heads/branch-1
460 + ) &&
461 + (
462 + cd worktree-2 &&
463 + git update-ref refs/worktree/branch-4 refs/heads/branch-1
464 + ) &&
465 +
466 + ln -sf ../../../../refs/heads/good-branch $worktree1_refdir_prefix/branch-symbolic-good &&
467 + git refs verify 2>err &&
468 + cat >expect <<-EOF &&
469 + warning: worktrees/worktree-1/refs/worktree/branch-symbolic-good: symlinkRef: use deprecated symbolic link for symref
470 + EOF
471 + rm $worktree1_refdir_prefix/branch-symbolic-good &&
472 + test_cmp expect err &&
473 +
474 + ln -sf ../../../../worktrees/worktree-1/good-branch $worktree2_refdir_prefix/branch-symbolic-good &&
475 + git refs verify 2>err &&
476 + cat >expect <<-EOF &&
477 + warning: worktrees/worktree-2/refs/worktree/branch-symbolic-good: symlinkRef: use deprecated symbolic link for symref
478 + EOF
479 + rm $worktree2_refdir_prefix/branch-symbolic-good &&
480 + test_cmp expect err &&
481 +
482 + ln -sf ../../worktrees/worktree-2/good-branch $main_worktree_refdir_prefix/branch-symbolic-good &&
483 + git refs verify 2>err &&
484 + cat >expect <<-EOF &&
485 + warning: refs/heads/branch-symbolic-good: symlinkRef: use deprecated symbolic link for symref
486 + EOF
487 + rm $main_worktree_refdir_prefix/branch-symbolic-good &&
488 + test_cmp expect err &&
489 +
490 + ln -sf ../../../../logs/branch-escape $worktree1_refdir_prefix/branch-symbolic &&
491 + git refs verify 2>err &&
492 + cat >expect <<-EOF &&
493 + warning: worktrees/worktree-1/refs/worktree/branch-symbolic: symlinkRef: use deprecated symbolic link for symref
494 + warning: worktrees/worktree-1/refs/worktree/branch-symbolic: symrefTargetIsNotARef: points to non-ref target '\''logs/branch-escape'\''
495 + EOF
496 + rm $worktree1_refdir_prefix/branch-symbolic &&
497 + test_cmp expect err &&
498 +
499 + for bad_referent_name in ".tag" "branch "
500 + do
501 + ln -sf ./"$bad_referent_name" $worktree1_refdir_prefix/bad-symbolic &&
502 + test_must_fail git refs verify 2>err &&
503 + cat >expect <<-EOF &&
504 + warning: worktrees/worktree-1/refs/worktree/bad-symbolic: symlinkRef: use deprecated symbolic link for symref
505 + error: worktrees/worktree-1/refs/worktree/bad-symbolic: badReferentName: points to invalid refname '\''worktrees/worktree-1/refs/worktree/$bad_referent_name'\''
506 + EOF
507 + rm $worktree1_refdir_prefix/bad-symbolic &&
508 + test_cmp expect err &&
509 +
510 + ln -sf ../../../../refs/heads/"$bad_referent_name" $worktree1_refdir_prefix/bad-symbolic &&
511 + test_must_fail git refs verify 2>err &&
512 + cat >expect <<-EOF &&
513 + warning: worktrees/worktree-1/refs/worktree/bad-symbolic: symlinkRef: use deprecated symbolic link for symref
514 + error: worktrees/worktree-1/refs/worktree/bad-symbolic: badReferentName: points to invalid refname '\''refs/heads/$bad_referent_name'\''
515 + EOF
516 + rm $worktree1_refdir_prefix/bad-symbolic &&
517 + test_cmp expect err &&
518 +
519 + ln -sf ./"$bad_referent_name" $worktree2_refdir_prefix/bad-symbolic &&
520 + test_must_fail git refs verify 2>err &&
521 + cat >expect <<-EOF &&
522 + warning: worktrees/worktree-2/refs/worktree/bad-symbolic: symlinkRef: use deprecated symbolic link for symref
523 + error: worktrees/worktree-2/refs/worktree/bad-symbolic: badReferentName: points to invalid refname '\''worktrees/worktree-2/refs/worktree/$bad_referent_name'\''
524 + EOF
525 + rm $worktree2_refdir_prefix/bad-symbolic &&
526 + test_cmp expect err &&
527 +
528 + ln -sf ../../../../refs/heads/"$bad_referent_name" $worktree2_refdir_prefix/bad-symbolic &&
529 + test_must_fail git refs verify 2>err &&
530 + cat >expect <<-EOF &&
531 + warning: worktrees/worktree-2/refs/worktree/bad-symbolic: symlinkRef: use deprecated symbolic link for symref
532 + error: worktrees/worktree-2/refs/worktree/bad-symbolic: badReferentName: points to invalid refname '\''refs/heads/$bad_referent_name'\''
533 + EOF
534 + rm $worktree2_refdir_prefix/bad-symbolic &&
535 + test_cmp expect err || return 1
536 + done
537 +'
538 +
539 test_expect_success 'ref content checks should work with worktrees' '
540 test_when_finished "rm -rf repo" &&
541 git init repo &&