update resolveSymlinkAncestor and add tests

This commit is contained in:
cvgw 2020-02-22 10:38:42 -08:00
parent a675ad998a
commit 01f6aba517
2 changed files with 202 additions and 3 deletions

View File

@ -125,8 +125,10 @@ 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)
@ -134,11 +136,15 @@ loop:
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

View File

@ -183,3 +183,196 @@ 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)
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)
}
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) {
testDir, targetPath := setupDirs(t)
defer os.RemoveAll(testDir)
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)
defer os.RemoveAll(testDir)
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)
defer os.RemoveAll(testDir)
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, targetPath := setupDirs(t)
defer os.RemoveAll(testDir)
targetDir := filepath.Dir(targetPath)
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)
}
})
}