Skip to content

Commit

Permalink
update resolveSymlinkAncestor and add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
cvgw committed Feb 22, 2020
1 parent a675ad9 commit ce8eebb
Show file tree
Hide file tree
Showing 2 changed files with 208 additions and 3 deletions.
12 changes: 9 additions & 3 deletions pkg/filesystem/resolve.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,20 +125,26 @@ func resolveSymlinkAncestor(path string) (string, error) {
if !filepath.IsAbs(path) {
return "", errors.New("dest path must be abs")
}

last := ""
newPath := path

loop:
for newPath != "/" {
fi, err := os.Lstat(newPath)
if err != nil {
return "", errors.Wrap(err, "failed to lstat")
}

switch mode := fi.Mode(); {
case mode&os.ModeSymlink != 0:
if util.IsSymlink(fi) {
last = filepath.Base(newPath)
newPath = filepath.Dir(newPath)
default:
} else {
// Even if the filenode pointed to by newPath is a regular file,
// one of its ancestors could be a symlink. We call filepath.EvalSymlinks
// to test whether there are any links in the path. If the output of
// EvalSymlinks is different than the input we know one of the nodes in the
// the path is a link.
target, err := filepath.EvalSymlinks(newPath)
if err != nil {
return "", err
Expand Down
199 changes: 199 additions & 0 deletions pkg/filesystem/resolve_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,3 +183,202 @@ func Test_ResolvePaths(t *testing.T) {
validateResults(t, files, expectedFiles, err)
})
}

func Test_resolveSymlinkAncestor(t *testing.T) {
setupDirs := func(t *testing.T) (string, string) {
testDir, err := ioutil.TempDir("", "")
if err != nil {
t.Fatal(err)
}

targetDir := filepath.Join(testDir, "bar", "baz")

if err := os.MkdirAll(targetDir, 0777); err != nil {
t.Fatal(err)
}

targetPath := filepath.Join(targetDir, "bam.txt")

if err := ioutil.WriteFile(targetPath, []byte("meow"), 0777); err != nil {
t.Fatal(err)
}

return testDir, targetPath
}

t.Run("path is a symlink", func(t *testing.T) {
testDir, targetPath := setupDirs(t)

linkDir := filepath.Join(testDir, "foo", "buzz")

if err := os.MkdirAll(linkDir, 0777); err != nil {
t.Fatal(err)
}

linkPath := filepath.Join(linkDir, "zoom.txt")

if err := os.Symlink(targetPath, linkPath); err != nil {
t.Fatal(err)
}

expected := linkPath

actual, err := resolveSymlinkAncestor(linkPath)
if err != nil {
t.Errorf("expected err to be nil but was %s", err)
}

if actual != expected {
t.Errorf("expected result to be %s not %s", expected, actual)
}
})

t.Run("path is a dead symlink", func(t *testing.T) {
testDir, targetPath := setupDirs(t)
defer os.RemoveAll(testDir)

linkDir := filepath.Join(testDir, "foo", "buzz")

if err := os.MkdirAll(linkDir, 0777); err != nil {
t.Fatal(err)
}

linkPath := filepath.Join(linkDir, "zoom.txt")

if err := os.Symlink(targetPath, linkPath); err != nil {
t.Fatal(err)
}

if err := os.Remove(targetPath); err != nil {
t.Fatal(err)
}

expected := linkPath

actual, err := resolveSymlinkAncestor(linkPath)
if err != nil {
t.Errorf("expected err to be nil but was %s", err)
}

if actual != expected {
t.Errorf("expected result to be %s not %s", expected, actual)
}
})

t.Run("path is not a symlink", func(t *testing.T) {
_, targetPath := setupDirs(t)

expected := targetPath

actual, err := resolveSymlinkAncestor(targetPath)
if err != nil {
t.Errorf("expected err to be nil but was %s", err)
}

if actual != expected {
t.Errorf("expected result to be %s not %s", expected, actual)
}
})

t.Run("parent of path is a symlink", func(t *testing.T) {
testDir, targetPath := setupDirs(t)
targetDir := filepath.Dir(targetPath)

linkDir := filepath.Join(testDir, "foo")

if err := os.MkdirAll(linkDir, 0777); err != nil {
t.Fatal(err)
}

linkDir = filepath.Join(linkDir, "gaz")

if err := os.Symlink(targetDir, linkDir); err != nil {
t.Fatal(err)
}

linkPath := filepath.Join(linkDir, filepath.Base(targetPath))

expected := linkDir

actual, err := resolveSymlinkAncestor(linkPath)
if err != nil {
t.Errorf("expected err to be nil but was %s", err)
}

if actual != expected {
t.Errorf("expected result to be %s not %s", expected, actual)
}
})

t.Run("parent of path is a dead symlink", func(t *testing.T) {
testDir, targetPath := setupDirs(t)
targetDir := filepath.Dir(targetPath)

linkDir := filepath.Join(testDir, "foo")

if err := os.MkdirAll(linkDir, 0777); err != nil {
t.Fatal(err)
}

linkDir = filepath.Join(linkDir, "gaz")

if err := os.Symlink(targetDir, linkDir); err != nil {
t.Fatal(err)
}

if err := os.RemoveAll(targetDir); err != nil {
t.Fatal(err)
}

linkPath := filepath.Join(linkDir, filepath.Base(targetPath))

_, err := resolveSymlinkAncestor(linkPath)
if err == nil {
t.Error("expected err to not be nil")
}
})

t.Run("great grandparent of path is a symlink", func(t *testing.T) {
testDir, err := ioutil.TempDir("", "")
if err != nil {
t.Fatal(err)
}

targetDir := filepath.Join(testDir, "bar", "baz")

if err := os.MkdirAll(targetDir, 0777); err != nil {
t.Fatal(err)
}

targetPath := filepath.Join(targetDir, "bam.txt")

if err := ioutil.WriteFile(targetPath, []byte("meow"), 0777); err != nil {
t.Fatal(err)
}

linkDir := filepath.Join(testDir, "foo")

if err := os.Symlink(filepath.Dir(targetDir), linkDir); err != nil {
t.Fatal(err)
}

linkPath := filepath.Join(
linkDir,
filepath.Join(
filepath.Base(targetDir),
filepath.Base(targetPath),
),
)

expected := linkDir

actual, err := resolveSymlinkAncestor(linkPath)
if err != nil {
t.Errorf("expected err to be nil but was %s", err)
}

if actual != expected {
t.Errorf("expected result to be %s not %s", expected, actual)
}
})
}

0 comments on commit ce8eebb

Please sign in to comment.