github.com/michael-k/docker@v1.7.0-rc2/pkg/fileutils/fileutils.go (about) 1 package fileutils 2 3 import ( 4 "errors" 5 "fmt" 6 "io" 7 "io/ioutil" 8 "os" 9 "path/filepath" 10 "strings" 11 12 "github.com/Sirupsen/logrus" 13 ) 14 15 func Exclusion(pattern string) bool { 16 return pattern[0] == '!' 17 } 18 19 func Empty(pattern string) bool { 20 return pattern == "" 21 } 22 23 // Cleanpatterns takes a slice of patterns returns a new 24 // slice of patterns cleaned with filepath.Clean, stripped 25 // of any empty patterns and lets the caller know whether the 26 // slice contains any exception patterns (prefixed with !). 27 func CleanPatterns(patterns []string) ([]string, [][]string, bool, error) { 28 // Loop over exclusion patterns and: 29 // 1. Clean them up. 30 // 2. Indicate whether we are dealing with any exception rules. 31 // 3. Error if we see a single exclusion marker on it's own (!). 32 cleanedPatterns := []string{} 33 patternDirs := [][]string{} 34 exceptions := false 35 for _, pattern := range patterns { 36 // Eliminate leading and trailing whitespace. 37 pattern = strings.TrimSpace(pattern) 38 if Empty(pattern) { 39 continue 40 } 41 if Exclusion(pattern) { 42 if len(pattern) == 1 { 43 logrus.Errorf("Illegal exclusion pattern: %s", pattern) 44 return nil, nil, false, errors.New("Illegal exclusion pattern: !") 45 } 46 exceptions = true 47 } 48 pattern = filepath.Clean(pattern) 49 cleanedPatterns = append(cleanedPatterns, pattern) 50 if Exclusion(pattern) { 51 pattern = pattern[1:] 52 } 53 patternDirs = append(patternDirs, strings.Split(pattern, "/")) 54 } 55 56 return cleanedPatterns, patternDirs, exceptions, nil 57 } 58 59 // Matches returns true if file matches any of the patterns 60 // and isn't excluded by any of the subsequent patterns. 61 func Matches(file string, patterns []string) (bool, error) { 62 file = filepath.Clean(file) 63 64 if file == "." { 65 // Don't let them exclude everything, kind of silly. 66 return false, nil 67 } 68 69 patterns, patDirs, _, err := CleanPatterns(patterns) 70 if err != nil { 71 return false, err 72 } 73 74 return OptimizedMatches(file, patterns, patDirs) 75 } 76 77 // Matches is basically the same as fileutils.Matches() but optimized for archive.go. 78 // It will assume that the inputs have been preprocessed and therefore the function 79 // doen't need to do as much error checking and clean-up. This was done to avoid 80 // repeating these steps on each file being checked during the archive process. 81 // The more generic fileutils.Matches() can't make these assumptions. 82 func OptimizedMatches(file string, patterns []string, patDirs [][]string) (bool, error) { 83 matched := false 84 parentPath := filepath.Dir(file) 85 parentPathDirs := strings.Split(parentPath, "/") 86 87 for i, pattern := range patterns { 88 negative := false 89 90 if Exclusion(pattern) { 91 negative = true 92 pattern = pattern[1:] 93 } 94 95 match, err := filepath.Match(pattern, file) 96 if err != nil { 97 logrus.Errorf("Error matching: %s (pattern: %s)", file, pattern) 98 return false, err 99 } 100 101 if !match && parentPath != "." { 102 // Check to see if the pattern matches one of our parent dirs. 103 if len(patDirs[i]) <= len(parentPathDirs) { 104 match, _ = filepath.Match(strings.Join(patDirs[i], "/"), 105 strings.Join(parentPathDirs[:len(patDirs[i])], "/")) 106 } 107 } 108 109 if match { 110 matched = !negative 111 } 112 } 113 114 if matched { 115 logrus.Debugf("Skipping excluded path: %s", file) 116 } 117 return matched, nil 118 } 119 120 func CopyFile(src, dst string) (int64, error) { 121 cleanSrc := filepath.Clean(src) 122 cleanDst := filepath.Clean(dst) 123 if cleanSrc == cleanDst { 124 return 0, nil 125 } 126 sf, err := os.Open(cleanSrc) 127 if err != nil { 128 return 0, err 129 } 130 defer sf.Close() 131 if err := os.Remove(cleanDst); err != nil && !os.IsNotExist(err) { 132 return 0, err 133 } 134 df, err := os.Create(cleanDst) 135 if err != nil { 136 return 0, err 137 } 138 defer df.Close() 139 return io.Copy(df, sf) 140 } 141 142 func GetTotalUsedFds() int { 143 if fds, err := ioutil.ReadDir(fmt.Sprintf("/proc/%d/fd", os.Getpid())); err != nil { 144 logrus.Errorf("Error opening /proc/%d/fd: %s", os.Getpid(), err) 145 } else { 146 return len(fds) 147 } 148 return -1 149 } 150 151 // ReadSymlinkedDirectory returns the target directory of a symlink. 152 // The target of the symbolic link may not be a file. 153 func ReadSymlinkedDirectory(path string) (string, error) { 154 var realPath string 155 var err error 156 if realPath, err = filepath.Abs(path); err != nil { 157 return "", fmt.Errorf("unable to get absolute path for %s: %s", path, err) 158 } 159 if realPath, err = filepath.EvalSymlinks(realPath); err != nil { 160 return "", fmt.Errorf("failed to canonicalise path for %s: %s", path, err) 161 } 162 realPathInfo, err := os.Stat(realPath) 163 if err != nil { 164 return "", fmt.Errorf("failed to stat target '%s' of '%s': %s", realPath, path, err) 165 } 166 if !realPathInfo.Mode().IsDir() { 167 return "", fmt.Errorf("canonical path points to a file '%s'", realPath) 168 } 169 return realPath, nil 170 }