github.com/chenchun/docker@v1.3.2-0.20150629222414-20467faf132b/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 return nil, nil, false, errors.New("Illegal exclusion pattern: !") 44 } 45 exceptions = true 46 } 47 pattern = filepath.Clean(pattern) 48 cleanedPatterns = append(cleanedPatterns, pattern) 49 if Exclusion(pattern) { 50 pattern = pattern[1:] 51 } 52 patternDirs = append(patternDirs, strings.Split(pattern, "/")) 53 } 54 55 return cleanedPatterns, patternDirs, exceptions, nil 56 } 57 58 // Matches returns true if file matches any of the patterns 59 // and isn't excluded by any of the subsequent patterns. 60 func Matches(file string, patterns []string) (bool, error) { 61 file = filepath.Clean(file) 62 63 if file == "." { 64 // Don't let them exclude everything, kind of silly. 65 return false, nil 66 } 67 68 patterns, patDirs, _, err := CleanPatterns(patterns) 69 if err != nil { 70 return false, err 71 } 72 73 return OptimizedMatches(file, patterns, patDirs) 74 } 75 76 // OptimizedMatches is basically the same as fileutils.Matches() but optimized for archive.go. 77 // It will assume that the inputs have been preprocessed and therefore the function 78 // doen't need to do as much error checking and clean-up. This was done to avoid 79 // repeating these steps on each file being checked during the archive process. 80 // The more generic fileutils.Matches() can't make these assumptions. 81 func OptimizedMatches(file string, patterns []string, patDirs [][]string) (bool, error) { 82 matched := false 83 parentPath := filepath.Dir(file) 84 parentPathDirs := strings.Split(parentPath, "/") 85 86 for i, pattern := range patterns { 87 negative := false 88 89 if Exclusion(pattern) { 90 negative = true 91 pattern = pattern[1:] 92 } 93 94 match, err := filepath.Match(pattern, file) 95 if err != nil { 96 return false, err 97 } 98 99 if !match && parentPath != "." { 100 // Check to see if the pattern matches one of our parent dirs. 101 if len(patDirs[i]) <= len(parentPathDirs) { 102 match, _ = filepath.Match(strings.Join(patDirs[i], "/"), 103 strings.Join(parentPathDirs[:len(patDirs[i])], "/")) 104 } 105 } 106 107 if match { 108 matched = !negative 109 } 110 } 111 112 if matched { 113 logrus.Debugf("Skipping excluded path: %s", file) 114 } 115 116 return matched, nil 117 } 118 119 func CopyFile(src, dst string) (int64, error) { 120 cleanSrc := filepath.Clean(src) 121 cleanDst := filepath.Clean(dst) 122 if cleanSrc == cleanDst { 123 return 0, nil 124 } 125 sf, err := os.Open(cleanSrc) 126 if err != nil { 127 return 0, err 128 } 129 defer sf.Close() 130 if err := os.Remove(cleanDst); err != nil && !os.IsNotExist(err) { 131 return 0, err 132 } 133 df, err := os.Create(cleanDst) 134 if err != nil { 135 return 0, err 136 } 137 defer df.Close() 138 return io.Copy(df, sf) 139 } 140 141 func GetTotalUsedFds() int { 142 if fds, err := ioutil.ReadDir(fmt.Sprintf("/proc/%d/fd", os.Getpid())); err != nil { 143 logrus.Errorf("Error opening /proc/%d/fd: %s", os.Getpid(), err) 144 } else { 145 return len(fds) 146 } 147 return -1 148 } 149 150 // ReadSymlinkedDirectory returns the target directory of a symlink. 151 // The target of the symbolic link may not be a file. 152 func ReadSymlinkedDirectory(path string) (string, error) { 153 var realPath string 154 var err error 155 if realPath, err = filepath.Abs(path); err != nil { 156 return "", fmt.Errorf("unable to get absolute path for %s: %s", path, err) 157 } 158 if realPath, err = filepath.EvalSymlinks(realPath); err != nil { 159 return "", fmt.Errorf("failed to canonicalise path for %s: %s", path, err) 160 } 161 realPathInfo, err := os.Stat(realPath) 162 if err != nil { 163 return "", fmt.Errorf("failed to stat target '%s' of '%s': %s", realPath, path, err) 164 } 165 if !realPathInfo.Mode().IsDir() { 166 return "", fmt.Errorf("canonical path points to a file '%s'", realPath) 167 } 168 return realPath, nil 169 }