Skip to content

Commit c72e1eb

Browse files
committed
fix
1 parent f53a0b8 commit c72e1eb

File tree

16 files changed

+175
-227
lines changed

16 files changed

+175
-227
lines changed

modules/fileicon/entry.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,17 @@ package fileicon
66
import "code.gitea.io/gitea/modules/git"
77

88
type EntryInfo struct {
9-
FullName string
9+
BaseName string
1010
EntryMode git.EntryMode
1111
SymlinkToMode git.EntryMode
1212
IsOpen bool
1313
}
1414

15-
func EntryInfoFromGitTreeEntry(gitEntry *git.TreeEntry) *EntryInfo {
16-
ret := &EntryInfo{FullName: gitEntry.Name(), EntryMode: gitEntry.Mode()}
15+
func EntryInfoFromGitTreeEntry(commit *git.Commit, fullPath string, gitEntry *git.TreeEntry) *EntryInfo {
16+
ret := &EntryInfo{BaseName: gitEntry.Name(), EntryMode: gitEntry.Mode()}
1717
if gitEntry.IsLink() {
18-
if te, err := gitEntry.FollowLink(); err == nil && te.IsDir() {
19-
ret.SymlinkToMode = te.Mode()
18+
if res, err := git.EntryFollowLink(commit, fullPath, gitEntry); err == nil && res.TargetEntry.IsDir() {
19+
ret.SymlinkToMode = res.TargetEntry.Mode()
2020
}
2121
}
2222
return ret

modules/fileicon/material.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ func (m *MaterialIconProvider) FindIconName(entry *EntryInfo) string {
134134
return "folder-git"
135135
}
136136

137-
fileNameLower := strings.ToLower(path.Base(entry.FullName))
137+
fileNameLower := strings.ToLower(path.Base(entry.BaseName))
138138
if entry.EntryMode.IsDir() {
139139
if s, ok := m.rules.FolderNames[fileNameLower]; ok {
140140
return s

modules/fileicon/material_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ func TestMain(m *testing.M) {
2020
func TestFindIconName(t *testing.T) {
2121
unittest.PrepareTestEnv(t)
2222
p := fileicon.DefaultMaterialIconProvider()
23-
assert.Equal(t, "php", p.FindIconName(&fileicon.EntryInfo{FullName: "foo.php", EntryMode: git.EntryModeBlob}))
24-
assert.Equal(t, "php", p.FindIconName(&fileicon.EntryInfo{FullName: "foo.PHP", EntryMode: git.EntryModeBlob}))
25-
assert.Equal(t, "javascript", p.FindIconName(&fileicon.EntryInfo{FullName: "foo.js", EntryMode: git.EntryModeBlob}))
26-
assert.Equal(t, "visualstudio", p.FindIconName(&fileicon.EntryInfo{FullName: "foo.vba", EntryMode: git.EntryModeBlob}))
23+
assert.Equal(t, "php", p.FindIconName(&fileicon.EntryInfo{BaseName: "foo.php", EntryMode: git.EntryModeBlob}))
24+
assert.Equal(t, "php", p.FindIconName(&fileicon.EntryInfo{BaseName: "foo.PHP", EntryMode: git.EntryModeBlob}))
25+
assert.Equal(t, "javascript", p.FindIconName(&fileicon.EntryInfo{BaseName: "foo.js", EntryMode: git.EntryModeBlob}))
26+
assert.Equal(t, "visualstudio", p.FindIconName(&fileicon.EntryInfo{BaseName: "foo.vba", EntryMode: git.EntryModeBlob}))
2727
}

modules/git/error.go

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -32,22 +32,6 @@ func (err ErrNotExist) Unwrap() error {
3232
return util.ErrNotExist
3333
}
3434

35-
// ErrSymlinkUnresolved entry.FollowLink error
36-
type ErrSymlinkUnresolved struct {
37-
Name string
38-
Message string
39-
}
40-
41-
func (err ErrSymlinkUnresolved) Error() string {
42-
return fmt.Sprintf("%s: %s", err.Name, err.Message)
43-
}
44-
45-
// IsErrSymlinkUnresolved if some error is ErrSymlinkUnresolved
46-
func IsErrSymlinkUnresolved(err error) bool {
47-
_, ok := err.(ErrSymlinkUnresolved)
48-
return ok
49-
}
50-
5135
// ErrBranchNotExist represents a "BranchNotExist" kind of error.
5236
type ErrBranchNotExist struct {
5337
Name string

modules/git/tree_blob_nogogit.go

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import (
1111
)
1212

1313
// GetTreeEntryByPath get the tree entries according the sub dir
14-
func (t *Tree) GetTreeEntryByPath(relpath string) (*TreeEntry, error) {
14+
func (t *Tree) GetTreeEntryByPath(relpath string) (_ *TreeEntry, err error) {
1515
if len(relpath) == 0 {
1616
return &TreeEntry{
1717
ptree: t,
@@ -21,10 +21,8 @@ func (t *Tree) GetTreeEntryByPath(relpath string) (*TreeEntry, error) {
2121
}, nil
2222
}
2323

24-
// FIXME: This should probably use git cat-file --batch to be a bit more efficient
2524
relpath = path.Clean(relpath)
2625
parts := strings.Split(relpath, "/")
27-
var err error
2826

2927
tree := t
3028
for _, name := range parts[:len(parts)-1] {
@@ -41,7 +39,6 @@ func (t *Tree) GetTreeEntryByPath(relpath string) (*TreeEntry, error) {
4139
}
4240
for _, v := range entries {
4341
if v.Name() == name {
44-
v.fullName = relpath
4542
return v, nil
4643
}
4744
}

modules/git/tree_entry.go

Lines changed: 32 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
package git
66

77
import (
8-
"io"
8+
"path"
99
"sort"
1010
"strings"
1111

@@ -24,87 +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
93-
}
94-
if entry.IsLink() {
95-
return nil, ErrSymlinkUnresolved{te.Name(), "too many levels of symbolic links"}
9673
}
97-
return entry, nil
98-
}
99-
100-
// TryFollowingLinks attempts to follow the symlinks of this entry to the origin
101-
// If that fails, it defaults to the entry itself
102-
func (te *TreeEntry) TryFollowingLinks() *TreeEntry {
103-
newEntry, err := te.FollowLinks()
104-
if err != nil {
105-
return te
74+
if treeEntry.IsLink() {
75+
return res, util.ErrorWrap(util.ErrUnprocessableContent, "%q has too many links", fullPath)
10676
}
107-
return newEntry
77+
return res, nil
10878
}
10979

11080
// returns the Tree pointed to by this TreeEntry, or nil if this is not a tree

modules/git/tree_entry_common_test.go

Lines changed: 57 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -6,101 +6,71 @@ package git
66
import (
77
"testing"
88

9+
"code.gitea.io/gitea/modules/util"
10+
911
"github.com/stretchr/testify/assert"
12+
"github.com/stretchr/testify/require"
1013
)
1114

1215
func TestFollowLink(t *testing.T) {
1316
r, err := openRepositoryWithDefaultContext("tests/repos/repo1_bare")
14-
assert.NoError(t, err)
17+
require.NoError(t, err)
1518
defer r.Close()
1619

1720
commit, err := r.GetCommit("37991dec2c8e592043f47155ce4808d4580f9123")
18-
assert.NoError(t, err)
21+
require.NoError(t, err)
1922

2023
// get the symlink
21-
lnk, err := commit.Tree.GetTreeEntryByPath("foo/bar/link_to_hello")
22-
assert.NoError(t, err)
23-
assert.True(t, lnk.IsLink())
24-
25-
// should be able to dereference to target
26-
target, err := lnk.FollowLink()
27-
assert.NoError(t, err)
28-
assert.Equal(t, "hello", target.Name())
29-
assert.Equal(t, "foo/nar/hello", target.FullPath())
30-
assert.False(t, target.IsLink())
31-
assert.Equal(t, "b14df6442ea5a1b382985a6549b85d435376c351", target.ID.String())
32-
33-
// should error when called on normal file
34-
target, err = commit.Tree.GetTreeEntryByPath("file1.txt")
35-
assert.NoError(t, err)
36-
_, err = target.FollowLink()
37-
assert.ErrorAs(t, err, ErrBadLink{})
38-
39-
// should error for broken links
40-
target, err = commit.Tree.GetTreeEntryByPath("foo/broken_link")
41-
assert.NoError(t, err)
42-
assert.True(t, target.IsLink())
43-
_, err = target.FollowLink()
44-
assert.ErrorAs(t, err, ErrBadLink{})
45-
46-
// should error for external links
47-
target, err = commit.Tree.GetTreeEntryByPath("foo/outside_repo")
48-
assert.NoError(t, err)
49-
assert.True(t, target.IsLink())
50-
_, err = target.FollowLink()
51-
assert.ErrorAs(t, err, ErrBadLink{})
52-
53-
// testing fix for short link bug
54-
target, err = commit.Tree.GetTreeEntryByPath("foo/link_short")
55-
assert.NoError(t, err)
56-
_, err = target.FollowLink()
57-
assert.ErrorAs(t, err, ErrBadLink{})
58-
}
59-
60-
func TestTryFollowingLinks(t *testing.T) {
61-
r, err := openRepositoryWithDefaultContext("tests/repos/repo1_bare")
62-
assert.NoError(t, err)
63-
defer r.Close()
64-
65-
commit, err := r.GetCommit("37991dec2c8e592043f47155ce4808d4580f9123")
66-
assert.NoError(t, err)
67-
68-
// get the symlink
69-
list, err := commit.Tree.GetTreeEntryByPath("foo/bar/link_to_hello")
70-
assert.NoError(t, err)
71-
assert.True(t, list.IsLink())
72-
73-
// should be able to dereference to target
74-
target := list.TryFollowingLinks()
75-
assert.NotEqual(t, target, list)
76-
assert.Equal(t, "hello", target.Name())
77-
assert.Equal(t, "foo/nar/hello", target.FullPath())
78-
assert.False(t, target.IsLink())
79-
assert.Equal(t, "b14df6442ea5a1b382985a6549b85d435376c351", target.ID.String())
80-
81-
// should default to original when called on normal file
82-
link, err := commit.Tree.GetTreeEntryByPath("file1.txt")
83-
assert.NoError(t, err)
84-
target = link.TryFollowingLinks()
85-
assert.Same(t, link, target)
86-
87-
// should default to original for broken links
88-
link, err = commit.Tree.GetTreeEntryByPath("foo/broken_link")
89-
assert.NoError(t, err)
90-
assert.True(t, link.IsLink())
91-
target = link.TryFollowingLinks()
92-
assert.Same(t, link, target)
93-
94-
// should default to original for external links
95-
link, err = commit.Tree.GetTreeEntryByPath("foo/outside_repo")
96-
assert.NoError(t, err)
97-
assert.True(t, link.IsLink())
98-
target = link.TryFollowingLinks()
99-
assert.Same(t, link, target)
100-
101-
// testing fix for short link bug
102-
link, err = commit.Tree.GetTreeEntryByPath("foo/link_short")
103-
assert.NoError(t, err)
104-
target = link.TryFollowingLinks()
105-
assert.Same(t, link, target)
24+
{
25+
lnkFullPath := "foo/bar/link_to_hello"
26+
lnk, err := commit.Tree.GetTreeEntryByPath("foo/bar/link_to_hello")
27+
require.NoError(t, err)
28+
assert.True(t, lnk.IsLink())
29+
30+
// should be able to dereference to target
31+
res, err := EntryFollowLink(commit, lnkFullPath, lnk)
32+
require.NoError(t, err)
33+
assert.Equal(t, "hello", res.TargetEntry.Name())
34+
assert.Equal(t, "foo/nar/hello", res.TargetFullPath)
35+
assert.False(t, res.TargetEntry.IsLink())
36+
assert.Equal(t, "b14df6442ea5a1b382985a6549b85d435376c351", res.TargetEntry.ID.String())
37+
}
38+
39+
{
40+
// should error when called on a normal file
41+
entry, err := commit.Tree.GetTreeEntryByPath("file1.txt")
42+
require.NoError(t, err)
43+
res, err := EntryFollowLink(commit, "file1.txt", entry)
44+
assert.ErrorIs(t, err, util.ErrUnprocessableContent)
45+
assert.Nil(t, res)
46+
}
47+
48+
{
49+
// should error for broken links
50+
entry, err := commit.Tree.GetTreeEntryByPath("foo/broken_link")
51+
require.NoError(t, err)
52+
assert.True(t, entry.IsLink())
53+
res, err := EntryFollowLink(commit, "foo/broken_link", entry)
54+
assert.ErrorIs(t, err, util.ErrNotExist)
55+
assert.Equal(t, "nar/broken_link", res.SymlinkContent)
56+
}
57+
58+
{
59+
// should error for external links
60+
entry, err := commit.Tree.GetTreeEntryByPath("foo/outside_repo")
61+
require.NoError(t, err)
62+
assert.True(t, entry.IsLink())
63+
res, err := EntryFollowLink(commit, "foo/outside_repo", entry)
64+
assert.ErrorIs(t, err, util.ErrNotExist)
65+
assert.Equal(t, "../../outside_repo", res.SymlinkContent)
66+
}
67+
68+
{
69+
// testing fix for short link bug
70+
entry, err := commit.Tree.GetTreeEntryByPath("foo/link_short")
71+
require.NoError(t, err)
72+
res, err := EntryFollowLink(commit, "foo/link_short", entry)
73+
assert.ErrorIs(t, err, util.ErrNotExist)
74+
assert.Equal(t, "a", res.SymlinkContent)
75+
}
10676
}

0 commit comments

Comments
 (0)