github.com/rentongzhang/docker@v1.8.2-rc1/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 // exclusion return true if the specified pattern is an exclusion 16 func exclusion(pattern string) bool { 17 return pattern[0] == '!' 18 } 19 20 // empty return true if the specified pattern is empty 21 func empty(pattern string) bool { 22 return pattern == "" 23 } 24 25 // CleanPatterns takes a slice of patterns returns a new 26 // slice of patterns cleaned with filepath.Clean, stripped 27 // of any empty patterns and lets the caller know whether the 28 // slice contains any exception patterns (prefixed with !). 29 func CleanPatterns(patterns []string) ([]string, [][]string, bool, error) { 30 // Loop over exclusion patterns and: 31 // 1. Clean them up. 32 // 2. Indicate whether we are dealing with any exception rules. 33 // 3. Error if we see a single exclusion marker on it's own (!). 34 cleanedPatterns := []string{} 35 patternDirs := [][]string{} 36 exceptions := false 37 for _, pattern := range patterns { 38 // Eliminate leading and trailing whitespace. 39 pattern = strings.TrimSpace(pattern) 40 if empty(pattern) { 41 continue 42 } 43 if exclusion(pattern) { 44 if len(pattern) == 1 { 45 return nil, nil, false, errors.New("Illegal exclusion pattern: !") 46 } 47 exceptions = true 48 } 49 pattern = filepath.Clean(pattern) 50 cleanedPatterns = append(cleanedPatterns, pattern) 51 if exclusion(pattern) { 52 pattern = pattern[1:] 53 } 54 patternDirs = append(patternDirs, strings.Split(pattern, "/")) 55 } 56 57 return cleanedPatterns, patternDirs, exceptions, nil 58 } 59 60 // Matches returns true if file matches any of the patterns 61 // and isn't excluded by any of the subsequent patterns. 62 func Matches(file string, patterns []string) (bool, error) { 63 file = filepath.Clean(file) 64 65 if file == "." { 66 // Don't let them exclude everything, kind of silly. 67 return false, nil 68 } 69 70 patterns, patDirs, _, err := CleanPatterns(patterns) 71 if err != nil { 72 return false, err 73 } 74 75 return OptimizedMatches(file, patterns, patDirs) 76 } 77 78 // OptimizedMatches is basically the same as fileutils.Matches() but optimized for archive.go. 79 // It will assume that the inputs have been preprocessed and therefore the function 80 // doen't need to do as much error checking and clean-up. This was done to avoid 81 // repeating these steps on each file being checked during the archive process. 82 // The more generic fileutils.Matches() can't make these assumptions. 83 func OptimizedMatches(file string, patterns []string, patDirs [][]string) (bool, error) { 84 matched := false 85 parentPath := filepath.Dir(file) 86 parentPathDirs := strings.Split(parentPath, "/") 87 88 for i, pattern := range patterns { 89 negative := false 90 91 if exclusion(pattern) { 92 negative = true 93 pattern = pattern[1:] 94 } 95 96 match, err := filepath.Match(pattern, file) 97 if err != nil { 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 118 return matched, nil 119 } 120 121 // CopyFile copies from src to dst until either EOF is reached 122 // on src or an error occurs. It verifies src exists and remove 123 // the dst if it exists. 124 func CopyFile(src, dst string) (int64, error) { 125 cleanSrc := filepath.Clean(src) 126 cleanDst := filepath.Clean(dst) 127 if cleanSrc == cleanDst { 128 return 0, nil 129 } 130 sf, err := os.Open(cleanSrc) 131 if err != nil { 132 return 0, err 133 } 134 defer sf.Close() 135 if err := os.Remove(cleanDst); err != nil && !os.IsNotExist(err) { 136 return 0, err 137 } 138 df, err := os.Create(cleanDst) 139 if err != nil { 140 return 0, err 141 } 142 defer df.Close() 143 return io.Copy(df, sf) 144 } 145 146 // GetTotalUsedFds Returns the number of used File Descriptors by 147 // reading it via /proc filesystem. 148 func GetTotalUsedFds() int { 149 if fds, err := ioutil.ReadDir(fmt.Sprintf("/proc/%d/fd", os.Getpid())); err != nil { 150 logrus.Errorf("Error opening /proc/%d/fd: %s", os.Getpid(), err) 151 } else { 152 return len(fds) 153 } 154 return -1 155 } 156 157 // ReadSymlinkedDirectory returns the target directory of a symlink. 158 // The target of the symbolic link may not be a file. 159 func ReadSymlinkedDirectory(path string) (string, error) { 160 var realPath string 161 var err error 162 if realPath, err = filepath.Abs(path); err != nil { 163 return "", fmt.Errorf("unable to get absolute path for %s: %s", path, err) 164 } 165 if realPath, err = filepath.EvalSymlinks(realPath); err != nil { 166 return "", fmt.Errorf("failed to canonicalise path for %s: %s", path, err) 167 } 168 realPathInfo, err := os.Stat(realPath) 169 if err != nil { 170 return "", fmt.Errorf("failed to stat target '%s' of '%s': %s", realPath, path, err) 171 } 172 if !realPathInfo.Mode().IsDir() { 173 return "", fmt.Errorf("canonical path points to a file '%s'", realPath) 174 } 175 return realPath, nil 176 } 177 178 // CreateIfNotExists creates a file or a directory only if it does not already exist. 179 func CreateIfNotExists(path string, isDir bool) error { 180 if _, err := os.Stat(path); err != nil { 181 if os.IsNotExist(err) { 182 if isDir { 183 return os.MkdirAll(path, 0755) 184 } 185 if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil { 186 return err 187 } 188 f, err := os.OpenFile(path, os.O_CREATE, 0755) 189 if err != nil { 190 return err 191 } 192 f.Close() 193 } 194 } 195 return nil 196 }