diff --git a/cache/contenthash/checksum.go b/cache/contenthash/checksum.go index ef129cc7..3fe5c658 100644 --- a/cache/contenthash/checksum.go +++ b/cache/contenthash/checksum.go @@ -736,26 +736,33 @@ func getFollowLinksWalk(root *iradix.Node, k []byte, follow bool, linksWalked *i dir, file := splitKey(k) - _, parent, err := getFollowLinksWalk(root, dir, follow, linksWalked) + k, parent, err := getFollowLinksWalk(root, dir, follow, linksWalked) if err != nil { return nil, nil, err } - if parent != nil && parent.Type == CacheRecordTypeSymlink { - *linksWalked++ - if *linksWalked > 255 { - return nil, nil, errors.Errorf("too many links") + if parent != nil { + if parent.Type == CacheRecordTypeSymlink { + *linksWalked++ + if *linksWalked > 255 { + return nil, nil, errors.Errorf("too many links") + } + dirPath := path.Clean(string(convertKeyToPath(dir))) + if dirPath == "." || dirPath == "/" { + dirPath = "" + } + link := path.Clean(parent.Linkname) + if !path.IsAbs(link) { + link = path.Join("/", path.Join(path.Dir(dirPath), link)) + } + return getFollowLinksWalk(root, append(convertPathToKey([]byte(link)), file...), follow, linksWalked) } - dirPath := path.Clean(string(convertKeyToPath(dir))) - if dirPath == "." || dirPath == "/" { - dirPath = "" - } - link := path.Clean(parent.Linkname) - if !path.IsAbs(link) { - link = path.Join("/", path.Join(path.Dir(dirPath), link)) - } - return getFollowLinksWalk(root, append(convertPathToKey([]byte(link)), file...), follow, linksWalked) - } + k = append(k, file...) + v, ok = root.Get(k) + if ok { + return k, v.(*CacheRecord), nil + } + } return nil, nil, nil } diff --git a/cache/contenthash/checksum_test.go b/cache/contenthash/checksum_test.go index 05d4f868..bc81f170 100644 --- a/cache/contenthash/checksum_test.go +++ b/cache/contenthash/checksum_test.go @@ -564,6 +564,61 @@ func TestSymlinkAbsDirSuffix(t *testing.T) { require.NoError(t, err) } +func TestSymlinkThroughParent(t *testing.T) { + t.Parallel() + tmpdir, err := ioutil.TempDir("", "buildkit-state") + require.NoError(t, err) + defer os.RemoveAll(tmpdir) + + snapshotter, err := native.NewSnapshotter(filepath.Join(tmpdir, "snapshots")) + require.NoError(t, err) + cm := setupCacheManager(t, tmpdir, snapshotter) + defer cm.Close() + + ch := []string{ + "ADD lib dir", + "ADD lib/sub dir", + "ADD lib/sub/foo file data0", + "ADD lib/sub/link symlink ../../lib2", + "ADD lib2 dir", + "ADD lib2/sub dir", + "ADD lib2/sub/foo file data0", + "ADD link1 symlink /lib", + "ADD link2 symlink /lib/", + "ADD link3 symlink /lib/.", + "ADD link4 symlink /lib/../lib", + "ADD link5 symlink ../lib", + } + ref := createRef(t, cm, ch) + + dgst, err := Checksum(context.TODO(), ref, "link1/sub/foo", true) + require.NoError(t, err) + require.Equal(t, dgstFileData0, dgst) + + dgst, err = Checksum(context.TODO(), ref, "link2/sub/foo", true) + require.NoError(t, err) + require.Equal(t, dgstFileData0, dgst) + + dgst, err = Checksum(context.TODO(), ref, "link3/sub/foo", true) + require.NoError(t, err) + require.Equal(t, dgstFileData0, dgst) + + dgst, err = Checksum(context.TODO(), ref, "link4/sub/foo", true) + require.NoError(t, err) + require.Equal(t, dgstFileData0, dgst) + + dgst, err = Checksum(context.TODO(), ref, "link5/sub/foo", true) + require.NoError(t, err) + require.Equal(t, dgstFileData0, dgst) + + dgst, err = Checksum(context.TODO(), ref, "link1/sub/link/sub/foo", true) + require.NoError(t, err) + require.Equal(t, dgstFileData0, dgst) + + err = ref.Release(context.TODO()) + require.NoError(t, err) +} + func TestSymlinkInPathHandleChange(t *testing.T) { t.Parallel() tmpdir, err := ioutil.TempDir("", "buildkit-state")