forked from grafana/grafana
-
Notifications
You must be signed in to change notification settings - Fork 0
/
filepath.go
131 lines (119 loc) · 3.66 KB
/
filepath.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
package util
import (
"errors"
"fmt"
"io/ioutil"
"os"
"path/filepath"
)
//ErrWalkSkipDir is the Error returned when we want to skip descending into a directory
var ErrWalkSkipDir = 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 == ErrWalkSkipDir {
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)
}
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)
}
var subFiles = make([]subFile, 0)
for _, fileInfo := range list {
path2 := filepath.Join(path, fileInfo.Name())
var resolvedPath2 string
if resolvedPath != "" {
resolvedPath2 = filepath.Join(resolvedPath, fileInfo.Name())
}
subFiles = append(subFiles, subFile{path: path2, resolvedPath: resolvedPath2, fileInfo: fileInfo})
}
if containsDistFolder(subFiles) {
err := walk(
filepath.Join(path, "dist"),
info,
filepath.Join(resolvedPath, "dist"),
symlinkPathsFollowed,
walkFn)
if err != nil {
return err
}
} else {
for _, p := range subFiles {
err = walk(p.path, p.fileInfo, p.resolvedPath, symlinkPathsFollowed, walkFn)
if err != nil {
return err
}
}
}
return nil
}
return nil
}
type subFile struct {
path, resolvedPath string
fileInfo os.FileInfo
}
func containsDistFolder(subFiles []subFile) bool {
for _, p := range subFiles {
if p.fileInfo.IsDir() && p.fileInfo.Name() == "dist" {
return true
}
}
return false
}