diff --git a/common/helper.go b/common/helper.go index e8e8cb5..e2724c4 100644 --- a/common/helper.go +++ b/common/helper.go @@ -170,25 +170,32 @@ func Sprintf(format string, values ...interface{}) (string, error) { } type PathMatchResult struct { - Path string - Match bool - IsLink bool + Path string + Match bool + IsLink bool + DoesExist bool } func PathMatch(path string, patternList []string) (PathMatchResult, error) { - result := PathMatchResult{} - if _, err := os.Stat(path); os.IsExist(err) { + result := PathMatchResult{Path: path, DoesExist: true} + + if _, err := os.Lstat(path); err == nil { resolvedPath, err := filepath.EvalSymlinks(path) if err != nil { + // error out on broken symlinks, because we cannot resolve + // their path with EvalSymlinks + result.DoesExist = false return result, err } else { - result.Path = resolvedPath + if resolvedPath != path { + result.IsLink = true + result.Path = resolvedPath + } } + } else { + result.DoesExist = false } - if result.Path != path { - result.IsLink = true - } for _, pattern := range patternList { match, err := filepath.Match(pattern, result.Path) if err != nil { diff --git a/common/helper_test.go b/common/helper_test.go index 99b5cd2..51308ff 100644 --- a/common/helper_test.go +++ b/common/helper_test.go @@ -20,6 +20,7 @@ import ( "os" "path/filepath" "regexp" + "runtime" "strings" "testing" ) @@ -98,3 +99,74 @@ func TestEncloseWithoutData(t *testing.T) { t.Fail() } } + +func TestPathMatch(t *testing.T) { + dir, err := ioutil.TempDir("", "test-path-match") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + + execfile := filepath.Join(dir, "myexec") + patternList := []string{"/usr/bin/moo", "/sbin/bar"} + result, err := PathMatch(execfile, patternList) + if err != nil || result.Match { + t.Fatalf("'%s' should not match patternList %v err '%v' result '%v'", execfile, patternList, err, result) + } + if result.DoesExist { + t.Fatalf("DoesExist should not be set") + } + + os.Create(execfile) + patternList = []string{"/usr/bin/moo", "/sbin/bar", execfile} + result, err = PathMatch(execfile, patternList) + if err != nil || !result.Match { + t.Fatalf("'%s' should match patternList %v err '%v' result '%v'", execfile, patternList, err, result) + } + if !result.DoesExist { + t.Fatalf("DoesExist should be set") + } + + patternList = []string{"/usr/bin/moo", "/sbin/bar", dir + "/*"} + result, err = PathMatch(execfile, patternList) + if err != nil || !result.Match { + t.Fatalf("'%s' should match globbing patternList %v err '%v' result '%v'", + execfile, patternList, err, result) + } + +} + +func TestPathMatchSymlink(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip() + } + + dir, err := ioutil.TempDir("", "test-path-match") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + + execfile := filepath.Join(dir, "myexec") + symlink := filepath.Join(dir, "symlink") + if err = os.Symlink(execfile, symlink); err != nil { + t.Fatal() + } + patternList := []string{"moo", "bar", execfile} + result, err := PathMatch(symlink, patternList) + if err == nil { + t.Fatalf("broken symlinks should report an error") + } + + os.Create(execfile) + result, err = PathMatch(symlink, patternList) + if err != nil || !result.Match { + t.Fatalf("'%s' should match patternList %v err '%v' result '%v'", execfile, patternList, err, result) + } + if !result.IsLink { + t.Fatalf("result.IsLink is false") + } + if result.Path != execfile { + t.Fatalf("result.Path did not contain resolved symlink") + } +}