Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions pkg/filetree/depth_first_path_walker.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"sort"
"strings"

"github.com/anchore/stereoscope/internal/log"
"github.com/anchore/stereoscope/pkg/file"
"github.com/anchore/stereoscope/pkg/filetree/filenode"
)
Expand Down Expand Up @@ -78,6 +79,10 @@ func (w *DepthFirstPathWalker) Walk(from file.Path) (file.Path, *filenode.FileNo
currentPath = w.pathStack.Pop()

currentNode, err = w.tree.node(currentPath, linkStrat)
if errors.Is(err, ErrLinkCycleDetected) {
log.WithFields("path", currentPath, "error", err).Warn("skipping path with link cycle during file tree walk")
continue
}
if err != nil {
return "", nil, err
}
Expand Down
42 changes: 42 additions & 0 deletions pkg/filetree/depth_first_path_walker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,48 @@ func TestDFS_WalkAll(t *testing.T) {
assertExpectedTraversal(t, possiblePaths, actualPaths)
}

func TestDFS_WalkAll_SkipsLinkCycles(t *testing.T) {
tr := New()

for _, p := range []file.Path{
"/usr/bin/a-before",
"/usr/bin/zz-after",
} {
_, err := tr.AddFile(p)
if err != nil {
t.Fatalf("failed to add path %q: %+v", p, err)
}
}

_, err := tr.AddSymLink("/usr/bin/xz", "/usr/bin/xzcat")
if err != nil {
t.Fatalf("could not setup link: %+v", err)
}
_, err = tr.AddSymLink("/usr/bin/xzcat", "/usr/bin/xz")
if err != nil {
t.Fatalf("could not setup link: %+v", err)
}

visited := file.NewPathSet()
walker := NewDepthFirstPathWalker(tr, func(path file.Path, _ filenode.FileNode) error {
visited.Add(path)
return nil
}, nil)

if err := walker.WalkAll(); err != nil {
t.Fatalf("could not walk: %+v", err)
}

for _, p := range []file.Path{
"/usr/bin/a-before",
"/usr/bin/zz-after",
} {
if !visited.Contains(p) {
t.Errorf("did not visit path %q", p)
}
}
}

func TestDFS_WalkAll_EarlyTermination(t *testing.T) {
tr, possiblePaths := dfsTestTree(t)

Expand Down
5 changes: 5 additions & 0 deletions pkg/filetree/search.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package filetree

import (
"errors"
"fmt"
"path"
"sort"
Expand Down Expand Up @@ -223,6 +224,10 @@ func (sc searchContext) firstMatchingReferences(glob string, entries []IndexEntr
var references []file.Resolution
for _, entry := range entries {
ref, err := sc.firstMatchingReference(glob, string(entry.RealPath))
if errors.Is(err, ErrLinkCycleDetected) {
log.WithFields("path", entry.RealPath, "error", err).Warn("skipping path with link cycle during file tree search")
continue
}
if err != nil {
return nil, err
}
Expand Down
39 changes: 39 additions & 0 deletions pkg/filetree/search_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,45 @@ func Test_searchContext_SearchByMIMEType(t *testing.T) {
}
}

func Test_searchContext_SearchByMIMEType_SkipsLinkCycles(t *testing.T) {
tree := New()
idx := NewIndex()
const mimeType = "application/x-executable"

for _, p := range []file.Path{
"/usr/bin/a-before",
"/usr/bin/zz-after",
} {
ref, err := tree.AddFile(p)
require.NoError(t, err)
require.NotNil(t, ref)
idx.Add(*ref, file.Metadata{MIMEType: mimeType})
}

for path, destination := range map[file.Path]file.Path{
"/usr/bin/xz": "/usr/bin/xzcat",
"/usr/bin/xzcat": "/usr/bin/xz",
} {
ref, err := tree.AddSymLink(path, destination)
require.NoError(t, err)
require.NotNil(t, ref)
idx.Add(*ref, file.Metadata{MIMEType: mimeType, Type: file.TypeSymLink})
}

results, err := NewSearchContext(tree, idx).SearchByMIMEType(mimeType)
require.NoError(t, err)
require.Len(t, results, 2)

var paths []string
for _, result := range results {
paths = append(paths, string(result.RealPath))
}
require.ElementsMatch(t, []string{
"/usr/bin/a-before",
"/usr/bin/zz-after",
}, paths)
}

func Test_complexSymlinkPerformance(t *testing.T) {
tr := New()
idx := NewIndex()
Expand Down
Loading