Pārlūkot izejas kodu

Merge branch 'feature/symlinks' of https://github.com/jmaitrehenry/grafana into jmaitrehenry-feature/symlinks

Conflicts:
	pkg/plugins/plugins.go

closes #2899
closes #2834
carl bergquist 10 gadi atpakaļ
vecāks
revīzija
d4e98cd9bc
2 mainītis faili ar 100 papildinājumiem un 1 dzēšanām
  1. 2 1
      pkg/plugins/plugins.go
  2. 98 0
      pkg/util/filepath.go

+ 2 - 1
pkg/plugins/plugins.go

@@ -10,6 +10,7 @@ import (
 
 	"github.com/grafana/grafana/pkg/log"
 	"github.com/grafana/grafana/pkg/setting"
+	"github.com/grafana/grafana/pkg/util"
 )
 
 var (
@@ -53,7 +54,7 @@ func scan(pluginDir string) error {
 		pluginPath: pluginDir,
 	}
 
-	if err := filepath.Walk(pluginDir, scanner.walker); err != nil {
+	if err := util.Walk(pluginDir, true, true, scanner.walker); err != nil {
 		return err
 	}
 

+ 98 - 0
pkg/util/filepath.go

@@ -0,0 +1,98 @@
+package util
+
+import (
+	"errors"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+)
+
+//WalkSkipDir is the Error returned when we want to skip descending into a directory
+var WalkSkipDir = errors.New("skip this directory")
+
+//WalkFunc is a callback function called for each path as a directory is walked
+//If resolvedPath != "", then we are following symbolic links.
+type WalkFunc func(resolvedPath string, info os.FileInfo, err error) error
+
+//Walk walks a path, optionally following symbolic links, and for each path,
+//it calls the walkFn passed.
+//
+//It is similar to filepath.Walk, except that it supports symbolic links and
+//can detect infinite loops while following sym links.
+//It solves the issue where your WalkFunc needs a path relative to the symbolic link
+//(resolving links within walkfunc loses the path to the symbolic link for each traversal).
+func Walk(path string, followSymlinks bool, detectSymlinkInfiniteLoop bool, walkFn WalkFunc) error {
+	info, err := os.Lstat(path)
+	if err != nil {
+		return err
+	}
+	var symlinkPathsFollowed map[string]bool
+	var resolvedPath string
+	if followSymlinks {
+		resolvedPath = path
+		if detectSymlinkInfiniteLoop {
+			symlinkPathsFollowed = make(map[string]bool, 8)
+		}
+	}
+	return walk(path, info, resolvedPath, symlinkPathsFollowed, walkFn)
+}
+
+//walk walks the path. It is a helper/sibling function to Walk.
+//It takes a resolvedPath into consideration. This way, paths being walked are
+//always relative to the path argument, even if symbolic links were resolved).
+//
+//If resolvedPath is "", then we are not following symbolic links.
+//If symlinkPathsFollowed is not nil, then we need to detect infinite loop.
+func walk(path string, info os.FileInfo, resolvedPath string,
+	symlinkPathsFollowed map[string]bool, walkFn WalkFunc) error {
+	if info == nil {
+		return errors.New("Walk: Nil FileInfo passed")
+	}
+	err := walkFn(resolvedPath, info, nil)
+	if err != nil {
+		if info.IsDir() && err == WalkSkipDir {
+			err = nil
+		}
+		return err
+	}
+	if resolvedPath != "" && info.Mode()&os.ModeSymlink == os.ModeSymlink {
+		path2, err := os.Readlink(resolvedPath)
+		if err != nil {
+			return err
+		}
+		//vout("SymLink Path: %v, links to: %v", resolvedPath, path2)
+		if symlinkPathsFollowed != nil {
+			if _, ok := symlinkPathsFollowed[path2]; ok {
+				errMsg := "Potential SymLink Infinite Loop. Path: %v, Link To: %v"
+				return fmt.Errorf(errMsg, resolvedPath, path2)
+			} else {
+				symlinkPathsFollowed[path2] = true
+			}
+		}
+		info2, err := os.Lstat(path2)
+		if err != nil {
+			return err
+		}
+		return walk(path, info2, path2, symlinkPathsFollowed, walkFn)
+	}
+	if info.IsDir() {
+		list, err := ioutil.ReadDir(path)
+		if err != nil {
+			return walkFn(resolvedPath, info, err)
+		}
+		for _, fileInfo := range list {
+			path2 := filepath.Join(path, fileInfo.Name())
+			var resolvedPath2 string
+			if resolvedPath != "" {
+				resolvedPath2 = filepath.Join(resolvedPath, fileInfo.Name())
+			}
+			err = walk(path2, fileInfo, resolvedPath2, symlinkPathsFollowed, walkFn)
+			if err != nil {
+				return err
+			}
+		}
+		return nil
+	}
+	return nil
+}