mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2026-05-12 22:10:25 +00:00
fix: "Follow symlink" to work with arbitrary links (#12246)
This change introduces a Path method on the TreeEntry struct, that collects the path by moving upwards in the tree. The existing FollowSymlink(s) methods interface has been changed, the previously returned string has been removed, as after the fix it wasn't used anywhere. Fixes: #9931 Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/12246 Reviewed-by: Gusted <gusted@noreply.codeberg.org>
This commit is contained in:
parent
93296305f9
commit
9977df96d5
5 changed files with 105 additions and 21 deletions
|
|
@ -5,6 +5,7 @@
|
|||
package git
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"sort"
|
||||
"strings"
|
||||
|
|
@ -136,11 +137,11 @@ func (te *TreeEntry) LinkTarget() (string, error) {
|
|||
}
|
||||
|
||||
// FollowLink returns the entry pointed to by a symlink
|
||||
func (te *TreeEntry) FollowLink() (*TreeEntry, string, error) {
|
||||
func (te *TreeEntry) FollowLink() (*TreeEntry, error) {
|
||||
// read the link
|
||||
lnk, err := te.LinkTarget()
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
t := te.ptree
|
||||
|
|
@ -151,35 +152,33 @@ func (te *TreeEntry) FollowLink() (*TreeEntry, string, error) {
|
|||
}
|
||||
|
||||
if t == nil {
|
||||
return nil, "", ErrBadLink{te.Name(), "points outside of repo"}
|
||||
return nil, ErrBadLink{te.Name(), "points outside of repo"}
|
||||
}
|
||||
|
||||
target, err := t.GetTreeEntryByPath(lnk)
|
||||
if err != nil {
|
||||
if IsErrNotExist(err) {
|
||||
return nil, "", ErrBadLink{te.Name(), "broken link"}
|
||||
return nil, ErrBadLink{te.Name(), "broken link"}
|
||||
}
|
||||
return nil, "", err
|
||||
return nil, err
|
||||
}
|
||||
return target, lnk, nil
|
||||
return target, nil
|
||||
}
|
||||
|
||||
// FollowLinks returns the entry ultimately pointed to by a symlink
|
||||
func (te *TreeEntry) FollowLinks() (*TreeEntry, string, error) {
|
||||
func (te *TreeEntry) FollowLinks() (*TreeEntry, error) {
|
||||
if !te.IsLink() {
|
||||
return nil, "", ErrBadLink{te.Name(), "not a symlink"}
|
||||
return nil, ErrBadLink{te.Name(), "not a symlink"}
|
||||
}
|
||||
entry := te
|
||||
entryLink := ""
|
||||
for range 999 {
|
||||
if entry.IsLink() {
|
||||
next, link, err := entry.FollowLink()
|
||||
entryLink = link
|
||||
next, err := entry.FollowLink()
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
return nil, err
|
||||
}
|
||||
if next.ID == entry.ID {
|
||||
return nil, "", ErrBadLink{
|
||||
return nil, ErrBadLink{
|
||||
entry.Name(),
|
||||
"recursive link",
|
||||
}
|
||||
|
|
@ -190,12 +189,12 @@ func (te *TreeEntry) FollowLinks() (*TreeEntry, string, error) {
|
|||
}
|
||||
}
|
||||
if entry.IsLink() {
|
||||
return nil, "", ErrBadLink{
|
||||
return nil, ErrBadLink{
|
||||
te.Name(),
|
||||
"too many levels of symbolic links",
|
||||
}
|
||||
}
|
||||
return entry, entryLink, nil
|
||||
return entry, nil
|
||||
}
|
||||
|
||||
// returns the Tree pointed to by this TreeEntry, or nil if this is not a tree
|
||||
|
|
@ -208,6 +207,42 @@ func (te *TreeEntry) Tree() *Tree {
|
|||
return t
|
||||
}
|
||||
|
||||
// returns the calulcated path within the tree of this TreeEntry, or an error if it can be determined
|
||||
func (te *TreeEntry) Path() (string, error) {
|
||||
targetPath := te.Name()
|
||||
parentTree := te.ptree
|
||||
if parentTree == nil {
|
||||
return "", fmt.Errorf("couldn't find the parent tree of the entry")
|
||||
}
|
||||
|
||||
prevID := parentTree.ID
|
||||
parentTree = parentTree.ptree
|
||||
for parentTree != nil {
|
||||
entries, err := parentTree.ListEntries()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("couldn't list entries: %v", err)
|
||||
}
|
||||
|
||||
var matchingEntry *TreeEntry
|
||||
for _, entry := range entries {
|
||||
if entry.ID == prevID {
|
||||
matchingEntry = entry
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if matchingEntry == nil {
|
||||
return "", fmt.Errorf("this shouldn't happen: couldn't find entry (ID: %s) in tree (ID: %s)", prevID, parentTree.ID)
|
||||
}
|
||||
|
||||
targetPath = matchingEntry.name + "/" + targetPath
|
||||
prevID = parentTree.ID
|
||||
parentTree = parentTree.ptree
|
||||
}
|
||||
|
||||
return targetPath, nil
|
||||
}
|
||||
|
||||
// GetSubJumpablePathName return the full path of subdirectory jumpable ( contains only one directory )
|
||||
func (te *TreeEntry) GetSubJumpablePathName() string {
|
||||
if te.IsSubmodule() || !te.IsDir() {
|
||||
|
|
|
|||
46
modules/git/tree_entry_test.go
Normal file
46
modules/git/tree_entry_test.go
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
// Copyright 2015 The Gogs Authors. All rights reserved.
|
||||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package git_test
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestTreeEntry_Path(t *testing.T) {
|
||||
repo, err := openRepositoryWithDefaultContext(filepath.Join(testReposDir, "templates_repo"))
|
||||
require.NoError(t, err)
|
||||
defer repo.Close()
|
||||
|
||||
tests := []struct {
|
||||
name string // description of this test case
|
||||
path string
|
||||
}{
|
||||
{
|
||||
name: "Top level dir",
|
||||
path: ".forgejo",
|
||||
},
|
||||
{
|
||||
name: "File in subdir",
|
||||
path: ".forgejo/default_merge_message/MERGE_TEMPLATE.md",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
tree, err := repo.GetTree("HEAD^{tree}")
|
||||
require.NoError(t, err)
|
||||
|
||||
te, err := tree.GetTreeEntryByPath(tt.path)
|
||||
require.NoError(t, err)
|
||||
|
||||
got, gotErr := te.Path()
|
||||
require.NoError(t, gotErr, "Path() failed: %v", gotErr)
|
||||
assert.Equal(t, tt.path, got, "Path() = %v, want %v", got, tt.path)
|
||||
})
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue