1
1
#define USE_THE_REPOSITORY_VARIABLE
2
2
3
3
#include "../git-compat-util.h"
4
+ #include "../abspath.h"
4
5
#include "../config.h"
5
6
#include "../copy.h"
6
7
#include "../environment.h"
@@ -568,7 +569,7 @@ static int read_ref_internal(struct ref_store *ref_store, const char *refname,
568
569
buf = sb_contents .buf ;
569
570
570
571
ret = parse_loose_ref_contents (ref_store -> repo -> hash_algo , buf ,
571
- oid , referent , type , & myerr );
572
+ oid , referent , type , NULL , & myerr );
572
573
573
574
out :
574
575
if (ret && !myerr )
@@ -605,7 +606,7 @@ static int files_read_symbolic_ref(struct ref_store *ref_store, const char *refn
605
606
int parse_loose_ref_contents (const struct git_hash_algo * algop ,
606
607
const char * buf , struct object_id * oid ,
607
608
struct strbuf * referent , unsigned int * type ,
608
- int * failure_errno )
609
+ const char * * trailing , int * failure_errno )
609
610
{
610
611
const char * p ;
611
612
if (skip_prefix (buf , "ref:" , & buf )) {
@@ -627,6 +628,10 @@ int parse_loose_ref_contents(const struct git_hash_algo *algop,
627
628
* failure_errno = EINVAL ;
628
629
return -1 ;
629
630
}
631
+
632
+ if (trailing )
633
+ * trailing = p ;
634
+
630
635
return 0 ;
631
636
}
632
637
@@ -3504,6 +3509,181 @@ typedef int (*files_fsck_refs_fn)(struct ref_store *ref_store,
3504
3509
const char * refs_check_dir ,
3505
3510
struct dir_iterator * iter );
3506
3511
3512
+ /*
3513
+ * Check the symref "referent" and "referent_path". For textual symref,
3514
+ * "referent" would be the content after "refs:". For symlink ref,
3515
+ * "referent" would be the relative path agaignst "gitdir" which should
3516
+ * be the same as the textual symref literally.
3517
+ */
3518
+ static int files_fsck_symref_target (struct fsck_options * o ,
3519
+ struct fsck_ref_report * report ,
3520
+ struct strbuf * referent ,
3521
+ struct strbuf * referent_path ,
3522
+ unsigned int symbolic_link )
3523
+ {
3524
+ size_t len = referent -> len - 1 ;
3525
+ struct stat st ;
3526
+ int ret = 0 ;
3527
+
3528
+ if (!starts_with (referent -> buf , "refs/" )) {
3529
+ ret = fsck_report_ref (o , report ,
3530
+ FSCK_MSG_ESCAPE_REFERENT ,
3531
+ "points to ref outside the refs directory" );
3532
+ goto out ;
3533
+ }
3534
+
3535
+ if (!symbolic_link && referent -> buf [referent -> len - 1 ] != '\n' ) {
3536
+ ret = fsck_report_ref (o , report ,
3537
+ FSCK_MSG_REF_MISSING_NEWLINE ,
3538
+ "missing newline" );
3539
+ len ++ ;
3540
+ }
3541
+
3542
+ if (!symbolic_link )
3543
+ strbuf_rtrim (referent );
3544
+
3545
+ if (check_refname_format (referent -> buf , 0 )) {
3546
+ ret = fsck_report_ref (o , report ,
3547
+ FSCK_MSG_BAD_REFERENT_NAME ,
3548
+ "points to refname with invalid format" );
3549
+ goto out ;
3550
+ }
3551
+
3552
+ if (!symbolic_link && len != referent -> len ) {
3553
+ ret = fsck_report_ref (o , report ,
3554
+ FSCK_MSG_TRAILING_REF_CONTENT ,
3555
+ "trailing garbage in ref" );
3556
+ }
3557
+
3558
+ /*
3559
+ * Dangling symrefs are common and so we don't report them.
3560
+ */
3561
+ if (lstat (referent_path -> buf , & st )) {
3562
+ if (errno != ENOENT ) {
3563
+ ret = error_errno (_ ("unable to stat '%s'" ),
3564
+ referent_path -> buf );
3565
+ }
3566
+ goto out ;
3567
+ }
3568
+
3569
+ /*
3570
+ * We cannot distinguish whether "refs/heads/a" is a directory or not by
3571
+ * using "check_refname_format(referent->buf, 0)". Instead, we need to
3572
+ * check the file type of the target.
3573
+ */
3574
+ if (S_ISDIR (st .st_mode )) {
3575
+ ret = fsck_report_ref (o , report ,
3576
+ FSCK_MSG_BAD_REFERENT_FILETYPE ,
3577
+ "points to the directory" );
3578
+ goto out ;
3579
+ }
3580
+
3581
+ out :
3582
+ return ret ;
3583
+ }
3584
+
3585
+ static int files_fsck_refs_content (struct ref_store * ref_store ,
3586
+ struct fsck_options * o ,
3587
+ const char * refs_check_dir ,
3588
+ struct dir_iterator * iter )
3589
+ {
3590
+ struct strbuf referent_path = STRBUF_INIT ;
3591
+ struct strbuf ref_content = STRBUF_INIT ;
3592
+ struct strbuf abs_gitdir = STRBUF_INIT ;
3593
+ struct strbuf referent = STRBUF_INIT ;
3594
+ struct strbuf refname = STRBUF_INIT ;
3595
+ struct fsck_ref_report report = {0 };
3596
+ const char * trailing = NULL ;
3597
+ unsigned int type = 0 ;
3598
+ int failure_errno = 0 ;
3599
+ struct object_id oid ;
3600
+ int ret = 0 ;
3601
+
3602
+ strbuf_addf (& refname , "%s/%s" , refs_check_dir , iter -> relative_path );
3603
+ report .path = refname .buf ;
3604
+
3605
+ if (S_ISLNK (iter -> st .st_mode )) {
3606
+ const char * relative_referent_path ;
3607
+
3608
+ ret = fsck_report_ref (o , & report ,
3609
+ FSCK_MSG_SYMLINK_REF ,
3610
+ "use deprecated symbolic link for symref" );
3611
+
3612
+ strbuf_add_absolute_path (& abs_gitdir , ref_store -> gitdir );
3613
+ strbuf_normalize_path (& abs_gitdir );
3614
+ if (!is_dir_sep (abs_gitdir .buf [abs_gitdir .len - 1 ]))
3615
+ strbuf_addch (& abs_gitdir , '/' );
3616
+
3617
+ strbuf_add_real_path (& referent_path , iter -> path .buf );
3618
+
3619
+ if (!skip_prefix (referent_path .buf ,
3620
+ abs_gitdir .buf ,
3621
+ & relative_referent_path )) {
3622
+ ret = fsck_report_ref (o , & report ,
3623
+ FSCK_MSG_ESCAPE_REFERENT ,
3624
+ "point to target outside gitdir" );
3625
+ goto cleanup ;
3626
+ }
3627
+
3628
+ strbuf_addstr (& referent , relative_referent_path );
3629
+ ret = files_fsck_symref_target (o , & report ,
3630
+ & referent , & referent_path , 1 );
3631
+
3632
+ goto cleanup ;
3633
+ }
3634
+
3635
+ if (strbuf_read_file (& ref_content , iter -> path .buf , 0 ) < 0 ) {
3636
+ ret = error_errno (_ ("unable to read ref '%s/%s'" ),
3637
+ refs_check_dir , iter -> relative_path );
3638
+ goto cleanup ;
3639
+ }
3640
+
3641
+ if (parse_loose_ref_contents (ref_store -> repo -> hash_algo ,
3642
+ ref_content .buf , & oid , & referent ,
3643
+ & type , & trailing , & failure_errno )) {
3644
+ ret = fsck_report_ref (o , & report ,
3645
+ FSCK_MSG_BAD_REF_CONTENT ,
3646
+ "invalid ref content" );
3647
+ goto cleanup ;
3648
+ }
3649
+
3650
+ if (!(type & REF_ISSYMREF )) {
3651
+ if (!* trailing ) {
3652
+ ret = fsck_report_ref (o , & report ,
3653
+ FSCK_MSG_REF_MISSING_NEWLINE ,
3654
+ "missing newline" );
3655
+ goto cleanup ;
3656
+ }
3657
+
3658
+ if (* trailing != '\n' || * (trailing + 1 )) {
3659
+ ret = fsck_report_ref (o , & report ,
3660
+ FSCK_MSG_TRAILING_REF_CONTENT ,
3661
+ "trailing garbage in ref" );
3662
+ goto cleanup ;
3663
+ }
3664
+ } else {
3665
+ strbuf_addf (& referent_path , "%s/%s" ,
3666
+ ref_store -> gitdir , referent .buf );
3667
+ /*
3668
+ * the referent may contain the spaces and the newline, need to
3669
+ * trim for path.
3670
+ */
3671
+ strbuf_rtrim (& referent_path );
3672
+ ret = files_fsck_symref_target (o , & report ,
3673
+ & referent ,
3674
+ & referent_path ,
3675
+ 0 );
3676
+ }
3677
+
3678
+ cleanup :
3679
+ strbuf_release (& refname );
3680
+ strbuf_release (& ref_content );
3681
+ strbuf_release (& referent );
3682
+ strbuf_release (& referent_path );
3683
+ strbuf_release (& abs_gitdir );
3684
+ return ret ;
3685
+ }
3686
+
3507
3687
static int files_fsck_refs_name (struct ref_store * ref_store UNUSED ,
3508
3688
struct fsck_options * o ,
3509
3689
const char * refs_check_dir ,
@@ -3520,7 +3700,7 @@ static int files_fsck_refs_name(struct ref_store *ref_store UNUSED,
3520
3700
goto cleanup ;
3521
3701
3522
3702
if (check_refname_format (iter -> basename , REFNAME_ALLOW_ONELEVEL )) {
3523
- struct fsck_ref_report report = { . path = NULL };
3703
+ struct fsck_ref_report report = { 0 };
3524
3704
3525
3705
strbuf_addf (& sb , "%s/%s" , refs_check_dir , iter -> relative_path );
3526
3706
report .path = sb .buf ;
@@ -3586,6 +3766,7 @@ static int files_fsck_refs(struct ref_store *ref_store,
3586
3766
{
3587
3767
files_fsck_refs_fn fsck_refs_fn []= {
3588
3768
files_fsck_refs_name ,
3769
+ files_fsck_refs_content ,
3589
3770
NULL ,
3590
3771
};
3591
3772
0 commit comments