Skip to content

Commit

Permalink
Merge pull request #147 from gabriel-samfira/fix-follow-symlinks
Browse files Browse the repository at this point in the history
Fix follow symlinkResolver on Windows
  • Loading branch information
tonistiigi authored Feb 14, 2023
2 parents 12dff54 + ed3e371 commit fe5b6a0
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 16 deletions.
15 changes: 10 additions & 5 deletions followlinks.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ func FollowLinks(root string, paths []string) ([]string, error) {
}
res := make([]string, 0, len(r.resolved))
for r := range r.resolved {
res = append(res, r)
res = append(res, filepath.ToSlash(r))
}
sort.Strings(res)
return dedupePaths(res), nil
Expand All @@ -31,6 +31,12 @@ type symlinkResolver struct {
}

func (r *symlinkResolver) append(p string) error {
if runtime.GOOS == "windows" && filepath.IsAbs(filepath.FromSlash(p)) {
absParts := strings.SplitN(p, ":", 2)
if len(absParts) == 2 {
p = absParts[1]
}
}
p = filepath.Join(".", p)
current := "."
for {
Expand All @@ -41,7 +47,6 @@ func (r *symlinkResolver) append(p string) error {
if err != nil {
return err
}

p = ""
if len(parts) == 2 {
p = parts[1]
Expand Down Expand Up @@ -76,7 +81,7 @@ func (r *symlinkResolver) readSymlink(p string, allowWildcard bool) ([]string, e
if allowWildcard && containsWildcards(base) {
fis, err := os.ReadDir(filepath.Dir(realPath))
if err != nil {
if errors.Is(err, os.ErrNotExist) {
if isNotFound(err) {
return nil, nil
}
return nil, errors.Wrap(err, "readdir")
Expand All @@ -96,7 +101,7 @@ func (r *symlinkResolver) readSymlink(p string, allowWildcard bool) ([]string, e

fi, err := os.Lstat(realPath)
if err != nil {
if errors.Is(err, os.ErrNotExist) {
if isNotFound(err) {
return nil, nil
}
return nil, errors.WithStack(err)
Expand Down Expand Up @@ -139,7 +144,7 @@ func dedupePaths(in []string) []string {
if s == "." {
return nil
}
if strings.HasPrefix(s, last+string(filepath.Separator)) {
if strings.HasPrefix(s, last+"/") {
continue
}
out = append(out, s)
Expand Down
39 changes: 28 additions & 11 deletions followlinks_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package fsutil

import (
"path/filepath"
"runtime"
"testing"

"github.com/containerd/continuity/fs/fstest"
Expand Down Expand Up @@ -46,9 +48,14 @@ func TestFollowLinksLoop(t *testing.T) {
func TestFollowLinksAbsolute(t *testing.T) {
tmpDir := t.TempDir()

abslutePathForBaz := "/foo/bar/baz"
if runtime.GOOS == "windows" {
abslutePathForBaz = "C:/foo/bar/baz"
}

apply := fstest.Apply(
fstest.CreateDir("dir", 0700),
fstest.Symlink("/foo/bar/baz", "dir/l1"),
fstest.Symlink(abslutePathForBaz, "dir/l1"),
fstest.CreateDir("foo", 0700),
fstest.Symlink("../", "foo/bar"),
fstest.CreateFile("baz", nil, 0600),
Expand All @@ -58,14 +65,14 @@ func TestFollowLinksAbsolute(t *testing.T) {
out, err := FollowLinks(tmpDir, []string{"dir/l1"})
require.NoError(t, err)

require.Equal(t, out, []string{"baz", "dir/l1", "foo/bar"})
require.Equal(t, []string{"baz", "dir/l1", "foo/bar"}, out)

// same but a link outside root
tmpDir = t.TempDir()

apply = fstest.Apply(
fstest.CreateDir("dir", 0700),
fstest.Symlink("/foo/bar/baz", "dir/l1"),
fstest.Symlink(abslutePathForBaz, "dir/l1"),
fstest.CreateDir("foo", 0700),
fstest.Symlink("../../../", "foo/bar"),
fstest.CreateFile("baz", nil, 0600),
Expand All @@ -75,7 +82,7 @@ func TestFollowLinksAbsolute(t *testing.T) {
out, err = FollowLinks(tmpDir, []string{"dir/l1"})
require.NoError(t, err)

require.Equal(t, out, []string{"baz", "dir/l1", "foo/bar"})
require.Equal(t, []string{"baz", "dir/l1", "foo/bar"}, out)
}

func TestFollowLinksNotExists(t *testing.T) {
Expand All @@ -95,7 +102,7 @@ func TestFollowLinksNotExists(t *testing.T) {
out, err = FollowLinks(tmpDir, []string{"f*/foo/t*"})
require.NoError(t, err)

require.Equal(t, out, []string{"f*/foo/t*"})
require.Equal(t, []string{"f*/foo/t*"}, out)
}

func TestFollowLinksNormalized(t *testing.T) {
Expand All @@ -106,10 +113,15 @@ func TestFollowLinksNormalized(t *testing.T) {

require.Equal(t, out, []string{"foo/bar"})

rootPath := "/"
if runtime.GOOS == "windows" {
rootPath = "C:/"
}

apply := fstest.Apply(
fstest.CreateDir("dir", 0700),
fstest.Symlink("/foo", "dir/l1"),
fstest.Symlink("/", "dir/l2"),
fstest.Symlink(filepath.Join(rootPath, "foo"), "dir/l1"),
fstest.Symlink(rootPath, "dir/l2"),
fstest.CreateDir("foo", 0700),
fstest.CreateFile("foo/bar", nil, 0600),
)
Expand All @@ -129,12 +141,17 @@ func TestFollowLinksNormalized(t *testing.T) {
func TestFollowLinksWildcard(t *testing.T) {
tmpDir := t.TempDir()

absolutePathForFoo := "/foo"
if runtime.GOOS == "windows" {
absolutePathForFoo = "C:/foo"
}

apply := fstest.Apply(
fstest.CreateDir("dir", 0700),
fstest.CreateDir("foo", 0700),
fstest.Symlink("/foo/bar1", "dir/l1"),
fstest.Symlink("/foo/bar2", "dir/l2"),
fstest.Symlink("/foo/bar3", "dir/anotherlink"),
fstest.Symlink(filepath.Join(absolutePathForFoo, "bar1"), "dir/l1"),
fstest.Symlink(filepath.Join(absolutePathForFoo, "bar2"), "dir/l2"),
fstest.Symlink(filepath.Join(absolutePathForFoo, "bar3"), "dir/anotherlink"),
fstest.Symlink("../baz", "foo/bar2"),
fstest.CreateFile("foo/bar1", nil, 0600),
fstest.CreateFile("foo/bar3", nil, 0600),
Expand All @@ -145,7 +162,7 @@ func TestFollowLinksWildcard(t *testing.T) {
out, err := FollowLinks(tmpDir, []string{"dir/l*"})
require.NoError(t, err)

require.Equal(t, out, []string{"baz", "dir/l*", "foo/bar1", "foo/bar2"})
require.Equal(t, []string{"baz", "dir/l*", "foo/bar1", "foo/bar2"}, out)

out, err = FollowLinks(tmpDir, []string{"dir"})
require.NoError(t, err)
Expand Down
14 changes: 14 additions & 0 deletions followlinks_unix.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//go:build !windows
// +build !windows

package fsutil

import (
"os"

"github.com/pkg/errors"
)

func isNotFound(err error) bool {
return errors.Is(err, os.ErrNotExist)
}
15 changes: 15 additions & 0 deletions followlinks_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package fsutil

import (
"os"

"github.com/pkg/errors"
"golang.org/x/sys/windows"
)

func isNotFound(err error) bool {
if errors.Is(err, os.ErrNotExist) || errors.Is(err, windows.ERROR_INVALID_NAME) {
return true
}
return false
}

0 comments on commit fe5b6a0

Please sign in to comment.