55package  git
66
77import  (
8- 	"io " 
8+ 	"path " 
99	"sort" 
1010	"strings" 
1111
@@ -24,77 +24,57 @@ func (te *TreeEntry) Type() string {
2424	}
2525}
2626
27- // FollowLink returns the entry pointed to by a symlink 
28- func  (te  * TreeEntry ) FollowLink () (* TreeEntry , error ) {
27+ type  EntryFollowResult  struct  {
28+ 	SymlinkContent  string 
29+ 	TargetFullPath  string 
30+ 	TargetEntry     * TreeEntry 
31+ }
32+ 
33+ func  EntryFollowLink (commit  * Commit , fullPath  string , te  * TreeEntry ) (* EntryFollowResult , error ) {
2934	if  ! te .IsLink () {
30- 		return  nil , ErrSymlinkUnresolved { te . Name () , "not a symlink" } 
35+ 		return  nil , util . ErrorWrap ( util . ErrUnprocessableContent , "%q is  not a symlink" ,  fullPath ) 
3136	}
3237
33- 	// read the  link 
34- 	r ,  err   :=   te . Blob (). DataAsync () 
35- 	if  err   !=   nil  {
36- 		return  nil , err 
38+ 	// git's filename max length is 4096, hopefully a  link won't be longer than multiple of that  
39+ 	const   maxSymlinkSize   =   20   *   4096 
40+ 	if  te . Blob (). Size ()  >   maxSymlinkSize  {
41+ 		return  nil , util . ErrorWrap ( util . ErrUnprocessableContent ,  "%q content exceeds symlink limit" ,  fullPath ) 
3742	}
38- 	closed  :=  false 
39- 	defer  func () {
40- 		if  ! closed  {
41- 			_  =  r .Close ()
42- 		}
43- 	}()
44- 	buf  :=  make ([]byte , te .Size ())
45- 	_ , err  =  io .ReadFull (r , buf )
43+ 
44+ 	link , err  :=  te .Blob ().GetBlobContent (maxSymlinkSize )
4645	if  err  !=  nil  {
4746		return  nil , err 
4847	}
49- 	_  =  r .Close ()
50- 	closed  =  true 
51- 
52- 	lnk  :=  string (buf )
53- 	t  :=  te .ptree 
54- 
55- 	// traverse up directories 
56- 	for  ; t  !=  nil  &&  strings .HasPrefix (lnk , "../" ); lnk  =  lnk [3 :] {
57- 		t  =  t .ptree 
48+ 	if  strings .HasPrefix (link , "/" ) {
49+ 		// It's said that absolute path will be stored as is in Git 
50+ 		return  & EntryFollowResult {SymlinkContent : link }, util .ErrorWrap (util .ErrUnprocessableContent , "%q is an absolute symlink" , fullPath )
5851	}
5952
60- 	if  t  ==  nil  {
61- 		return  nil , ErrSymlinkUnresolved {te .Name (), "points outside of repo" }
62- 	}
63- 
64- 	target , err  :=  t .GetTreeEntryByPath (lnk )
53+ 	targetFullPath  :=  path .Join (path .Dir (fullPath ), link )
54+ 	targetEntry , err  :=  commit .GetTreeEntryByPath (targetFullPath )
6555	if  err  !=  nil  {
66- 		if  IsErrNotExist (err ) {
67- 			return  nil , ErrSymlinkUnresolved {te .Name (), "broken link" }
68- 		}
69- 		return  nil , err 
56+ 		return  & EntryFollowResult {SymlinkContent : link }, err 
7057	}
71- 	return  target , nil 
58+ 	return  & EntryFollowResult { SymlinkContent :  link ,  TargetFullPath :  targetFullPath ,  TargetEntry :  targetEntry } , nil 
7259}
7360
74- // FollowLinks returns the entry ultimately pointed to by a symlink 
75- func  (te  * TreeEntry ) FollowLinks (optLimit  ... int ) (* TreeEntry , error ) {
76- 	if  ! te .IsLink () {
77- 		return  nil , ErrSymlinkUnresolved {te .Name (), "not a symlink" }
78- 	}
61+ func  EntryFollowLinks (commit  * Commit , firstFullPath  string , firstTreeEntry  * TreeEntry , optLimit  ... int ) (res  * EntryFollowResult , err  error ) {
7962	limit  :=  util .OptionalArg (optLimit , 10 )
80- 	entry   :=  te 
63+ 	treeEntry ,  fullPath   :=  firstTreeEntry ,  firstFullPath 
8164	for  range  limit  {
82- 		if  ! entry .IsLink () {
83- 			break 
84- 		}
85- 		next , err  :=  entry .FollowLink ()
65+ 		res , err  =  EntryFollowLink (commit , fullPath , treeEntry )
8666		if  err  !=  nil  {
87- 			return  nil , err 
67+ 			return  res , err 
8868		}
89- 		if  next .ID  ==  entry .ID  {
90- 			return  nil , ErrSymlinkUnresolved {entry .Name (), "recursive link" }
69+ 		treeEntry , fullPath  =  res .TargetEntry , res .TargetFullPath 
70+ 		if  ! treeEntry .IsLink () {
71+ 			break 
9172		}
92- 		entry  =  next 
9373	}
94- 	if  entry .IsLink () {
95- 		return  nil ,  ErrSymlinkUnresolved { te . Name () , "too many levels of symbolic  links" } 
74+ 	if  treeEntry .IsLink () {
75+ 		return  res ,  util . ErrorWrap ( util . ErrUnprocessableContent , "%q has  too many links" ,  firstFullPath ) 
9676	}
97- 	return  entry , nil 
77+ 	return  res , nil 
9878}
9979
10080// returns the Tree pointed to by this TreeEntry, or nil if this is not a tree 
0 commit comments