11#define USE_THE_REPOSITORY_VARIABLE
22
33#include "../git-compat-util.h"
4+ #include "../abspath.h"
45#include "../config.h"
56#include "../copy.h"
67#include "../environment.h"
@@ -568,7 +569,7 @@ static int read_ref_internal(struct ref_store *ref_store, const char *refname,
568569 buf = sb_contents .buf ;
569570
570571 ret = parse_loose_ref_contents (ref_store -> repo -> hash_algo , buf ,
571- oid , referent , type , & myerr );
572+ oid , referent , type , NULL , & myerr );
572573
573574out :
574575 if (ret && !myerr )
@@ -605,7 +606,7 @@ static int files_read_symbolic_ref(struct ref_store *ref_store, const char *refn
605606int parse_loose_ref_contents (const struct git_hash_algo * algop ,
606607 const char * buf , struct object_id * oid ,
607608 struct strbuf * referent , unsigned int * type ,
608- int * failure_errno )
609+ const char * * trailing , int * failure_errno )
609610{
610611 const char * p ;
611612 if (skip_prefix (buf , "ref:" , & buf )) {
@@ -627,6 +628,10 @@ int parse_loose_ref_contents(const struct git_hash_algo *algop,
627628 * failure_errno = EINVAL ;
628629 return -1 ;
629630 }
631+
632+ if (trailing )
633+ * trailing = p ;
634+
630635 return 0 ;
631636}
632637
@@ -3504,6 +3509,181 @@ typedef int (*files_fsck_refs_fn)(struct ref_store *ref_store,
35043509 const char * refs_check_dir ,
35053510 struct dir_iterator * iter );
35063511
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+
35073687static int files_fsck_refs_name (struct ref_store * ref_store UNUSED ,
35083688 struct fsck_options * o ,
35093689 const char * refs_check_dir ,
@@ -3520,7 +3700,7 @@ static int files_fsck_refs_name(struct ref_store *ref_store UNUSED,
35203700 goto cleanup ;
35213701
35223702 if (check_refname_format (iter -> basename , REFNAME_ALLOW_ONELEVEL )) {
3523- struct fsck_ref_report report = { . path = NULL };
3703+ struct fsck_ref_report report = { 0 };
35243704
35253705 strbuf_addf (& sb , "%s/%s" , refs_check_dir , iter -> relative_path );
35263706 report .path = sb .buf ;
@@ -3586,6 +3766,7 @@ static int files_fsck_refs(struct ref_store *ref_store,
35863766{
35873767 files_fsck_refs_fn fsck_refs_fn []= {
35883768 files_fsck_refs_name ,
3769+ files_fsck_refs_content ,
35893770 NULL ,
35903771 };
35913772
0 commit comments