Skip to content

Commit 9dd2ff9

Browse files
committed
Revert "Recurse into recursively linked directories not more than once."
This reverts commit da5fee5.
1 parent da5fee5 commit 9dd2ff9

File tree

4 files changed

+37
-34
lines changed

4 files changed

+37
-34
lines changed

internal/smartlink/smartlink.go

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import (
1010
)
1111

1212
// LinkContents will link the contents of src to desc
13-
func LinkContents(src, dest string) error {
13+
func LinkContents(src, dest string, visited map[string]bool) error {
1414
if !fileutils.DirExists(src) {
1515
return errs.New("src dir does not exist: %s", src)
1616
}
@@ -24,12 +24,23 @@ func LinkContents(src, dest string) error {
2424
return errs.Wrap(err, "Could not resolve src and dest paths")
2525
}
2626

27+
if visited == nil {
28+
visited = make(map[string]bool)
29+
}
30+
if _, exists := visited[src]; exists {
31+
// We've encountered a recursive link. This is most often the case when the resolved src has
32+
// already been visited. In that case, just link the dest to the src (which may be a directory;
33+
// this is fine).
34+
return linkFile(src, dest)
35+
}
36+
visited[src] = true
37+
2738
entries, err := os.ReadDir(src)
2839
if err != nil {
2940
return errs.Wrap(err, "Reading dir %s failed", src)
3041
}
3142
for _, entry := range entries {
32-
if err := Link(filepath.Join(src, entry.Name()), filepath.Join(dest, entry.Name()), nil); err != nil {
43+
if err := Link(filepath.Join(src, entry.Name()), filepath.Join(dest, entry.Name()), visited); err != nil {
3344
return errs.Wrap(err, "Link failed")
3445
}
3546
}
@@ -39,27 +50,23 @@ func LinkContents(src, dest string) error {
3950

4051
// Link creates a link from src to target. MS decided to support Symlinks but only if you opt into developer mode (go figure),
4152
// which we cannot reasonably force on our users. So on Windows we will instead create dirs and hardlinks.
42-
func Link(src, dest string, visited map[string]int) error {
53+
func Link(src, dest string, visited map[string]bool) error {
4354
var err error
4455
src, dest, err = resolvePaths(src, dest)
4556
if err != nil {
4657
return errs.Wrap(err, "Could not resolve src and dest paths")
4758
}
4859

4960
if visited == nil {
50-
visited = make(map[string]int)
61+
visited = make(map[string]bool)
5162
}
52-
if count, exists := visited[src]; exists {
63+
if _, exists := visited[src]; exists {
5364
// We've encountered a recursive link. This is most often the case when the resolved src has
54-
// already been visited. We will recurse into the directory no more than once, so that any
55-
// runtime paths that reference the link will not silently fail.
56-
if count > 1 {
57-
return nil
58-
}
59-
visited[src]++
60-
} else {
61-
visited[src] = 1
65+
// already been visited. In that case, just link the dest to the src (which may be a directory;
66+
// this is fine).
67+
return linkFile(src, dest)
6268
}
69+
visited[src] = true
6370

6471
if fileutils.IsDir(src) {
6572
if err := fileutils.Mkdir(dest); err != nil {

internal/smartlink/smartlink_lin_mac.go

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,10 @@ package smartlink
55

66
import (
77
"os"
8-
9-
"github.com/ActiveState/cli/internal/errs"
10-
"github.com/ActiveState/cli/internal/fileutils"
118
)
129

1310
// file will create a symlink from src to dest, and falls back on a hardlink if no symlink is available.
1411
// This is a workaround for the fact that Windows does not support symlinks without admin privileges.
1512
func linkFile(src, dest string) error {
16-
if fileutils.IsDir(src) {
17-
return errs.New("src is a directory, not a file: %s", src)
18-
}
1913
return os.Symlink(src, dest)
2014
}

internal/smartlink/smartlink_test.go

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@ package smartlink
33
import (
44
"os"
55
"path/filepath"
6+
"runtime"
67
"testing"
78

9+
"github.com/ActiveState/cli/internal/fileutils"
810
"github.com/stretchr/testify/require"
911
)
1012

@@ -40,17 +42,14 @@ func TestLinkContentsWithCircularLink(t *testing.T) {
4042
err = os.Symlink(subDir, circularLink)
4143
require.NoError(t, err)
4244

43-
err = LinkContents(srcDir, destDir)
45+
err = LinkContents(srcDir, destDir, nil)
46+
if runtime.GOOS == "windows" {
47+
require.Error(t, err)
48+
return // hard links to directories are not allowed on Windows
49+
}
4450
require.NoError(t, err)
4551

4652
// Verify file structure.
47-
// src/
48-
// ├── regular.txt
49-
// └── subdir/
50-
// ├── circle
51-
// │ │ (no subdir/)
52-
// │ └── subfile.txt
53-
// └── subfile.txt
5453
destFile := filepath.Join(destDir, "regular.txt")
5554
require.FileExists(t, destFile)
5655
content, err := os.ReadFile(destFile)
@@ -63,11 +62,14 @@ func TestLinkContentsWithCircularLink(t *testing.T) {
6362
require.NoError(t, err)
6463
require.Equal(t, "sub content", string(subContent))
6564

66-
require.NoDirExists(t, filepath.Join(destDir, "subdir", "circle", "circle"))
67-
68-
destCircularSubFile := filepath.Join(destDir, "subdir", "circle", "subfile.txt")
69-
require.FileExists(t, destCircularSubFile)
70-
subContent, err = os.ReadFile(destCircularSubFile)
65+
destCircular := filepath.Join(destDir, "subdir", "circle")
66+
require.FileExists(t, destCircular)
67+
target, err := fileutils.ResolveUniquePath(destCircular)
7168
require.NoError(t, err)
72-
require.Equal(t, "sub content", string(subContent))
69+
srcCircular := filepath.Join(srcDir, "subdir")
70+
if runtime.GOOS == "darwin" {
71+
srcCircular, err = fileutils.ResolveUniquePath(srcCircular) // needed for full $TMPDIR resolution
72+
require.NoError(t, err)
73+
}
74+
require.Equal(t, target, srcCircular)
7375
}

pkg/runtime/depot.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ func (d *depot) DeployViaLink(id strfmt.UUID, relativeSrc, absoluteDest string)
177177
}
178178

179179
// Copy or link the artifact files, depending on whether the artifact in question relies on file transformations
180-
if err := smartlink.LinkContents(absoluteSrc, absoluteDest); err != nil {
180+
if err := smartlink.LinkContents(absoluteSrc, absoluteDest, nil); err != nil {
181181
return errs.Wrap(err, "failed to link artifact")
182182
}
183183

0 commit comments

Comments
 (0)